@harrylabsj/kiwi 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +150 -0
- package/contracts/candidate-agent-dto-1.0.README.md +39 -0
- package/contracts/candidate-agent-dto-1.0.schema.json +172 -0
- package/contracts/interop/kiwi-buyer-message-send.json +43 -0
- package/contracts/interop/knp-data-part-examples.json +243 -0
- package/contracts/kiwi-catalog/1.0/agent-record.schema.json +78 -0
- package/contracts/kiwi-catalog/1.0/listing-record.schema.json +88 -0
- package/contracts/kiwi-catalog/1.0/listing-search-result.schema.json +60 -0
- package/contracts/negotiation/1.0/schema.json +501 -0
- package/contracts/shopping.negotiation/0.1/capabilities.schema.json +47 -0
- package/contracts/shopping.negotiation/0.1/decision.schema.json +120 -0
- package/contracts/shopping.negotiation/0.1/policy-result.schema.json +52 -0
- package/contracts/shopping.negotiation/0.1/snapshot.schema.json +172 -0
- package/dist/a2a/client/client.d.ts +33 -0
- package/dist/a2a/client/client.js +161 -0
- package/dist/a2a/client/client.js.map +1 -0
- package/dist/a2a/client/error.d.ts +35 -0
- package/dist/a2a/client/error.js +34 -0
- package/dist/a2a/client/error.js.map +1 -0
- package/dist/a2a/client/index.d.ts +29 -0
- package/dist/a2a/client/index.js +26 -0
- package/dist/a2a/client/index.js.map +1 -0
- package/dist/a2a/client/jsonrpc.d.ts +49 -0
- package/dist/a2a/client/jsonrpc.js +85 -0
- package/dist/a2a/client/jsonrpc.js.map +1 -0
- package/dist/a2a/client/parse.d.ts +18 -0
- package/dist/a2a/client/parse.js +134 -0
- package/dist/a2a/client/parse.js.map +1 -0
- package/dist/a2a/client/types.d.ts +102 -0
- package/dist/a2a/client/types.js +26 -0
- package/dist/a2a/client/types.js.map +1 -0
- package/dist/a2a/client/url-policy.d.ts +44 -0
- package/dist/a2a/client/url-policy.js +242 -0
- package/dist/a2a/client/url-policy.js.map +1 -0
- package/dist/a2a/negotiate.d.ts +68 -0
- package/dist/a2a/negotiate.js +288 -0
- package/dist/a2a/negotiate.js.map +1 -0
- package/dist/a2a/node.d.ts +37 -0
- package/dist/a2a/node.js +155 -0
- package/dist/a2a/node.js.map +1 -0
- package/dist/a2a/server/auth.d.ts +38 -0
- package/dist/a2a/server/auth.js +112 -0
- package/dist/a2a/server/auth.js.map +1 -0
- package/dist/a2a/server/card.d.ts +19 -0
- package/dist/a2a/server/card.js +81 -0
- package/dist/a2a/server/card.js.map +1 -0
- package/dist/a2a/server/errors.d.ts +109 -0
- package/dist/a2a/server/errors.js +178 -0
- package/dist/a2a/server/errors.js.map +1 -0
- package/dist/a2a/server/handler.d.ts +22 -0
- package/dist/a2a/server/handler.js +57 -0
- package/dist/a2a/server/handler.js.map +1 -0
- package/dist/a2a/server/inbound-message.d.ts +37 -0
- package/dist/a2a/server/inbound-message.js +104 -0
- package/dist/a2a/server/inbound-message.js.map +1 -0
- package/dist/a2a/server/index.d.ts +43 -0
- package/dist/a2a/server/index.js +42 -0
- package/dist/a2a/server/index.js.map +1 -0
- package/dist/a2a/server/merchant-handler.d.ts +69 -0
- package/dist/a2a/server/merchant-handler.js +316 -0
- package/dist/a2a/server/merchant-handler.js.map +1 -0
- package/dist/a2a/server/pipeline.d.ts +69 -0
- package/dist/a2a/server/pipeline.js +329 -0
- package/dist/a2a/server/pipeline.js.map +1 -0
- package/dist/a2a/server/server.d.ts +64 -0
- package/dist/a2a/server/server.js +420 -0
- package/dist/a2a/server/server.js.map +1 -0
- package/dist/a2a/server/task-registry.d.ts +31 -0
- package/dist/a2a/server/task-registry.js +67 -0
- package/dist/a2a/server/task-registry.js.map +1 -0
- package/dist/a2a/server/throttle.d.ts +154 -0
- package/dist/a2a/server/throttle.js +289 -0
- package/dist/a2a/server/throttle.js.map +1 -0
- package/dist/a2a/server/types.d.ts +191 -0
- package/dist/a2a/server/types.js +17 -0
- package/dist/a2a/server/types.js.map +1 -0
- package/dist/a2a/server/ucp.d.ts +49 -0
- package/dist/a2a/server/ucp.js +85 -0
- package/dist/a2a/server/ucp.js.map +1 -0
- package/dist/a2a/task/index.d.ts +24 -0
- package/dist/a2a/task/index.js +22 -0
- package/dist/a2a/task/index.js.map +1 -0
- package/dist/a2a/task/ledger.d.ts +48 -0
- package/dist/a2a/task/ledger.js +65 -0
- package/dist/a2a/task/ledger.js.map +1 -0
- package/dist/a2a/task/poller.d.ts +93 -0
- package/dist/a2a/task/poller.js +184 -0
- package/dist/a2a/task/poller.js.map +1 -0
- package/dist/a2a/task/state.d.ts +72 -0
- package/dist/a2a/task/state.js +119 -0
- package/dist/a2a/task/state.js.map +1 -0
- package/dist/a2a/ucp-agent.d.ts +49 -0
- package/dist/a2a/ucp-agent.js +78 -0
- package/dist/a2a/ucp-agent.js.map +1 -0
- package/dist/agent/agent-db.d.ts +31 -0
- package/dist/agent/agent-db.js +91 -0
- package/dist/agent/agent-db.js.map +1 -0
- package/dist/agent/buyer/buyer-tools.d.ts +70 -0
- package/dist/agent/buyer/buyer-tools.js +1353 -0
- package/dist/agent/buyer/buyer-tools.js.map +1 -0
- package/dist/agent/buyer/ranker.d.ts +53 -0
- package/dist/agent/buyer/ranker.js +153 -0
- package/dist/agent/buyer/ranker.js.map +1 -0
- package/dist/agent/buyer/scheduler.d.ts +58 -0
- package/dist/agent/buyer/scheduler.js +317 -0
- package/dist/agent/buyer/scheduler.js.map +1 -0
- package/dist/agent/buyer/search-loop.d.ts +42 -0
- package/dist/agent/buyer/search-loop.js +280 -0
- package/dist/agent/buyer/search-loop.js.map +1 -0
- package/dist/agent/buyer/task-store.d.ts +178 -0
- package/dist/agent/buyer/task-store.js +637 -0
- package/dist/agent/buyer/task-store.js.map +1 -0
- package/dist/agent/buyer/types.d.ts +222 -0
- package/dist/agent/buyer/types.js +124 -0
- package/dist/agent/buyer/types.js.map +1 -0
- package/dist/agent/chat-tools.d.ts +41 -0
- package/dist/agent/chat-tools.js +193 -0
- package/dist/agent/chat-tools.js.map +1 -0
- package/dist/agent/chat-tui.d.ts +49 -0
- package/dist/agent/chat-tui.js +314 -0
- package/dist/agent/chat-tui.js.map +1 -0
- package/dist/agent/connector/fake-connector.d.ts +50 -0
- package/dist/agent/connector/fake-connector.js +138 -0
- package/dist/agent/connector/fake-connector.js.map +1 -0
- package/dist/agent/connector/http-connector.d.ts +49 -0
- package/dist/agent/connector/http-connector.js +208 -0
- package/dist/agent/connector/http-connector.js.map +1 -0
- package/dist/agent/connector/types.d.ts +103 -0
- package/dist/agent/connector/types.js +97 -0
- package/dist/agent/connector/types.js.map +1 -0
- package/dist/agent/fake-chat-model.d.ts +22 -0
- package/dist/agent/fake-chat-model.js +134 -0
- package/dist/agent/fake-chat-model.js.map +1 -0
- package/dist/agent/kernel.d.ts +258 -0
- package/dist/agent/kernel.js +1099 -0
- package/dist/agent/kernel.js.map +1 -0
- package/dist/agent/memory/schema.d.ts +32 -0
- package/dist/agent/memory/schema.js +291 -0
- package/dist/agent/memory/schema.js.map +1 -0
- package/dist/agent/memory/store.d.ts +193 -0
- package/dist/agent/memory/store.js +881 -0
- package/dist/agent/memory/store.js.map +1 -0
- package/dist/agent/memory/types.d.ts +139 -0
- package/dist/agent/memory/types.js +119 -0
- package/dist/agent/memory/types.js.map +1 -0
- package/dist/agent/memory/vault.d.ts +49 -0
- package/dist/agent/memory/vault.js +125 -0
- package/dist/agent/memory/vault.js.map +1 -0
- package/dist/agent/merchant/action-candidate.d.ts +106 -0
- package/dist/agent/merchant/action-candidate.js +241 -0
- package/dist/agent/merchant/action-candidate.js.map +1 -0
- package/dist/agent/merchant/credential-broker.d.ts +67 -0
- package/dist/agent/merchant/credential-broker.js +76 -0
- package/dist/agent/merchant/credential-broker.js.map +1 -0
- package/dist/agent/merchant/fake-merchant-client.d.ts +47 -0
- package/dist/agent/merchant/fake-merchant-client.js +143 -0
- package/dist/agent/merchant/fake-merchant-client.js.map +1 -0
- package/dist/agent/merchant/merchant-client.d.ts +49 -0
- package/dist/agent/merchant/merchant-client.js +186 -0
- package/dist/agent/merchant/merchant-client.js.map +1 -0
- package/dist/agent/merchant/merchant-tools.d.ts +74 -0
- package/dist/agent/merchant/merchant-tools.js +582 -0
- package/dist/agent/merchant/merchant-tools.js.map +1 -0
- package/dist/agent/merchant/types.d.ts +119 -0
- package/dist/agent/merchant/types.js +133 -0
- package/dist/agent/merchant/types.js.map +1 -0
- package/dist/agent/mode.d.ts +32 -0
- package/dist/agent/mode.js +34 -0
- package/dist/agent/mode.js.map +1 -0
- package/dist/agent/negotiation-chat.d.ts +81 -0
- package/dist/agent/negotiation-chat.js +330 -0
- package/dist/agent/negotiation-chat.js.map +1 -0
- package/dist/agent/session.d.ts +22 -0
- package/dist/agent/session.js +164 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/agent/system-prompt.d.ts +26 -0
- package/dist/agent/system-prompt.js +77 -0
- package/dist/agent/system-prompt.js.map +1 -0
- package/dist/agent/write-gate.d.ts +98 -0
- package/dist/agent/write-gate.js +76 -0
- package/dist/agent/write-gate.js.map +1 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +1139 -0
- package/dist/cli.js.map +1 -0
- package/dist/commerce/data-source.d.ts +88 -0
- package/dist/commerce/data-source.js +25 -0
- package/dist/commerce/data-source.js.map +1 -0
- package/dist/commerce/erp-source.d.ts +51 -0
- package/dist/commerce/erp-source.js +184 -0
- package/dist/commerce/erp-source.js.map +1 -0
- package/dist/commerce/fake-client.d.ts +167 -0
- package/dist/commerce/fake-client.js +549 -0
- package/dist/commerce/fake-client.js.map +1 -0
- package/dist/commerce/http-client.d.ts +73 -0
- package/dist/commerce/http-client.js +284 -0
- package/dist/commerce/http-client.js.map +1 -0
- package/dist/commerce/local-db-source.d.ts +50 -0
- package/dist/commerce/local-db-source.js +162 -0
- package/dist/commerce/local-db-source.js.map +1 -0
- package/dist/commerce/shopping-cli-source.d.ts +57 -0
- package/dist/commerce/shopping-cli-source.js +206 -0
- package/dist/commerce/shopping-cli-source.js.map +1 -0
- package/dist/commerce/types.d.ts +146 -0
- package/dist/commerce/types.js +31 -0
- package/dist/commerce/types.js.map +1 -0
- package/dist/config/profile.d.ts +122 -0
- package/dist/config/profile.js +386 -0
- package/dist/config/profile.js.map +1 -0
- package/dist/contracts/negotiation-schema.d.ts +28 -0
- package/dist/contracts/negotiation-schema.js +101 -0
- package/dist/contracts/negotiation-schema.js.map +1 -0
- package/dist/contracts/schemas.d.ts +31 -0
- package/dist/contracts/schemas.js +122 -0
- package/dist/contracts/schemas.js.map +1 -0
- package/dist/counterparty/a2a-direct/index.d.ts +41 -0
- package/dist/counterparty/a2a-direct/index.js +330 -0
- package/dist/counterparty/a2a-direct/index.js.map +1 -0
- package/dist/counterparty/channel.d.ts +200 -0
- package/dist/counterparty/channel.js +61 -0
- package/dist/counterparty/channel.js.map +1 -0
- package/dist/counterparty/index.d.ts +28 -0
- package/dist/counterparty/index.js +24 -0
- package/dist/counterparty/index.js.map +1 -0
- package/dist/counterparty/platform-api/index.d.ts +41 -0
- package/dist/counterparty/platform-api/index.js +65 -0
- package/dist/counterparty/platform-api/index.js.map +1 -0
- package/dist/counterparty/registry.d.ts +51 -0
- package/dist/counterparty/registry.js +71 -0
- package/dist/counterparty/registry.js.map +1 -0
- package/dist/counterparty/shopping-cli-hosted/index.d.ts +36 -0
- package/dist/counterparty/shopping-cli-hosted/index.js +337 -0
- package/dist/counterparty/shopping-cli-hosted/index.js.map +1 -0
- package/dist/discovery/agent-card/error.d.ts +28 -0
- package/dist/discovery/agent-card/error.js +31 -0
- package/dist/discovery/agent-card/error.js.map +1 -0
- package/dist/discovery/agent-card/extensions.d.ts +20 -0
- package/dist/discovery/agent-card/extensions.js +59 -0
- package/dist/discovery/agent-card/extensions.js.map +1 -0
- package/dist/discovery/agent-card/index.d.ts +29 -0
- package/dist/discovery/agent-card/index.js +38 -0
- package/dist/discovery/agent-card/index.js.map +1 -0
- package/dist/discovery/agent-card/secrets.d.ts +30 -0
- package/dist/discovery/agent-card/secrets.js +240 -0
- package/dist/discovery/agent-card/secrets.js.map +1 -0
- package/dist/discovery/agent-card/types.d.ts +110 -0
- package/dist/discovery/agent-card/types.js +37 -0
- package/dist/discovery/agent-card/types.js.map +1 -0
- package/dist/discovery/agent-card/validate.d.ts +31 -0
- package/dist/discovery/agent-card/validate.js +210 -0
- package/dist/discovery/agent-card/validate.js.map +1 -0
- package/dist/discovery/capability/error.d.ts +23 -0
- package/dist/discovery/capability/error.js +28 -0
- package/dist/discovery/capability/error.js.map +1 -0
- package/dist/discovery/capability/index.d.ts +21 -0
- package/dist/discovery/capability/index.js +21 -0
- package/dist/discovery/capability/index.js.map +1 -0
- package/dist/discovery/capability/intersect.d.ts +56 -0
- package/dist/discovery/capability/intersect.js +137 -0
- package/dist/discovery/capability/intersect.js.map +1 -0
- package/dist/discovery/catalog-source/errors.d.ts +32 -0
- package/dist/discovery/catalog-source/errors.js +41 -0
- package/dist/discovery/catalog-source/errors.js.map +1 -0
- package/dist/discovery/catalog-source/index.d.ts +30 -0
- package/dist/discovery/catalog-source/index.js +29 -0
- package/dist/discovery/catalog-source/index.js.map +1 -0
- package/dist/discovery/catalog-source/kiwi-record.d.ts +183 -0
- package/dist/discovery/catalog-source/kiwi-record.js +72 -0
- package/dist/discovery/catalog-source/kiwi-record.js.map +1 -0
- package/dist/discovery/catalog-source/kiwi-schema.d.ts +33 -0
- package/dist/discovery/catalog-source/kiwi-schema.js +106 -0
- package/dist/discovery/catalog-source/kiwi-schema.js.map +1 -0
- package/dist/discovery/catalog-source/kiwi-source.d.ts +38 -0
- package/dist/discovery/catalog-source/kiwi-source.js +318 -0
- package/dist/discovery/catalog-source/kiwi-source.js.map +1 -0
- package/dist/discovery/catalog-source/register.d.ts +48 -0
- package/dist/discovery/catalog-source/register.js +83 -0
- package/dist/discovery/catalog-source/register.js.map +1 -0
- package/dist/discovery/catalog-source/schema.d.ts +21 -0
- package/dist/discovery/catalog-source/schema.js +63 -0
- package/dist/discovery/catalog-source/schema.js.map +1 -0
- package/dist/discovery/catalog-source/source.d.ts +45 -0
- package/dist/discovery/catalog-source/source.js +197 -0
- package/dist/discovery/catalog-source/source.js.map +1 -0
- package/dist/discovery/catalog-source/types.d.ts +122 -0
- package/dist/discovery/catalog-source/types.js +30 -0
- package/dist/discovery/catalog-source/types.js.map +1 -0
- package/dist/discovery/index.d.ts +25 -0
- package/dist/discovery/index.js +25 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/resolve.d.ts +175 -0
- package/dist/discovery/resolve.js +367 -0
- package/dist/discovery/resolve.js.map +1 -0
- package/dist/discovery/ucp/error.d.ts +25 -0
- package/dist/discovery/ucp/error.js +27 -0
- package/dist/discovery/ucp/error.js.map +1 -0
- package/dist/discovery/ucp/index.d.ts +33 -0
- package/dist/discovery/ucp/index.js +28 -0
- package/dist/discovery/ucp/index.js.map +1 -0
- package/dist/discovery/ucp/intersect.d.ts +148 -0
- package/dist/discovery/ucp/intersect.js +312 -0
- package/dist/discovery/ucp/intersect.js.map +1 -0
- package/dist/discovery/ucp/resolver.d.ts +62 -0
- package/dist/discovery/ucp/resolver.js +214 -0
- package/dist/discovery/ucp/resolver.js.map +1 -0
- package/dist/discovery/ucp/types.d.ts +97 -0
- package/dist/discovery/ucp/types.js +103 -0
- package/dist/discovery/ucp/types.js.map +1 -0
- package/dist/discovery/ucp/validate.d.ts +38 -0
- package/dist/discovery/ucp/validate.js +232 -0
- package/dist/discovery/ucp/validate.js.map +1 -0
- package/dist/discovery/ucp/vendor.d.ts +60 -0
- package/dist/discovery/ucp/vendor.js +61 -0
- package/dist/discovery/ucp/vendor.js.map +1 -0
- package/dist/doctor.d.ts +26 -0
- package/dist/doctor.js +113 -0
- package/dist/doctor.js.map +1 -0
- package/dist/exit-codes.d.ts +26 -0
- package/dist/exit-codes.js +27 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/fanout/disclosure.d.ts +138 -0
- package/dist/fanout/disclosure.js +188 -0
- package/dist/fanout/disclosure.js.map +1 -0
- package/dist/fanout/index.d.ts +32 -0
- package/dist/fanout/index.js +30 -0
- package/dist/fanout/index.js.map +1 -0
- package/dist/fanout/orchestrator.d.ts +153 -0
- package/dist/fanout/orchestrator.js +439 -0
- package/dist/fanout/orchestrator.js.map +1 -0
- package/dist/fanout/policy.d.ts +128 -0
- package/dist/fanout/policy.js +130 -0
- package/dist/fanout/policy.js.map +1 -0
- package/dist/handoff/acp-commerce.d.ts +58 -0
- package/dist/handoff/acp-commerce.js +69 -0
- package/dist/handoff/acp-commerce.js.map +1 -0
- package/dist/handoff/authorization.d.ts +127 -0
- package/dist/handoff/authorization.js +107 -0
- package/dist/handoff/authorization.js.map +1 -0
- package/dist/handoff/candidate.d.ts +94 -0
- package/dist/handoff/candidate.js +172 -0
- package/dist/handoff/candidate.js.map +1 -0
- package/dist/handoff/channel.d.ts +88 -0
- package/dist/handoff/channel.js +189 -0
- package/dist/handoff/channel.js.map +1 -0
- package/dist/handoff/completion.d.ts +44 -0
- package/dist/handoff/completion.js +136 -0
- package/dist/handoff/completion.js.map +1 -0
- package/dist/handoff/delivery.d.ts +59 -0
- package/dist/handoff/delivery.js +185 -0
- package/dist/handoff/delivery.js.map +1 -0
- package/dist/handoff/destination.d.ts +39 -0
- package/dist/handoff/destination.js +99 -0
- package/dist/handoff/destination.js.map +1 -0
- package/dist/handoff/errors.d.ts +34 -0
- package/dist/handoff/errors.js +47 -0
- package/dist/handoff/errors.js.map +1 -0
- package/dist/handoff/idempotency.d.ts +51 -0
- package/dist/handoff/idempotency.js +168 -0
- package/dist/handoff/idempotency.js.map +1 -0
- package/dist/handoff/index.d.ts +46 -0
- package/dist/handoff/index.js +48 -0
- package/dist/handoff/index.js.map +1 -0
- package/dist/handoff/ledger.d.ts +94 -0
- package/dist/handoff/ledger.js +195 -0
- package/dist/handoff/ledger.js.map +1 -0
- package/dist/handoff/lifecycle.d.ts +29 -0
- package/dist/handoff/lifecycle.js +102 -0
- package/dist/handoff/lifecycle.js.map +1 -0
- package/dist/handoff/metrics.d.ts +56 -0
- package/dist/handoff/metrics.js +78 -0
- package/dist/handoff/metrics.js.map +1 -0
- package/dist/handoff/operator-approval.d.ts +143 -0
- package/dist/handoff/operator-approval.js +287 -0
- package/dist/handoff/operator-approval.js.map +1 -0
- package/dist/handoff/order-record.d.ts +104 -0
- package/dist/handoff/order-record.js +235 -0
- package/dist/handoff/order-record.js.map +1 -0
- package/dist/handoff/package.d.ts +102 -0
- package/dist/handoff/package.js +132 -0
- package/dist/handoff/package.js.map +1 -0
- package/dist/handoff/transaction.d.ts +96 -0
- package/dist/handoff/transaction.js +263 -0
- package/dist/handoff/transaction.js.map +1 -0
- package/dist/handoff/ucp-checkout/cart-channel.d.ts +111 -0
- package/dist/handoff/ucp-checkout/cart-channel.js +370 -0
- package/dist/handoff/ucp-checkout/cart-channel.js.map +1 -0
- package/dist/handoff/ucp-checkout/cart-parse.d.ts +22 -0
- package/dist/handoff/ucp-checkout/cart-parse.js +102 -0
- package/dist/handoff/ucp-checkout/cart-parse.js.map +1 -0
- package/dist/handoff/ucp-checkout/cart-types.d.ts +93 -0
- package/dist/handoff/ucp-checkout/cart-types.js +38 -0
- package/dist/handoff/ucp-checkout/cart-types.js.map +1 -0
- package/dist/handoff/ucp-checkout/channel.d.ts +159 -0
- package/dist/handoff/ucp-checkout/channel.js +636 -0
- package/dist/handoff/ucp-checkout/channel.js.map +1 -0
- package/dist/handoff/ucp-checkout/client.d.ts +88 -0
- package/dist/handoff/ucp-checkout/client.js +228 -0
- package/dist/handoff/ucp-checkout/client.js.map +1 -0
- package/dist/handoff/ucp-checkout/endpoint.d.ts +36 -0
- package/dist/handoff/ucp-checkout/endpoint.js +145 -0
- package/dist/handoff/ucp-checkout/endpoint.js.map +1 -0
- package/dist/handoff/ucp-checkout/index.d.ts +35 -0
- package/dist/handoff/ucp-checkout/index.js +36 -0
- package/dist/handoff/ucp-checkout/index.js.map +1 -0
- package/dist/handoff/ucp-checkout/parse.d.ts +71 -0
- package/dist/handoff/ucp-checkout/parse.js +349 -0
- package/dist/handoff/ucp-checkout/parse.js.map +1 -0
- package/dist/handoff/ucp-checkout/totals.d.ts +41 -0
- package/dist/handoff/ucp-checkout/totals.js +36 -0
- package/dist/handoff/ucp-checkout/totals.js.map +1 -0
- package/dist/handoff/ucp-checkout/types.d.ts +149 -0
- package/dist/handoff/ucp-checkout/types.js +58 -0
- package/dist/handoff/ucp-checkout/types.js.map +1 -0
- package/dist/handoff/url-safety.d.ts +44 -0
- package/dist/handoff/url-safety.js +121 -0
- package/dist/handoff/url-safety.js.map +1 -0
- package/dist/i18n.d.ts +24 -0
- package/dist/i18n.js +27 -0
- package/dist/i18n.js.map +1 -0
- package/dist/negotiation/action-candidate.d.ts +97 -0
- package/dist/negotiation/action-candidate.js +160 -0
- package/dist/negotiation/action-candidate.js.map +1 -0
- package/dist/negotiation/condition/evaluator.d.ts +36 -0
- package/dist/negotiation/condition/evaluator.js +123 -0
- package/dist/negotiation/condition/evaluator.js.map +1 -0
- package/dist/negotiation/context-map/index.d.ts +23 -0
- package/dist/negotiation/context-map/index.js +22 -0
- package/dist/negotiation/context-map/index.js.map +1 -0
- package/dist/negotiation/context-map/store.d.ts +51 -0
- package/dist/negotiation/context-map/store.js +186 -0
- package/dist/negotiation/context-map/store.js.map +1 -0
- package/dist/negotiation/context-map/types.d.ts +29 -0
- package/dist/negotiation/context-map/types.js +57 -0
- package/dist/negotiation/context-map/types.js.map +1 -0
- package/dist/negotiation/domain/common.d.ts +95 -0
- package/dist/negotiation/domain/common.js +216 -0
- package/dist/negotiation/domain/common.js.map +1 -0
- package/dist/negotiation/domain/envelope.d.ts +55 -0
- package/dist/negotiation/domain/envelope.js +110 -0
- package/dist/negotiation/domain/envelope.js.map +1 -0
- package/dist/negotiation/domain/identifiers.d.ts +65 -0
- package/dist/negotiation/domain/identifiers.js +130 -0
- package/dist/negotiation/domain/identifiers.js.map +1 -0
- package/dist/negotiation/domain/objects.d.ts +149 -0
- package/dist/negotiation/domain/objects.js +335 -0
- package/dist/negotiation/domain/objects.js.map +1 -0
- package/dist/negotiation/idempotency/index.d.ts +26 -0
- package/dist/negotiation/idempotency/index.js +25 -0
- package/dist/negotiation/idempotency/index.js.map +1 -0
- package/dist/negotiation/idempotency/store.d.ts +52 -0
- package/dist/negotiation/idempotency/store.js +189 -0
- package/dist/negotiation/idempotency/store.js.map +1 -0
- package/dist/negotiation/idempotency/types.d.ts +102 -0
- package/dist/negotiation/idempotency/types.js +47 -0
- package/dist/negotiation/idempotency/types.js.map +1 -0
- package/dist/negotiation/jcs.d.ts +21 -0
- package/dist/negotiation/jcs.js +114 -0
- package/dist/negotiation/jcs.js.map +1 -0
- package/dist/negotiation/ledger/event.d.ts +142 -0
- package/dist/negotiation/ledger/event.js +206 -0
- package/dist/negotiation/ledger/event.js.map +1 -0
- package/dist/negotiation/ledger/index.d.ts +26 -0
- package/dist/negotiation/ledger/index.js +25 -0
- package/dist/negotiation/ledger/index.js.map +1 -0
- package/dist/negotiation/ledger/store.d.ts +97 -0
- package/dist/negotiation/ledger/store.js +338 -0
- package/dist/negotiation/ledger/store.js.map +1 -0
- package/dist/negotiation/prompt.d.ts +34 -0
- package/dist/negotiation/prompt.js +113 -0
- package/dist/negotiation/prompt.js.map +1 -0
- package/dist/negotiation/recovery/index.d.ts +26 -0
- package/dist/negotiation/recovery/index.js +24 -0
- package/dist/negotiation/recovery/index.js.map +1 -0
- package/dist/negotiation/recovery/outbound.d.ts +49 -0
- package/dist/negotiation/recovery/outbound.js +34 -0
- package/dist/negotiation/recovery/outbound.js.map +1 -0
- package/dist/negotiation/recovery/recover.d.ts +59 -0
- package/dist/negotiation/recovery/recover.js +486 -0
- package/dist/negotiation/recovery/recover.js.map +1 -0
- package/dist/negotiation/recovery/types.d.ts +85 -0
- package/dist/negotiation/recovery/types.js +17 -0
- package/dist/negotiation/recovery/types.js.map +1 -0
- package/dist/negotiation/state/phase.d.ts +70 -0
- package/dist/negotiation/state/phase.js +200 -0
- package/dist/negotiation/state/phase.js.map +1 -0
- package/dist/negotiation/types.d.ts +139 -0
- package/dist/negotiation/types.js +24 -0
- package/dist/negotiation/types.js.map +1 -0
- package/dist/net/safe-http.d.ts +54 -0
- package/dist/net/safe-http.js +116 -0
- package/dist/net/safe-http.js.map +1 -0
- package/dist/operator/controller.d.ts +181 -0
- package/dist/operator/controller.js +784 -0
- package/dist/operator/controller.js.map +1 -0
- package/dist/operator/runner.d.ts +128 -0
- package/dist/operator/runner.js +248 -0
- package/dist/operator/runner.js.map +1 -0
- package/dist/operator/store.d.ts +39 -0
- package/dist/operator/store.js +169 -0
- package/dist/operator/store.js.map +1 -0
- package/dist/operator/strategy.d.ts +54 -0
- package/dist/operator/strategy.js +249 -0
- package/dist/operator/strategy.js.map +1 -0
- package/dist/operator/tui.d.ts +27 -0
- package/dist/operator/tui.js +395 -0
- package/dist/operator/tui.js.map +1 -0
- package/dist/operator/types.d.ts +224 -0
- package/dist/operator/types.js +17 -0
- package/dist/operator/types.js.map +1 -0
- package/dist/product-buyer.d.ts +74 -0
- package/dist/product-buyer.js +161 -0
- package/dist/product-buyer.js.map +1 -0
- package/dist/product-cli.d.ts +56 -0
- package/dist/product-cli.js +175 -0
- package/dist/product-cli.js.map +1 -0
- package/dist/product-compat.d.ts +50 -0
- package/dist/product-compat.js +63 -0
- package/dist/product-compat.js.map +1 -0
- package/dist/product-init.d.ts +73 -0
- package/dist/product-init.js +203 -0
- package/dist/product-init.js.map +1 -0
- package/dist/product-publish.d.ts +85 -0
- package/dist/product-publish.js +216 -0
- package/dist/product-publish.js.map +1 -0
- package/dist/protocol/legacy-shopping-negotiation/adapter.d.ts +42 -0
- package/dist/protocol/legacy-shopping-negotiation/adapter.js +561 -0
- package/dist/protocol/legacy-shopping-negotiation/adapter.js.map +1 -0
- package/dist/protocol/legacy-shopping-negotiation/mapping.d.ts +78 -0
- package/dist/protocol/legacy-shopping-negotiation/mapping.js +321 -0
- package/dist/protocol/legacy-shopping-negotiation/mapping.js.map +1 -0
- package/dist/protocol/legacy-shopping-negotiation/money.d.ts +49 -0
- package/dist/protocol/legacy-shopping-negotiation/money.js +70 -0
- package/dist/protocol/legacy-shopping-negotiation/money.js.map +1 -0
- package/dist/protocol/legacy-shopping-negotiation/types.d.ts +107 -0
- package/dist/protocol/legacy-shopping-negotiation/types.js +34 -0
- package/dist/protocol/legacy-shopping-negotiation/types.js.map +1 -0
- package/dist/runtime/buyer-policy.d.ts +58 -0
- package/dist/runtime/buyer-policy.js +89 -0
- package/dist/runtime/buyer-policy.js.map +1 -0
- package/dist/runtime/fake-model.d.ts +77 -0
- package/dist/runtime/fake-model.js +307 -0
- package/dist/runtime/fake-model.js.map +1 -0
- package/dist/runtime/foreground.d.ts +56 -0
- package/dist/runtime/foreground.js +97 -0
- package/dist/runtime/foreground.js.map +1 -0
- package/dist/runtime/heartbeat.d.ts +36 -0
- package/dist/runtime/heartbeat.js +49 -0
- package/dist/runtime/heartbeat.js.map +1 -0
- package/dist/runtime/merchant-turn.d.ts +35 -0
- package/dist/runtime/merchant-turn.js +20 -0
- package/dist/runtime/merchant-turn.js.map +1 -0
- package/dist/runtime/model.d.ts +40 -0
- package/dist/runtime/model.js +86 -0
- package/dist/runtime/model.js.map +1 -0
- package/dist/runtime/negotiation-turn.d.ts +101 -0
- package/dist/runtime/negotiation-turn.js +317 -0
- package/dist/runtime/negotiation-turn.js.map +1 -0
- package/dist/runtime/tools.d.ts +64 -0
- package/dist/runtime/tools.js +236 -0
- package/dist/runtime/tools.js.map +1 -0
- package/dist/supervisor/init.d.ts +31 -0
- package/dist/supervisor/init.js +222 -0
- package/dist/supervisor/init.js.map +1 -0
- package/dist/supervisor/logs.d.ts +28 -0
- package/dist/supervisor/logs.js +101 -0
- package/dist/supervisor/logs.js.map +1 -0
- package/dist/supervisor/manage.d.ts +72 -0
- package/dist/supervisor/manage.js +419 -0
- package/dist/supervisor/manage.js.map +1 -0
- package/dist/supervisor/manifest.d.ts +69 -0
- package/dist/supervisor/manifest.js +209 -0
- package/dist/supervisor/manifest.js.map +1 -0
- package/dist/supervisor/stack-config.d.ts +79 -0
- package/dist/supervisor/stack-config.js +262 -0
- package/dist/supervisor/stack-config.js.map +1 -0
- package/dist/supervisor/wrapper.d.ts +17 -0
- package/dist/supervisor/wrapper.js +138 -0
- package/dist/supervisor/wrapper.js.map +1 -0
- package/dist/trust/identity/auth-verifier.d.ts +96 -0
- package/dist/trust/identity/auth-verifier.js +166 -0
- package/dist/trust/identity/auth-verifier.js.map +1 -0
- package/dist/trust/identity/identity-binding.d.ts +63 -0
- package/dist/trust/identity/identity-binding.js +59 -0
- package/dist/trust/identity/identity-binding.js.map +1 -0
- package/dist/trust/identity/index.d.ts +38 -0
- package/dist/trust/identity/index.js +31 -0
- package/dist/trust/identity/index.js.map +1 -0
- package/dist/trust/identity/jws.d.ts +64 -0
- package/dist/trust/identity/jws.js +178 -0
- package/dist/trust/identity/jws.js.map +1 -0
- package/dist/trust/identity/keys.d.ts +71 -0
- package/dist/trust/identity/keys.js +118 -0
- package/dist/trust/identity/keys.js.map +1 -0
- package/dist/trust/identity/message-signature.d.ts +105 -0
- package/dist/trust/identity/message-signature.js +297 -0
- package/dist/trust/identity/message-signature.js.map +1 -0
- package/dist/trust/identity/nonce.d.ts +45 -0
- package/dist/trust/identity/nonce.js +50 -0
- package/dist/trust/identity/nonce.js.map +1 -0
- package/dist/trust/identity/signature-base.d.ts +84 -0
- package/dist/trust/identity/signature-base.js +261 -0
- package/dist/trust/identity/signature-base.js.map +1 -0
- package/dist/trust/identity/trust-policy.d.ts +92 -0
- package/dist/trust/identity/trust-policy.js +95 -0
- package/dist/trust/identity/trust-policy.js.map +1 -0
- package/dist/trust/index.d.ts +21 -0
- package/dist/trust/index.js +22 -0
- package/dist/trust/index.js.map +1 -0
- package/dist/trust/records/evaluator.d.ts +76 -0
- package/dist/trust/records/evaluator.js +122 -0
- package/dist/trust/records/evaluator.js.map +1 -0
- package/dist/trust/records/index.d.ts +28 -0
- package/dist/trust/records/index.js +26 -0
- package/dist/trust/records/index.js.map +1 -0
- package/dist/trust/records/store.d.ts +75 -0
- package/dist/trust/records/store.js +339 -0
- package/dist/trust/records/store.js.map +1 -0
- package/dist/trust/records/types.d.ts +159 -0
- package/dist/trust/records/types.js +40 -0
- package/dist/trust/records/types.js.map +1 -0
- package/dist/tui/banner.d.ts +46 -0
- package/dist/tui/banner.js +99 -0
- package/dist/tui/banner.js.map +1 -0
- package/dist/tui/styles.d.ts +70 -0
- package/dist/tui/styles.js +197 -0
- package/dist/tui/styles.js.map +1 -0
- package/dist/tui/theme.d.ts +63 -0
- package/dist/tui/theme.js +136 -0
- package/dist/tui/theme.js.map +1 -0
- package/examples/profiles/buyer.deepseek.yaml +34 -0
- package/examples/profiles/buyer.fake.yaml +36 -0
- package/examples/profiles/buyer.local.yaml +40 -0
- package/examples/profiles/merchant.deepseek.yaml +32 -0
- package/examples/profiles/merchant.fake.yaml +31 -0
- package/examples/profiles/merchant.local.yaml +34 -0
- package/package.json +55 -0
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 harrylabsj
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* OperatorController — the typed operator control plane (design §13).
|
|
18
|
+
*
|
|
19
|
+
* Owns the operator session: strategy, mode, approval state, pause/resume
|
|
20
|
+
* and shutdown. Every state transition is recorded as an append-only event
|
|
21
|
+
* and the state itself is derived by folding the event stream (reducer),
|
|
22
|
+
* so a restart rebuilds the session from the store.
|
|
23
|
+
*
|
|
24
|
+
* Boundaries:
|
|
25
|
+
* - no HTTP: formal writes go through NegotiationRunner -> CommerceClient;
|
|
26
|
+
* - HardPolicy (profile) is never widened by a strategy patch;
|
|
27
|
+
* - supervised is the default mode and never submits before approve();
|
|
28
|
+
* - approve is idempotent per candidate_id (no duplicate formal messages);
|
|
29
|
+
* - waiting approval, timeout-free: shutdown/reject/revise abandon the
|
|
30
|
+
* claim, they never complete an unsubmitted one;
|
|
31
|
+
* - operator messages are private; only candidate drafts (already produced
|
|
32
|
+
* by the backend) are ever submitted, and only after the gate.
|
|
33
|
+
*/
|
|
34
|
+
import { randomUUID } from "node:crypto";
|
|
35
|
+
import { OPERATOR_MODES, } from "./types.js";
|
|
36
|
+
export const DEFAULT_OPERATOR_MODE = "supervised";
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Approval gate (design §9)
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/** Route a candidate by mode; autopilot escalates to approval on risk. */
|
|
41
|
+
export function routeCandidate(decision, mode, profile) {
|
|
42
|
+
if (mode === "manual")
|
|
43
|
+
return "advice_only";
|
|
44
|
+
if (mode === "supervised")
|
|
45
|
+
return "await_approval";
|
|
46
|
+
// autopilot: risk escalation paths still require a human (design §9).
|
|
47
|
+
if (decision.request_human_review || decision.action === "escalate")
|
|
48
|
+
return "await_approval";
|
|
49
|
+
if ((decision.confidence ?? 1) < 0.5)
|
|
50
|
+
return "await_approval";
|
|
51
|
+
const reviewOn = profile.role === "buyer"
|
|
52
|
+
? (profile.buyer_policy?.human_review_on ?? [])
|
|
53
|
+
: (profile.merchant_policy?.human_review_on ?? []);
|
|
54
|
+
if (decision.reason_codes.some((code) => reviewOn.includes(code)))
|
|
55
|
+
return "await_approval";
|
|
56
|
+
return "auto_submit";
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Reducer: OperatorState is a fold over the event stream
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
export function initialOperatorState() {
|
|
62
|
+
return {
|
|
63
|
+
started: false,
|
|
64
|
+
shutdown: false,
|
|
65
|
+
mode: DEFAULT_OPERATOR_MODE,
|
|
66
|
+
paused: false,
|
|
67
|
+
strategy: { version: 1, directives: [] },
|
|
68
|
+
approval: { kind: "idle" },
|
|
69
|
+
candidates: new Map(),
|
|
70
|
+
stats: {
|
|
71
|
+
operator_messages: 0,
|
|
72
|
+
patches_applied: 0,
|
|
73
|
+
patches_rejected: 0,
|
|
74
|
+
candidates_generated: 0,
|
|
75
|
+
decisions_submitted: 0,
|
|
76
|
+
approvals: 0,
|
|
77
|
+
rejections: 0,
|
|
78
|
+
revisions: 0,
|
|
79
|
+
},
|
|
80
|
+
event_count: 0,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function clearApprovalFor(state, candidateId) {
|
|
84
|
+
if (state.approval.kind !== "idle" && state.approval.candidate_id === candidateId) {
|
|
85
|
+
state.approval = { kind: "idle" };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Fold one event into the state (mutates and returns `state`, fold-style).
|
|
90
|
+
* Pure with respect to its inputs: same events -> same state.
|
|
91
|
+
*/
|
|
92
|
+
export function reduceOperatorEvent(state, event) {
|
|
93
|
+
state.event_count += 1;
|
|
94
|
+
switch (event.type) {
|
|
95
|
+
case "operator.message":
|
|
96
|
+
state.stats.operator_messages += 1;
|
|
97
|
+
break;
|
|
98
|
+
case "strategy.patch.proposed":
|
|
99
|
+
// A relax patch parks here until an explicit confirmation applies it.
|
|
100
|
+
if (event.payload.patch.kind === "relax") {
|
|
101
|
+
state.strategy.pending_relax = event.payload.patch;
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case "strategy.patch.applied": {
|
|
105
|
+
const patch = event.payload.patch;
|
|
106
|
+
delete state.strategy.pending_relax;
|
|
107
|
+
// out_of_scope / chat are rejected before application, but the fold
|
|
108
|
+
// must never trust that invariant — keep them out of directives so a
|
|
109
|
+
// corrupted or replayed event cannot steer candidate generation.
|
|
110
|
+
if (patch.kind !== "forbidden" && patch.kind !== "out_of_scope" && patch.kind !== "chat") {
|
|
111
|
+
state.strategy.directives.push({
|
|
112
|
+
kind: patch.kind,
|
|
113
|
+
scope: patch.scope,
|
|
114
|
+
directive: patch.directive,
|
|
115
|
+
summary: patch.summary,
|
|
116
|
+
applied_at: event.occurred_at,
|
|
117
|
+
});
|
|
118
|
+
state.stats.patches_applied += 1;
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case "strategy.patch.rejected":
|
|
123
|
+
delete state.strategy.pending_relax;
|
|
124
|
+
state.stats.patches_rejected += 1;
|
|
125
|
+
break;
|
|
126
|
+
case "mode.changed":
|
|
127
|
+
state.mode = event.payload.to;
|
|
128
|
+
break;
|
|
129
|
+
case "negotiation.paused":
|
|
130
|
+
state.paused = true;
|
|
131
|
+
break;
|
|
132
|
+
case "negotiation.resumed":
|
|
133
|
+
state.paused = false;
|
|
134
|
+
break;
|
|
135
|
+
case "candidate.generated": {
|
|
136
|
+
const candidate = event.payload.candidate;
|
|
137
|
+
state.candidates.set(candidate.candidate_id, candidate);
|
|
138
|
+
state.stats.candidates_generated += 1;
|
|
139
|
+
if (candidate.route === "await_approval") {
|
|
140
|
+
state.approval = { kind: "awaiting_approval", candidate_id: candidate.candidate_id };
|
|
141
|
+
}
|
|
142
|
+
else if (candidate.route === "advice_only") {
|
|
143
|
+
state.approval = { kind: "advice_ready", candidate_id: candidate.candidate_id };
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case "candidate.approved": {
|
|
148
|
+
const candidate = state.candidates.get(event.payload.candidate_id);
|
|
149
|
+
if (candidate)
|
|
150
|
+
candidate.status = "approved";
|
|
151
|
+
state.stats.approvals += 1;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case "candidate.rejected": {
|
|
155
|
+
const candidate = state.candidates.get(event.payload.candidate_id);
|
|
156
|
+
if (candidate)
|
|
157
|
+
candidate.status = "rejected";
|
|
158
|
+
clearApprovalFor(state, event.payload.candidate_id);
|
|
159
|
+
state.stats.rejections += 1;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case "candidate.revised": {
|
|
163
|
+
const candidate = state.candidates.get(event.payload.candidate_id);
|
|
164
|
+
if (candidate)
|
|
165
|
+
candidate.status = "superseded";
|
|
166
|
+
clearApprovalFor(state, event.payload.candidate_id);
|
|
167
|
+
state.stats.revisions += 1;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case "decision.submitted": {
|
|
171
|
+
const candidate = state.candidates.get(event.payload.candidate_id);
|
|
172
|
+
if (candidate)
|
|
173
|
+
candidate.status = "submitted";
|
|
174
|
+
state.stats.decisions_submitted += 1;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case "turn.settled": {
|
|
178
|
+
const candidate = state.candidates.get(event.payload.candidate_id);
|
|
179
|
+
if (candidate) {
|
|
180
|
+
if (event.payload.settlement === "completed") {
|
|
181
|
+
candidate.status = "settled";
|
|
182
|
+
}
|
|
183
|
+
else if (candidate.status !== "rejected" && candidate.status !== "superseded") {
|
|
184
|
+
// failed / abandoned: the candidate must never be submitted later.
|
|
185
|
+
candidate.status = "expired";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
clearApprovalFor(state, event.payload.candidate_id);
|
|
189
|
+
// Turn-scope directives expire once a candidate settles (design §7.3).
|
|
190
|
+
state.strategy.directives = state.strategy.directives.filter((d) => d.scope !== "turn");
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return state;
|
|
195
|
+
}
|
|
196
|
+
export class OperatorController {
|
|
197
|
+
profile;
|
|
198
|
+
store;
|
|
199
|
+
engine;
|
|
200
|
+
runner;
|
|
201
|
+
now;
|
|
202
|
+
origin;
|
|
203
|
+
state;
|
|
204
|
+
eventsLog = [];
|
|
205
|
+
/** Live prepared turns for candidates generated by this process. */
|
|
206
|
+
prepared = new Map();
|
|
207
|
+
/** Messages rejected this session; not re-claimed until a later run. */
|
|
208
|
+
rejectedMessageIds = new Set();
|
|
209
|
+
/** Candidate ids whose submit is currently in flight (approve idempotency). */
|
|
210
|
+
submitting = new Set();
|
|
211
|
+
/** Last counterpart public message seen by prepare (TUI transcript pane). */
|
|
212
|
+
lastCounterpartMessage;
|
|
213
|
+
eventSeq = 0;
|
|
214
|
+
candidateSeq = 0;
|
|
215
|
+
constructor(options) {
|
|
216
|
+
this.profile = options.profile;
|
|
217
|
+
this.store = options.store;
|
|
218
|
+
this.engine = options.engine;
|
|
219
|
+
this.runner = options.runner;
|
|
220
|
+
this.now = options.now ?? (() => new Date().toISOString());
|
|
221
|
+
this.origin = options.origin ?? "local_tui";
|
|
222
|
+
this.state = initialOperatorState();
|
|
223
|
+
}
|
|
224
|
+
getState() {
|
|
225
|
+
return this.state;
|
|
226
|
+
}
|
|
227
|
+
/** Load and fold the persisted event stream (fail closed on corruption). */
|
|
228
|
+
async start() {
|
|
229
|
+
const events = await this.store.readAll();
|
|
230
|
+
for (const event of events) {
|
|
231
|
+
reduceOperatorEvent(this.state, event);
|
|
232
|
+
this.eventsLog.push(event);
|
|
233
|
+
}
|
|
234
|
+
this.eventSeq = events.length;
|
|
235
|
+
this.candidateSeq = events.filter((e) => e.type === "candidate.generated").length;
|
|
236
|
+
// Recovery rule (design §14.3/§14.4): a candidate from a previous run
|
|
237
|
+
// cannot be re-validated against the live claim, so it is expired —
|
|
238
|
+
// never submitted. The operator regenerates with fresh state. `submitted`
|
|
239
|
+
// is included: a crash between decision.submitted and turn.settled would
|
|
240
|
+
// otherwise leave the session wedged (approve/reject/revise all refuse and
|
|
241
|
+
// prepareNextCandidate stays blocked), because the approval is only
|
|
242
|
+
// cleared by turn.settled.
|
|
243
|
+
for (const candidate of this.state.candidates.values()) {
|
|
244
|
+
if (candidate.status === "awaiting_approval" ||
|
|
245
|
+
candidate.status === "advice_only" ||
|
|
246
|
+
candidate.status === "approved" ||
|
|
247
|
+
candidate.status === "submitted") {
|
|
248
|
+
candidate.status = "expired";
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (this.state.approval.kind !== "idle") {
|
|
252
|
+
const candidate = this.state.candidates.get(this.state.approval.candidate_id);
|
|
253
|
+
if (!candidate || candidate.status === "expired") {
|
|
254
|
+
this.state.approval = { kind: "idle" };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
this.state.started = true;
|
|
258
|
+
}
|
|
259
|
+
// ---- event plumbing -----------------------------------------------------
|
|
260
|
+
nextBase(visibility) {
|
|
261
|
+
this.eventSeq += 1;
|
|
262
|
+
return {
|
|
263
|
+
// A random suffix keeps ids unique across restarts even when a rejected
|
|
264
|
+
// append consumed a sequence number without persisting (design §6).
|
|
265
|
+
event_id: `evt-${this.eventSeq}-${randomUUID().slice(0, 8)}`,
|
|
266
|
+
occurred_at: this.now(),
|
|
267
|
+
agent_id: this.profile.agent_id,
|
|
268
|
+
role: this.profile.role,
|
|
269
|
+
origin: this.origin,
|
|
270
|
+
visibility,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
async record(event) {
|
|
274
|
+
await this.store.append(event);
|
|
275
|
+
reduceOperatorEvent(this.state, event);
|
|
276
|
+
this.eventsLog.push(event);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Compile context: profile HardPolicy values overridden by the LAST applied
|
|
280
|
+
* budget/floor directive, so compiling again sees the session-effective
|
|
281
|
+
* values (e.g. after a confirmed raise, "提高到 500" is no longer a raise).
|
|
282
|
+
*/
|
|
283
|
+
strategyContext() {
|
|
284
|
+
const context = { role: this.profile.role };
|
|
285
|
+
if (this.profile.buyer_policy) {
|
|
286
|
+
let max = this.profile.buyer_policy.max_total_price_private;
|
|
287
|
+
for (const d of this.state.strategy.directives) {
|
|
288
|
+
// Only numeric constraint directives carry a budget hint. A
|
|
289
|
+
// soft_preference mentioning 预算 must not override it.
|
|
290
|
+
if ((d.kind === "tighten" || d.kind === "relax") &&
|
|
291
|
+
/预算|budget/i.test(d.directive)) {
|
|
292
|
+
const n = /\d+(?:\.\d+)?/.exec(d.directive);
|
|
293
|
+
if (n !== null)
|
|
294
|
+
max = Number(n[0]);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
context.buyer_max_total_price = max;
|
|
298
|
+
}
|
|
299
|
+
if (this.profile.merchant_policy?.min_unit_price_private !== undefined) {
|
|
300
|
+
let floor = this.profile.merchant_policy.min_unit_price_private;
|
|
301
|
+
for (const d of this.state.strategy.directives) {
|
|
302
|
+
if ((d.kind === "tighten" || d.kind === "relax") &&
|
|
303
|
+
/底价|最低价|floor/i.test(d.directive)) {
|
|
304
|
+
const n = /\d+(?:\.\d+)?/.exec(d.directive);
|
|
305
|
+
if (n !== null)
|
|
306
|
+
floor = Number(n[0]);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
context.merchant_min_unit_price = floor;
|
|
310
|
+
}
|
|
311
|
+
return context;
|
|
312
|
+
}
|
|
313
|
+
// ---- strategy + operator messages ---------------------------------------
|
|
314
|
+
async sendOperatorMessage(text) {
|
|
315
|
+
const trimmed = text.trim();
|
|
316
|
+
if (trimmed === "")
|
|
317
|
+
return { kind: "rejected", reason: "空消息" };
|
|
318
|
+
if (this.state.shutdown)
|
|
319
|
+
return { kind: "rejected", reason: "控制器已关闭" };
|
|
320
|
+
try {
|
|
321
|
+
await this.record({
|
|
322
|
+
...this.nextBase("private"),
|
|
323
|
+
type: "operator.message",
|
|
324
|
+
payload: { text: trimmed },
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
// The event store refuses secret-like values (fail closed). This must
|
|
329
|
+
// reject the message inline — never crash the whole TUI session.
|
|
330
|
+
return {
|
|
331
|
+
kind: "rejected",
|
|
332
|
+
reason: "该消息疑似包含敏感值(token/密钥等),未记录(fail closed)。请去掉敏感内容后重试。",
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
const patch = this.engine.compile(trimmed, this.strategyContext());
|
|
336
|
+
await this.record({
|
|
337
|
+
...this.nextBase("private"),
|
|
338
|
+
type: "strategy.patch.proposed",
|
|
339
|
+
payload: { patch },
|
|
340
|
+
});
|
|
341
|
+
const risk = this.engine.assess(patch);
|
|
342
|
+
if (risk.level === "blocked") {
|
|
343
|
+
await this.record({
|
|
344
|
+
...this.nextBase("private"),
|
|
345
|
+
type: "strategy.patch.rejected",
|
|
346
|
+
payload: { patch, reason: risk.reason },
|
|
347
|
+
});
|
|
348
|
+
return { kind: "rejected", patch, reason: risk.reason };
|
|
349
|
+
}
|
|
350
|
+
if (risk.level === "confirm") {
|
|
351
|
+
// The reducer parked the patch in strategy.pending_relax.
|
|
352
|
+
return { kind: "needs_confirmation", patch };
|
|
353
|
+
}
|
|
354
|
+
await this.record({
|
|
355
|
+
...this.nextBase("private"),
|
|
356
|
+
type: "strategy.patch.applied",
|
|
357
|
+
payload: { patch },
|
|
358
|
+
});
|
|
359
|
+
return { kind: "applied", patch };
|
|
360
|
+
}
|
|
361
|
+
/** Apply the parked relax patch after the operator's explicit confirmation. */
|
|
362
|
+
async confirmStrategy() {
|
|
363
|
+
const pending = this.state.strategy.pending_relax;
|
|
364
|
+
if (!pending)
|
|
365
|
+
return { ok: false, error: "没有待确认的策略变更" };
|
|
366
|
+
await this.record({
|
|
367
|
+
...this.nextBase("private"),
|
|
368
|
+
type: "strategy.patch.applied",
|
|
369
|
+
payload: { patch: pending },
|
|
370
|
+
});
|
|
371
|
+
return { ok: true };
|
|
372
|
+
}
|
|
373
|
+
// ---- mode ---------------------------------------------------------------
|
|
374
|
+
async setMode(mode, options) {
|
|
375
|
+
if (this.state.shutdown)
|
|
376
|
+
return { kind: "error", error: "控制器已关闭" };
|
|
377
|
+
if (!OPERATOR_MODES.includes(mode)) {
|
|
378
|
+
return { kind: "error", error: `未知模式: ${mode}(可选 ${OPERATOR_MODES.join("/")})` };
|
|
379
|
+
}
|
|
380
|
+
const from = this.state.mode;
|
|
381
|
+
if (from === mode)
|
|
382
|
+
return { kind: "unchanged", mode };
|
|
383
|
+
// Design §8: switching INTO autopilot requires explicit confirmation.
|
|
384
|
+
if (mode === "autopilot" && options?.confirmed !== true) {
|
|
385
|
+
return {
|
|
386
|
+
kind: "needs_confirmation",
|
|
387
|
+
mode,
|
|
388
|
+
reason: "切换到 autopilot 需要显式确认:/mode autopilot confirm",
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
await this.record({
|
|
392
|
+
...this.nextBase("private"),
|
|
393
|
+
type: "mode.changed",
|
|
394
|
+
payload: { from, to: mode },
|
|
395
|
+
});
|
|
396
|
+
return { kind: "changed", mode };
|
|
397
|
+
}
|
|
398
|
+
// ---- candidate lifecycle --------------------------------------------------
|
|
399
|
+
/**
|
|
400
|
+
* Pull the next pending message and generate a candidate. No Commerce
|
|
401
|
+
* write happens unless the gate routes to auto_submit (autopilot).
|
|
402
|
+
*/
|
|
403
|
+
async prepareNextCandidate() {
|
|
404
|
+
if (this.state.shutdown)
|
|
405
|
+
return { kind: "blocked", reason: "控制器已关闭" };
|
|
406
|
+
if (this.state.paused)
|
|
407
|
+
return { kind: "blocked", reason: "已暂停,/resume 后恢复" };
|
|
408
|
+
if (this.state.approval.kind !== "idle") {
|
|
409
|
+
return { kind: "blocked", reason: "已有待处理候选,请先 /approve、/reject 或 /revise" };
|
|
410
|
+
}
|
|
411
|
+
// Applied session/turn directives steer candidate generation (design §7).
|
|
412
|
+
// The runner clamps them to the profile's HardPolicy, so they can narrow
|
|
413
|
+
// but never widen the submit-time gates.
|
|
414
|
+
const prepared = await this.runner.prepare({
|
|
415
|
+
skipMessageIds: this.rejectedMessageIds,
|
|
416
|
+
directives: [...this.state.strategy.directives],
|
|
417
|
+
});
|
|
418
|
+
if (!prepared)
|
|
419
|
+
return { kind: "no_work" };
|
|
420
|
+
this.candidateSeq += 1;
|
|
421
|
+
const candidateId = `cand-${this.candidateSeq}`;
|
|
422
|
+
const route = routeCandidate(prepared.decision, this.state.mode, this.profile);
|
|
423
|
+
const candidate = {
|
|
424
|
+
candidate_id: candidateId,
|
|
425
|
+
binding: prepared.binding,
|
|
426
|
+
decision: prepared.decision,
|
|
427
|
+
analysis: prepared.analysis,
|
|
428
|
+
route,
|
|
429
|
+
status: route === "await_approval"
|
|
430
|
+
? "awaiting_approval"
|
|
431
|
+
: route === "advice_only"
|
|
432
|
+
? "advice_only"
|
|
433
|
+
: "approved",
|
|
434
|
+
created_at: this.now(),
|
|
435
|
+
};
|
|
436
|
+
if (prepared.counterpart_message !== undefined) {
|
|
437
|
+
this.lastCounterpartMessage = prepared.counterpart_message;
|
|
438
|
+
}
|
|
439
|
+
this.prepared.set(candidateId, prepared);
|
|
440
|
+
// Keep the claim alive while the operator deliberates (design §10): a
|
|
441
|
+
// >300s decision must not let stale recovery steal the claim.
|
|
442
|
+
this.runner.startHeartbeat(prepared);
|
|
443
|
+
await this.record({
|
|
444
|
+
...this.nextBase("public_draft"),
|
|
445
|
+
type: "candidate.generated",
|
|
446
|
+
payload: { candidate },
|
|
447
|
+
});
|
|
448
|
+
if (route === "await_approval")
|
|
449
|
+
return { kind: "awaiting_approval", candidate };
|
|
450
|
+
if (route === "advice_only")
|
|
451
|
+
return { kind: "advice_ready", candidate };
|
|
452
|
+
const submitted = await this.submitCandidate(candidateId);
|
|
453
|
+
if (submitted.kind === "submitted") {
|
|
454
|
+
const result = {
|
|
455
|
+
kind: "auto_submitted",
|
|
456
|
+
candidate_id: candidateId,
|
|
457
|
+
policy_result: submitted.policy_result,
|
|
458
|
+
};
|
|
459
|
+
if (submitted.message_id !== undefined)
|
|
460
|
+
result.message_id = submitted.message_id;
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
if (submitted.kind === "invalid")
|
|
464
|
+
return { kind: "blocked", reason: submitted.reason };
|
|
465
|
+
return { kind: "blocked", reason: "auto submit replayed" };
|
|
466
|
+
}
|
|
467
|
+
async submitCandidate(candidateId) {
|
|
468
|
+
const candidate = this.state.candidates.get(candidateId);
|
|
469
|
+
if (!candidate)
|
|
470
|
+
return { kind: "invalid", reason: `未知候选 ${candidateId}` };
|
|
471
|
+
// Idempotent approval replay: one candidate_id -> at most one formal message.
|
|
472
|
+
if (candidate.status === "submitted" || candidate.status === "settled") {
|
|
473
|
+
return { kind: "replayed", candidate_id: candidateId };
|
|
474
|
+
}
|
|
475
|
+
// A second approve while the first submit is still in flight (web/mobile
|
|
476
|
+
// concurrency) must not re-submit or crash on the settled claim.
|
|
477
|
+
if (this.submitting.has(candidateId)) {
|
|
478
|
+
return { kind: "replayed", candidate_id: candidateId };
|
|
479
|
+
}
|
|
480
|
+
const prepared = this.prepared.get(candidateId);
|
|
481
|
+
if (!prepared || candidate.status === "expired") {
|
|
482
|
+
return { kind: "invalid", reason: "候选已过期(需重新生成),不会提交" };
|
|
483
|
+
}
|
|
484
|
+
this.submitting.add(candidateId);
|
|
485
|
+
let outcome;
|
|
486
|
+
try {
|
|
487
|
+
outcome = await this.runner.submit(prepared);
|
|
488
|
+
}
|
|
489
|
+
catch (err) {
|
|
490
|
+
// 提交异常兜底(评审项 M7):网关瞬时故障时 submit 抛错——此前
|
|
491
|
+
// finally 只清 submitting,心跳继续每 60s 续命 claim:消息永远不被
|
|
492
|
+
// stale recovery 回收、也不回到 pending,会话永久楔死(对照 headless
|
|
493
|
+
// turn 路径有完整 abandon 兜底,本路径缺失)。此处停心跳 + abandon
|
|
494
|
+
// claim(消息重新可领取)+ 落 failed 事件 + 返回可读错误;prepared
|
|
495
|
+
// 保留——重试 /approve 走 submit 的幂等键,不重复提交。
|
|
496
|
+
await this.runner.stopHeartbeat().catch(() => undefined);
|
|
497
|
+
await this.runner.abandon(prepared, "submit error").catch(() => undefined);
|
|
498
|
+
await this.record({
|
|
499
|
+
...this.nextBase("private"),
|
|
500
|
+
type: "turn.settled",
|
|
501
|
+
payload: {
|
|
502
|
+
candidate_id: candidateId,
|
|
503
|
+
settlement: "failed",
|
|
504
|
+
reason: `submit error: ${err instanceof Error ? err.message : String(err)}`,
|
|
505
|
+
},
|
|
506
|
+
}).catch(() => undefined);
|
|
507
|
+
return {
|
|
508
|
+
kind: "invalid",
|
|
509
|
+
reason: `提交失败(claim 已释放,可重新尝试):${err instanceof Error ? err.message : String(err)}`,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
finally {
|
|
513
|
+
this.submitting.delete(candidateId);
|
|
514
|
+
}
|
|
515
|
+
if (outcome.settlement === "failed") {
|
|
516
|
+
// A failed settle leaves the message reclaimable; stop the autopilot
|
|
517
|
+
// TUI loop from silently re-claiming + re-submitting on every input.
|
|
518
|
+
this.rejectedMessageIds.add(prepared.binding.message_id);
|
|
519
|
+
}
|
|
520
|
+
const submittedPayload = {
|
|
521
|
+
candidate_id: candidateId,
|
|
522
|
+
action: candidate.decision.action,
|
|
523
|
+
policy_result: outcome.policy_result.result,
|
|
524
|
+
};
|
|
525
|
+
if (outcome.policy_result.message_id !== undefined) {
|
|
526
|
+
submittedPayload.message_id = outcome.policy_result.message_id;
|
|
527
|
+
}
|
|
528
|
+
await this.record({
|
|
529
|
+
...this.nextBase("public_sent"),
|
|
530
|
+
type: "decision.submitted",
|
|
531
|
+
payload: submittedPayload,
|
|
532
|
+
});
|
|
533
|
+
await this.record({
|
|
534
|
+
...this.nextBase("private"),
|
|
535
|
+
type: "turn.settled",
|
|
536
|
+
payload: {
|
|
537
|
+
candidate_id: candidateId,
|
|
538
|
+
settlement: outcome.settlement,
|
|
539
|
+
...(outcome.settlement === "failed" ? { reason: outcome.policy_result.public_reason } : {}),
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
this.prepared.delete(candidateId);
|
|
543
|
+
await this.runner.stopHeartbeat();
|
|
544
|
+
const result = {
|
|
545
|
+
kind: "submitted",
|
|
546
|
+
candidate_id: candidateId,
|
|
547
|
+
policy_result: outcome.policy_result.result,
|
|
548
|
+
settlement: outcome.settlement,
|
|
549
|
+
};
|
|
550
|
+
if (outcome.policy_result.message_id !== undefined) {
|
|
551
|
+
result.message_id = outcome.policy_result.message_id;
|
|
552
|
+
}
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
/** Approve the current awaiting candidate (idempotent per candidate_id). */
|
|
556
|
+
async approve(candidateId) {
|
|
557
|
+
if (this.state.shutdown)
|
|
558
|
+
return { kind: "invalid", reason: "控制器已关闭" };
|
|
559
|
+
const id = candidateId ??
|
|
560
|
+
(this.state.approval.kind === "awaiting_approval"
|
|
561
|
+
? this.state.approval.candidate_id
|
|
562
|
+
: undefined);
|
|
563
|
+
if (id === undefined) {
|
|
564
|
+
return {
|
|
565
|
+
kind: "invalid",
|
|
566
|
+
reason: this.state.approval.kind === "advice_ready"
|
|
567
|
+
? "manual 模式只提供建议,不自动提交;可 /reject 放弃或 /revise 重算"
|
|
568
|
+
: "没有等待审批的候选",
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (this.state.approval.kind !== "awaiting_approval" ||
|
|
572
|
+
this.state.approval.candidate_id !== id) {
|
|
573
|
+
const existing = this.state.candidates.get(id);
|
|
574
|
+
if (existing && (existing.status === "submitted" || existing.status === "settled")) {
|
|
575
|
+
return { kind: "replayed", candidate_id: id };
|
|
576
|
+
}
|
|
577
|
+
return { kind: "invalid", reason: `候选 ${id} 不在待审批状态` };
|
|
578
|
+
}
|
|
579
|
+
await this.record({
|
|
580
|
+
...this.nextBase("private"),
|
|
581
|
+
type: "candidate.approved",
|
|
582
|
+
payload: { candidate_id: id },
|
|
583
|
+
});
|
|
584
|
+
return this.submitCandidate(id);
|
|
585
|
+
}
|
|
586
|
+
currentCandidateId(allowAdvice) {
|
|
587
|
+
if (this.state.approval.kind === "awaiting_approval")
|
|
588
|
+
return this.state.approval.candidate_id;
|
|
589
|
+
if (allowAdvice && this.state.approval.kind === "advice_ready") {
|
|
590
|
+
return this.state.approval.candidate_id;
|
|
591
|
+
}
|
|
592
|
+
return undefined;
|
|
593
|
+
}
|
|
594
|
+
/** Reject the current candidate and abandon its claim. Never submits. */
|
|
595
|
+
async reject(candidateId, reason) {
|
|
596
|
+
if (this.state.shutdown)
|
|
597
|
+
return { ok: false, error: "控制器已关闭" };
|
|
598
|
+
const id = candidateId ?? this.currentCandidateId(true);
|
|
599
|
+
if (id === undefined)
|
|
600
|
+
return { ok: false, error: "没有待处理的候选" };
|
|
601
|
+
const candidate = this.state.candidates.get(id);
|
|
602
|
+
if (!candidate ||
|
|
603
|
+
(candidate.status !== "awaiting_approval" && candidate.status !== "advice_only")) {
|
|
604
|
+
return { ok: false, error: `候选 ${id} 不可驳回` };
|
|
605
|
+
}
|
|
606
|
+
await this.record({
|
|
607
|
+
...this.nextBase("private"),
|
|
608
|
+
type: "candidate.rejected",
|
|
609
|
+
payload: { candidate_id: id, ...(reason !== undefined ? { reason } : {}) },
|
|
610
|
+
});
|
|
611
|
+
this.rejectedMessageIds.add(candidate.binding.message_id);
|
|
612
|
+
const prepared = this.prepared.get(id);
|
|
613
|
+
if (prepared) {
|
|
614
|
+
await this.runner.abandon(prepared, reason ?? "operator rejected candidate");
|
|
615
|
+
this.prepared.delete(id);
|
|
616
|
+
await this.runner.stopHeartbeat();
|
|
617
|
+
}
|
|
618
|
+
await this.record({
|
|
619
|
+
...this.nextBase("private"),
|
|
620
|
+
type: "turn.settled",
|
|
621
|
+
payload: { candidate_id: id, settlement: "abandoned", reason: "operator rejected" },
|
|
622
|
+
});
|
|
623
|
+
return { ok: true };
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Record a turn instruction, abandon the old candidate's claim and
|
|
627
|
+
* regenerate. The instruction is compiled as a turn-scope directive and
|
|
628
|
+
* expires when the next candidate settles (design §7.3).
|
|
629
|
+
*/
|
|
630
|
+
async revise(instruction, candidateId) {
|
|
631
|
+
if (this.state.shutdown)
|
|
632
|
+
return { kind: "blocked", reason: "控制器已关闭" };
|
|
633
|
+
const trimmed = instruction.trim();
|
|
634
|
+
if (trimmed === "")
|
|
635
|
+
return { kind: "blocked", reason: "用法: /revise <新指令>" };
|
|
636
|
+
const id = candidateId ?? this.currentCandidateId(true);
|
|
637
|
+
if (id === undefined)
|
|
638
|
+
return { kind: "blocked", reason: "没有可重算的候选" };
|
|
639
|
+
const candidate = this.state.candidates.get(id);
|
|
640
|
+
if (!candidate ||
|
|
641
|
+
(candidate.status !== "awaiting_approval" && candidate.status !== "advice_only")) {
|
|
642
|
+
return { kind: "blocked", reason: `候选 ${id} 不可重算` };
|
|
643
|
+
}
|
|
644
|
+
// Compile BEFORE touching the candidate: a relax instruction must never
|
|
645
|
+
// apply silently (design §8 — relax requires an explicit confirmation),
|
|
646
|
+
// and a forbidden one is refused outright. Both leave the current
|
|
647
|
+
// candidate, its claim and the strategy untouched.
|
|
648
|
+
const patch = this.engine.compile(trimmed, this.strategyContext());
|
|
649
|
+
if (patch.kind === "relax" ||
|
|
650
|
+
patch.kind === "forbidden" ||
|
|
651
|
+
patch.kind === "out_of_scope" ||
|
|
652
|
+
patch.kind === "chat") {
|
|
653
|
+
// A refused /revise must still leave an audit trail (§6).
|
|
654
|
+
await this.record({
|
|
655
|
+
...this.nextBase("private"),
|
|
656
|
+
type: "strategy.patch.rejected",
|
|
657
|
+
payload: { patch, reason: "operator /revise 指令被拒绝" },
|
|
658
|
+
});
|
|
659
|
+
if (patch.kind === "relax") {
|
|
660
|
+
return {
|
|
661
|
+
kind: "blocked",
|
|
662
|
+
reason: `放宽约束不能通过 /revise 直接生效:${patch.summary}。候选保持不变;` +
|
|
663
|
+
"如确需放宽,请以普通指令发送并用 /strategy confirm 确认(仍不会突破 profile 硬约束)。",
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
if (patch.kind === "forbidden") {
|
|
667
|
+
return { kind: "blocked", reason: `指令被拒绝:${patch.summary}。候选保持不变。` };
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
kind: "blocked",
|
|
671
|
+
reason: `该指令无法用于重算:${patch.summary}。候选保持不变。`,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
await this.record({
|
|
675
|
+
...this.nextBase("private"),
|
|
676
|
+
type: "candidate.revised",
|
|
677
|
+
payload: { candidate_id: id, instruction: trimmed },
|
|
678
|
+
});
|
|
679
|
+
const prepared = this.prepared.get(id);
|
|
680
|
+
if (prepared) {
|
|
681
|
+
await this.runner.abandon(prepared, "operator requested revise");
|
|
682
|
+
this.prepared.delete(id);
|
|
683
|
+
await this.runner.stopHeartbeat();
|
|
684
|
+
}
|
|
685
|
+
await this.record({
|
|
686
|
+
...this.nextBase("private"),
|
|
687
|
+
type: "turn.settled",
|
|
688
|
+
payload: { candidate_id: id, settlement: "abandoned", reason: "operator revised" },
|
|
689
|
+
});
|
|
690
|
+
// Apply the instruction as a turn-scope strategy patch before regenerating.
|
|
691
|
+
await this.record({
|
|
692
|
+
...this.nextBase("private"),
|
|
693
|
+
type: "strategy.patch.applied",
|
|
694
|
+
payload: { patch: { ...patch, scope: "turn" } },
|
|
695
|
+
});
|
|
696
|
+
return this.prepareNextCandidate();
|
|
697
|
+
}
|
|
698
|
+
// ---- pause / resume -------------------------------------------------------
|
|
699
|
+
async pause() {
|
|
700
|
+
if (this.state.shutdown)
|
|
701
|
+
return { ok: false, error: "控制器已关闭" };
|
|
702
|
+
if (this.state.paused)
|
|
703
|
+
return { ok: true };
|
|
704
|
+
await this.record({ ...this.nextBase("private"), type: "negotiation.paused", payload: {} });
|
|
705
|
+
return { ok: true };
|
|
706
|
+
}
|
|
707
|
+
async resume() {
|
|
708
|
+
if (this.state.shutdown)
|
|
709
|
+
return { ok: false, error: "控制器已关闭" };
|
|
710
|
+
if (!this.state.paused)
|
|
711
|
+
return { ok: true };
|
|
712
|
+
await this.record({ ...this.nextBase("private"), type: "negotiation.resumed", payload: {} });
|
|
713
|
+
return { ok: true };
|
|
714
|
+
}
|
|
715
|
+
// ---- inspection -----------------------------------------------------------
|
|
716
|
+
why() {
|
|
717
|
+
const id = this.currentCandidateId(true);
|
|
718
|
+
const directives = this.state.strategy.directives.map((d) => `[${d.kind}/${d.scope}] ${d.summary}`);
|
|
719
|
+
if (id === undefined) {
|
|
720
|
+
return {
|
|
721
|
+
analysis: [],
|
|
722
|
+
reason_codes: [],
|
|
723
|
+
active_directives: directives,
|
|
724
|
+
note: "当前没有候选决策",
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
const candidate = this.state.candidates.get(id);
|
|
728
|
+
if (!candidate) {
|
|
729
|
+
return {
|
|
730
|
+
analysis: [],
|
|
731
|
+
reason_codes: [],
|
|
732
|
+
active_directives: directives,
|
|
733
|
+
note: "当前没有候选决策",
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
return {
|
|
737
|
+
candidate_id: id,
|
|
738
|
+
route: candidate.route,
|
|
739
|
+
analysis: [...candidate.analysis],
|
|
740
|
+
reason_codes: [...candidate.decision.reason_codes],
|
|
741
|
+
active_directives: directives,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
/** Recent events, newest last. Private to this operator session. */
|
|
745
|
+
history(limit = 20) {
|
|
746
|
+
return this.eventsLog.slice(-Math.max(1, limit));
|
|
747
|
+
}
|
|
748
|
+
usage() {
|
|
749
|
+
return { ...this.state.stats, events: this.state.event_count };
|
|
750
|
+
}
|
|
751
|
+
// ---- shutdown ---------------------------------------------------------------
|
|
752
|
+
/**
|
|
753
|
+
* Safe shutdown (design §17): a candidate still waiting for approval is
|
|
754
|
+
* best-effort abandoned (never completed) and kept as an audit record.
|
|
755
|
+
*/
|
|
756
|
+
async shutdown() {
|
|
757
|
+
if (this.state.shutdown)
|
|
758
|
+
return { events: this.state.event_count };
|
|
759
|
+
const result = { events: 0 };
|
|
760
|
+
const pendingId = this.currentCandidateId(true);
|
|
761
|
+
if (pendingId !== undefined) {
|
|
762
|
+
const prepared = this.prepared.get(pendingId);
|
|
763
|
+
if (prepared) {
|
|
764
|
+
await this.runner.abandon(prepared, "operator shutdown with pending candidate");
|
|
765
|
+
this.prepared.delete(pendingId);
|
|
766
|
+
await this.runner.stopHeartbeat();
|
|
767
|
+
}
|
|
768
|
+
await this.record({
|
|
769
|
+
...this.nextBase("private"),
|
|
770
|
+
type: "turn.settled",
|
|
771
|
+
payload: {
|
|
772
|
+
candidate_id: pendingId,
|
|
773
|
+
settlement: "abandoned",
|
|
774
|
+
reason: "operator shutdown",
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
result.abandoned_candidate = pendingId;
|
|
778
|
+
}
|
|
779
|
+
this.state.shutdown = true;
|
|
780
|
+
result.events = this.state.event_count;
|
|
781
|
+
return result;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
//# sourceMappingURL=controller.js.map
|