@try-works/dsh-tool-mode 0.0.0-stage → 0.5.45
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 +205 -0
- package/README.md +303 -2
- package/bin/dsh-tool-mode-credentials.mjs +20 -0
- package/bin/dsh-tool-mode-mcp-stdio.mjs +80 -0
- package/bin/dsh-tool-mode.mjs +211 -0
- package/builtins.lock.json +26 -0
- package/catalog/desert-ant-models.json +693 -0
- package/catalog/playbooks.json +531 -0
- package/client/client.model-tools.js +1910 -0
- package/client/index.js +4158 -0
- package/cordis.patch.yml +39 -0
- package/lib/adapters/index.d.ts +61 -0
- package/lib/adapters/index.d.ts.map +1 -0
- package/lib/adapters/index.js +82 -0
- package/lib/adapters/types.d.ts +106 -0
- package/lib/adapters/types.d.ts.map +1 -0
- package/lib/adapters/types.js +85 -0
- package/lib/approval.d.ts +189 -0
- package/lib/approval.d.ts.map +1 -0
- package/lib/approval.js +510 -0
- package/lib/asset-acquisition.d.ts +149 -0
- package/lib/asset-acquisition.d.ts.map +1 -0
- package/lib/asset-acquisition.js +175 -0
- package/lib/authority.d.ts +185 -0
- package/lib/authority.d.ts.map +1 -0
- package/lib/authority.js +223 -0
- package/lib/bindings.d.ts +144 -0
- package/lib/bindings.d.ts.map +1 -0
- package/lib/bindings.js +314 -0
- package/lib/builtins.d.ts +27 -0
- package/lib/builtins.d.ts.map +1 -0
- package/lib/builtins.js +37 -0
- package/lib/capability.d.ts +58 -0
- package/lib/capability.d.ts.map +1 -0
- package/lib/capability.js +192 -0
- package/lib/capture-path.d.ts +97 -0
- package/lib/capture-path.d.ts.map +1 -0
- package/lib/capture-path.js +152 -0
- package/lib/catalog.d.ts +37 -0
- package/lib/catalog.d.ts.map +1 -0
- package/lib/catalog.js +274 -0
- package/lib/contract-payload.d.ts +39 -0
- package/lib/contract-payload.d.ts.map +1 -0
- package/lib/contract-payload.js +56 -0
- package/lib/custom-tools/compile.d.ts +25 -0
- package/lib/custom-tools/compile.d.ts.map +1 -0
- package/lib/custom-tools/compile.js +61 -0
- package/lib/custom-tools/composite-executor.d.ts +42 -0
- package/lib/custom-tools/composite-executor.d.ts.map +1 -0
- package/lib/custom-tools/composite-executor.js +113 -0
- package/lib/custom-tools/dispatch.d.ts +28 -0
- package/lib/custom-tools/dispatch.d.ts.map +1 -0
- package/lib/custom-tools/dispatch.js +34 -0
- package/lib/custom-tools/http-executor.d.ts +48 -0
- package/lib/custom-tools/http-executor.d.ts.map +1 -0
- package/lib/custom-tools/http-executor.js +241 -0
- package/lib/custom-tools/index.d.ts +12 -0
- package/lib/custom-tools/index.d.ts.map +1 -0
- package/lib/custom-tools/index.js +12 -0
- package/lib/custom-tools/mcp-surface.d.ts +14 -0
- package/lib/custom-tools/mcp-surface.d.ts.map +1 -0
- package/lib/custom-tools/mcp-surface.js +41 -0
- package/lib/custom-tools/prompt-executor.d.ts +39 -0
- package/lib/custom-tools/prompt-executor.d.ts.map +1 -0
- package/lib/custom-tools/prompt-executor.js +59 -0
- package/lib/custom-tools/registry.d.ts +69 -0
- package/lib/custom-tools/registry.d.ts.map +1 -0
- package/lib/custom-tools/registry.js +130 -0
- package/lib/custom-tools/schema.d.ts +26 -0
- package/lib/custom-tools/schema.d.ts.map +1 -0
- package/lib/custom-tools/schema.js +375 -0
- package/lib/custom-tools/service.d.ts +90 -0
- package/lib/custom-tools/service.d.ts.map +1 -0
- package/lib/custom-tools/service.js +203 -0
- package/lib/custom-tools/store.d.ts +61 -0
- package/lib/custom-tools/store.d.ts.map +1 -0
- package/lib/custom-tools/store.js +240 -0
- package/lib/custom-tools/types.d.ts +131 -0
- package/lib/custom-tools/types.d.ts.map +1 -0
- package/lib/custom-tools/types.js +11 -0
- package/lib/decision-combine.d.ts +113 -0
- package/lib/decision-combine.d.ts.map +1 -0
- package/lib/decision-combine.js +156 -0
- package/lib/decision-evaluate.d.ts +185 -0
- package/lib/decision-evaluate.d.ts.map +1 -0
- package/lib/decision-evaluate.js +227 -0
- package/lib/deployment-coordinator.d.ts +213 -0
- package/lib/deployment-coordinator.d.ts.map +1 -0
- package/lib/deployment-coordinator.js +274 -0
- package/lib/diagnostics.d.ts +169 -0
- package/lib/diagnostics.d.ts.map +1 -0
- package/lib/diagnostics.js +136 -0
- package/lib/discovery-contracts.d.ts +14 -0
- package/lib/discovery-contracts.d.ts.map +1 -0
- package/lib/discovery-contracts.js +90 -0
- package/lib/discovery-describe.d.ts +35 -0
- package/lib/discovery-describe.d.ts.map +1 -0
- package/lib/discovery-describe.js +99 -0
- package/lib/embedding-index.d.ts +55 -0
- package/lib/embedding-index.d.ts.map +1 -0
- package/lib/embedding-index.js +126 -0
- package/lib/extension-dependencies.d.ts +355 -0
- package/lib/extension-dependencies.d.ts.map +1 -0
- package/lib/extension-dependencies.js +987 -0
- package/lib/extension-dispatch.d.ts +150 -0
- package/lib/extension-dispatch.d.ts.map +1 -0
- package/lib/extension-dispatch.js +178 -0
- package/lib/extension-page-state.d.ts +69 -0
- package/lib/extension-page-state.d.ts.map +1 -0
- package/lib/extension-page-state.js +82 -0
- package/lib/extension-registrar.d.ts +116 -0
- package/lib/extension-registrar.d.ts.map +1 -0
- package/lib/extension-registrar.js +144 -0
- package/lib/extensions-api.d.ts +346 -0
- package/lib/extensions-api.d.ts.map +1 -0
- package/lib/extensions-api.js +17 -0
- package/lib/extensions-remote.d.ts +57 -0
- package/lib/extensions-remote.d.ts.map +1 -0
- package/lib/extensions-remote.js +69 -0
- package/lib/extensions-service.d.ts +568 -0
- package/lib/extensions-service.d.ts.map +1 -0
- package/lib/extensions-service.js +2552 -0
- package/lib/harness-budgets.d.ts +158 -0
- package/lib/harness-budgets.d.ts.map +1 -0
- package/lib/harness-budgets.js +152 -0
- package/lib/harness-llm.d.ts +138 -0
- package/lib/harness-llm.d.ts.map +1 -0
- package/lib/harness-llm.js +218 -0
- package/lib/harness-serving.d.ts +115 -0
- package/lib/harness-serving.d.ts.map +1 -0
- package/lib/harness-serving.js +176 -0
- package/lib/headless.d.ts +97 -0
- package/lib/headless.d.ts.map +1 -0
- package/lib/headless.js +407 -0
- package/lib/http-acquisition.d.ts +27 -0
- package/lib/http-acquisition.d.ts.map +1 -0
- package/lib/http-acquisition.js +140 -0
- package/lib/index.d.ts +546 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +5273 -0
- package/lib/invocation-accounting.d.ts +169 -0
- package/lib/invocation-accounting.d.ts.map +1 -0
- package/lib/invocation-accounting.js +148 -0
- package/lib/jev.d.ts +218 -0
- package/lib/jev.d.ts.map +1 -0
- package/lib/jev.js +366 -0
- package/lib/legacy-migration.d.ts +95 -0
- package/lib/legacy-migration.d.ts.map +1 -0
- package/lib/legacy-migration.js +429 -0
- package/lib/license-basis.d.ts +29 -0
- package/lib/license-basis.d.ts.map +1 -0
- package/lib/license-basis.js +58 -0
- package/lib/managed-workers.d.ts +158 -0
- package/lib/managed-workers.d.ts.map +1 -0
- package/lib/managed-workers.js +269 -0
- package/lib/mcp/auth-selection.d.ts +35 -0
- package/lib/mcp/auth-selection.d.ts.map +1 -0
- package/lib/mcp/auth-selection.js +64 -0
- package/lib/mcp/client-grant.d.ts +89 -0
- package/lib/mcp/client-grant.d.ts.map +1 -0
- package/lib/mcp/client-grant.js +63 -0
- package/lib/mcp/credential-cli.d.ts +38 -0
- package/lib/mcp/credential-cli.d.ts.map +1 -0
- package/lib/mcp/credential-cli.js +148 -0
- package/lib/mcp/credential-store.d.ts +143 -0
- package/lib/mcp/credential-store.d.ts.map +1 -0
- package/lib/mcp/credential-store.js +461 -0
- package/lib/mcp/export-policy.d.ts +25 -0
- package/lib/mcp/export-policy.d.ts.map +1 -0
- package/lib/mcp/export-policy.js +31 -0
- package/lib/mcp/host-binding.d.ts +144 -0
- package/lib/mcp/host-binding.d.ts.map +1 -0
- package/lib/mcp/host-binding.js +280 -0
- package/lib/mcp/http.d.ts +180 -0
- package/lib/mcp/http.d.ts.map +1 -0
- package/lib/mcp/http.js +602 -0
- package/lib/mcp/oauth.d.ts +149 -0
- package/lib/mcp/oauth.d.ts.map +1 -0
- package/lib/mcp/oauth.js +396 -0
- package/lib/mcp/protocol.d.ts +329 -0
- package/lib/mcp/protocol.d.ts.map +1 -0
- package/lib/mcp/protocol.js +459 -0
- package/lib/mcp/rate-limit.d.ts +68 -0
- package/lib/mcp/rate-limit.d.ts.map +1 -0
- package/lib/mcp/rate-limit.js +114 -0
- package/lib/mcp/sdk-v2.d.ts +17 -0
- package/lib/mcp/sdk-v2.d.ts.map +1 -0
- package/lib/mcp/sdk-v2.js +204 -0
- package/lib/mcp/server.d.ts +149 -0
- package/lib/mcp/server.d.ts.map +1 -0
- package/lib/mcp/server.js +302 -0
- package/lib/mcp/stdio.d.ts +80 -0
- package/lib/mcp/stdio.d.ts.map +1 -0
- package/lib/mcp/stdio.js +184 -0
- package/lib/mcp/subscriptions.d.ts +177 -0
- package/lib/mcp/subscriptions.d.ts.map +1 -0
- package/lib/mcp/subscriptions.js +288 -0
- package/lib/mcp-status.d.ts +96 -0
- package/lib/mcp-status.d.ts.map +1 -0
- package/lib/mcp-status.js +70 -0
- package/lib/mcp-usage.d.ts +110 -0
- package/lib/mcp-usage.d.ts.map +1 -0
- package/lib/mcp-usage.js +189 -0
- package/lib/model-qualification.d.ts +192 -0
- package/lib/model-qualification.d.ts.map +1 -0
- package/lib/model-qualification.js +178 -0
- package/lib/model-runtime-lane.d.ts +69 -0
- package/lib/model-runtime-lane.d.ts.map +1 -0
- package/lib/model-runtime-lane.js +122 -0
- package/lib/model-runtime-recipe.d.ts +132 -0
- package/lib/model-runtime-recipe.d.ts.map +1 -0
- package/lib/model-runtime-recipe.js +291 -0
- package/lib/planning.d.ts +256 -0
- package/lib/planning.d.ts.map +1 -0
- package/lib/planning.js +471 -0
- package/lib/playbook.d.ts +42 -0
- package/lib/playbook.d.ts.map +1 -0
- package/lib/playbook.js +65 -0
- package/lib/prepared-assets.d.ts +269 -0
- package/lib/prepared-assets.d.ts.map +1 -0
- package/lib/prepared-assets.js +471 -0
- package/lib/provider-bridge.d.ts +107 -0
- package/lib/provider-bridge.d.ts.map +1 -0
- package/lib/provider-bridge.js +423 -0
- package/lib/ptc-authority.d.ts +154 -0
- package/lib/ptc-authority.d.ts.map +1 -0
- package/lib/ptc-authority.js +242 -0
- package/lib/ptc-confinement.d.ts +60 -0
- package/lib/ptc-confinement.d.ts.map +1 -0
- package/lib/ptc-confinement.js +212 -0
- package/lib/ptc-runtime.d.ts +166 -0
- package/lib/ptc-runtime.d.ts.map +1 -0
- package/lib/ptc-runtime.js +231 -0
- package/lib/publication.d.ts +161 -0
- package/lib/publication.d.ts.map +1 -0
- package/lib/publication.js +181 -0
- package/lib/receipt-store.d.ts +57 -0
- package/lib/receipt-store.d.ts.map +1 -0
- package/lib/receipt-store.js +143 -0
- package/lib/receipt.d.ts +231 -0
- package/lib/receipt.d.ts.map +1 -0
- package/lib/receipt.js +165 -0
- package/lib/registrar.d.ts +75 -0
- package/lib/registrar.d.ts.map +1 -0
- package/lib/registrar.js +152 -0
- package/lib/resource-governance.d.ts +204 -0
- package/lib/resource-governance.d.ts.map +1 -0
- package/lib/resource-governance.js +213 -0
- package/lib/server-lifecycle.d.ts +175 -0
- package/lib/server-lifecycle.d.ts.map +1 -0
- package/lib/server-lifecycle.js +269 -0
- package/lib/skills.d.ts +31 -0
- package/lib/skills.d.ts.map +1 -0
- package/lib/skills.js +170 -0
- package/lib/speech-bridge.d.ts +74 -0
- package/lib/speech-bridge.d.ts.map +1 -0
- package/lib/speech-bridge.js +115 -0
- package/lib/state.d.ts +100 -0
- package/lib/state.d.ts.map +1 -0
- package/lib/state.js +160 -0
- package/lib/title-proposal.d.ts +194 -0
- package/lib/title-proposal.d.ts.map +1 -0
- package/lib/title-proposal.js +244 -0
- package/lib/tool-inventory.d.ts +90 -0
- package/lib/tool-inventory.d.ts.map +1 -0
- package/lib/tool-inventory.js +102 -0
- package/lib/tool-mode-run.d.ts +310 -0
- package/lib/tool-mode-run.d.ts.map +1 -0
- package/lib/tool-mode-run.js +411 -0
- package/lib/tool-sources.d.ts +80 -0
- package/lib/tool-sources.d.ts.map +1 -0
- package/lib/tool-sources.js +123 -0
- package/lib/tool-usage-projection.d.ts +390 -0
- package/lib/tool-usage-projection.d.ts.map +1 -0
- package/lib/tool-usage-projection.js +1038 -0
- package/lib/tool-usage-report.d.ts +153 -0
- package/lib/tool-usage-report.d.ts.map +1 -0
- package/lib/tool-usage-report.js +274 -0
- package/lib/transport.d.ts +158 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +332 -0
- package/lib/types.d.ts +134 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +8 -0
- package/lib/update-surface.d.ts +79 -0
- package/lib/update-surface.d.ts.map +1 -0
- package/lib/update-surface.js +224 -0
- package/lib/version.d.ts +3 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +27 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.d.ts +13 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.js +85 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts +16 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.js +87 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts +38 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.js +445 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts +20 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.js +112 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts +14 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.js +63 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts +14 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.js +76 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts +82 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.js +274 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts +26 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.js +53 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +133 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.js +64 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +121 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.js +246 -0
- package/node_modules/@try-works/tool-mode-contracts/package.json +35 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/bin/dsh-tool-mode.mjs +20 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts +80 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.js +533 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts +86 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.js +145 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts +70 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.js +201 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts +57 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.js +83 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts +33 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.js +33 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts +86 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.js +191 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts +137 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.js +349 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts +274 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.js +932 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts +210 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.js +368 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts +63 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.js +149 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts +35 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.js +179 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts +179 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.js +299 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts +395 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.js +839 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.d.ts +44 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.js +255 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts +185 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.js +504 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts +17 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.js +80 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.d.ts +221 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.js +188 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts +52 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.js +102 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts +171 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.js +271 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts +242 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.js +58 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts +128 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.js +113 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.d.ts +41 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.js +52 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts +2 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.js +351 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts +157 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.js +688 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/package.json +54 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/schema/extension-manifest-1.1.0.schema.json +253 -0
- package/node_modules/@try-works/tool-mode-recipes/bin/tool-mode-recipe.mjs +15 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts +460 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.js +647 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts +61 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.js +1106 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts +237 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.js +205 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts +85 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.js +176 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts +119 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.js +201 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts +25 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.js +25 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts +345 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.js +1465 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts +51 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.js +310 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts +231 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.js +435 -0
- package/node_modules/@try-works/tool-mode-recipes/package.json +56 -0
- package/node_modules/@try-works/tool-mode-recipes/recipes/describe-image.json +38 -0
- package/node_modules/@try-works/tool-mode-recipes/recipes/text-title.json +45 -0
- package/node_modules/@try-works/tool-mode-recipes/schema/harness-model-recipe.schema.json +273 -0
- package/package.json +127 -4
- package/remote/spec.mjs +993 -0
- package/remote/typert.host.mjs +14 -0
- package/remote/typert.remote-client.mjs +16 -0
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `toolModeUsage` session projection (Tool Insights plan §3.1).
|
|
3
|
+
*
|
|
4
|
+
* A pure fold over DSH session events. The projection STATE is the host-only detail
|
|
5
|
+
* (exact per-tool matrix, 16-bucket duration histogram, error codes); the projection
|
|
6
|
+
* WIRE is the compact value that rides every `session.list` row. Splitting them this
|
|
7
|
+
* way keeps the session list small while the report channel can still answer exact
|
|
8
|
+
* range questions from the detail.
|
|
9
|
+
*
|
|
10
|
+
* Truthfulness rules the fold enforces:
|
|
11
|
+
*
|
|
12
|
+
* - A call is counted when it is DISPATCHED (`tool/call`, `tool/ptc-dispatch-start`),
|
|
13
|
+
* and its duration is added only when a result settles it. A call whose result never
|
|
14
|
+
* lands is `unsettled` — never a fabricated success, and never in a percentile.
|
|
15
|
+
* - `callId` is provider-minted, so pairing uses `Object.hasOwn`: a result naming
|
|
16
|
+
* `constructor`/`toString` must read as unmatched, not as an inherited function.
|
|
17
|
+
* - Model attribution comes from the step's assistant message, then the request-header
|
|
18
|
+
* route in force, then the explicit `unattributed` bucket. Code Mode sub-calls
|
|
19
|
+
* inherit the parent program's model.
|
|
20
|
+
* - Every bound is explicit and loses information LOUDLY (`unsettled`, `toolsTruncated`
|
|
21
|
+
* / `modelsTruncated`) rather than silently.
|
|
22
|
+
* - All state is plain JSON: the projection cache persists it.
|
|
23
|
+
*
|
|
24
|
+
* @module @try-works/dsh-tool-mode/tool-usage-projection
|
|
25
|
+
*/
|
|
26
|
+
import { staticToolOwner } from "./tool-sources.js";
|
|
27
|
+
/** The projection key the client reads as `projectionValues.toolModeUsage`. */
|
|
28
|
+
export const TOOL_USAGE_KEY = 'toolModeUsage';
|
|
29
|
+
/** The persisted-state version; a stale cache row is re-folded instead of trusted. */
|
|
30
|
+
export const TOOL_USAGE_STATE_VERSION = 1;
|
|
31
|
+
/** The explicit bucket for calls with no readable model. */
|
|
32
|
+
export const UNATTRIBUTED_MODEL = 'unattributed';
|
|
33
|
+
/** The explicit per-tool bucket once the distinct-tool bound is reached. */
|
|
34
|
+
export const TOOLS_OVERFLOW = '(overflow)';
|
|
35
|
+
/**
|
|
36
|
+
* Duration histogram upper edges, in milliseconds (16 buckets).
|
|
37
|
+
*
|
|
38
|
+
* Log-scale because tool latencies span four orders of magnitude; the last bucket is
|
|
39
|
+
* open-ended. Percentiles are derived from this histogram, while `maxMs` stays exact.
|
|
40
|
+
*/
|
|
41
|
+
export const DURATION_BUCKETS_MS = Object.freeze([
|
|
42
|
+
10, 25, 50, 100, 200, 300, 500, 750, 1_000, 2_000, 3_000, 5_000, 10_000, 30_000, 60_000, 600_000,
|
|
43
|
+
]);
|
|
44
|
+
/** Hard bounds. The wire caps and the fold caps come from one table. */
|
|
45
|
+
export const TOOL_USAGE_CAPS = Object.freeze({
|
|
46
|
+
/** In-flight (call dispatched, result not yet seen) records retained. */
|
|
47
|
+
pending: 100,
|
|
48
|
+
/** Recent calls retained in the detail state; the wire carries {@link TOOL_USAGE_CAPS.wireRecent}. */
|
|
49
|
+
recent: 200,
|
|
50
|
+
/** Recent calls the compact wire carries. */
|
|
51
|
+
wireRecent: 20,
|
|
52
|
+
/** Distinct tool performance rows per session. */
|
|
53
|
+
tools: 100,
|
|
54
|
+
/** Distinct models per session. */
|
|
55
|
+
models: 20,
|
|
56
|
+
/** Distinct tool names tracked per model. */
|
|
57
|
+
modelTools: 100,
|
|
58
|
+
/** Retained local-time days. */
|
|
59
|
+
days: 60,
|
|
60
|
+
/** Duration buckets per tool. */
|
|
61
|
+
buckets: 16,
|
|
62
|
+
/** Top tools the compact wire carries. */
|
|
63
|
+
topTools: 5,
|
|
64
|
+
});
|
|
65
|
+
/** A fresh empty state. */
|
|
66
|
+
export function initToolUsageState() {
|
|
67
|
+
return {
|
|
68
|
+
stateVersion: TOOL_USAGE_STATE_VERSION,
|
|
69
|
+
calls: 0,
|
|
70
|
+
errors: 0,
|
|
71
|
+
unsettled: 0,
|
|
72
|
+
toolMs: 0,
|
|
73
|
+
uniqueTools: 0,
|
|
74
|
+
byTool: {},
|
|
75
|
+
byModel: {},
|
|
76
|
+
byOwner: {},
|
|
77
|
+
byDay: {},
|
|
78
|
+
byErrorCode: {},
|
|
79
|
+
recent: [],
|
|
80
|
+
pending: {},
|
|
81
|
+
route: null,
|
|
82
|
+
stepModel: null,
|
|
83
|
+
lastTurn: null,
|
|
84
|
+
toolsTruncated: 0,
|
|
85
|
+
modelsTruncated: 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** The host-local `YYYY-MM-DD` day key for one timestamp (shared with the report). */
|
|
89
|
+
export function toolUsageDayKey(time) {
|
|
90
|
+
return dayKeyOf(time);
|
|
91
|
+
}
|
|
92
|
+
/** One local-time day key (`YYYY-MM-DD`). */
|
|
93
|
+
function dayKeyOf(time) {
|
|
94
|
+
const date = new Date(time);
|
|
95
|
+
const pad = (value) => String(value).padStart(2, '0');
|
|
96
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
97
|
+
}
|
|
98
|
+
/** The histogram bucket for one settled duration. */
|
|
99
|
+
function bucketOf(durationMs) {
|
|
100
|
+
for (let index = 0; index < DURATION_BUCKETS_MS.length - 1; index += 1) {
|
|
101
|
+
const edge = DURATION_BUCKETS_MS[index];
|
|
102
|
+
if (edge !== undefined && durationMs <= edge)
|
|
103
|
+
return index;
|
|
104
|
+
}
|
|
105
|
+
return DURATION_BUCKETS_MS.length - 1;
|
|
106
|
+
}
|
|
107
|
+
/** Serialized byte length, with `-1` for an unserializable value (never `0`). */
|
|
108
|
+
function byteLengthOf(value) {
|
|
109
|
+
try {
|
|
110
|
+
const serialized = JSON.stringify(value);
|
|
111
|
+
return serialized === undefined ? 0 : Buffer.byteLength(serialized, 'utf8');
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return -1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** The model that emitted a call in `turn`/`step`, never a guess. */
|
|
118
|
+
function modelFor(state, turn, step) {
|
|
119
|
+
if (state.stepModel !== null && state.stepModel.turn === turn && state.stepModel.step === step) {
|
|
120
|
+
return state.stepModel.model;
|
|
121
|
+
}
|
|
122
|
+
return state.route?.model ?? UNATTRIBUTED_MODEL;
|
|
123
|
+
}
|
|
124
|
+
/** The model for a Code Mode sub-call: the parent program's step model, then the route. */
|
|
125
|
+
function codeModelFor(state) {
|
|
126
|
+
return state.stepModel?.model ?? state.route?.model ?? UNATTRIBUTED_MODEL;
|
|
127
|
+
}
|
|
128
|
+
/** A copy of `state` with the oldest tool/day keys pruned past the day bound. */
|
|
129
|
+
function pruneDays(state) {
|
|
130
|
+
const days = Object.keys(state.byDay).sort();
|
|
131
|
+
if (days.length <= TOOL_USAGE_CAPS.days)
|
|
132
|
+
return state;
|
|
133
|
+
const keep = new Set(days.slice(days.length - TOOL_USAGE_CAPS.days));
|
|
134
|
+
const byDay = {};
|
|
135
|
+
for (const [day, usage] of Object.entries(state.byDay))
|
|
136
|
+
if (keep.has(day))
|
|
137
|
+
byDay[day] = usage;
|
|
138
|
+
const byTool = {};
|
|
139
|
+
for (const [name, bucket] of Object.entries(state.byTool)) {
|
|
140
|
+
const toolDays = {};
|
|
141
|
+
for (const [day, count] of Object.entries(bucket.byDay))
|
|
142
|
+
if (keep.has(day))
|
|
143
|
+
toolDays[day] = count;
|
|
144
|
+
byTool[name] = { ...bucket, byDay: toolDays };
|
|
145
|
+
}
|
|
146
|
+
const byModel = {};
|
|
147
|
+
for (const [model, usage] of Object.entries(state.byModel)) {
|
|
148
|
+
const modelDays = {};
|
|
149
|
+
for (const [day, count] of Object.entries(usage.byDay))
|
|
150
|
+
if (keep.has(day))
|
|
151
|
+
modelDays[day] = count;
|
|
152
|
+
byModel[model] = { ...usage, byDay: modelDays };
|
|
153
|
+
}
|
|
154
|
+
const byOwner = {};
|
|
155
|
+
for (const [owner, usage] of Object.entries(state.byOwner)) {
|
|
156
|
+
const ownerDays = {};
|
|
157
|
+
for (const [day, count] of Object.entries(usage.byDay))
|
|
158
|
+
if (keep.has(day))
|
|
159
|
+
ownerDays[day] = count;
|
|
160
|
+
byOwner[owner] = { ...usage, byDay: ownerDays };
|
|
161
|
+
}
|
|
162
|
+
return { ...state, byDay, byTool, byModel, byOwner };
|
|
163
|
+
}
|
|
164
|
+
/** A copy of `state` with one tool/day counter incremented. */
|
|
165
|
+
function bumpToolDay(bucket, day, calls, errors, ms, unsettled, codeCalls) {
|
|
166
|
+
const current = bucket.byDay[day] ?? { calls: 0, errors: 0, ms: 0, maxMs: 0, unsettled: 0, codeCalls: 0 };
|
|
167
|
+
return {
|
|
168
|
+
...bucket,
|
|
169
|
+
byDay: {
|
|
170
|
+
...bucket.byDay,
|
|
171
|
+
[day]: {
|
|
172
|
+
calls: current.calls + calls,
|
|
173
|
+
errors: current.errors + errors,
|
|
174
|
+
ms: current.ms + ms,
|
|
175
|
+
maxMs: Math.max(current.maxMs, ms),
|
|
176
|
+
unsettled: current.unsettled + unsettled,
|
|
177
|
+
codeCalls: current.codeCalls + codeCalls,
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/** A copy of `state` with one day counter incremented. */
|
|
183
|
+
function bumpDay(state, day, calls, errors, ms, unsettled, code) {
|
|
184
|
+
const current = state.byDay[day] ?? { calls: 0, errors: 0, ms: 0, unsettled: 0, byCode: {} };
|
|
185
|
+
return {
|
|
186
|
+
...state.byDay,
|
|
187
|
+
[day]: {
|
|
188
|
+
calls: current.calls + calls,
|
|
189
|
+
errors: current.errors + errors,
|
|
190
|
+
ms: current.ms + ms,
|
|
191
|
+
unsettled: current.unsettled + unsettled,
|
|
192
|
+
byCode: code === null ? current.byCode : { ...current.byCode, [code]: (current.byCode[code] ?? 0) + 1 },
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** A copy of one model's day counter with the given increments applied. */
|
|
197
|
+
function bumpModelDay(usage, day, calls, errors, ms) {
|
|
198
|
+
const current = usage.byDay[day] ?? { calls: 0, errors: 0, ms: 0, maxMs: 0 };
|
|
199
|
+
return { ...usage.byDay, [day]: { calls: current.calls + calls, errors: current.errors + errors, ms: current.ms + ms, maxMs: Math.max(current.maxMs, ms) } };
|
|
200
|
+
}
|
|
201
|
+
/** A copy of one owner's day counter with the given increments applied. */
|
|
202
|
+
function bumpOwnerDay(usage, day, calls, errors, ms) {
|
|
203
|
+
const current = usage?.byDay[day] ?? { calls: 0, errors: 0, ms: 0 };
|
|
204
|
+
return { ...usage?.byDay, [day]: { calls: current.calls + calls, errors: current.errors + errors, ms: current.ms + ms } };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The tool bucket for a name, honouring the distinct-tool bound.
|
|
208
|
+
*
|
|
209
|
+
* Past the bound the call is still counted in the totals but no 101st performance row
|
|
210
|
+
* is invented; `toolsTruncated` records how many calls lost their per-tool detail.
|
|
211
|
+
* @param state - the current state.
|
|
212
|
+
* @param name - the tool name.
|
|
213
|
+
* @returns the effective row key (`''` when untracked), its bucket and the new state.
|
|
214
|
+
*/
|
|
215
|
+
function toolBucket(state, name) {
|
|
216
|
+
const existing = state.byTool[name];
|
|
217
|
+
if (existing !== undefined)
|
|
218
|
+
return { key: name, bucket: existing, state };
|
|
219
|
+
if (Object.keys(state.byTool).length >= TOOL_USAGE_CAPS.tools) {
|
|
220
|
+
return {
|
|
221
|
+
key: '',
|
|
222
|
+
bucket: emptyToolBucket(),
|
|
223
|
+
state: { ...state, toolsTruncated: state.toolsTruncated + 1 },
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return { key: name, bucket: emptyToolBucket(), state: { ...state, uniqueTools: state.uniqueTools + 1 } };
|
|
227
|
+
}
|
|
228
|
+
/** A copy of a histogram with one settled duration added. */
|
|
229
|
+
function addToHistogram(histogram, ms) {
|
|
230
|
+
const next = [...histogram];
|
|
231
|
+
const index = bucketOf(ms);
|
|
232
|
+
next[index] = (next[index] ?? 0) + 1;
|
|
233
|
+
return next;
|
|
234
|
+
}
|
|
235
|
+
/** A zeroed 16-bucket histogram. */
|
|
236
|
+
function emptyHistogram() {
|
|
237
|
+
return Array.from({ length: TOOL_USAGE_CAPS.buckets }, () => 0);
|
|
238
|
+
}
|
|
239
|
+
/** A zeroed tool bucket. */
|
|
240
|
+
function emptyToolBucket() {
|
|
241
|
+
return {
|
|
242
|
+
calls: 0,
|
|
243
|
+
errors: 0,
|
|
244
|
+
unsettled: 0,
|
|
245
|
+
codeCalls: 0,
|
|
246
|
+
directCalls: 0,
|
|
247
|
+
totalMs: 0,
|
|
248
|
+
maxMs: 0,
|
|
249
|
+
argsBytes: 0,
|
|
250
|
+
resultBytes: 0,
|
|
251
|
+
firstUsed: null,
|
|
252
|
+
lastUsed: null,
|
|
253
|
+
histogram: Array.from({ length: TOOL_USAGE_CAPS.buckets }, () => 0),
|
|
254
|
+
byDay: {},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/** The model bucket for a name, honouring the distinct-model bound. */
|
|
258
|
+
function modelBucket(state, model) {
|
|
259
|
+
const existing = state.byModel[model];
|
|
260
|
+
if (existing !== undefined)
|
|
261
|
+
return { bucket: existing, state };
|
|
262
|
+
if (Object.keys(state.byModel).length >= TOOL_USAGE_CAPS.models) {
|
|
263
|
+
// Past the bound, calls stay counted in the totals but attribution is explicit.
|
|
264
|
+
const unattributed = state.byModel[UNATTRIBUTED_MODEL] ?? { calls: 0, errors: 0, ms: 0, tools: [], histogram: emptyHistogram(), byDay: {} };
|
|
265
|
+
return {
|
|
266
|
+
bucket: unattributed,
|
|
267
|
+
state: {
|
|
268
|
+
...state,
|
|
269
|
+
byModel: { ...state.byModel, [UNATTRIBUTED_MODEL]: unattributed },
|
|
270
|
+
modelsTruncated: state.modelsTruncated + 1,
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return { bucket: { calls: 0, errors: 0, ms: 0, tools: [], histogram: emptyHistogram(), byDay: {} }, state };
|
|
275
|
+
}
|
|
276
|
+
/** Add a tool name to a model's bounded distinct-tool list. */
|
|
277
|
+
function modelTools(usage, name) {
|
|
278
|
+
if (usage.tools.includes(name))
|
|
279
|
+
return usage.tools;
|
|
280
|
+
if (usage.tools.length >= TOOL_USAGE_CAPS.modelTools)
|
|
281
|
+
return usage.tools;
|
|
282
|
+
return [...usage.tools, name];
|
|
283
|
+
}
|
|
284
|
+
/** Append one recent entry, evicting the oldest past the bound. */
|
|
285
|
+
function appendRecent(state, entry) {
|
|
286
|
+
const recent = [...state.recent, entry];
|
|
287
|
+
return recent.length > TOOL_USAGE_CAPS.recent ? recent.slice(recent.length - TOOL_USAGE_CAPS.recent) : recent;
|
|
288
|
+
}
|
|
289
|
+
/** Patch a recent entry when its call settles or becomes unsettled. */
|
|
290
|
+
function patchRecent(recent, callId, kind, patch) {
|
|
291
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
292
|
+
const entry = recent[index];
|
|
293
|
+
if (entry === undefined || entry.callId !== callId || entry.kind !== kind)
|
|
294
|
+
continue;
|
|
295
|
+
const next = [...recent];
|
|
296
|
+
next[index] = { ...entry, ...patch };
|
|
297
|
+
return next;
|
|
298
|
+
}
|
|
299
|
+
return [...recent];
|
|
300
|
+
}
|
|
301
|
+
/** Drop one pending call, counting it unsettled (the result never landed). */
|
|
302
|
+
function dropUnsettled(state, key) {
|
|
303
|
+
const pending = state.pending[key];
|
|
304
|
+
if (pending === undefined)
|
|
305
|
+
return state;
|
|
306
|
+
const rest = { ...state.pending };
|
|
307
|
+
delete rest[key];
|
|
308
|
+
const day = dayKeyOf(pending.time);
|
|
309
|
+
const bucket = state.byTool[pending.name];
|
|
310
|
+
const byTool = bucket === undefined
|
|
311
|
+
? state.byTool
|
|
312
|
+
: { ...state.byTool, [pending.name]: { ...bumpToolDay(bucket, day, 0, 0, 0, 1, 0), unsettled: bucket.unsettled + 1 } };
|
|
313
|
+
return {
|
|
314
|
+
...state,
|
|
315
|
+
pending: rest,
|
|
316
|
+
byTool,
|
|
317
|
+
byDay: bumpDay(state, day, 0, 0, 0, 1, null),
|
|
318
|
+
unsettled: state.unsettled + 1,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/** Evict the oldest pending call when the bound is reached. */
|
|
322
|
+
function evictOldestPending(state) {
|
|
323
|
+
let oldestKey;
|
|
324
|
+
let oldestTime = Number.POSITIVE_INFINITY;
|
|
325
|
+
for (const [key, pending] of Object.entries(state.pending)) {
|
|
326
|
+
if (pending.time < oldestTime) {
|
|
327
|
+
oldestTime = pending.time;
|
|
328
|
+
oldestKey = key;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return oldestKey === undefined ? state : dropUnsettled(state, oldestKey);
|
|
332
|
+
}
|
|
333
|
+
/** Fold one event into the state. Uninteresting events return the same reference. */
|
|
334
|
+
export function foldToolUsageEvent(state, event) {
|
|
335
|
+
switch (event.type) {
|
|
336
|
+
case 'request/header': {
|
|
337
|
+
const provider = event.data.header.config.provider;
|
|
338
|
+
const model = event.data.header.config.model;
|
|
339
|
+
if (typeof model !== 'string' || model.length === 0)
|
|
340
|
+
return state;
|
|
341
|
+
return { ...state, route: { provider: typeof provider === 'string' ? provider : '', model } };
|
|
342
|
+
}
|
|
343
|
+
case 'assistant/message': {
|
|
344
|
+
const model = event.data.message.source.model;
|
|
345
|
+
if (typeof model !== 'string' || model.length === 0)
|
|
346
|
+
return state;
|
|
347
|
+
return { ...state, stepModel: { turn: event.data.turn, step: event.data.step, model } };
|
|
348
|
+
}
|
|
349
|
+
case 'tool/call': {
|
|
350
|
+
const day = dayKeyOf(event.time);
|
|
351
|
+
const model = modelFor(state, event.data.turn, event.data.step);
|
|
352
|
+
const owner = staticToolOwner(event.data.name).owner;
|
|
353
|
+
const { key: toolKey, bucket, state: withBucket } = toolBucket(state, event.data.name);
|
|
354
|
+
let next = {
|
|
355
|
+
...withBucket,
|
|
356
|
+
calls: withBucket.calls + 1,
|
|
357
|
+
byDay: bumpDay(withBucket, day, 1, 0, 0, 0, null),
|
|
358
|
+
byOwner: { ...withBucket.byOwner, [owner]: bumpOwner(withBucket.byOwner[owner], day, 1, 0, 0) },
|
|
359
|
+
byTool: toolKey === '' ? withBucket.byTool : {
|
|
360
|
+
...withBucket.byTool,
|
|
361
|
+
[toolKey]: bumpToolDay({
|
|
362
|
+
...bucket,
|
|
363
|
+
calls: bucket.calls + 1,
|
|
364
|
+
directCalls: bucket.directCalls + 1,
|
|
365
|
+
argsBytes: bucket.argsBytes + Buffer.byteLength(event.data.arguments, 'utf8'),
|
|
366
|
+
firstUsed: bucket.firstUsed === null ? event.time : Math.min(bucket.firstUsed, event.time),
|
|
367
|
+
lastUsed: bucket.lastUsed === null ? event.time : Math.max(bucket.lastUsed, event.time),
|
|
368
|
+
}, day, 1, 0, 0, 0, 0),
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
next = pruneDays(next);
|
|
372
|
+
const { bucket: modelUsage, state: withModel } = modelBucket(next, model);
|
|
373
|
+
next = {
|
|
374
|
+
...withModel,
|
|
375
|
+
byModel: {
|
|
376
|
+
...withModel.byModel,
|
|
377
|
+
[model]: {
|
|
378
|
+
...modelUsage,
|
|
379
|
+
calls: modelUsage.calls + 1,
|
|
380
|
+
tools: modelTools(modelUsage, event.data.name),
|
|
381
|
+
byDay: bumpModelDay(modelUsage, day, 1, 0, 0),
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
if (Object.keys(next.pending).length >= TOOL_USAGE_CAPS.pending)
|
|
386
|
+
next = evictOldestPending(next);
|
|
387
|
+
next = {
|
|
388
|
+
...next,
|
|
389
|
+
pending: {
|
|
390
|
+
...next.pending,
|
|
391
|
+
[event.data.callId]: { name: event.data.name, time: event.time, kind: 'direct', model },
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
return { ...next, recent: appendRecent(next, {
|
|
395
|
+
time: event.time,
|
|
396
|
+
name: event.data.name,
|
|
397
|
+
model,
|
|
398
|
+
kind: 'direct',
|
|
399
|
+
callId: event.data.callId,
|
|
400
|
+
ms: null,
|
|
401
|
+
ok: null,
|
|
402
|
+
code: null,
|
|
403
|
+
}) };
|
|
404
|
+
}
|
|
405
|
+
case 'tool/result': {
|
|
406
|
+
const callId = event.data.message.source.callId;
|
|
407
|
+
if (!Object.hasOwn(state.pending, callId))
|
|
408
|
+
return state;
|
|
409
|
+
const pending = state.pending[callId];
|
|
410
|
+
if (pending === undefined)
|
|
411
|
+
return state;
|
|
412
|
+
const ms = Math.max(0, event.time - pending.time);
|
|
413
|
+
const isError = event.data.message.isError === true || event.data.error !== undefined;
|
|
414
|
+
const code = typeof event.data.error?.code === 'string' ? event.data.error.code : null;
|
|
415
|
+
const day = dayKeyOf(pending.time);
|
|
416
|
+
const owner = staticToolOwner(pending.name).owner;
|
|
417
|
+
// A call past the distinct-tool bound has no per-tool row; its totals below are
|
|
418
|
+
// still updated, but no 101st row is invented.
|
|
419
|
+
const bucket = state.byTool[pending.name];
|
|
420
|
+
const errors = isError ? 1 : 0;
|
|
421
|
+
const histogram = bucket === undefined ? undefined : [...bucket.histogram];
|
|
422
|
+
if (histogram !== undefined)
|
|
423
|
+
histogram[bucketOf(ms)] = (histogram[bucketOf(ms)] ?? 0) + 1;
|
|
424
|
+
const pendingRest = { ...state.pending };
|
|
425
|
+
delete pendingRest[callId];
|
|
426
|
+
const modelUsage = state.byModel[pending.model];
|
|
427
|
+
return {
|
|
428
|
+
...state,
|
|
429
|
+
errors: state.errors + errors,
|
|
430
|
+
toolMs: state.toolMs + ms,
|
|
431
|
+
byErrorCode: code === null ? state.byErrorCode : { ...state.byErrorCode, [code]: (state.byErrorCode[code] ?? 0) + 1 },
|
|
432
|
+
byDay: bumpDay(state, day, 0, errors, ms, 0, code),
|
|
433
|
+
byOwner: { ...state.byOwner, [owner]: bumpOwner(state.byOwner[owner], day, 0, errors, ms) },
|
|
434
|
+
byModel: modelUsage === undefined
|
|
435
|
+
? state.byModel
|
|
436
|
+
: {
|
|
437
|
+
...state.byModel,
|
|
438
|
+
[pending.model]: {
|
|
439
|
+
...modelUsage,
|
|
440
|
+
errors: modelUsage.errors + errors,
|
|
441
|
+
ms: modelUsage.ms + ms,
|
|
442
|
+
histogram: addToHistogram(modelUsage.histogram, ms),
|
|
443
|
+
byDay: bumpModelDay(modelUsage, day, 0, errors, ms),
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
byTool: bucket === undefined ? state.byTool : {
|
|
447
|
+
...state.byTool,
|
|
448
|
+
[pending.name]: bumpToolDay({
|
|
449
|
+
...bucket,
|
|
450
|
+
errors: bucket.errors + errors,
|
|
451
|
+
totalMs: bucket.totalMs + ms,
|
|
452
|
+
maxMs: Math.max(bucket.maxMs, ms),
|
|
453
|
+
resultBytes: bucket.resultBytes + (event.data.message.content === undefined ? 0 : byteLengthOf(event.data.message.content)),
|
|
454
|
+
histogram: histogram ?? bucket.histogram,
|
|
455
|
+
}, day, 0, errors, ms, 0, 0),
|
|
456
|
+
},
|
|
457
|
+
pending: pendingRest,
|
|
458
|
+
recent: patchRecent(state.recent, callId, 'direct', { ms, ok: !isError, code }),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
case 'tool/ptc-dispatch-start': {
|
|
462
|
+
const day = dayKeyOf(event.time);
|
|
463
|
+
const model = codeModelFor(state);
|
|
464
|
+
const owner = staticToolOwner(event.data.name).owner;
|
|
465
|
+
const { key: toolKey, bucket, state: withBucket } = toolBucket(state, event.data.name);
|
|
466
|
+
let next = {
|
|
467
|
+
...withBucket,
|
|
468
|
+
calls: withBucket.calls + 1,
|
|
469
|
+
byDay: bumpDay(withBucket, day, 1, 0, 0, 0, null),
|
|
470
|
+
byOwner: { ...withBucket.byOwner, [owner]: bumpOwner(withBucket.byOwner[owner], day, 1, 0, 0) },
|
|
471
|
+
byTool: toolKey === '' ? withBucket.byTool : {
|
|
472
|
+
...withBucket.byTool,
|
|
473
|
+
[toolKey]: bumpToolDay({
|
|
474
|
+
...bucket,
|
|
475
|
+
calls: bucket.calls + 1,
|
|
476
|
+
codeCalls: bucket.codeCalls + 1,
|
|
477
|
+
argsBytes: bucket.argsBytes + byteLengthOf(event.data.arguments),
|
|
478
|
+
firstUsed: bucket.firstUsed === null ? event.time : Math.min(bucket.firstUsed, event.time),
|
|
479
|
+
lastUsed: bucket.lastUsed === null ? event.time : Math.max(bucket.lastUsed, event.time),
|
|
480
|
+
}, day, 1, 0, 0, 0, 1),
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
next = pruneDays(next);
|
|
484
|
+
const { bucket: modelUsage, state: withModel } = modelBucket(next, model);
|
|
485
|
+
next = {
|
|
486
|
+
...withModel,
|
|
487
|
+
byModel: {
|
|
488
|
+
...withModel.byModel,
|
|
489
|
+
[model]: {
|
|
490
|
+
...modelUsage,
|
|
491
|
+
calls: modelUsage.calls + 1,
|
|
492
|
+
tools: modelTools(modelUsage, event.data.name),
|
|
493
|
+
byDay: bumpModelDay(modelUsage, day, 1, 0, 0),
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
const key = `code:${event.data.subCallId}`;
|
|
498
|
+
if (Object.keys(next.pending).length >= TOOL_USAGE_CAPS.pending)
|
|
499
|
+
next = evictOldestPending(next);
|
|
500
|
+
next = {
|
|
501
|
+
...next,
|
|
502
|
+
pending: {
|
|
503
|
+
...next.pending,
|
|
504
|
+
[key]: {
|
|
505
|
+
name: event.data.name,
|
|
506
|
+
time: event.time,
|
|
507
|
+
kind: 'code',
|
|
508
|
+
model,
|
|
509
|
+
parentCallId: event.data.parentCallId,
|
|
510
|
+
rootCallId: event.data.rootCallId,
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
};
|
|
514
|
+
return {
|
|
515
|
+
...next,
|
|
516
|
+
recent: appendRecent(next, {
|
|
517
|
+
time: event.time,
|
|
518
|
+
name: event.data.name,
|
|
519
|
+
model,
|
|
520
|
+
kind: 'code',
|
|
521
|
+
callId: event.data.subCallId,
|
|
522
|
+
parentCallId: event.data.parentCallId,
|
|
523
|
+
rootCallId: event.data.rootCallId,
|
|
524
|
+
ms: null,
|
|
525
|
+
ok: null,
|
|
526
|
+
code: null,
|
|
527
|
+
}),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
case 'tool/ptc-dispatch': {
|
|
531
|
+
const key = `code:${event.data.subCallId}`;
|
|
532
|
+
if (!Object.hasOwn(state.pending, key))
|
|
533
|
+
return state;
|
|
534
|
+
const pending = state.pending[key];
|
|
535
|
+
if (pending === undefined)
|
|
536
|
+
return state;
|
|
537
|
+
const ms = Math.max(0, event.time - pending.time);
|
|
538
|
+
const isError = event.data.isError === true;
|
|
539
|
+
const code = typeof event.data.error?.code === 'string' ? event.data.error.code : null;
|
|
540
|
+
const day = dayKeyOf(pending.time);
|
|
541
|
+
const owner = staticToolOwner(pending.name).owner;
|
|
542
|
+
// Untracked past the tool bound: totals still settle, no 101st row is invented.
|
|
543
|
+
const bucket = state.byTool[pending.name];
|
|
544
|
+
const errors = isError ? 1 : 0;
|
|
545
|
+
const histogram = bucket === undefined ? undefined : [...bucket.histogram];
|
|
546
|
+
if (histogram !== undefined)
|
|
547
|
+
histogram[bucketOf(ms)] = (histogram[bucketOf(ms)] ?? 0) + 1;
|
|
548
|
+
const pendingRest = { ...state.pending };
|
|
549
|
+
delete pendingRest[key];
|
|
550
|
+
const modelUsage = state.byModel[pending.model];
|
|
551
|
+
return {
|
|
552
|
+
...state,
|
|
553
|
+
errors: state.errors + errors,
|
|
554
|
+
toolMs: state.toolMs + ms,
|
|
555
|
+
byErrorCode: code === null ? state.byErrorCode : { ...state.byErrorCode, [code]: (state.byErrorCode[code] ?? 0) + 1 },
|
|
556
|
+
byDay: bumpDay(state, day, 0, errors, ms, 0, code),
|
|
557
|
+
byOwner: { ...state.byOwner, [owner]: bumpOwner(state.byOwner[owner], day, 0, errors, ms) },
|
|
558
|
+
byModel: modelUsage === undefined
|
|
559
|
+
? state.byModel
|
|
560
|
+
: {
|
|
561
|
+
...state.byModel,
|
|
562
|
+
[pending.model]: {
|
|
563
|
+
...modelUsage,
|
|
564
|
+
errors: modelUsage.errors + errors,
|
|
565
|
+
ms: modelUsage.ms + ms,
|
|
566
|
+
histogram: addToHistogram(modelUsage.histogram, ms),
|
|
567
|
+
byDay: bumpModelDay(modelUsage, day, 0, errors, ms),
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
byTool: bucket === undefined ? state.byTool : {
|
|
571
|
+
...state.byTool,
|
|
572
|
+
[pending.name]: bumpToolDay({
|
|
573
|
+
...bucket,
|
|
574
|
+
errors: bucket.errors + errors,
|
|
575
|
+
totalMs: bucket.totalMs + ms,
|
|
576
|
+
maxMs: Math.max(bucket.maxMs, ms),
|
|
577
|
+
resultBytes: bucket.resultBytes + byteLengthOf(event.data.content),
|
|
578
|
+
histogram: histogram ?? bucket.histogram,
|
|
579
|
+
}, day, 0, errors, ms, 0, 0),
|
|
580
|
+
},
|
|
581
|
+
pending: pendingRest,
|
|
582
|
+
recent: patchRecent(state.recent, event.data.subCallId, 'code', { ms, ok: !isError, code }),
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
case 'turn/end': {
|
|
586
|
+
let next = state;
|
|
587
|
+
for (const key of Object.keys(next.pending))
|
|
588
|
+
next = dropUnsettled(next, key);
|
|
589
|
+
if (next.lastTurn === event.data.turn)
|
|
590
|
+
return next === state ? state : next;
|
|
591
|
+
return { ...next, lastTurn: event.data.turn };
|
|
592
|
+
}
|
|
593
|
+
default:
|
|
594
|
+
return state;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
/** A copy of one owner counter with the given increments applied. */
|
|
598
|
+
function bumpOwner(current, day, calls, errors, ms) {
|
|
599
|
+
return {
|
|
600
|
+
calls: (current?.calls ?? 0) + calls,
|
|
601
|
+
errors: (current?.errors ?? 0) + errors,
|
|
602
|
+
ms: (current?.ms ?? 0) + ms,
|
|
603
|
+
byDay: bumpOwnerDay(current, day, calls, errors, ms),
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
/** The compact wire value, derived only from the detail state. */
|
|
607
|
+
export function toolUsageWire(state) {
|
|
608
|
+
const byModel = {};
|
|
609
|
+
for (const [model, usage] of Object.entries(state.byModel)) {
|
|
610
|
+
byModel[model] = { calls: usage.calls, errors: usage.errors, tools: usage.tools.length, ms: usage.ms };
|
|
611
|
+
}
|
|
612
|
+
const byOwner = {};
|
|
613
|
+
for (const [owner, usage] of Object.entries(state.byOwner)) {
|
|
614
|
+
byOwner[owner] = { calls: usage.calls, errors: usage.errors, ms: usage.ms };
|
|
615
|
+
}
|
|
616
|
+
const byDay = {};
|
|
617
|
+
for (const [day, usage] of Object.entries(state.byDay)) {
|
|
618
|
+
byDay[day] = { calls: usage.calls, errors: usage.errors, ms: usage.ms, unsettled: usage.unsettled };
|
|
619
|
+
}
|
|
620
|
+
const topTools = Object.entries(state.byTool)
|
|
621
|
+
.map(([name, bucket]) => ({ name, calls: bucket.calls, ms: bucket.totalMs }))
|
|
622
|
+
.sort((left, right) => right.calls - left.calls || left.name.localeCompare(right.name))
|
|
623
|
+
.slice(0, TOOL_USAGE_CAPS.topTools);
|
|
624
|
+
return {
|
|
625
|
+
stateVersion: TOOL_USAGE_STATE_VERSION,
|
|
626
|
+
calls: state.calls,
|
|
627
|
+
errors: state.errors,
|
|
628
|
+
unsettled: state.unsettled,
|
|
629
|
+
toolMs: state.toolMs,
|
|
630
|
+
uniqueTools: state.uniqueTools,
|
|
631
|
+
byOwner,
|
|
632
|
+
byModel,
|
|
633
|
+
byDay,
|
|
634
|
+
recent: state.recent.slice(-TOOL_USAGE_CAPS.wireRecent).map(entry => ({
|
|
635
|
+
time: entry.time,
|
|
636
|
+
name: entry.name,
|
|
637
|
+
model: entry.model,
|
|
638
|
+
ms: entry.ms,
|
|
639
|
+
ok: entry.ok,
|
|
640
|
+
kind: entry.kind,
|
|
641
|
+
code: entry.code,
|
|
642
|
+
})),
|
|
643
|
+
topTools,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
/** One validation failure, named with the path that failed. */
|
|
647
|
+
export class ToolUsageSchemaError extends Error {
|
|
648
|
+
name = 'ToolUsageSchemaError';
|
|
649
|
+
}
|
|
650
|
+
/** Describe a rejected value without echoing its content. */
|
|
651
|
+
function describeValue(value) {
|
|
652
|
+
if (value === null)
|
|
653
|
+
return 'null';
|
|
654
|
+
if (Array.isArray(value))
|
|
655
|
+
return 'array';
|
|
656
|
+
return typeof value;
|
|
657
|
+
}
|
|
658
|
+
/** Refuse one value at a path. */
|
|
659
|
+
function fail(path, expected, value) {
|
|
660
|
+
throw new ToolUsageSchemaError(`${path}: expected ${expected}, received ${describeValue(value)}`);
|
|
661
|
+
}
|
|
662
|
+
/** Validate an object with NO undeclared fields. */
|
|
663
|
+
function objectAt(value, path, fields) {
|
|
664
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
665
|
+
fail(path, 'an object', value);
|
|
666
|
+
// SAFETY: the guard above proved a non-null, non-array object; the cast only restores
|
|
667
|
+
// the keyed view `Object.keys`/field reads require, and every field is validated below.
|
|
668
|
+
const record = value;
|
|
669
|
+
for (const key of Object.keys(record)) {
|
|
670
|
+
if (!fields.includes(key))
|
|
671
|
+
fail(`${path}.${key}`, 'no such field', record[key]);
|
|
672
|
+
}
|
|
673
|
+
return record;
|
|
674
|
+
}
|
|
675
|
+
/** Validate a finite number (optionally an integer and/or non-negative). */
|
|
676
|
+
function numberAt(value, path, options = {}) {
|
|
677
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
678
|
+
fail(path, 'a finite number', value);
|
|
679
|
+
if (options.int === true && !Number.isInteger(value))
|
|
680
|
+
fail(path, 'an integer', value);
|
|
681
|
+
if (options.min !== undefined && value < options.min)
|
|
682
|
+
fail(path, `a number >= ${options.min}`, value);
|
|
683
|
+
return value;
|
|
684
|
+
}
|
|
685
|
+
/** A non-negative integer counter. */
|
|
686
|
+
function countAt(value, path) {
|
|
687
|
+
return numberAt(value, path, { int: true, min: 0 });
|
|
688
|
+
}
|
|
689
|
+
/** A number or null. */
|
|
690
|
+
function nullableNumberAt(value, path) {
|
|
691
|
+
return value === null ? null : numberAt(value, path);
|
|
692
|
+
}
|
|
693
|
+
/** A string. */
|
|
694
|
+
function stringAt(value, path) {
|
|
695
|
+
if (typeof value !== 'string')
|
|
696
|
+
fail(path, 'a string', value);
|
|
697
|
+
return value;
|
|
698
|
+
}
|
|
699
|
+
/** A string or null. */
|
|
700
|
+
function nullableStringAt(value, path) {
|
|
701
|
+
return value === null ? null : stringAt(value, path);
|
|
702
|
+
}
|
|
703
|
+
/** A boolean. */
|
|
704
|
+
function booleanAt(value, path) {
|
|
705
|
+
if (typeof value !== 'boolean')
|
|
706
|
+
fail(path, 'a boolean', value);
|
|
707
|
+
return value;
|
|
708
|
+
}
|
|
709
|
+
/** A boolean or null. */
|
|
710
|
+
function nullableBooleanAt(value, path) {
|
|
711
|
+
return value === null ? null : booleanAt(value, path);
|
|
712
|
+
}
|
|
713
|
+
/** An array. */
|
|
714
|
+
function arrayAt(value, path) {
|
|
715
|
+
if (!Array.isArray(value))
|
|
716
|
+
fail(path, 'an array', value);
|
|
717
|
+
return value;
|
|
718
|
+
}
|
|
719
|
+
/** The entries of a string-keyed record. */
|
|
720
|
+
function recordAt(value, path) {
|
|
721
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
722
|
+
fail(path, 'a string-keyed record', value);
|
|
723
|
+
// SAFETY: the guard above proved a non-null, non-array object; `Object.entries` is the
|
|
724
|
+
// only operation, and every entry value is validated by the caller's value parser.
|
|
725
|
+
return Object.entries(value);
|
|
726
|
+
}
|
|
727
|
+
/** One call kind. */
|
|
728
|
+
function kindAt(value, path) {
|
|
729
|
+
if (value !== 'direct' && value !== 'code')
|
|
730
|
+
fail(path, '"direct" or "code"', value);
|
|
731
|
+
return value;
|
|
732
|
+
}
|
|
733
|
+
const TOOL_DAY_FIELDS = ['calls', 'errors', 'ms', 'maxMs', 'unsettled', 'codeCalls'];
|
|
734
|
+
const BUCKET_FIELDS = ['calls', 'errors', 'unsettled', 'codeCalls', 'directCalls', 'totalMs', 'maxMs', 'argsBytes', 'resultBytes', 'firstUsed', 'lastUsed', 'histogram', 'byDay'];
|
|
735
|
+
const MODEL_FIELDS = ['calls', 'errors', 'ms', 'tools', 'histogram', 'byDay'];
|
|
736
|
+
const OWNER_FIELDS = ['calls', 'errors', 'ms', 'byDay'];
|
|
737
|
+
const DAY_FIELDS = ['calls', 'errors', 'ms', 'unsettled', 'byCode'];
|
|
738
|
+
const RECENT_FIELDS = ['time', 'name', 'model', 'kind', 'callId', 'parentCallId', 'rootCallId', 'ms', 'ok', 'code'];
|
|
739
|
+
const PENDING_FIELDS = ['name', 'time', 'kind', 'model', 'parentCallId', 'rootCallId'];
|
|
740
|
+
/** Parse one tool/day counter. */
|
|
741
|
+
function parseToolDay(value, path) {
|
|
742
|
+
const record = objectAt(value, path, TOOL_DAY_FIELDS);
|
|
743
|
+
return {
|
|
744
|
+
calls: countAt(record['calls'], `${path}.calls`),
|
|
745
|
+
errors: countAt(record['errors'], `${path}.errors`),
|
|
746
|
+
ms: numberAt(record['ms'], `${path}.ms`, { min: 0 }),
|
|
747
|
+
maxMs: numberAt(record['maxMs'], `${path}.maxMs`, { min: 0 }),
|
|
748
|
+
unsettled: countAt(record['unsettled'], `${path}.unsettled`),
|
|
749
|
+
codeCalls: countAt(record['codeCalls'], `${path}.codeCalls`),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
/** Parse a record of tool/day counters. */
|
|
753
|
+
function parseToolDayRecord(value, path) {
|
|
754
|
+
const out = {};
|
|
755
|
+
for (const [day, entry] of recordAt(value, path))
|
|
756
|
+
out[day] = parseToolDay(entry, `${path}.${day}`);
|
|
757
|
+
return out;
|
|
758
|
+
}
|
|
759
|
+
/** Parse one tool performance row. */
|
|
760
|
+
function parseToolBucket(value, path) {
|
|
761
|
+
const record = objectAt(value, path, BUCKET_FIELDS);
|
|
762
|
+
const histogram = arrayAt(record['histogram'], `${path}.histogram`);
|
|
763
|
+
if (histogram.length !== TOOL_USAGE_CAPS.buckets)
|
|
764
|
+
fail(`${path}.histogram`, `${TOOL_USAGE_CAPS.buckets} entries`, histogram.length);
|
|
765
|
+
return {
|
|
766
|
+
calls: countAt(record['calls'], `${path}.calls`),
|
|
767
|
+
errors: countAt(record['errors'], `${path}.errors`),
|
|
768
|
+
unsettled: countAt(record['unsettled'], `${path}.unsettled`),
|
|
769
|
+
codeCalls: countAt(record['codeCalls'], `${path}.codeCalls`),
|
|
770
|
+
directCalls: countAt(record['directCalls'], `${path}.directCalls`),
|
|
771
|
+
totalMs: numberAt(record['totalMs'], `${path}.totalMs`, { min: 0 }),
|
|
772
|
+
maxMs: numberAt(record['maxMs'], `${path}.maxMs`, { min: 0 }),
|
|
773
|
+
argsBytes: numberAt(record['argsBytes'], `${path}.argsBytes`),
|
|
774
|
+
resultBytes: numberAt(record['resultBytes'], `${path}.resultBytes`),
|
|
775
|
+
firstUsed: nullableNumberAt(record['firstUsed'], `${path}.firstUsed`),
|
|
776
|
+
lastUsed: nullableNumberAt(record['lastUsed'], `${path}.lastUsed`),
|
|
777
|
+
histogram: histogram.map((entry, index) => countAt(entry, `${path}.histogram[${index}]`)),
|
|
778
|
+
byDay: parseToolDayRecord(record['byDay'], `${path}.byDay`),
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
/** Parse a model/day record. */
|
|
782
|
+
function parseModelDayRecord(value, path) {
|
|
783
|
+
const out = {};
|
|
784
|
+
for (const [day, entry] of recordAt(value, path)) {
|
|
785
|
+
const record = objectAt(entry, `${path}.${day}`, ['calls', 'errors', 'ms', 'maxMs']);
|
|
786
|
+
out[day] = {
|
|
787
|
+
calls: countAt(record['calls'], `${path}.${day}.calls`),
|
|
788
|
+
errors: countAt(record['errors'], `${path}.${day}.errors`),
|
|
789
|
+
ms: numberAt(record['ms'], `${path}.${day}.ms`, { min: 0 }),
|
|
790
|
+
maxMs: numberAt(record['maxMs'], `${path}.${day}.maxMs`, { min: 0 }),
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
return out;
|
|
794
|
+
}
|
|
795
|
+
/** Parse an owner/day record (owners carry no durations-per-call bound). */
|
|
796
|
+
function parseOwnerDayRecord(value, path) {
|
|
797
|
+
const out = {};
|
|
798
|
+
for (const [day, entry] of recordAt(value, path)) {
|
|
799
|
+
const record = objectAt(entry, `${path}.${day}`, ['calls', 'errors', 'ms']);
|
|
800
|
+
out[day] = {
|
|
801
|
+
calls: countAt(record['calls'], `${path}.${day}.calls`),
|
|
802
|
+
errors: countAt(record['errors'], `${path}.${day}.errors`),
|
|
803
|
+
ms: numberAt(record['ms'], `${path}.${day}.ms`, { min: 0 }),
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
return out;
|
|
807
|
+
}
|
|
808
|
+
/** Parse one model usage row. */
|
|
809
|
+
function parseModelUsage(value, path) {
|
|
810
|
+
const record = objectAt(value, path, MODEL_FIELDS);
|
|
811
|
+
const histogram = arrayAt(record['histogram'], `${path}.histogram`);
|
|
812
|
+
if (histogram.length !== TOOL_USAGE_CAPS.buckets)
|
|
813
|
+
fail(`${path}.histogram`, `${TOOL_USAGE_CAPS.buckets} entries`, histogram.length);
|
|
814
|
+
return {
|
|
815
|
+
calls: countAt(record['calls'], `${path}.calls`),
|
|
816
|
+
errors: countAt(record['errors'], `${path}.errors`),
|
|
817
|
+
ms: numberAt(record['ms'], `${path}.ms`, { min: 0 }),
|
|
818
|
+
tools: arrayAt(record['tools'], `${path}.tools`).map((entry, index) => stringAt(entry, `${path}.tools[${index}]`)),
|
|
819
|
+
histogram: histogram.map((entry, index) => countAt(entry, `${path}.histogram[${index}]`)),
|
|
820
|
+
byDay: parseModelDayRecord(record['byDay'], `${path}.byDay`),
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
/** Parse one owner usage row. */
|
|
824
|
+
function parseOwnerUsage(value, path) {
|
|
825
|
+
const record = objectAt(value, path, OWNER_FIELDS);
|
|
826
|
+
return {
|
|
827
|
+
calls: countAt(record['calls'], `${path}.calls`),
|
|
828
|
+
errors: countAt(record['errors'], `${path}.errors`),
|
|
829
|
+
ms: numberAt(record['ms'], `${path}.ms`, { min: 0 }),
|
|
830
|
+
byDay: parseOwnerDayRecord(record['byDay'], `${path}.byDay`),
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
/** Parse one day usage row. */
|
|
834
|
+
function parseDayUsage(value, path) {
|
|
835
|
+
const record = objectAt(value, path, DAY_FIELDS);
|
|
836
|
+
const byCode = {};
|
|
837
|
+
for (const [code, entry] of recordAt(record['byCode'], `${path}.byCode`))
|
|
838
|
+
byCode[code] = countAt(entry, `${path}.byCode.${code}`);
|
|
839
|
+
return {
|
|
840
|
+
calls: countAt(record['calls'], `${path}.calls`),
|
|
841
|
+
errors: countAt(record['errors'], `${path}.errors`),
|
|
842
|
+
ms: numberAt(record['ms'], `${path}.ms`, { min: 0 }),
|
|
843
|
+
unsettled: countAt(record['unsettled'], `${path}.unsettled`),
|
|
844
|
+
byCode,
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
/** Parse one retained recent call. */
|
|
848
|
+
function parseRecentCall(value, path) {
|
|
849
|
+
const record = objectAt(value, path, RECENT_FIELDS);
|
|
850
|
+
return {
|
|
851
|
+
time: numberAt(record['time'], `${path}.time`),
|
|
852
|
+
name: stringAt(record['name'], `${path}.name`),
|
|
853
|
+
model: stringAt(record['model'], `${path}.model`),
|
|
854
|
+
kind: kindAt(record['kind'], `${path}.kind`),
|
|
855
|
+
callId: stringAt(record['callId'], `${path}.callId`),
|
|
856
|
+
...record['parentCallId'] === undefined ? {} : { parentCallId: stringAt(record['parentCallId'], `${path}.parentCallId`) },
|
|
857
|
+
...record['rootCallId'] === undefined ? {} : { rootCallId: stringAt(record['rootCallId'], `${path}.rootCallId`) },
|
|
858
|
+
ms: nullableNumberAt(record['ms'], `${path}.ms`),
|
|
859
|
+
ok: nullableBooleanAt(record['ok'], `${path}.ok`),
|
|
860
|
+
code: nullableStringAt(record['code'], `${path}.code`),
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
/** Parse one in-flight call. */
|
|
864
|
+
function parsePendingCall(value, path) {
|
|
865
|
+
const record = objectAt(value, path, PENDING_FIELDS);
|
|
866
|
+
return {
|
|
867
|
+
name: stringAt(record['name'], `${path}.name`),
|
|
868
|
+
time: numberAt(record['time'], `${path}.time`),
|
|
869
|
+
kind: kindAt(record['kind'], `${path}.kind`),
|
|
870
|
+
model: stringAt(record['model'], `${path}.model`),
|
|
871
|
+
...record['parentCallId'] === undefined ? {} : { parentCallId: stringAt(record['parentCallId'], `${path}.parentCallId`) },
|
|
872
|
+
...record['rootCallId'] === undefined ? {} : { rootCallId: stringAt(record['rootCallId'], `${path}.rootCallId`) },
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
const STATE_FIELDS = [
|
|
876
|
+
'stateVersion', 'calls', 'errors', 'unsettled', 'toolMs', 'uniqueTools', 'byTool', 'byModel',
|
|
877
|
+
'byOwner', 'byDay', 'byErrorCode', 'recent', 'pending', 'route', 'stepModel', 'lastTurn',
|
|
878
|
+
'toolsTruncated', 'modelsTruncated',
|
|
879
|
+
];
|
|
880
|
+
/** Parse the persisted fold state (the projection cache's input boundary). */
|
|
881
|
+
function parseToolUsageState(value) {
|
|
882
|
+
const record = objectAt(value, 'state', STATE_FIELDS);
|
|
883
|
+
if (record['stateVersion'] !== TOOL_USAGE_STATE_VERSION)
|
|
884
|
+
fail('state.stateVersion', String(TOOL_USAGE_STATE_VERSION), record['stateVersion']);
|
|
885
|
+
const byTool = {};
|
|
886
|
+
for (const [name, entry] of recordAt(record['byTool'], 'state.byTool'))
|
|
887
|
+
byTool[name] = parseToolBucket(entry, `state.byTool.${name}`);
|
|
888
|
+
const byModel = {};
|
|
889
|
+
for (const [model, entry] of recordAt(record['byModel'], 'state.byModel'))
|
|
890
|
+
byModel[model] = parseModelUsage(entry, `state.byModel.${model}`);
|
|
891
|
+
const byOwner = {};
|
|
892
|
+
for (const [owner, entry] of recordAt(record['byOwner'], 'state.byOwner'))
|
|
893
|
+
byOwner[owner] = parseOwnerUsage(entry, `state.byOwner.${owner}`);
|
|
894
|
+
const byDay = {};
|
|
895
|
+
for (const [day, entry] of recordAt(record['byDay'], 'state.byDay'))
|
|
896
|
+
byDay[day] = parseDayUsage(entry, `state.byDay.${day}`);
|
|
897
|
+
const byErrorCode = {};
|
|
898
|
+
for (const [code, entry] of recordAt(record['byErrorCode'], 'state.byErrorCode'))
|
|
899
|
+
byErrorCode[code] = countAt(entry, `state.byErrorCode.${code}`);
|
|
900
|
+
const pending = {};
|
|
901
|
+
for (const [key, entry] of recordAt(record['pending'], 'state.pending'))
|
|
902
|
+
pending[key] = parsePendingCall(entry, `state.pending.${key}`);
|
|
903
|
+
const route = record['route'] === null ? null : (() => {
|
|
904
|
+
const parsed = objectAt(record['route'], 'state.route', ['provider', 'model']);
|
|
905
|
+
return { provider: stringAt(parsed['provider'], 'state.route.provider'), model: stringAt(parsed['model'], 'state.route.model') };
|
|
906
|
+
})();
|
|
907
|
+
const stepModel = record['stepModel'] === null ? null : (() => {
|
|
908
|
+
const parsed = objectAt(record['stepModel'], 'state.stepModel', ['turn', 'step', 'model']);
|
|
909
|
+
return {
|
|
910
|
+
turn: numberAt(parsed['turn'], 'state.stepModel.turn', { int: true }),
|
|
911
|
+
step: numberAt(parsed['step'], 'state.stepModel.step', { int: true }),
|
|
912
|
+
model: stringAt(parsed['model'], 'state.stepModel.model'),
|
|
913
|
+
};
|
|
914
|
+
})();
|
|
915
|
+
return {
|
|
916
|
+
stateVersion: TOOL_USAGE_STATE_VERSION,
|
|
917
|
+
calls: countAt(record['calls'], 'state.calls'),
|
|
918
|
+
errors: countAt(record['errors'], 'state.errors'),
|
|
919
|
+
unsettled: countAt(record['unsettled'], 'state.unsettled'),
|
|
920
|
+
toolMs: numberAt(record['toolMs'], 'state.toolMs', { min: 0 }),
|
|
921
|
+
uniqueTools: countAt(record['uniqueTools'], 'state.uniqueTools'),
|
|
922
|
+
byTool,
|
|
923
|
+
byModel,
|
|
924
|
+
byOwner,
|
|
925
|
+
byDay,
|
|
926
|
+
byErrorCode,
|
|
927
|
+
recent: arrayAt(record['recent'], 'state.recent').map((entry, index) => parseRecentCall(entry, `state.recent[${index}]`)),
|
|
928
|
+
pending,
|
|
929
|
+
route,
|
|
930
|
+
stepModel,
|
|
931
|
+
lastTurn: record['lastTurn'] === null ? null : numberAt(record['lastTurn'], 'state.lastTurn', { int: true }),
|
|
932
|
+
toolsTruncated: countAt(record['toolsTruncated'], 'state.toolsTruncated'),
|
|
933
|
+
modelsTruncated: countAt(record['modelsTruncated'], 'state.modelsTruncated'),
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
const WIRE_RECENT_FIELDS = ['time', 'name', 'model', 'ms', 'ok', 'kind', 'code'];
|
|
937
|
+
const WIRE_FIELDS = ['stateVersion', 'calls', 'errors', 'unsettled', 'toolMs', 'uniqueTools', 'byOwner', 'byModel', 'byDay', 'recent', 'topTools'];
|
|
938
|
+
/** Parse the compact wire value (the host's outbound boundary). */
|
|
939
|
+
function parseToolUsageWire(value) {
|
|
940
|
+
const record = objectAt(value, 'wire', WIRE_FIELDS);
|
|
941
|
+
if (record['stateVersion'] !== TOOL_USAGE_STATE_VERSION)
|
|
942
|
+
fail('wire.stateVersion', String(TOOL_USAGE_STATE_VERSION), record['stateVersion']);
|
|
943
|
+
const byOwner = {};
|
|
944
|
+
for (const [owner, entry] of recordAt(record['byOwner'], 'wire.byOwner')) {
|
|
945
|
+
const parsed = objectAt(entry, `wire.byOwner.${owner}`, ['calls', 'errors', 'ms']);
|
|
946
|
+
byOwner[owner] = {
|
|
947
|
+
calls: countAt(parsed['calls'], `wire.byOwner.${owner}.calls`),
|
|
948
|
+
errors: countAt(parsed['errors'], `wire.byOwner.${owner}.errors`),
|
|
949
|
+
ms: numberAt(parsed['ms'], `wire.byOwner.${owner}.ms`, { min: 0 }),
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
const byModel = {};
|
|
953
|
+
for (const [model, entry] of recordAt(record['byModel'], 'wire.byModel')) {
|
|
954
|
+
const parsed = objectAt(entry, `wire.byModel.${model}`, ['calls', 'errors', 'tools', 'ms']);
|
|
955
|
+
byModel[model] = {
|
|
956
|
+
calls: countAt(parsed['calls'], `wire.byModel.${model}.calls`),
|
|
957
|
+
errors: countAt(parsed['errors'], `wire.byModel.${model}.errors`),
|
|
958
|
+
tools: countAt(parsed['tools'], `wire.byModel.${model}.tools`),
|
|
959
|
+
ms: numberAt(parsed['ms'], `wire.byModel.${model}.ms`, { min: 0 }),
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
const byDay = {};
|
|
963
|
+
for (const [day, entry] of recordAt(record['byDay'], 'wire.byDay')) {
|
|
964
|
+
const parsed = objectAt(entry, `wire.byDay.${day}`, ['calls', 'errors', 'ms', 'unsettled']);
|
|
965
|
+
byDay[day] = {
|
|
966
|
+
calls: countAt(parsed['calls'], `wire.byDay.${day}.calls`),
|
|
967
|
+
errors: countAt(parsed['errors'], `wire.byDay.${day}.errors`),
|
|
968
|
+
ms: numberAt(parsed['ms'], `wire.byDay.${day}.ms`, { min: 0 }),
|
|
969
|
+
unsettled: countAt(parsed['unsettled'], `wire.byDay.${day}.unsettled`),
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
return {
|
|
973
|
+
stateVersion: TOOL_USAGE_STATE_VERSION,
|
|
974
|
+
calls: countAt(record['calls'], 'wire.calls'),
|
|
975
|
+
errors: countAt(record['errors'], 'wire.errors'),
|
|
976
|
+
unsettled: countAt(record['unsettled'], 'wire.unsettled'),
|
|
977
|
+
toolMs: numberAt(record['toolMs'], 'wire.toolMs', { min: 0 }),
|
|
978
|
+
uniqueTools: countAt(record['uniqueTools'], 'wire.uniqueTools'),
|
|
979
|
+
byOwner,
|
|
980
|
+
byModel,
|
|
981
|
+
byDay,
|
|
982
|
+
recent: arrayAt(record['recent'], 'wire.recent').map((entry, index) => {
|
|
983
|
+
const parsed = objectAt(entry, `wire.recent[${index}]`, WIRE_RECENT_FIELDS);
|
|
984
|
+
return {
|
|
985
|
+
time: numberAt(parsed['time'], `wire.recent[${index}].time`),
|
|
986
|
+
name: stringAt(parsed['name'], `wire.recent[${index}].name`),
|
|
987
|
+
model: stringAt(parsed['model'], `wire.recent[${index}].model`),
|
|
988
|
+
ms: nullableNumberAt(parsed['ms'], `wire.recent[${index}].ms`),
|
|
989
|
+
ok: nullableBooleanAt(parsed['ok'], `wire.recent[${index}].ok`),
|
|
990
|
+
kind: kindAt(parsed['kind'], `wire.recent[${index}].kind`),
|
|
991
|
+
code: nullableStringAt(parsed['code'], `wire.recent[${index}].code`),
|
|
992
|
+
};
|
|
993
|
+
}),
|
|
994
|
+
topTools: arrayAt(record['topTools'], 'wire.topTools').map((entry, index) => {
|
|
995
|
+
const parsed = objectAt(entry, `wire.topTools[${index}]`, ['name', 'calls', 'ms']);
|
|
996
|
+
return {
|
|
997
|
+
name: stringAt(parsed['name'], `wire.topTools[${index}].name`),
|
|
998
|
+
calls: countAt(parsed['calls'], `wire.topTools[${index}].calls`),
|
|
999
|
+
ms: numberAt(parsed['ms'], `wire.topTools[${index}].ms`, { min: 0 }),
|
|
1000
|
+
};
|
|
1001
|
+
}),
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
/** Wrap one parser in the schema contract. */
|
|
1005
|
+
function schemaOf(parse) {
|
|
1006
|
+
return {
|
|
1007
|
+
parse,
|
|
1008
|
+
safeParse: (value) => {
|
|
1009
|
+
try {
|
|
1010
|
+
return { success: true, data: parse(value) };
|
|
1011
|
+
}
|
|
1012
|
+
catch (error) {
|
|
1013
|
+
return { success: false, error: error instanceof Error ? error : new Error(String(error)) };
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
/** The strict persisted-state schema (the projection cache's input boundary). */
|
|
1019
|
+
export const toolUsageStateSchema = schemaOf(parseToolUsageState);
|
|
1020
|
+
/** The compact wire's strict shape (the host's outbound boundary). */
|
|
1021
|
+
export const toolUsageWireSchema = schemaOf(parseToolUsageWire);
|
|
1022
|
+
/**
|
|
1023
|
+
* The registered unit. Its STATE is the detail; its WIRE is the compact value.
|
|
1024
|
+
*
|
|
1025
|
+
* `apply` receives a full `SessionEvent`; the fold reads only the seven event types
|
|
1026
|
+
* declared by {@link ToolUsageEvent} and returns the state unchanged otherwise.
|
|
1027
|
+
*/
|
|
1028
|
+
export const toolUsageProjectionDefinition = {
|
|
1029
|
+
key: TOOL_USAGE_KEY,
|
|
1030
|
+
stateVersion: TOOL_USAGE_STATE_VERSION,
|
|
1031
|
+
stateSchema: toolUsageStateSchema,
|
|
1032
|
+
init: () => initToolUsageState(),
|
|
1033
|
+
// SAFETY: the projection registry passes the session's own `SessionEvent`; the fold
|
|
1034
|
+
// reads only the seven event types declared by `ToolUsageEvent` and ignores the rest.
|
|
1035
|
+
apply: (state, event) => foldToolUsageEvent(state, event),
|
|
1036
|
+
wire: { viewSchema: toolUsageWireSchema, view: toolUsageWire },
|
|
1037
|
+
};
|
|
1038
|
+
//# sourceMappingURL=tool-usage-projection.js.map
|