Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 56 additions & 2 deletions packages/loopx-finance-value-discovery/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,62 @@ The packet enforces:

It rejects raw provider bodies, private paths, credentials, account or
portfolio material, future-dated evidence, unsupported fields, and malformed
public URLs. It never emits investment advice, a price target, a trade, or an
automatic watch.
public URLs. The discovery reducer never emits investment advice, a price
target, a trade, or an automatic watch.

## Simulation-only transaction confirmation / 仅模拟交易确认

Extension 0.7.0 adds the
`finance_transaction_approval_input_v0` consumer, deliberately separate from
the discovery reducer. It turns an explicit, already-bounded candidate action
into Core's canonical `loopx_operation_request_v0`. The request freezes the
order action, public evidence links, evidence observation time, expected
economics, expiry, and no-trade conditions into the same projection shown by
the Dashboard and the Goal Channel Lark card. It always targets
`account:simulation` and the `finance.operation.simulate` executor permission.
It cannot place an order, sign, transfer, infer an approver, or create standing
trading authority.

0.7.0 新增的 `finance_transaction_approval_input_v0` consumer 与发现 reducer
明确隔离。它只把一个已经收敛、显式提出的候选动作转换成 Core 的
`loopx_operation_request_v0`,并把下单动作、公开证据链接、证据观察时间、
预期经济性、过期时间和禁交易条件冻结进 Dashboard 与 Goal Channel 飞书卡片
共用的投影。目标固定为 `account:simulation`,权限固定为
`finance.operation.simulate`;它不能真实下单、签名、转账、推断审批人或产生
持续交易权限。

The executor revision must come from the enabled `loopx-finance-execution`
extension readback. Authorized principals must be explicit provider-qualified
identities from the current Goal Channel operator authority; the Finance builder
neither discovers nor broadens them. Build the packet once, retain both its
idempotency key and canonical request, then let Core persist and deliver it
through the existing two-step entrypoint:

```bash
loopx-finance-value-discovery build-operation-request \
--input-json transaction-approval.json > approval-packet.json
jq '.operation_request' approval-packet.json > operation-request.json
loopx goal-channel prepare-operation \
--goal-id <goal> --agent-id <agent> \
--summary "Review one simulated finance transaction" \
--idempotency-key "$(jq -r '.idempotency_key' approval-packet.json)" \
--request-json operation-request.json --execute --format json
loopx goal-channel deliver-operation \
--goal-id <goal> --proposal-id <operation-id> --execute --format json
```

`prepare-operation` only writes Core's canonical proposal; `deliver-operation`
projects that same proposal to the bound Lark group. The Dashboard reads the
same safe projection. Confirm/reject callbacks remain Core-owned and produce an
idempotent receipt. Managed Turn callers may submit the input schema to the
extension runtime and select `operation_request` from its returned packet.

`executor_revision` 必须来自已启用 `loopx-finance-execution` 的真实 readback;
审批人必须来自当前 Goal Channel 操作权限,并以 provider-qualified principal
显式传入,Finance builder 不发现或扩大审批人范围。两者都不会被猜测。
`prepare-operation` 只写入 Core 的 canonical proposal,`deliver-operation` 再将
同一 proposal 投影到 Goal 绑定群;Dashboard 也读取同一安全投影。确认/拒绝
callback 与幂等 receipt 继续由 Core 负责。

## Public-Safe Research Surface

Expand Down
2 changes: 1 addition & 1 deletion packages/loopx-finance-value-discovery/extension.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
schema_version = "loopx_extension_manifest_v0"
id = "loopx-finance-value-discovery"
version = "0.6.0"
version = "0.7.0"
requires_loopx_api = ">=1,<2"
permissions = []

Expand Down
2 changes: 1 addition & 1 deletion packages/loopx-finance-value-discovery/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "loopx-finance-value-discovery"
version = "0.6.0"
version = "0.7.0"
description = "Public-safe finance value-discovery extension for LoopX."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
list_finance_metric_packs,
replay_finance_metric_pack_evaluation,
)
from .operation_request import (
FINANCE_TRANSACTION_APPROVAL_INPUT_SCHEMA_VERSION,
FINANCE_TRANSACTION_APPROVAL_PACKET_SCHEMA_VERSION,
build_finance_transaction_approval_packet,
)
from .reducer import (
EVIDENCE_AXES,
FINANCE_VALUE_DISCOVERY_CARD_SCHEMA_VERSION,
Expand Down Expand Up @@ -55,6 +60,8 @@
"FINANCE_METRIC_PACK_REPLAY_SCHEMA_VERSION",
"FINANCE_RESEARCH_DASHBOARD_INPUT_SCHEMA_VERSION",
"FINANCE_RESEARCH_DASHBOARD_PACKET_SCHEMA_VERSION",
"FINANCE_TRANSACTION_APPROVAL_INPUT_SCHEMA_VERSION",
"FINANCE_TRANSACTION_APPROVAL_PACKET_SCHEMA_VERSION",
"FINANCE_VALUE_DISCOVERY_CARD_SCHEMA_VERSION",
"FINANCE_VALUE_DISCOVERY_EXTENSION_PROTOCOL",
"FINANCE_VALUE_DISCOVERY_INPUT_SCHEMA_VERSION",
Expand All @@ -63,6 +70,7 @@
"build_finance_case_evaluation",
"build_finance_metric_pack_evaluation",
"build_finance_research_dashboard_packet",
"build_finance_transaction_approval_packet",
"build_finance_value_discovery_packet",
"evaluate_finance_case_gates",
"list_finance_metric_packs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
replay_finance_case_evaluation,
)
from .presentation_compat import presentation_api_error
from .operation_request import (
FINANCE_TRANSACTION_APPROVAL_INPUT_SCHEMA_VERSION,
build_finance_transaction_approval_packet,
)


FINANCE_RESEARCH_DASHBOARD_INPUT_SCHEMA_VERSION = "finance_research_dashboard_input_v0"
Expand Down Expand Up @@ -120,6 +124,19 @@ def _direct_parser() -> argparse.ArgumentParser:
)
pack_replay_parser.add_argument("--input-json", required=True)
pack_replay_parser.add_argument("--expected-json", required=True)
operation_parser = sub.add_parser(
"build-operation-request",
help=(
"Build one simulation-only finance request for LoopX Goal Channel "
"confirmation."
),
)
operation_parser.add_argument("--input-json", required=True)
operation_parser.add_argument(
"--request-only",
action="store_true",
help="Print only the canonical loopx_operation_request_v0 object.",
)
sub.add_parser("list-packs", help="List bundled industry metric packs.")
lark_parser = sub.add_parser(
"render-lark-card",
Expand Down Expand Up @@ -151,6 +168,8 @@ def run(argv: Sequence[str] | None = None) -> int:
from .dashboard import build_finance_research_dashboard_packet

packet = build_finance_research_dashboard_packet(payload)
elif schema_version == FINANCE_TRANSACTION_APPROVAL_INPUT_SCHEMA_VERSION:
packet = build_finance_transaction_approval_packet(payload)
else:
packet = build_finance_value_discovery_packet(payload)
except Exception as exc:
Expand Down Expand Up @@ -189,6 +208,10 @@ def run(argv: Sequence[str] | None = None) -> int:
_load_json(args.input_json),
_load_json(args.expected_json),
)
elif args.command == "build-operation-request":
packet = build_finance_transaction_approval_packet(
_load_json(args.input_json)
)
elif args.command == "list-packs":
packet = list_finance_metric_packs()
elif args.command == "render-lark-card":
Expand All @@ -204,13 +227,15 @@ def run(argv: Sequence[str] | None = None) -> int:
else:
raise ValueError(
"use --doctor, reduce, evaluate, replay, attribute-beta, "
"replay-beta, evaluate-pack, replay-pack, list-packs, or "
"render-lark-card"
"replay-beta, evaluate-pack, replay-pack, list-packs, "
"render-lark-card, or build-operation-request"
)
except Exception as exc:
print(json.dumps(_error_packet(exc), indent=2, sort_keys=True))
return 1
if args.command != "reduce" or args.format == "json":
if args.command == "build-operation-request" and args.request_only:
print(json.dumps(packet["operation_request"], indent=2, sort_keys=True))
elif args.command != "reduce" or args.format == "json":
print(json.dumps(packet, indent=2, sort_keys=True))
else:
print(render_finance_value_discovery_markdown(packet), end="")
Expand Down
Loading