@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,1099 @@
|
|
|
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
|
+
* AgentKernel (design §4.1): lifecycle and concurrency owner of one Kiwi
|
|
18
|
+
* agent instance.
|
|
19
|
+
*
|
|
20
|
+
* v0.3.0-A responsibilities:
|
|
21
|
+
* - open/recover the persistent main conversation (Pi AgentHarness Session);
|
|
22
|
+
* - serialize every user message, slash command and (later) event through
|
|
23
|
+
* one queue — only the kernel submits state changes;
|
|
24
|
+
* - retrieve relevant memories per turn (logged, redaction-leveled) and
|
|
25
|
+
* inject them into the system prompt as an untamperable briefing;
|
|
26
|
+
* - expose memory write tools to the model (governance lives in
|
|
27
|
+
* MemoryStore) and deterministic slash commands to the operator.
|
|
28
|
+
*/
|
|
29
|
+
import { AgentHarness, } from "@earendil-works/pi-agent-core";
|
|
30
|
+
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
31
|
+
import path from "node:path";
|
|
32
|
+
import { ensureAgentPaths, openAgentDatabase } from "./agent-db.js";
|
|
33
|
+
import { HandoffEventStore, HandoffIdempotencyStore, foldCandidateLifecycle, deliveryState, recordLaunch, recordOpenEvidence, } from "../handoff/index.js";
|
|
34
|
+
import { buildBuyerTools } from "./buyer/buyer-tools.js";
|
|
35
|
+
import { TaskScheduler } from "./buyer/scheduler.js";
|
|
36
|
+
import { BuyerTaskStore } from "./buyer/task-store.js";
|
|
37
|
+
import { buildMemoryTools } from "./chat-tools.js";
|
|
38
|
+
import { AGENT_MODES, DEFAULT_AGENT_MODE, isAgentMode } from "./mode.js";
|
|
39
|
+
import { WriteApprovalCandidateStore, executeApprovedCandidate, } from "./merchant/action-candidate.js";
|
|
40
|
+
import { buildMerchantTools } from "./merchant/merchant-tools.js";
|
|
41
|
+
import { DeterministicNegotiationRunner } from "../operator/runner.js";
|
|
42
|
+
import { MemoryStore } from "./memory/store.js";
|
|
43
|
+
import { MemoryError } from "./memory/types.js";
|
|
44
|
+
import { PrivateVault } from "./memory/vault.js";
|
|
45
|
+
import { openMainSession } from "./session.js";
|
|
46
|
+
import { registerCatalogAgent } from "../discovery/catalog-source/register.js";
|
|
47
|
+
import { KiwiCatalogSource } from "../discovery/catalog-source/kiwi-source.js";
|
|
48
|
+
import { baseSystemPrompt, renderMemoryBriefing } from "./system-prompt.js";
|
|
49
|
+
export const MAIN_SESSION_ID = "main";
|
|
50
|
+
/**
|
|
51
|
+
* 把一轮 A2A 磋商结果写入 MemoryStore(episode namespace,active 无需人工确认):
|
|
52
|
+
* 持久上下文,`/why` 可查、跨重启可恢复。buyer 工具经 recordNegotiation 回调复用。
|
|
53
|
+
*/
|
|
54
|
+
async function rememberNegotiation(store, input) {
|
|
55
|
+
const summary = [
|
|
56
|
+
`A2A 磋商完成:negotiation ${input.negotiationId},商家 ${input.catalogAgentId}`,
|
|
57
|
+
`,${input.quantity} 件 ${input.sku},报价 ${input.offerPriceMinor === undefined ? "?" : (input.offerPriceMinor / 100).toFixed(2)} 元/件`,
|
|
58
|
+
`,条件价(量≥100)${input.dealPriceMinor === undefined ? "?" : (input.dealPriceMinor / 100).toFixed(2)} 元/件`,
|
|
59
|
+
input.agreementId !== undefined ? `,agreement ${input.agreementId}` : "",
|
|
60
|
+
].join("");
|
|
61
|
+
const outcome = store.remember({
|
|
62
|
+
namespace: "episode",
|
|
63
|
+
key: `a2a-negotiation:${input.negotiationId}`,
|
|
64
|
+
value: {
|
|
65
|
+
kind: "a2a_negotiation",
|
|
66
|
+
negotiation_id: input.negotiationId,
|
|
67
|
+
catalog_agent_id: input.catalogAgentId,
|
|
68
|
+
sku: input.sku,
|
|
69
|
+
quantity: input.quantity,
|
|
70
|
+
offer_price_minor: input.offerPriceMinor,
|
|
71
|
+
deal_price_minor: input.dealPriceMinor,
|
|
72
|
+
agreement_id: input.agreementId,
|
|
73
|
+
},
|
|
74
|
+
source_kind: "observed",
|
|
75
|
+
sensitivity: "normal",
|
|
76
|
+
confidence: 1,
|
|
77
|
+
explicit_user_statement: false,
|
|
78
|
+
evidence: {
|
|
79
|
+
source_type: "import",
|
|
80
|
+
source_ref: input.negotiationId,
|
|
81
|
+
summary,
|
|
82
|
+
},
|
|
83
|
+
actor: "system",
|
|
84
|
+
});
|
|
85
|
+
return outcome.kind === "conflict" ? outcome.existing.memory_id : outcome.memory.memory_id;
|
|
86
|
+
}
|
|
87
|
+
function assistantText(message) {
|
|
88
|
+
return message.content
|
|
89
|
+
.filter((b) => b.type === "text")
|
|
90
|
+
.map((b) => b.text)
|
|
91
|
+
.join("\n")
|
|
92
|
+
.trim();
|
|
93
|
+
}
|
|
94
|
+
/** The owner's own Restricted memory values (Vault). Owner-only by isolation. */
|
|
95
|
+
function listRestrictedValues(store) {
|
|
96
|
+
const out = [];
|
|
97
|
+
for (const m of store.listMemories({ sensitivity: "restricted" })) {
|
|
98
|
+
if (m.vault_ref === undefined)
|
|
99
|
+
continue;
|
|
100
|
+
try {
|
|
101
|
+
out.push({ key: m.key, value: store.openVaultValue(m.vault_ref) });
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// data key unavailable on this run: skip rather than crash the read
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Map a buyer task's intent/constraints into negotiation hints so the
|
|
111
|
+
* autonomous loop counters toward the user's actual goal ("买 2 个、预算 120、
|
|
112
|
+
* 砍到 100") instead of the profile defaults.
|
|
113
|
+
*/
|
|
114
|
+
function taskNegotiationHints(store, task) {
|
|
115
|
+
const hints = {};
|
|
116
|
+
if (task.intent.quantity !== undefined)
|
|
117
|
+
hints.quantity_cap = task.intent.quantity;
|
|
118
|
+
if (task.intent.target_unit_price !== undefined) {
|
|
119
|
+
hints.buyer_target_unit_price = task.intent.target_unit_price;
|
|
120
|
+
}
|
|
121
|
+
if (task.constraints.max_unit_price !== undefined) {
|
|
122
|
+
hints.buyer_max_unit_price = task.constraints.max_unit_price;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const budget = store.resolveBudget(task.constraints);
|
|
126
|
+
if (budget !== undefined)
|
|
127
|
+
hints.buyer_max_total_price = budget;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// vault unavailable this run: fall back to the profile budget
|
|
131
|
+
}
|
|
132
|
+
return hints;
|
|
133
|
+
}
|
|
134
|
+
const COMMANDS_HELP = `/memory [preferences|private] 查看记忆概览 / 学习到的偏好 / 私密资料字段与状态
|
|
135
|
+
/forget <memory-id|描述> 删除记忆(tombstone + 审计)
|
|
136
|
+
/correct <memory-id> <新内容> 修正记忆(保留前后版本审计)
|
|
137
|
+
/confirm <memory-id> 人工确认候选记忆生效(硬约束/敏感记忆必须人工确认)
|
|
138
|
+
/private 查看你自己的私密阈值明文(成本/底价,仅你可见,勿写入对外消息)
|
|
139
|
+
/why 说明最近的回答使用了哪些记忆
|
|
140
|
+
/mode [manual|supervised|autopilot] [confirm] 查看或切换写操作审批模式
|
|
141
|
+
/pending 列出等待批准的写操作候选
|
|
142
|
+
/approve <candidate-id> 批准并执行一个写操作候选(重新校验前置状态)
|
|
143
|
+
/reject <candidate-id> 驳回一个写操作候选(绝不执行)
|
|
144
|
+
/profile <file.yaml> 加载并切换另一个 agent profile(buyer/merchant 等)
|
|
145
|
+
/register --agent-card-url <url> [--catalog <url>] [--domain <d>]
|
|
146
|
+
把本 merchant 注册进 agent catalog(buyer 发现用)
|
|
147
|
+
/help 本帮助
|
|
148
|
+
/quit 退出`;
|
|
149
|
+
/** 读会话 JSONL 中最后一条模型记录(model_change 或 assistant 消息的 provider/model)。 */
|
|
150
|
+
function sessionLastModel(file) {
|
|
151
|
+
let last;
|
|
152
|
+
try {
|
|
153
|
+
for (const line of readFileSync(file, "utf-8").split("\n")) {
|
|
154
|
+
if (line.trim() === "")
|
|
155
|
+
continue;
|
|
156
|
+
let entry;
|
|
157
|
+
try {
|
|
158
|
+
entry = JSON.parse(line);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (entry.type === "model_change" &&
|
|
164
|
+
typeof entry.provider === "string" &&
|
|
165
|
+
typeof entry.modelId === "string") {
|
|
166
|
+
last = { provider: entry.provider, modelId: entry.modelId };
|
|
167
|
+
}
|
|
168
|
+
else if (entry.type === "message" &&
|
|
169
|
+
entry.message !== null &&
|
|
170
|
+
typeof entry.message === "object") {
|
|
171
|
+
const m = entry.message;
|
|
172
|
+
if (m.role === "assistant" && typeof m.provider === "string" && typeof m.model === "string") {
|
|
173
|
+
last = { provider: m.provider, modelId: m.model };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// 读不了就当作没有历史,不重置。
|
|
180
|
+
}
|
|
181
|
+
return last;
|
|
182
|
+
}
|
|
183
|
+
export class AgentKernel {
|
|
184
|
+
profile;
|
|
185
|
+
principal;
|
|
186
|
+
paths;
|
|
187
|
+
db;
|
|
188
|
+
store;
|
|
189
|
+
session;
|
|
190
|
+
harness;
|
|
191
|
+
briefing;
|
|
192
|
+
/** 统一时钟(构造注入或墙钟;与 open() 的 clock 闭包同源语义)。 */
|
|
193
|
+
clock;
|
|
194
|
+
chain = Promise.resolve();
|
|
195
|
+
shutdownRequested = false;
|
|
196
|
+
closed = false;
|
|
197
|
+
taskStore;
|
|
198
|
+
scheduler;
|
|
199
|
+
approvals;
|
|
200
|
+
/** v0.7.0 KTH:handoff 存储(open() 构造,buyer 角色注入)。 */
|
|
201
|
+
handoffRuntime;
|
|
202
|
+
commerceClient;
|
|
203
|
+
merchantClient;
|
|
204
|
+
broker;
|
|
205
|
+
/** Shared mutable mode ref (tools read it via a getter before the kernel exists). */
|
|
206
|
+
modeRef = { value: DEFAULT_AGENT_MODE };
|
|
207
|
+
/** Live execution hooks for pending candidates (v0.3.0-C /approve). */
|
|
208
|
+
pendingHooks;
|
|
209
|
+
/** Current conversation-turn id, shared with the remember tool (evidence dedup §9.3). */
|
|
210
|
+
turnId;
|
|
211
|
+
turnSeq = 0;
|
|
212
|
+
/** Negotiation conversations that reached consensus — the auto-follow must never re-open them. */
|
|
213
|
+
settledNegotiations = new Set();
|
|
214
|
+
constructor(options) {
|
|
215
|
+
this.profile = options.profile;
|
|
216
|
+
this.paths = options.paths;
|
|
217
|
+
this.clock = options.now ?? (() => new Date().toISOString());
|
|
218
|
+
this.db = options.db;
|
|
219
|
+
this.store = options.store;
|
|
220
|
+
this.principal = options.principal;
|
|
221
|
+
this.session = options.session;
|
|
222
|
+
this.harness = options.harness;
|
|
223
|
+
if (options.taskStore !== undefined)
|
|
224
|
+
this.taskStore = options.taskStore;
|
|
225
|
+
if (options.scheduler !== undefined)
|
|
226
|
+
this.scheduler = options.scheduler;
|
|
227
|
+
if (options.approvals !== undefined)
|
|
228
|
+
this.approvals = options.approvals;
|
|
229
|
+
if (options.handoffRuntime !== undefined)
|
|
230
|
+
this.handoffRuntime = options.handoffRuntime;
|
|
231
|
+
if (options.commerceClient !== undefined)
|
|
232
|
+
this.commerceClient = options.commerceClient;
|
|
233
|
+
if (options.merchantClient !== undefined)
|
|
234
|
+
this.merchantClient = options.merchantClient;
|
|
235
|
+
if (options.broker !== undefined)
|
|
236
|
+
this.broker = options.broker;
|
|
237
|
+
if (options.modeRef !== undefined)
|
|
238
|
+
this.modeRef = options.modeRef;
|
|
239
|
+
this.pendingHooks = options.pendingHooks ?? new Map();
|
|
240
|
+
this.turnId = options.turnId;
|
|
241
|
+
}
|
|
242
|
+
static async open(options) {
|
|
243
|
+
const paths = options.paths ?? ensureAgentPaths(options.profile.agent_id);
|
|
244
|
+
// 会话来自不同模型(如 fake→deepseek)时重置:旧模型的消息会让新模型首轮
|
|
245
|
+
// 产生空响应("模型没有返回内容")。模型变更 = 新的对话历史。
|
|
246
|
+
if (options.model !== undefined && existsSync(paths.mainSession)) {
|
|
247
|
+
const sessionModel = sessionLastModel(paths.mainSession);
|
|
248
|
+
if (sessionModel !== undefined &&
|
|
249
|
+
(sessionModel.provider !== options.model.provider || sessionModel.modelId !== options.model.id)) {
|
|
250
|
+
rmSync(paths.mainSession, { force: true });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const db = openAgentDatabase(paths.db);
|
|
254
|
+
const vault = options.vault ?? new PrivateVault();
|
|
255
|
+
const store = new MemoryStore({ db, vault, ...(options.now ? { now: options.now } : {}) });
|
|
256
|
+
const principal = store.ensurePrincipal({
|
|
257
|
+
principal_id: options.profile.agent_id,
|
|
258
|
+
owner_id: options.profile.owner_id,
|
|
259
|
+
role: options.profile.role,
|
|
260
|
+
});
|
|
261
|
+
store.bindPrincipal(principal.principal_id);
|
|
262
|
+
let session;
|
|
263
|
+
try {
|
|
264
|
+
session = await openMainSession(paths);
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
db.close(); // never leak the SQLite handle on a fail-closed open
|
|
268
|
+
throw err;
|
|
269
|
+
}
|
|
270
|
+
// Buyer capability pack (v0.3.0-B): task store + scheduler + tools.
|
|
271
|
+
// All clocks are normalized to UTC ISO (SQLite compares timestamps
|
|
272
|
+
// lexicographically; mixed offsets would silently break due checks).
|
|
273
|
+
const clock = () => new Date(Date.parse((options.now ?? (() => new Date().toISOString()))())).toISOString();
|
|
274
|
+
const modeRef = { value: options.mode ?? DEFAULT_AGENT_MODE };
|
|
275
|
+
const pendingHooks = new Map();
|
|
276
|
+
const turnId = { current: MAIN_SESSION_ID };
|
|
277
|
+
const registerPending = (id, hooks) => {
|
|
278
|
+
pendingHooks.set(id, hooks);
|
|
279
|
+
};
|
|
280
|
+
const approvals = new WriteApprovalCandidateStore({ db, principalId: principal.principal_id, now: clock });
|
|
281
|
+
let taskStore;
|
|
282
|
+
let scheduler;
|
|
283
|
+
let buyerTools = [];
|
|
284
|
+
let merchantTools = [];
|
|
285
|
+
let handoffRuntime;
|
|
286
|
+
if (options.profile.role === "buyer" && options.connector !== undefined) {
|
|
287
|
+
taskStore = new BuyerTaskStore({ db, principalId: principal.principal_id, now: clock, vault });
|
|
288
|
+
scheduler = new TaskScheduler({
|
|
289
|
+
store: taskStore,
|
|
290
|
+
connectors: [options.connector],
|
|
291
|
+
now: clock,
|
|
292
|
+
});
|
|
293
|
+
// v0.7.0 KTH:handoff 存储(Ledger 事件 + 执行幂等)落在 agent data dir,
|
|
294
|
+
// 注入 buyer 工具(handoff_agreement 工具挂载)。
|
|
295
|
+
const handoffDir = path.dirname(paths.db);
|
|
296
|
+
const handoffLedger = new HandoffEventStore({ dir: handoffDir, now: clock });
|
|
297
|
+
const handoffIdempotency = new HandoffIdempotencyStore({ dir: handoffDir, now: clock });
|
|
298
|
+
handoffRuntime = { ledger: handoffLedger, idempotency: handoffIdempotency };
|
|
299
|
+
buyerTools = buildBuyerTools({
|
|
300
|
+
store: taskStore,
|
|
301
|
+
connector: options.connector,
|
|
302
|
+
profile: options.profile,
|
|
303
|
+
...(options.commerceClient !== undefined ? { commerceClient: options.commerceClient } : {}),
|
|
304
|
+
...(options.broker !== undefined ? { broker: options.broker } : {}),
|
|
305
|
+
...(options.catalog !== undefined
|
|
306
|
+
? {
|
|
307
|
+
catalog: options.catalog,
|
|
308
|
+
// CD #27 wiring:buyer kernel 配置 catalog 时注入 KiwiCatalogSource,
|
|
309
|
+
// search_listings / shortlist_listing 工具才挂载(历史教训:工具
|
|
310
|
+
// 只在测试里可达,运行时从未注入——dead code)。
|
|
311
|
+
catalogSource: new KiwiCatalogSource({ baseUrl: options.catalog }),
|
|
312
|
+
}
|
|
313
|
+
: {}),
|
|
314
|
+
approvals,
|
|
315
|
+
mode: () => modeRef.value,
|
|
316
|
+
registerPending,
|
|
317
|
+
now: clock,
|
|
318
|
+
recordNegotiation: (input) => rememberNegotiation(store, input),
|
|
319
|
+
handoff: { ledger: handoffLedger, idempotency: handoffIdempotency },
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
else if (options.profile.role === "merchant" &&
|
|
323
|
+
options.merchantClient !== undefined &&
|
|
324
|
+
options.broker !== undefined) {
|
|
325
|
+
merchantTools = buildMerchantTools({
|
|
326
|
+
profile: options.profile,
|
|
327
|
+
merchantClient: options.merchantClient,
|
|
328
|
+
// Negotiation token (commerceClient) is optional: absent → 磋商工具
|
|
329
|
+
// fail closed,目录/库存只读与审批式写工具照常可用(见 §15.3/§15.4)。
|
|
330
|
+
...(options.commerceClient !== undefined ? { commerceClient: options.commerceClient } : {}),
|
|
331
|
+
broker: options.broker,
|
|
332
|
+
approvals,
|
|
333
|
+
mode: () => modeRef.value,
|
|
334
|
+
registerPending,
|
|
335
|
+
now: clock,
|
|
336
|
+
privateValues: () => listRestrictedValues(store),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
const base = baseSystemPrompt(options.profile, principal);
|
|
340
|
+
let briefing;
|
|
341
|
+
const harness = new AgentHarness({
|
|
342
|
+
session,
|
|
343
|
+
models: options.models,
|
|
344
|
+
model: options.model,
|
|
345
|
+
tools: [...buildMemoryTools(store, { turnId: () => turnId.current }), ...buyerTools, ...merchantTools],
|
|
346
|
+
systemPrompt: async () => (briefing === undefined ? base : `${base}\n\n${briefing}`),
|
|
347
|
+
// §18.1: a hung model/provider request must NOT wedge the chat forever —
|
|
348
|
+
// abort after the profile's turn timeout and surface an error text.
|
|
349
|
+
streamOptions: {
|
|
350
|
+
timeoutMs: options.profile.runtime.turn_timeout_seconds * 1000,
|
|
351
|
+
},
|
|
352
|
+
...(options.thinkingLevel !== undefined ? { thinkingLevel: options.thinkingLevel } : {}),
|
|
353
|
+
});
|
|
354
|
+
// 强制使用 profile 指定的模型:pi-agent-core 会从 session 的 model_change
|
|
355
|
+
// 记录恢复历史模型——同一 agent_id 换过模型(如 fake→deepseek)时,旧会话
|
|
356
|
+
// 会把 fake 模型恢复回来覆盖新传入的 real 模型。显式 setModel 覆盖之。
|
|
357
|
+
if (options.model !== undefined) {
|
|
358
|
+
await harness.setModel(options.model);
|
|
359
|
+
}
|
|
360
|
+
const kernel = new AgentKernel({
|
|
361
|
+
profile: options.profile,
|
|
362
|
+
paths,
|
|
363
|
+
db,
|
|
364
|
+
store,
|
|
365
|
+
principal,
|
|
366
|
+
session,
|
|
367
|
+
harness,
|
|
368
|
+
approvals,
|
|
369
|
+
...(handoffRuntime !== undefined ? { handoffRuntime } : {}),
|
|
370
|
+
modeRef,
|
|
371
|
+
pendingHooks,
|
|
372
|
+
turnId,
|
|
373
|
+
now: clock,
|
|
374
|
+
...(taskStore !== undefined ? { taskStore } : {}),
|
|
375
|
+
...(scheduler !== undefined ? { scheduler } : {}),
|
|
376
|
+
...(options.commerceClient !== undefined ? { commerceClient: options.commerceClient } : {}),
|
|
377
|
+
...(options.merchantClient !== undefined ? { merchantClient: options.merchantClient } : {}),
|
|
378
|
+
...(options.broker !== undefined ? { broker: options.broker } : {}),
|
|
379
|
+
});
|
|
380
|
+
kernel.briefingSetter = (value) => {
|
|
381
|
+
briefing = value;
|
|
382
|
+
};
|
|
383
|
+
return kernel;
|
|
384
|
+
}
|
|
385
|
+
briefingSetter = () => undefined;
|
|
386
|
+
/**
|
|
387
|
+
* v0.7.0 KTH:handoff 运行态摘要(/handoff 命令用)——候选生命周期 +
|
|
388
|
+
* 交付观察状态,从 Ledger 事件投影(#17 用户可见目标与摘要、#18 可审计)。
|
|
389
|
+
*/
|
|
390
|
+
get handoffSummary() {
|
|
391
|
+
if (this.handoffRuntime === undefined)
|
|
392
|
+
return { enabled: false };
|
|
393
|
+
const candidates = [];
|
|
394
|
+
const handoffs = [];
|
|
395
|
+
for (const negotiationId of this.handoffRuntime.ledger.listNegotiations()) {
|
|
396
|
+
const events = this.handoffRuntime.ledger.events(negotiationId);
|
|
397
|
+
const seenCandidates = new Set();
|
|
398
|
+
const seenHandoffs = new Set();
|
|
399
|
+
for (const event of events) {
|
|
400
|
+
if (event.handoff_candidate_id !== undefined && !seenCandidates.has(event.handoff_candidate_id)) {
|
|
401
|
+
seenCandidates.add(event.handoff_candidate_id);
|
|
402
|
+
const lifecycle = foldCandidateLifecycle(events.filter((e) => e.handoff_candidate_id === event.handoff_candidate_id));
|
|
403
|
+
const candidateEvents = events.filter((e) => e.handoff_candidate_id === event.handoff_candidate_id);
|
|
404
|
+
const created = candidateEvents.find((e) => e.event_kind === "handoff_candidate_created");
|
|
405
|
+
const embedded = created?.outcome.kind === "ok"
|
|
406
|
+
? created.outcome.result?.candidate
|
|
407
|
+
: undefined;
|
|
408
|
+
candidates.push({
|
|
409
|
+
candidate_id: event.handoff_candidate_id,
|
|
410
|
+
negotiation_id: negotiationId,
|
|
411
|
+
lifecycle: lifecycle ?? "UNKNOWN",
|
|
412
|
+
destination_type: typeof embedded?.destination_type === "string" ? embedded.destination_type : "?",
|
|
413
|
+
destination_ref: typeof embedded?.destination_ref === "string" ? embedded.destination_ref : "?",
|
|
414
|
+
display_summary: (() => {
|
|
415
|
+
const summary = embedded?.display_summary;
|
|
416
|
+
return {
|
|
417
|
+
merchant: typeof summary?.merchant === "string" ? summary.merchant : "?",
|
|
418
|
+
summary: typeof summary?.summary === "string" ? summary.summary : "?",
|
|
419
|
+
};
|
|
420
|
+
})(),
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (event.handoff_id !== undefined && !seenHandoffs.has(event.handoff_id)) {
|
|
424
|
+
seenHandoffs.add(event.handoff_id);
|
|
425
|
+
handoffs.push({
|
|
426
|
+
handoff_id: event.handoff_id,
|
|
427
|
+
delivery: deliveryState(events.filter((e) => e.handoff_id === event.handoff_id)) ?? "?",
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return { enabled: true, candidates, handoffs };
|
|
433
|
+
}
|
|
434
|
+
/** 模拟 OS/browser/deep-link handler 启动(LAUNCHED;不证明页面加载)。 */
|
|
435
|
+
async launchHandoff(handoffId, negotiationId) {
|
|
436
|
+
if (this.handoffRuntime === undefined)
|
|
437
|
+
return "Handoff 未启用。";
|
|
438
|
+
const events = this.handoffRuntime.ledger.events(negotiationId);
|
|
439
|
+
const handoffEvents = events.filter((e) => e.handoff_id === handoffId);
|
|
440
|
+
if (handoffEvents.length === 0)
|
|
441
|
+
return `未知 handoff ${handoffId}(negotiation ${negotiationId})。`;
|
|
442
|
+
const candidateId = handoffEvents[0]?.handoff_candidate_id;
|
|
443
|
+
if (candidateId === undefined)
|
|
444
|
+
return "handoff 事件缺少候选引用。";
|
|
445
|
+
const candidateEvents = events.filter((e) => e.handoff_candidate_id === candidateId);
|
|
446
|
+
const created = candidateEvents.find((e) => e.event_kind === "handoff_candidate_created");
|
|
447
|
+
const embedded = created?.outcome.kind === "ok"
|
|
448
|
+
? created.outcome.result?.candidate
|
|
449
|
+
: undefined;
|
|
450
|
+
if (embedded === undefined)
|
|
451
|
+
return "候选文档缺失,无法启动。";
|
|
452
|
+
const { validateHandoffCandidate } = await import("../handoff/index.js");
|
|
453
|
+
const candidate = validateHandoffCandidate(embedded);
|
|
454
|
+
recordLaunch({
|
|
455
|
+
ledger: this.handoffRuntime.ledger,
|
|
456
|
+
candidate,
|
|
457
|
+
handoff_id: handoffId,
|
|
458
|
+
identity: { sender_identity: candidate.buyer_identity_ref, counterparty_identity: candidate.merchant_identity_ref, actor: "buyer" },
|
|
459
|
+
capability: { capability: "com.harrylabsj.kiwi.shopping.negotiation", protocol_version: "1.0" },
|
|
460
|
+
now: () => new Date().toISOString(),
|
|
461
|
+
});
|
|
462
|
+
return `handoff ${handoffId} 已启动(LAUNCHED)——不证明页面加载。`;
|
|
463
|
+
}
|
|
464
|
+
/** 本地回调证据:handoff-open 演示(OPENED_CONFIRMED 证据门,KTH §9)。 */
|
|
465
|
+
async confirmHandoffOpened(handoffId, negotiationId) {
|
|
466
|
+
if (this.handoffRuntime === undefined)
|
|
467
|
+
return "Handoff 未启用。";
|
|
468
|
+
const events = this.handoffRuntime.ledger.events(negotiationId);
|
|
469
|
+
const handoffEvents = events.filter((e) => e.handoff_id === handoffId);
|
|
470
|
+
if (handoffEvents.length === 0)
|
|
471
|
+
return `未知 handoff ${handoffId}(negotiation ${negotiationId})。`;
|
|
472
|
+
const candidateId = handoffEvents[0]?.handoff_candidate_id;
|
|
473
|
+
if (candidateId === undefined)
|
|
474
|
+
return "handoff 事件缺少候选引用。";
|
|
475
|
+
const created = events
|
|
476
|
+
.filter((e) => e.handoff_candidate_id === candidateId)
|
|
477
|
+
.find((e) => e.event_kind === "handoff_candidate_created");
|
|
478
|
+
const embedded = created?.outcome.kind === "ok"
|
|
479
|
+
? created.outcome.result?.candidate
|
|
480
|
+
: undefined;
|
|
481
|
+
if (embedded === undefined)
|
|
482
|
+
return "候选文档缺失,无法确认。";
|
|
483
|
+
const { validateHandoffCandidate } = await import("../handoff/index.js");
|
|
484
|
+
const candidate = validateHandoffCandidate(embedded);
|
|
485
|
+
recordOpenEvidence({
|
|
486
|
+
ledger: this.handoffRuntime.ledger,
|
|
487
|
+
candidate,
|
|
488
|
+
handoff_id: handoffId,
|
|
489
|
+
identity: { sender_identity: candidate.buyer_identity_ref, counterparty_identity: candidate.merchant_identity_ref, actor: "buyer" },
|
|
490
|
+
capability: { capability: "com.harrylabsj.kiwi.shopping.negotiation", protocol_version: "1.0" },
|
|
491
|
+
now: () => new Date().toISOString(),
|
|
492
|
+
evidence: { kind: "local_callback", handoff_id: handoffId, at: new Date().toISOString() },
|
|
493
|
+
});
|
|
494
|
+
return `handoff ${handoffId} 已确认打开(OPENED_CONFIRMED,evidence=local_callback)。`;
|
|
495
|
+
}
|
|
496
|
+
get memoryStore() {
|
|
497
|
+
return this.store;
|
|
498
|
+
}
|
|
499
|
+
/** Buyer task store (undefined for merchant kernels). */
|
|
500
|
+
get buyerTasks() {
|
|
501
|
+
return this.taskStore;
|
|
502
|
+
}
|
|
503
|
+
/** WriteApprovalCandidate approval store (both roles; undefined only if unopened). */
|
|
504
|
+
get actionCandidates() {
|
|
505
|
+
return this.approvals;
|
|
506
|
+
}
|
|
507
|
+
/** Current runtime write-approval mode (design §16). */
|
|
508
|
+
getMode() {
|
|
509
|
+
return this.modeRef.value;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Change the runtime mode. Switching INTO autopilot requires an explicit
|
|
513
|
+
* `confirm` — the same fail-closed rule as the operator control plane
|
|
514
|
+
* (docs/operator-tui-v0.2.md §8).
|
|
515
|
+
*/
|
|
516
|
+
setMode(mode, options) {
|
|
517
|
+
if (!isAgentMode(mode)) {
|
|
518
|
+
return { ok: false, error: `未知模式:${String(mode)}(可选 ${AGENT_MODES.join("/")})` };
|
|
519
|
+
}
|
|
520
|
+
if (this.modeRef.value === mode)
|
|
521
|
+
return { ok: true };
|
|
522
|
+
if (mode === "autopilot" && options?.confirmed !== true) {
|
|
523
|
+
return { ok: false, error: "切换到 autopilot 需要显式确认:/mode autopilot confirm" };
|
|
524
|
+
}
|
|
525
|
+
this.modeRef.value = mode;
|
|
526
|
+
return { ok: true };
|
|
527
|
+
}
|
|
528
|
+
/** Pending WriteApprovalCandidates awaiting /approve (fail closed on expiry). */
|
|
529
|
+
listPendingApprovals() {
|
|
530
|
+
if (this.approvals === undefined)
|
|
531
|
+
return [];
|
|
532
|
+
const pending = this.approvals.listPending(); // 内部 expireDue:过期候选已标记 expired
|
|
533
|
+
// 清理已终结候选的执行钩子闭包(评审项 P3-5):expired/superseded/
|
|
534
|
+
// executed/rejected 后的 hooks 不再需要,防内存无界增长。
|
|
535
|
+
const live = new Set(pending.map((c) => c.candidate_id));
|
|
536
|
+
for (const id of [...this.pendingHooks.keys()]) {
|
|
537
|
+
if (!live.has(id))
|
|
538
|
+
this.pendingHooks.delete(id);
|
|
539
|
+
}
|
|
540
|
+
return pending;
|
|
541
|
+
}
|
|
542
|
+
/** 释放候选执行钩子闭包(评审项 P3-5;候选生命周期终结后调用)。 */
|
|
543
|
+
releasePending(candidateId) {
|
|
544
|
+
this.pendingHooks.delete(candidateId);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Approve + execute a pending WriteApprovalCandidate. Execution re-reads the
|
|
548
|
+
* preconditions and re-hashes them (§16): a stale or expired approval is
|
|
549
|
+
* superseded, never executed. A candidate left over from a previous process
|
|
550
|
+
* (no live execution hooks) is expired, matching operator-plane recovery.
|
|
551
|
+
*/
|
|
552
|
+
approveCandidate(candidateId) {
|
|
553
|
+
// Serialized with all other kernel work. Note: slash handlers run INSIDE
|
|
554
|
+
// the kernel chain, so they call approveCandidateInner directly to avoid
|
|
555
|
+
// a nested-enqueue deadlock.
|
|
556
|
+
return this.enqueue(() => this.approveCandidateInner(candidateId));
|
|
557
|
+
}
|
|
558
|
+
/** Approval execution without re-enqueueing (callers must already be serialized). */
|
|
559
|
+
async approveCandidateInner(candidateId) {
|
|
560
|
+
if (this.approvals === undefined) {
|
|
561
|
+
throw new MemoryError("validation", "审批存储不可用");
|
|
562
|
+
}
|
|
563
|
+
const candidate = this.approvals.get(candidateId);
|
|
564
|
+
if (candidate === undefined) {
|
|
565
|
+
throw new MemoryError("validation", `未知审批候选 ${candidateId}`);
|
|
566
|
+
}
|
|
567
|
+
// manual 模式语义(评审项 P3-3):manual = advice only(never executes)。
|
|
568
|
+
// routeWriteCandidate 在 manual 分支仍注册执行钩子(供 /pending 显示与
|
|
569
|
+
// /revise 重算),但批准路径必须拒绝——此前 /approve 绕过模式直接执行,
|
|
570
|
+
// 与 operator 平面分叉(controller 对 advice_only 候选明确拒绝批准:
|
|
571
|
+
// "manual 模式只提供建议,不自动提交")。
|
|
572
|
+
if (this.getMode() === "manual") {
|
|
573
|
+
return {
|
|
574
|
+
kind: "not_approvable",
|
|
575
|
+
candidate,
|
|
576
|
+
reason: "manual 模式只提供建议,不自动执行(/approve 拒绝 advice-only 候选)",
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
const hooks = this.pendingHooks.get(candidateId);
|
|
580
|
+
if (hooks === undefined) {
|
|
581
|
+
// Cross-restart recovery (design §18.3): without live hooks the
|
|
582
|
+
// candidate cannot be re-validated against the current marketplace.
|
|
583
|
+
this.approvals.expireDue();
|
|
584
|
+
this.approvals.supersede(candidateId);
|
|
585
|
+
return {
|
|
586
|
+
kind: "not_approvable",
|
|
587
|
+
candidate: this.approvals.get(candidateId),
|
|
588
|
+
reason: `候选 ${candidateId} 没有可用的执行钩子(可能在重启前生成);已失效,请重新生成。`,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
this.approvals.markApproved(candidateId);
|
|
592
|
+
const outcome = await executeApprovedCandidate(this.approvals, candidateId, hooks);
|
|
593
|
+
// 无论结果(executed / stale=superseded / expired),候选生命周期已终结,
|
|
594
|
+
// 执行钩子不再需要——释放闭包防泄漏(评审项 P3-5)。
|
|
595
|
+
if (outcome.kind !== "not_approvable") {
|
|
596
|
+
this.releasePending(candidateId);
|
|
597
|
+
}
|
|
598
|
+
return outcome;
|
|
599
|
+
}
|
|
600
|
+
/** Reject a pending/advice-only WriteApprovalCandidate. Never executes. */
|
|
601
|
+
rejectCandidate(candidateId) {
|
|
602
|
+
if (this.approvals === undefined)
|
|
603
|
+
return { ok: false, error: "审批存储不可用" };
|
|
604
|
+
const candidate = this.approvals.get(candidateId);
|
|
605
|
+
if (candidate === undefined)
|
|
606
|
+
return { ok: false, error: `未知审批候选 ${candidateId}` };
|
|
607
|
+
if (candidate.status !== "pending_approval" && candidate.status !== "approved") {
|
|
608
|
+
return { ok: false, error: `候选 ${candidateId} 状态为 ${candidate.status},不可驳回` };
|
|
609
|
+
}
|
|
610
|
+
this.approvals.reject(candidateId);
|
|
611
|
+
this.releasePending(candidateId);
|
|
612
|
+
return { ok: true };
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Run one scheduler tick (buyer kernels only): due wakeups, tracking-rule
|
|
616
|
+
* checks and notifications, all derived from the database (restart-safe).
|
|
617
|
+
* Serialized with everything else the kernel does.
|
|
618
|
+
*/
|
|
619
|
+
schedulerTick(budget = {}) {
|
|
620
|
+
return this.enqueue(async () => {
|
|
621
|
+
// 惰性过期清扫(评审项 L1):到期未执行的 handoff 候选落 expired 事件,
|
|
622
|
+
// TUI /handoff 不再永久显示"从未获批/从未执行"。
|
|
623
|
+
this.handoffRuntime?.ledger.sweepExpiredCandidates(this.clock());
|
|
624
|
+
if (this.scheduler === undefined) {
|
|
625
|
+
return {
|
|
626
|
+
checked_rules: 0,
|
|
627
|
+
notifications: [],
|
|
628
|
+
tasks_searched: [],
|
|
629
|
+
tasks_expired: [],
|
|
630
|
+
errors: [],
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
return this.scheduler.tick(budget);
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
/** 惰性过期清扫入口(/handoff 渲染前调用;幂等,可重复调)。 */
|
|
637
|
+
sweepHandoffCandidates() {
|
|
638
|
+
if (this.handoffRuntime === undefined)
|
|
639
|
+
return 0;
|
|
640
|
+
return this.handoffRuntime.ledger.sweepExpiredCandidates(this.clock());
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Autonomous negotiation follow-up (autopilot only): if this agent has a
|
|
644
|
+
* pending negotiation message, drive ONE deterministic turn to handle it.
|
|
645
|
+
* The deterministic decision negotiates within the profile's HardPolicy
|
|
646
|
+
* (buyer budget / merchant floor+discount) and auto-submits through the
|
|
647
|
+
* gateway gate — no per-turn human and no LLM tool-loop fragility. Escalated
|
|
648
|
+
* (out-of-rule) decisions park for a human. Returns a progress line for the
|
|
649
|
+
* chat, or undefined when there is nothing to do.
|
|
650
|
+
*/
|
|
651
|
+
negotiationAutoTick() {
|
|
652
|
+
// 与 schedulerTick 同级:整个 tick 必须进 kernel 串行链(enqueue)。此前
|
|
653
|
+
// 被 chat-tui 定时器直接调用,可与最长 turn_timeout 的模型回合并发操作
|
|
654
|
+
// 同一 pending conversation(双路同时 claim/提交同一磋商)。
|
|
655
|
+
return this.enqueue(async () => this.negotiationAutoTickUnlocked());
|
|
656
|
+
}
|
|
657
|
+
async negotiationAutoTickUnlocked() {
|
|
658
|
+
if (this.modeRef.value !== "autopilot")
|
|
659
|
+
return undefined;
|
|
660
|
+
if (this.commerceClient === undefined)
|
|
661
|
+
return undefined;
|
|
662
|
+
// Skip conversations already settled/at-consensus BEFORE claiming — the
|
|
663
|
+
// stateless deterministic decision would otherwise re-counter the same
|
|
664
|
+
// offer forever and re-open settled negotiations every tick.
|
|
665
|
+
let pending;
|
|
666
|
+
try {
|
|
667
|
+
pending = await this.commerceClient.listPendingMessages();
|
|
668
|
+
}
|
|
669
|
+
catch {
|
|
670
|
+
return undefined;
|
|
671
|
+
}
|
|
672
|
+
if (pending.every((m) => this.settledNegotiations.has(m.conversation_id))) {
|
|
673
|
+
return undefined;
|
|
674
|
+
}
|
|
675
|
+
// Buyer: negotiate toward the linked task's goal (quantity / target unit
|
|
676
|
+
// price / budget) instead of the profile defaults.
|
|
677
|
+
let hints;
|
|
678
|
+
const firstPending = pending[0];
|
|
679
|
+
if (firstPending !== undefined && this.profile.role === "buyer" && this.taskStore !== undefined) {
|
|
680
|
+
const link = this.taskStore.linkByConversation(firstPending.conversation_id);
|
|
681
|
+
if (link !== undefined) {
|
|
682
|
+
const task = this.taskStore.getTask(link.task_id);
|
|
683
|
+
if (task !== undefined)
|
|
684
|
+
hints = taskNegotiationHints(this.taskStore, task);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
const runner = new DeterministicNegotiationRunner(this.profile, this.commerceClient);
|
|
688
|
+
const prepared = await runner
|
|
689
|
+
.prepare({ ...(hints !== undefined ? { hints } : {}) })
|
|
690
|
+
.catch(() => undefined);
|
|
691
|
+
if (prepared === undefined)
|
|
692
|
+
return undefined;
|
|
693
|
+
const convId = prepared.binding.conversation_id;
|
|
694
|
+
// Termination: STOP when the counterpart just accepted our offer — report
|
|
695
|
+
// the consensus once and never re-open this conversation.
|
|
696
|
+
if (prepared.counterpart_action === "accept_nonbinding") {
|
|
697
|
+
this.settledNegotiations.add(convId);
|
|
698
|
+
await runner.abandon(prepared, "counterpart accepted non-binding — consensus").catch(() => undefined);
|
|
699
|
+
return `已达成共识(对方接受非绑定报价),磋商结束,不再继续。`;
|
|
700
|
+
}
|
|
701
|
+
if (this.settledNegotiations.has(convId)) {
|
|
702
|
+
await runner.abandon(prepared, "negotiation already settled").catch(() => undefined);
|
|
703
|
+
return undefined;
|
|
704
|
+
}
|
|
705
|
+
const outcome = await runner.submit(prepared).catch(() => undefined);
|
|
706
|
+
if (outcome === undefined)
|
|
707
|
+
return "磋商处理失败(网关异常),下一轮自动重试。";
|
|
708
|
+
// Our own non-binding acceptance ends the negotiation.
|
|
709
|
+
if (prepared.decision.action === "accept_nonbinding")
|
|
710
|
+
this.settledNegotiations.add(convId);
|
|
711
|
+
const counterpart = prepared.counterpart_message !== undefined
|
|
712
|
+
? `对方:「${prepared.counterpart_message.slice(0, 60)}」`
|
|
713
|
+
: "";
|
|
714
|
+
const proposal = prepared.decision.proposal;
|
|
715
|
+
const offer = proposal !== undefined
|
|
716
|
+
? `,出价 ${proposal.unit_price}${proposal.currency ?? ""}×${proposal.quantity}`
|
|
717
|
+
: "";
|
|
718
|
+
const sent = prepared.decision.public_message !== ""
|
|
719
|
+
? `;已发「${prepared.decision.public_message.slice(0, 60)}」`
|
|
720
|
+
: "";
|
|
721
|
+
const detail = outcome.settlement === "failed" ? `(${outcome.policy_result.public_reason})` : "";
|
|
722
|
+
return (`已自动处理 ${convId}:${counterpart} → ` +
|
|
723
|
+
`${prepared.decision.action}${offer} = ${outcome.policy_result.result}${sent}${detail}`);
|
|
724
|
+
}
|
|
725
|
+
get isShutdownRequested() {
|
|
726
|
+
return this.shutdownRequested;
|
|
727
|
+
}
|
|
728
|
+
/** Serialize all kernel work — one message, command or event at a time. */
|
|
729
|
+
enqueue(work) {
|
|
730
|
+
const next = this.chain.then(work);
|
|
731
|
+
this.chain = next.catch(() => undefined);
|
|
732
|
+
return next;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Handle one user input line: slash commands run deterministically;
|
|
736
|
+
* anything else goes to the model with a per-turn memory briefing.
|
|
737
|
+
*/
|
|
738
|
+
/**
|
|
739
|
+
* 把一段上下文注入会话(不触发模型回复):`/negotiate` 等确定性动作的结果
|
|
740
|
+
* 写入 session(user 角色、带「系统记录」标记),下一轮用户提问时模型能看到
|
|
741
|
+
* (消除"两个脑")。
|
|
742
|
+
*/
|
|
743
|
+
async injectContext(text) {
|
|
744
|
+
await this.harness.appendMessage({
|
|
745
|
+
role: "user",
|
|
746
|
+
content: `[系统记录] ${text}`,
|
|
747
|
+
timestamp: Date.now(),
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* 把一轮 A2A 磋商结果写入记忆(episode namespace,active 无需人工确认):
|
|
752
|
+
* 持久上下文,`/why` 可查、跨重启可恢复。
|
|
753
|
+
*/
|
|
754
|
+
recordNegotiation(input) {
|
|
755
|
+
return rememberNegotiation(this.store, input);
|
|
756
|
+
}
|
|
757
|
+
handleUserText(text) {
|
|
758
|
+
return this.enqueue(async () => {
|
|
759
|
+
if (this.closed)
|
|
760
|
+
throw new MemoryError("validation", "kernel is closed");
|
|
761
|
+
if (text.startsWith("/")) {
|
|
762
|
+
return await this.handleSlash(text);
|
|
763
|
+
}
|
|
764
|
+
const memories = this.store.retrieve({
|
|
765
|
+
session_id: MAIN_SESSION_ID,
|
|
766
|
+
purpose: "clarify",
|
|
767
|
+
text,
|
|
768
|
+
});
|
|
769
|
+
this.briefingSetter(renderMemoryBriefing(memories));
|
|
770
|
+
// Advance the turn id so several remember calls inside this one turn
|
|
771
|
+
// dedup into a single evidence piece (§9.3).
|
|
772
|
+
this.turnId.current = `${MAIN_SESSION_ID}:${++this.turnSeq}`;
|
|
773
|
+
try {
|
|
774
|
+
const message = await this.harness.prompt(text);
|
|
775
|
+
const reply = assistantText(message);
|
|
776
|
+
// §18.1: a model failure (throw OR empty response) must leave the
|
|
777
|
+
// conversation and TUI intact.
|
|
778
|
+
if (reply === "") {
|
|
779
|
+
return { text: "模型没有返回内容,请重试。", quit: false };
|
|
780
|
+
}
|
|
781
|
+
return { text: reply, quit: false };
|
|
782
|
+
}
|
|
783
|
+
catch (err) {
|
|
784
|
+
return {
|
|
785
|
+
text: `模型处理失败:${err instanceof Error ? err.message : String(err)}。对话状态未改变,请重试。`,
|
|
786
|
+
quit: false,
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
finally {
|
|
790
|
+
this.briefingSetter(undefined);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
async handleSlash(input) {
|
|
795
|
+
const [command = "", ...rest] = input.trim().split(/\s+/);
|
|
796
|
+
const arg = rest.join(" ").trim();
|
|
797
|
+
try {
|
|
798
|
+
switch (command) {
|
|
799
|
+
case "/memory":
|
|
800
|
+
return { text: this.renderMemory(arg), quit: false };
|
|
801
|
+
case "/forget":
|
|
802
|
+
return { text: this.handleForget(arg), quit: false };
|
|
803
|
+
case "/correct":
|
|
804
|
+
return { text: this.handleCorrect(arg), quit: false };
|
|
805
|
+
case "/confirm":
|
|
806
|
+
return { text: this.handleConfirm(arg), quit: false };
|
|
807
|
+
case "/private":
|
|
808
|
+
return { text: this.renderPrivate(), quit: false };
|
|
809
|
+
case "/why":
|
|
810
|
+
return { text: this.renderWhy(), quit: false };
|
|
811
|
+
case "/mode":
|
|
812
|
+
return { text: this.handleMode(arg), quit: false };
|
|
813
|
+
case "/pending":
|
|
814
|
+
return { text: this.renderPending(), quit: false };
|
|
815
|
+
case "/approve":
|
|
816
|
+
return { text: await this.handleApprove(arg), quit: false };
|
|
817
|
+
case "/reject":
|
|
818
|
+
return { text: this.handleReject(arg), quit: false };
|
|
819
|
+
case "/register":
|
|
820
|
+
return { text: await this.handleRegister(arg), quit: false };
|
|
821
|
+
case "/help":
|
|
822
|
+
return { text: COMMANDS_HELP, quit: false };
|
|
823
|
+
case "/quit":
|
|
824
|
+
this.shutdownRequested = true;
|
|
825
|
+
return { text: "正在安全退出…", quit: true };
|
|
826
|
+
default:
|
|
827
|
+
return { text: `未知命令 ${command},输入 /help 查看命令。`, quit: false };
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
catch (err) {
|
|
831
|
+
const message = err instanceof MemoryError ? err.message : String(err);
|
|
832
|
+
return { text: `命令失败:${message}`, quit: false };
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// ---- slash command implementations ---------------------------------------
|
|
836
|
+
renderMemory(arg) {
|
|
837
|
+
if (arg === "preferences") {
|
|
838
|
+
const prefs = this.store.listMemories({ namespace: "preference" });
|
|
839
|
+
if (prefs.length === 0)
|
|
840
|
+
return "[记忆] 还没有学习到的偏好。";
|
|
841
|
+
const lines = prefs.map((m) => ` · ${m.memory_id} [${m.status}] ${m.key}(置信度 ${m.confidence} · ${m.source_kind} · 证据 ${m.evidence_count})`);
|
|
842
|
+
return ["[记忆] 学习到的偏好(候选需确认后才生效):", ...lines].join("\n");
|
|
843
|
+
}
|
|
844
|
+
if (arg === "private") {
|
|
845
|
+
const restricted = this.store.listMemories({ sensitivity: "restricted" });
|
|
846
|
+
const privates = this.store.listMemories({ sensitivity: "private" });
|
|
847
|
+
const lines = ["[记忆] 私密资料(只显示字段名与状态,不回显明文):"];
|
|
848
|
+
if (restricted.length === 0 && privates.length === 0) {
|
|
849
|
+
lines.push(" (暂无)");
|
|
850
|
+
}
|
|
851
|
+
for (const m of restricted) {
|
|
852
|
+
lines.push(` · ${m.memory_id} [${m.status}] ${m.key}(restricted · Vault 加密保存)`);
|
|
853
|
+
}
|
|
854
|
+
for (const m of privates) {
|
|
855
|
+
lines.push(` · ${m.memory_id} [${m.status}] ${m.key}(private)`);
|
|
856
|
+
}
|
|
857
|
+
return lines.join("\n");
|
|
858
|
+
}
|
|
859
|
+
const items = this.store.listMemories({});
|
|
860
|
+
if (items.length === 0)
|
|
861
|
+
return "[记忆] 还没有任何记忆。";
|
|
862
|
+
const lines = items.map((m) => {
|
|
863
|
+
const pending = m.status === "candidate" ? " · 待确认" : "";
|
|
864
|
+
return ` · ${m.memory_id} [${m.status}] ${m.namespace}/${m.key}(置信度 ${m.confidence}${pending})`;
|
|
865
|
+
});
|
|
866
|
+
return [`[记忆] 共 ${items.length} 条(candidate/active/needs_review):`, ...lines].join("\n");
|
|
867
|
+
}
|
|
868
|
+
findMemoryForForget(arg) {
|
|
869
|
+
if (arg.startsWith("mem_")) {
|
|
870
|
+
const item = this.store.getMemory(arg);
|
|
871
|
+
return item ?? `找不到记忆 ${arg}`;
|
|
872
|
+
}
|
|
873
|
+
const needle = arg.toLowerCase();
|
|
874
|
+
const matches = this.store
|
|
875
|
+
.listMemories({})
|
|
876
|
+
.filter((m) => m.key.toLowerCase().includes(needle));
|
|
877
|
+
if (matches.length === 0)
|
|
878
|
+
return `找不到与「${arg}」匹配的记忆`;
|
|
879
|
+
if (matches.length > 1) {
|
|
880
|
+
const lines = matches.map((m) => ` · ${m.memory_id} ${m.key}`);
|
|
881
|
+
return [`「${arg}」匹配到多条记忆,请用 memory-id 指定:`, ...lines].join("\n");
|
|
882
|
+
}
|
|
883
|
+
return matches[0];
|
|
884
|
+
}
|
|
885
|
+
handleForget(arg) {
|
|
886
|
+
if (arg === "")
|
|
887
|
+
return "用法:/forget <memory-id|描述>";
|
|
888
|
+
const target = this.findMemoryForForget(arg);
|
|
889
|
+
if (typeof target === "string")
|
|
890
|
+
return target;
|
|
891
|
+
this.store.forgetMemory(target.memory_id, "user", "via /forget");
|
|
892
|
+
return `[记忆] 已遗忘 ${target.memory_id}(${target.key}),不再用于任何回答。`;
|
|
893
|
+
}
|
|
894
|
+
handleCorrect(arg) {
|
|
895
|
+
const splitAt = arg.indexOf(" ");
|
|
896
|
+
if (splitAt <= 0)
|
|
897
|
+
return "用法:/correct <memory-id> <新内容>";
|
|
898
|
+
const memoryId = arg.slice(0, splitAt).trim();
|
|
899
|
+
const raw = arg.slice(splitAt + 1).trim();
|
|
900
|
+
if (raw === "")
|
|
901
|
+
return "用法:/correct <memory-id> <新内容>";
|
|
902
|
+
let value;
|
|
903
|
+
try {
|
|
904
|
+
value = JSON.parse(raw);
|
|
905
|
+
}
|
|
906
|
+
catch {
|
|
907
|
+
value = raw;
|
|
908
|
+
}
|
|
909
|
+
const memory = this.store.correctMemory(memoryId, { value }, "user", "via /correct");
|
|
910
|
+
return `[记忆] 已修正 ${memory.memory_id}(版本 ${memory.version}),旧值保留在审计事件中。`;
|
|
911
|
+
}
|
|
912
|
+
/** Owner-only reveal of Restricted (Vault) values — never echoed to a buyer. */
|
|
913
|
+
renderPrivate() {
|
|
914
|
+
const values = listRestrictedValues(this.store);
|
|
915
|
+
if (values.length === 0)
|
|
916
|
+
return "[私有] 暂无私密阈值记忆。";
|
|
917
|
+
return [
|
|
918
|
+
"[私有] 你的私密阈值(仅你可见;绝不要把它们写进对外消息或报价):",
|
|
919
|
+
...values.map((v) => ` · ${v.key} = ${v.value}`),
|
|
920
|
+
].join("\n");
|
|
921
|
+
}
|
|
922
|
+
/** Human-only promotion of a candidate (design §10.1): constraints and
|
|
923
|
+
* restricted values require /confirm before they take effect. */
|
|
924
|
+
handleConfirm(arg) {
|
|
925
|
+
if (arg === "")
|
|
926
|
+
return "用法:/confirm <memory-id>";
|
|
927
|
+
if (!arg.startsWith("mem_")) {
|
|
928
|
+
return "请用 memory-id 指定(/memory 可查看候选的 id)。";
|
|
929
|
+
}
|
|
930
|
+
const memory = this.store.confirmMemory(arg, "user");
|
|
931
|
+
return `[记忆] 已确认 ${memory.memory_id}(${memory.key}),现在生效。`;
|
|
932
|
+
}
|
|
933
|
+
renderWhy() {
|
|
934
|
+
const { entries } = this.store.explainLastRetrieval(MAIN_SESSION_ID);
|
|
935
|
+
if (entries.length === 0) {
|
|
936
|
+
return "[记忆] 最近的回答没有使用任何记忆。";
|
|
937
|
+
}
|
|
938
|
+
const lines = entries.map((e) => ` · ${e.memory_id} ${e.namespace}/${e.key}(用途 ${e.purpose} · 精度 ${e.redaction_level} · 置信度 ${e.confidence})`);
|
|
939
|
+
return ["[记忆] 最近一轮回答使用了以下记忆:", ...lines].join("\n");
|
|
940
|
+
}
|
|
941
|
+
// ---- write-approval slash commands (design §16) ---------------------------
|
|
942
|
+
handleMode(arg) {
|
|
943
|
+
if (arg === "") {
|
|
944
|
+
return `[模式] 当前写操作审批模式:${this.getMode()}(manual/supervised/autopilot;autopilot 需 confirm)`;
|
|
945
|
+
}
|
|
946
|
+
const [target, confirm] = arg.split(/\s+/);
|
|
947
|
+
const result = this.setMode(target, { confirmed: confirm === "confirm" });
|
|
948
|
+
if (!result.ok)
|
|
949
|
+
return `[模式] ${result.error ?? "切换失败"}`;
|
|
950
|
+
return `[模式] 已切换到 ${this.getMode()}。写操作将按新模式路由(supervised 需 /approve)。`;
|
|
951
|
+
}
|
|
952
|
+
renderPending() {
|
|
953
|
+
const pending = this.listPendingApprovals();
|
|
954
|
+
if (pending.length === 0)
|
|
955
|
+
return "[审批] 当前没有等待批准的写操作候选。";
|
|
956
|
+
const lines = pending.map((c, i) => {
|
|
957
|
+
const args = JSON.stringify(c.arguments);
|
|
958
|
+
return ` ${i + 1}. ${c.candidate_id} [${c.risk}] ${c.tool}(截止 ${c.expires_at})args=${args.slice(0, 80)}`;
|
|
959
|
+
});
|
|
960
|
+
return [
|
|
961
|
+
"[审批] 等待批准的写操作候选(/approve <编号|id> 批准;/approve all 全部批准;/reject <编号|id> 驳回):",
|
|
962
|
+
...lines,
|
|
963
|
+
].join("\n");
|
|
964
|
+
}
|
|
965
|
+
async handleApprove(arg) {
|
|
966
|
+
if (arg === "")
|
|
967
|
+
return "用法:/approve <编号|id|all>";
|
|
968
|
+
const trimmed = arg.trim();
|
|
969
|
+
if (trimmed === "all") {
|
|
970
|
+
const pending = this.listPendingApprovals();
|
|
971
|
+
const outcomes = [];
|
|
972
|
+
for (const c of pending) {
|
|
973
|
+
const r = await this.approveCandidateInner(c.candidate_id);
|
|
974
|
+
const note = r.kind === "stale" || r.kind === "not_approvable" ? `(${r.reason})` : "";
|
|
975
|
+
outcomes.push(`${c.tool}: ${r.kind}${note}`);
|
|
976
|
+
}
|
|
977
|
+
return `[审批] 已处理 ${outcomes.length} 个候选:\n${outcomes.map((o) => ` · ${o}`).join("\n")}`;
|
|
978
|
+
}
|
|
979
|
+
const id = this.resolveApprovalId(trimmed);
|
|
980
|
+
if (id === undefined) {
|
|
981
|
+
return `[审批] 未知审批候选 ${trimmed}(用 /pending 看编号,或输入完整 id/唯一前缀)。`;
|
|
982
|
+
}
|
|
983
|
+
try {
|
|
984
|
+
// Already inside the kernel chain (handleUserText enqueues handleSlash);
|
|
985
|
+
// calling approveCandidate would re-enqueue and deadlock.
|
|
986
|
+
const result = await this.approveCandidateInner(id);
|
|
987
|
+
if (result.kind === "executed") {
|
|
988
|
+
return `[审批] 候选 ${id} 已执行。结果:${JSON.stringify(result.output)}`;
|
|
989
|
+
}
|
|
990
|
+
if (result.kind === "stale") {
|
|
991
|
+
return `[审批] 候选 ${id} 已失效(${result.reason}),未执行;请重新生成候选。`;
|
|
992
|
+
}
|
|
993
|
+
if (result.kind === "expired") {
|
|
994
|
+
return `[审批] 候选 ${id} 已过期,未执行。`;
|
|
995
|
+
}
|
|
996
|
+
return `[审批] 候选 ${id} 无法执行:${result.reason}`;
|
|
997
|
+
}
|
|
998
|
+
catch (err) {
|
|
999
|
+
return `[审批] 命令失败:${err instanceof Error ? err.message : String(err)}`;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
handleReject(arg) {
|
|
1003
|
+
if (arg === "")
|
|
1004
|
+
return "用法:/reject <编号|id>";
|
|
1005
|
+
const id = this.resolveApprovalId(arg.trim());
|
|
1006
|
+
if (id === undefined) {
|
|
1007
|
+
return `[审批] 未知审批候选 ${arg.trim()}(用 /pending 看编号,或输入完整 id/唯一前缀)。`;
|
|
1008
|
+
}
|
|
1009
|
+
const result = this.rejectCandidate(id);
|
|
1010
|
+
if (!result.ok)
|
|
1011
|
+
return `[审批] ${result.error ?? "驳回失败"}`;
|
|
1012
|
+
return `[审批] 候选 ${id} 已驳回,绝不会执行。`;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* `/register`:把本 merchant 注册进 agent catalog(buyer 据此发现)。
|
|
1016
|
+
* 用法:/register --agent-card-url <url> [--catalog <url>] [--domain <d>]
|
|
1017
|
+
* agent-card-url 可来自 `kiwi agent serve` 的 A2A server(缺省 env KIWI_AGENT_CARD_URL)。
|
|
1018
|
+
* catalog 缺省 env KIWI_CATALOG_URL 或 http://127.0.0.1:8600。
|
|
1019
|
+
*/
|
|
1020
|
+
async handleRegister(arg) {
|
|
1021
|
+
const flagValue = (flag) => {
|
|
1022
|
+
const idx = arg.indexOf(flag);
|
|
1023
|
+
if (idx === -1)
|
|
1024
|
+
return undefined;
|
|
1025
|
+
const rest = arg.slice(idx + flag.length).trim();
|
|
1026
|
+
const word = rest.split(/\s+/)[0] ?? "";
|
|
1027
|
+
return word !== "" && !word.startsWith("--") ? word : undefined;
|
|
1028
|
+
};
|
|
1029
|
+
const agentCardUrl = flagValue("--agent-card-url") ?? process.env.KIWI_AGENT_CARD_URL;
|
|
1030
|
+
if (agentCardUrl === undefined) {
|
|
1031
|
+
return ("需要 merchant A2A server 的 agent card URL。先运行 `kiwi agent serve --profile <merchant>`," +
|
|
1032
|
+
"然后 /register --agent-card-url http://127.0.0.1:9000/.well-known/agent-card.json" +
|
|
1033
|
+
"(或设置环境变量 KIWI_AGENT_CARD_URL)。");
|
|
1034
|
+
}
|
|
1035
|
+
const catalog = flagValue("--catalog") ?? process.env.KIWI_CATALOG_URL ?? "http://127.0.0.1:8600";
|
|
1036
|
+
const safeAgentId = this.profile.agent_id.replace(/[^a-z0-9-]/gi, "-").toLowerCase();
|
|
1037
|
+
const domain = flagValue("--domain") ?? process.env.KIWI_CATALOG_DOMAIN ?? `merchant-${safeAgentId}.local`;
|
|
1038
|
+
const ucpProfileUrl = agentCardUrl.replace(/\/agent-card\.json$/, "/ucp");
|
|
1039
|
+
try {
|
|
1040
|
+
const result = await registerCatalogAgent({
|
|
1041
|
+
catalogBaseUrl: catalog,
|
|
1042
|
+
domain,
|
|
1043
|
+
agentCardUrl,
|
|
1044
|
+
ucpProfileUrl: ucpProfileUrl !== agentCardUrl ? ucpProfileUrl : undefined,
|
|
1045
|
+
merchantId: this.profile.agent_id,
|
|
1046
|
+
ownerTokenSecret: process.env.KIWI_CATALOG_OWNER_TOKEN_SECRET,
|
|
1047
|
+
});
|
|
1048
|
+
return `[注册] 已注册到 agent catalog ${catalog}:${result.catalogAgentId ?? "?"}(status=${result.status ?? "?"})。buyer 可经 catalog 发现本 merchant。`;
|
|
1049
|
+
}
|
|
1050
|
+
catch (err) {
|
|
1051
|
+
return `[注册] 失败(A2A server 仍在运行):${err instanceof Error ? err.message : String(err)}`;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Resolve a user-facing approval target: a /pending index, an exact id, or a
|
|
1056
|
+
* UNIQUE prefix of a pending candidate (LLM flows truncate long UUIDs).
|
|
1057
|
+
* Ambiguous prefixes and out-of-range indices stay rejected.
|
|
1058
|
+
*/
|
|
1059
|
+
resolveApprovalId(input) {
|
|
1060
|
+
if (this.approvals === undefined)
|
|
1061
|
+
return undefined;
|
|
1062
|
+
const pending = this.approvals.listPending();
|
|
1063
|
+
// 只把纯十进制当序号(评审项:Number("0x10")=16、Number("1e2")=100 会
|
|
1064
|
+
// 把进制/科学计数法输入解析成序号索引,误批非预期的候选)。
|
|
1065
|
+
const n = /^\d+$/.test(input) ? Number(input) : Number.NaN;
|
|
1066
|
+
if (Number.isInteger(n) && n >= 1 && n <= pending.length) {
|
|
1067
|
+
return pending[n - 1]?.candidate_id;
|
|
1068
|
+
}
|
|
1069
|
+
const exact = this.approvals.get(input);
|
|
1070
|
+
if (exact !== undefined)
|
|
1071
|
+
return exact.candidate_id;
|
|
1072
|
+
const matches = pending.filter((c) => c.candidate_id.startsWith(input));
|
|
1073
|
+
return matches.length === 1 ? matches[0]?.candidate_id : undefined;
|
|
1074
|
+
}
|
|
1075
|
+
// ---- lifecycle ------------------------------------------------------------
|
|
1076
|
+
/** Flush the session and close the database. Idempotent. */
|
|
1077
|
+
async close() {
|
|
1078
|
+
if (this.closed)
|
|
1079
|
+
return;
|
|
1080
|
+
this.closed = true;
|
|
1081
|
+
// 等 enqueue 链(评审项 L4):排队中的工具调用/命令/scheduler tick 全部
|
|
1082
|
+
// 落定后再关库——此前只等模型 harness 空闲,排队中的后续 work 在
|
|
1083
|
+
// db.close() 之后仍会执行(/profile 切换旧 kernel 的空窗),行为未定义。
|
|
1084
|
+
try {
|
|
1085
|
+
await this.chain;
|
|
1086
|
+
}
|
|
1087
|
+
catch {
|
|
1088
|
+
// 链上错误已各自 catch(this.chain 赋值时 catch),此处仅确保等待完成。
|
|
1089
|
+
}
|
|
1090
|
+
try {
|
|
1091
|
+
await this.harness.waitForIdle();
|
|
1092
|
+
}
|
|
1093
|
+
catch {
|
|
1094
|
+
// best-effort flush; closing the store is what must not be skipped
|
|
1095
|
+
}
|
|
1096
|
+
this.db.close();
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
//# sourceMappingURL=kernel.js.map
|