@try-works/dsh-model-as-tool 0.1.2 → 0.3.6
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/bin/dsh-tool-mode-credentials.mjs +20 -0
- package/bin/dsh-tool-mode.mjs +202 -0
- package/catalog/desert-ant-models.json +693 -693
- package/client/client.model-tools.js +592 -0
- package/client/index.js +1679 -0
- package/lib/adapters/index.d.ts.map +1 -1
- package/lib/adapters/index.js +4 -4
- package/lib/bindings.d.ts.map +1 -1
- package/lib/bindings.js +26 -25
- package/lib/capability.d.ts.map +1 -1
- package/lib/capability.js +17 -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/diagnostics.d.ts +166 -0
- package/lib/diagnostics.d.ts.map +1 -0
- package/lib/diagnostics.js +135 -0
- package/lib/extension-dependencies.d.ts +197 -0
- package/lib/extension-dependencies.d.ts.map +1 -0
- package/lib/extension-dependencies.js +369 -0
- package/lib/extension-dispatch.d.ts +117 -0
- package/lib/extension-dispatch.d.ts.map +1 -0
- package/lib/extension-dispatch.js +124 -0
- package/lib/extension-registrar.d.ts +100 -0
- package/lib/extension-registrar.d.ts.map +1 -0
- package/lib/extension-registrar.js +134 -0
- package/lib/extensions-api.d.ts +217 -0
- package/lib/extensions-api.d.ts.map +1 -0
- package/lib/extensions-api.js +17 -0
- package/lib/extensions-remote.d.ts +54 -0
- package/lib/extensions-remote.d.ts.map +1 -0
- package/lib/extensions-remote.js +66 -0
- package/lib/extensions-service.d.ts +334 -0
- package/lib/extensions-service.d.ts.map +1 -0
- package/lib/extensions-service.js +1351 -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 +95 -0
- package/lib/harness-serving.d.ts.map +1 -0
- package/lib/harness-serving.js +157 -0
- package/lib/headless.d.ts +97 -0
- package/lib/headless.d.ts.map +1 -0
- package/lib/headless.js +349 -0
- package/lib/http-acquisition.d.ts +27 -0
- package/lib/http-acquisition.d.ts.map +1 -0
- package/lib/http-acquisition.js +136 -0
- package/lib/index.d.ts +250 -7
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2625 -70
- 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/legacy-migration.d.ts +95 -0
- package/lib/legacy-migration.d.ts.map +1 -0
- package/lib/legacy-migration.js +429 -0
- package/lib/managed-workers.d.ts +151 -0
- package/lib/managed-workers.d.ts.map +1 -0
- package/lib/managed-workers.js +249 -0
- package/lib/mcp/client-grant.d.ts +74 -0
- package/lib/mcp/client-grant.d.ts.map +1 -0
- package/lib/mcp/client-grant.js +60 -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 +454 -0
- package/lib/mcp/host-binding.d.ts +25 -0
- package/lib/mcp/host-binding.d.ts.map +1 -1
- package/lib/mcp/host-binding.js +107 -14
- package/lib/mcp/http.d.ts +68 -18
- package/lib/mcp/http.d.ts.map +1 -1
- package/lib/mcp/http.js +390 -62
- 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 +46 -7
- package/lib/mcp/protocol.d.ts.map +1 -1
- package/lib/mcp/protocol.js +63 -16
- 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 +181 -0
- package/lib/mcp/server.d.ts +11 -1
- package/lib/mcp/server.d.ts.map +1 -1
- package/lib/mcp/server.js +80 -13
- package/lib/mcp/stdio.d.ts.map +1 -1
- package/lib/mcp/stdio.js +9 -1
- package/lib/mcp/subscriptions.d.ts +43 -1
- package/lib/mcp/subscriptions.d.ts.map +1 -1
- package/lib/mcp/subscriptions.js +90 -9
- 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/planning.d.ts +246 -0
- package/lib/planning.d.ts.map +1 -0
- package/lib/planning.js +457 -0
- package/lib/prepared-assets.d.ts +178 -0
- package/lib/prepared-assets.d.ts.map +1 -0
- package/lib/prepared-assets.js +286 -0
- package/lib/provider-bridge.d.ts +37 -0
- package/lib/provider-bridge.d.ts.map +1 -1
- package/lib/provider-bridge.js +189 -12
- package/lib/ptc-authority.d.ts +140 -0
- package/lib/ptc-authority.d.ts.map +1 -0
- package/lib/ptc-authority.js +216 -0
- package/lib/ptc-runtime.d.ts +156 -0
- package/lib/ptc-runtime.d.ts.map +1 -0
- package/lib/ptc-runtime.js +191 -0
- package/lib/publication.d.ts +2 -1
- package/lib/publication.d.ts.map +1 -1
- package/lib/publication.js +13 -5
- package/lib/receipt.d.ts.map +1 -1
- package/lib/receipt.js +8 -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-mode-run.d.ts +235 -0
- package/lib/tool-mode-run.d.ts.map +1 -0
- package/lib/tool-mode-run.js +283 -0
- package/lib/types.d.ts +3 -1
- package/lib/types.d.ts.map +1 -1
- 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/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 +85 -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 +48 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +104 -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 +55 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +107 -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 +34 -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 +83 -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 +125 -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 +31 -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 +31 -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 +185 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts +126 -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 +297 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts +216 -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 +800 -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 +235 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts +367 -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 +807 -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/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 +118 -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/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 +150 -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 +670 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/package.json +53 -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 +427 -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 +592 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts +60 -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 +936 -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 +200 -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 +105 -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 +129 -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 +325 -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 +1310 -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 +227 -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 +407 -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 +50 -17
- package/remote/spec.mjs +439 -0
- package/remote/typert.host.mjs +14 -0
- package/remote/typert.remote-client.mjs +16 -0
- package/lib/adapters/tongue.d.ts +0 -31
- package/lib/adapters/tongue.d.ts.map +0 -1
- package/lib/adapters/tongue.js +0 -93
- package/lib/adapters/voz.d.ts +0 -28
- package/lib/adapters/voz.d.ts.map +0 -1
- package/lib/adapters/voz.js +0 -102
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.d.ts +0 -0
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.d.ts.map +0 -0
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.js +0 -0
package/lib/planning.js
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host-side `tools_plan` discipline (NDL-02, NDL-03).
|
|
3
|
+
*
|
|
4
|
+
* The Needle extension implements the ENGINE half of the planning contract: one-shot
|
|
5
|
+
* proposals over a caller-supplied tool list, with the list validated by the extension
|
|
6
|
+
* itself. What NDL-02 forbids is the HOST trusting that caller-supplied list. The list is
|
|
7
|
+
* the authorization surface: whatever schemas the planner receives, it may propose calls
|
|
8
|
+
* against, and a caller who could hand over the whole registry — or a list of its own
|
|
9
|
+
* invention — would be using `tools_plan` as a permission oracle. So the host owns four
|
|
10
|
+
* things the extension never sees:
|
|
11
|
+
*
|
|
12
|
+
* 1. **The frozen snapshot.** At first use, the host captures the currently registered
|
|
13
|
+
* tool schemas (name, description, parameters — nothing executable) into a snapshot
|
|
14
|
+
* that is deep-copied and deep-frozen. Later registry changes cannot leak into it, and
|
|
15
|
+
* a holder of the snapshot cannot mutate it. The planner receives ONLY entries from
|
|
16
|
+
* this snapshot; it never receives the caller's declared tool list, an unfiltered
|
|
17
|
+
* registry, or anything callable (NDL-02's "narrow public broker").
|
|
18
|
+
*
|
|
19
|
+
* 2. **The requested-name gate.** The caller asks for names; the host intersects them
|
|
20
|
+
* with the LIVE registry. Unknown names (never registered, or withdrawn), excluded
|
|
21
|
+
* names (management and composition), duplicates, and live-but-unsnapshotted names are
|
|
22
|
+
* all REJECTED — the whole call fails with every rejection named, never a silently
|
|
23
|
+
* smaller offer set (the CODE-03 "reject rather than omit" discipline).
|
|
24
|
+
*
|
|
25
|
+
* 3. **Host-side re-validation.** When the provider returns proposals, the host
|
|
26
|
+
* re-validates every proposal against the SNAPSHOT schema, not the provider's own
|
|
27
|
+
* verdict. A proposal the engine fabricated outside the offered set, or arguments that
|
|
28
|
+
* violate the frozen schema, are moved to `rejected` with the violations named.
|
|
29
|
+
*
|
|
30
|
+
* 4. **Fresh policy checks.** A proposal is DATA; executing it goes through the registry's
|
|
31
|
+
* own admission later (NDL-03). The host still refuses to return a proposal whose tool
|
|
32
|
+
* is no longer registered — deactivated or withdrawn after the snapshot was captured —
|
|
33
|
+
* so a caller never receives a proposal for a tool that is not currently callable.
|
|
34
|
+
*
|
|
35
|
+
* Everything here is a pure function over plain data: the DSH context, the store and the
|
|
36
|
+
* lifecycle stay in the host wiring, so the discipline is testable without mounting a
|
|
37
|
+
* plugin and without the pinned engine artifacts.
|
|
38
|
+
*
|
|
39
|
+
* @module @try-works/dsh-model-as-tool/planning
|
|
40
|
+
*/
|
|
41
|
+
import { Ajv } from 'ajv';
|
|
42
|
+
import { EXCLUDED_BINDINGS } from "./ptc-authority.js";
|
|
43
|
+
/**
|
|
44
|
+
* The validator the proposal re-checks compile with.
|
|
45
|
+
*
|
|
46
|
+
* Deliberately NON-STRICT, matching the extension's own `checkArguments` policy: the
|
|
47
|
+
* snapshot entries come from the DSH registry's model-facing projection, which may omit
|
|
48
|
+
* keywords a strict compiler refuses (`additionalProperties` in particular), and a
|
|
49
|
+
* caller-facing tool list must not be rejected because the registry's projection is not
|
|
50
|
+
* strict-authored. The strict compiler stays for DECLARED schemas, which are authored
|
|
51
|
+
* under the contracts package's own discipline.
|
|
52
|
+
*/
|
|
53
|
+
const PROPOSAL_AJV = new Ajv({ allErrors: true, strict: false });
|
|
54
|
+
/** The caller-facing name of the planning contract (NDL-02). */
|
|
55
|
+
export const PLANNING_TOOL_NAME = 'tools_plan';
|
|
56
|
+
/**
|
|
57
|
+
* Tool names the planning surface must never offer or propose (NDL-02 + CODE-07).
|
|
58
|
+
*
|
|
59
|
+
* The program-binding exclusions are reused rather than restated: a proposal targeting
|
|
60
|
+
* `tool_mode_run` (the recursive composition door) or the management surface would hand a
|
|
61
|
+
* caller the same proxy grant CODE-07 withholds from programs. `tools_plan` itself is
|
|
62
|
+
* added so a plan can never propose planning — recursive planning is noise, and the
|
|
63
|
+
* planning tool is not a leaf inference tool.
|
|
64
|
+
*
|
|
65
|
+
* SEALED like {@link EXCLUDED_BINDINGS} — a frozen ARRAY, because freezing a `Set` does
|
|
66
|
+
* not stop `add`/`delete` — so the planning surface and the program-binding surface
|
|
67
|
+
* cannot desynchronize through a runtime mutation.
|
|
68
|
+
*/
|
|
69
|
+
export const PLANNING_EXCLUDED_TOOLS = Object.freeze([...EXCLUDED_BINDINGS, PLANNING_TOOL_NAME]);
|
|
70
|
+
/**
|
|
71
|
+
* Deep-copy a JSON value, guarding against cycles.
|
|
72
|
+
*
|
|
73
|
+
* Hand-rolled rather than `structuredClone` so the module stays independent of which
|
|
74
|
+
* global typings a build resolves, and so a cyclic schema cannot recurse forever.
|
|
75
|
+
* @param value - the value to copy.
|
|
76
|
+
* @param seen - objects already copied, keyed by source.
|
|
77
|
+
* @returns the copy.
|
|
78
|
+
*/
|
|
79
|
+
function deepClone(value, seen) {
|
|
80
|
+
if (value === null || typeof value !== 'object')
|
|
81
|
+
return value;
|
|
82
|
+
const existing = seen.get(value);
|
|
83
|
+
if (existing !== undefined)
|
|
84
|
+
return existing;
|
|
85
|
+
if (Array.isArray(value)) {
|
|
86
|
+
const out = [];
|
|
87
|
+
seen.set(value, out);
|
|
88
|
+
for (const item of value)
|
|
89
|
+
out.push(deepClone(item, seen));
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
const out = {};
|
|
93
|
+
seen.set(value, out);
|
|
94
|
+
for (const [key, child] of Object.entries(value)) {
|
|
95
|
+
out[key] = deepClone(child, seen);
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Freeze a value transitively, guarding against cycles.
|
|
101
|
+
* @param value - the value to freeze.
|
|
102
|
+
* @param seen - objects already visited, so a cyclic schema cannot recurse forever.
|
|
103
|
+
*/
|
|
104
|
+
function deepFreeze(value, seen) {
|
|
105
|
+
if (value === null || typeof value !== 'object')
|
|
106
|
+
return;
|
|
107
|
+
if (seen.has(value))
|
|
108
|
+
return;
|
|
109
|
+
seen.add(value);
|
|
110
|
+
for (const child of Object.values(value))
|
|
111
|
+
deepFreeze(child, seen);
|
|
112
|
+
Object.freeze(value);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Capture the frozen host-approved tool schema snapshot (NDL-02).
|
|
116
|
+
*
|
|
117
|
+
* The capture is a DEEP COPY: mutating the registry afterwards — registering a tool,
|
|
118
|
+
* withdrawing one, changing a description — cannot change the snapshot, and mutating the
|
|
119
|
+
* snapshot is refused by the runtime because every node is frozen. Excluded names are
|
|
120
|
+
* skipped rather than captured-then-filtered, so a management tool's schema never enters
|
|
121
|
+
* the snapshot at all.
|
|
122
|
+
*
|
|
123
|
+
* An empty description is replaced with a stated placeholder rather than captured empty:
|
|
124
|
+
* the planner-side input contract requires a non-empty description, and a capture that
|
|
125
|
+
* guaranteed a later refusal would freeze a call failure into the snapshot.
|
|
126
|
+
* @param schemas - the registry view at capture time.
|
|
127
|
+
* @param options - the generation, and optionally the excluded names (defaults to
|
|
128
|
+
* {@link PLANNING_EXCLUDED_TOOLS}).
|
|
129
|
+
* @returns the frozen snapshot.
|
|
130
|
+
*/
|
|
131
|
+
export function captureToolSchemaSnapshot(schemas, options) {
|
|
132
|
+
const excluded = options.excluded ?? PLANNING_EXCLUDED_TOOLS;
|
|
133
|
+
const tools = new Map();
|
|
134
|
+
for (const schema of schemas) {
|
|
135
|
+
if (excluded.includes(schema.name))
|
|
136
|
+
continue;
|
|
137
|
+
if (typeof schema.parameters !== 'object' || schema.parameters === null || Array.isArray(schema.parameters)) {
|
|
138
|
+
// A tool whose parameters cannot be described as JSON Schema cannot be planned
|
|
139
|
+
// against; capturing it would hand the planner an entry it cannot use.
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const description = typeof schema.description === 'string' && schema.description.trim().length > 0
|
|
143
|
+
? schema.description
|
|
144
|
+
: 'No host description is declared for this tool.';
|
|
145
|
+
const entry = {
|
|
146
|
+
name: schema.name,
|
|
147
|
+
description,
|
|
148
|
+
// deepClone, then freeze: the snapshot must share NO structure with the
|
|
149
|
+
// registry's live objects, or a later registry edit would reach a caller.
|
|
150
|
+
parameters: deepClone(schema.parameters, new Map()),
|
|
151
|
+
};
|
|
152
|
+
deepFreeze(entry.parameters, new Set());
|
|
153
|
+
deepFreeze(entry, new Set());
|
|
154
|
+
tools.set(schema.name, entry);
|
|
155
|
+
}
|
|
156
|
+
// The OUTER record is frozen too: "the snapshot is frozen" must include the
|
|
157
|
+
// container a caller holds, not only the entries inside it (the map is frozen
|
|
158
|
+
// below via the same walk, and the record itself last).
|
|
159
|
+
const snapshot = { generation: options.generation, tools };
|
|
160
|
+
deepFreeze(snapshot, new Set());
|
|
161
|
+
return snapshot;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Intersect the requested names with the live registry and the frozen snapshot (NDL-02).
|
|
165
|
+
*
|
|
166
|
+
* All-or-nothing, mirroring the CODE-03 bindings gate: if ANY requested name is
|
|
167
|
+
* unattainable the whole call is refused with every rejection named, because handing the
|
|
168
|
+
* caller a smaller offer set than it asked for would produce a plan for a different
|
|
169
|
+
* request than the one it stated.
|
|
170
|
+
* @param snapshot - the frozen snapshot, the ONLY source of schemas.
|
|
171
|
+
* @param requested - the names the caller asked to plan over.
|
|
172
|
+
* @param liveLeaf - the leaf tool names currently registered, exclusions already applied.
|
|
173
|
+
* @param excluded - the names refused by policy regardless of registration. Defaults to
|
|
174
|
+
* {@link PLANNING_EXCLUDED_TOOLS}.
|
|
175
|
+
* @returns the offered entries, or every rejection.
|
|
176
|
+
*/
|
|
177
|
+
export function offerToolsFromSnapshot(snapshot, requested, liveLeaf, excluded = PLANNING_EXCLUDED_TOOLS) {
|
|
178
|
+
const rejections = [];
|
|
179
|
+
const offered = [];
|
|
180
|
+
const seen = new Set();
|
|
181
|
+
for (const name of requested) {
|
|
182
|
+
if (seen.has(name)) {
|
|
183
|
+
rejections.push({
|
|
184
|
+
name,
|
|
185
|
+
reason: 'duplicate',
|
|
186
|
+
remedy: 'Request each tool once; a repeated name makes the offered set ambiguous.',
|
|
187
|
+
});
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
seen.add(name);
|
|
191
|
+
if (excluded.includes(name)) {
|
|
192
|
+
rejections.push({
|
|
193
|
+
name,
|
|
194
|
+
reason: 'excluded',
|
|
195
|
+
remedy: `"${name}" is not a leaf tool a plan may target: management and composition bindings are excluded (CODE-07, NDL-02).`,
|
|
196
|
+
});
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (!liveLeaf.has(name)) {
|
|
200
|
+
rejections.push({
|
|
201
|
+
name,
|
|
202
|
+
reason: 'unknown',
|
|
203
|
+
remedy: 'The tool is not currently registered; its owning extension may be deactivated, or the name may never have existed.',
|
|
204
|
+
});
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const entry = snapshot.tools.get(name);
|
|
208
|
+
if (entry === undefined) {
|
|
209
|
+
rejections.push({
|
|
210
|
+
name,
|
|
211
|
+
reason: 'not-in-snapshot',
|
|
212
|
+
remedy: 'The tool is registered but was added after the frozen snapshot was captured, so no authorized schema exists for it.',
|
|
213
|
+
});
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
offered.push(entry);
|
|
217
|
+
}
|
|
218
|
+
if (rejections.length > 0)
|
|
219
|
+
return { ok: false, rejections };
|
|
220
|
+
return { ok: true, offered };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Describe a refused request for a caller (NDL-02).
|
|
224
|
+
* @param rejections - the refusals.
|
|
225
|
+
* @returns a readable summary naming every rejection and why.
|
|
226
|
+
*/
|
|
227
|
+
export function describePlanningRejections(rejections) {
|
|
228
|
+
const lines = rejections.map(entry => ` ${entry.name}: ${entry.reason} — ${entry.remedy}`);
|
|
229
|
+
return `tools_plan cannot offer the requested tools:\n${lines.join('\n')}`;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Raised when a provider returns a proposal record that is not proposal-shaped.
|
|
233
|
+
*
|
|
234
|
+
* The provider's declared output contract types every field and closes the record, so a
|
|
235
|
+
* malformed entry is a PROVIDER DEFECT — and it is surfaced as one rather than silently
|
|
236
|
+
* coerced. Coercing a non-object `arguments` into `{}` would present a record the
|
|
237
|
+
* provider never produced as if the host had validated it, and dropping undeclared
|
|
238
|
+
* fields would hide a contract violation the output validator should refuse.
|
|
239
|
+
*/
|
|
240
|
+
export class ProviderDefectError extends Error {
|
|
241
|
+
name = 'ProviderDefectError';
|
|
242
|
+
}
|
|
243
|
+
/** The keys the declared proposal contract admits. */
|
|
244
|
+
const PROPOSAL_FIELDS = new Set(['name', 'arguments', 'offered', 'valid', 'issues']);
|
|
245
|
+
/**
|
|
246
|
+
* Normalize one proposal record from the provider's payload, STRICTLY.
|
|
247
|
+
*
|
|
248
|
+
* Structural violations throw {@link ProviderDefectError}: a non-object record, a missing
|
|
249
|
+
* or non-string name, non-object arguments, or undeclared fields. The typed fields the
|
|
250
|
+
* declared output schema constrains (`offered`, `valid`, `issues`) are read tolerantly —
|
|
251
|
+
* their types are enforced by the output validator that runs after the re-check.
|
|
252
|
+
* @param raw - the record.
|
|
253
|
+
* @returns the normalized entry.
|
|
254
|
+
* @throws ProviderDefectError when the record is not proposal-shaped.
|
|
255
|
+
*/
|
|
256
|
+
function normalizeEntry(raw) {
|
|
257
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
258
|
+
throw new ProviderDefectError('a provider returned a proposal record that is not an object');
|
|
259
|
+
}
|
|
260
|
+
const record = raw;
|
|
261
|
+
const extra = Object.keys(record).filter(key => !PROPOSAL_FIELDS.has(key));
|
|
262
|
+
if (extra.length > 0) {
|
|
263
|
+
throw new ProviderDefectError(`a provider returned a proposal record with undeclared field(s) ${extra.map(key => JSON.stringify(key)).join(', ')}`);
|
|
264
|
+
}
|
|
265
|
+
if (typeof record.name !== 'string') {
|
|
266
|
+
throw new ProviderDefectError('a provider returned a proposal record whose name is not a string');
|
|
267
|
+
}
|
|
268
|
+
const rawArguments = record.arguments;
|
|
269
|
+
if (typeof rawArguments !== 'object' || rawArguments === null || Array.isArray(rawArguments)) {
|
|
270
|
+
throw new ProviderDefectError(`a provider returned a proposal record whose arguments are not an object (${JSON.stringify(rawArguments)})`);
|
|
271
|
+
}
|
|
272
|
+
const issues = Array.isArray(record.issues)
|
|
273
|
+
? record.issues.filter((issue) => typeof issue === 'string').slice(0, 32)
|
|
274
|
+
: [];
|
|
275
|
+
return {
|
|
276
|
+
name: record.name,
|
|
277
|
+
arguments: rawArguments,
|
|
278
|
+
offered: record.offered === true,
|
|
279
|
+
valid: record.valid === true,
|
|
280
|
+
issues,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Re-check the provider's proposals against the frozen snapshot, the per-call offered set
|
|
285
|
+
* AND the live authorization state (NDL-02, NDL-03).
|
|
286
|
+
*
|
|
287
|
+
* The provider's own validation is not trusted here: it validated against the list the
|
|
288
|
+
* HOST built, but the host still re-checks every proposal before it reaches a caller,
|
|
289
|
+
* because a proposal is the one thing a caller will treat as authorized. Four checks:
|
|
290
|
+
*
|
|
291
|
+
* - the name must be IN the frozen snapshot (the engine cannot invent a plannable tool);
|
|
292
|
+
* - the arguments must satisfy the SNAPSHOT's schema, compiled fresh per snapshot entry;
|
|
293
|
+
* - the name must be in the OFFERED set for THIS call — a provider proposing a tool the
|
|
294
|
+
* caller never requested must not pass it through as valid, even when it is
|
|
295
|
+
* snapshotted, schema-valid and still registered;
|
|
296
|
+
* - the tool must STILL BE AUTHORIZED right now (NDL-03): a tool deactivated, withdrawn
|
|
297
|
+
* or quarantined after capture fails the fresh policy check, and the proposal is
|
|
298
|
+
* refused rather than returned as if it were executable.
|
|
299
|
+
*
|
|
300
|
+
* A proposal failing any check is MOVED to `rejected` with the issues appended, and one
|
|
301
|
+
* bounded warning per moved proposal is returned for the receipt and the meta envelope.
|
|
302
|
+
* A proposal record that is not proposal-shaped throws {@link ProviderDefectError}: the
|
|
303
|
+
* defect belongs to the provider, and silently repairing it would hide the violation.
|
|
304
|
+
* @param snapshot - the frozen snapshot.
|
|
305
|
+
* @param payload - the provider's result.
|
|
306
|
+
* @param offeredNames - the names the HOST offered for this call.
|
|
307
|
+
* @param isCurrentlyAuthorized - the live authorization predicate, read at re-check time;
|
|
308
|
+
* `true` admits, a string is the refusal reason.
|
|
309
|
+
* @returns the amended payload and the warnings.
|
|
310
|
+
*/
|
|
311
|
+
export function recheckProposals(snapshot, payload, offeredNames, isCurrentlyAuthorized) {
|
|
312
|
+
const warnings = [];
|
|
313
|
+
const compiled = new Map();
|
|
314
|
+
/** Compile a snapshot entry's schema once; an uncompilable schema is an issue, not a crash. */
|
|
315
|
+
const validatorFor = (entry) => {
|
|
316
|
+
const cached = compiled.get(entry.name);
|
|
317
|
+
if (cached !== undefined)
|
|
318
|
+
return cached;
|
|
319
|
+
let validate;
|
|
320
|
+
try {
|
|
321
|
+
validate = PROPOSAL_AJV.compile(entry.parameters);
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// Kept uncompiled: every proposal for this entry then carries the compile failure
|
|
325
|
+
// as an issue instead of the host throwing mid-call.
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
const validator = value => (validate(value) ? [] : [...(validate.errors ?? [])]
|
|
329
|
+
.map(error => `${error.instancePath === '' ? '(root)' : error.instancePath} ${error.message ?? 'is invalid'}`));
|
|
330
|
+
compiled.set(entry.name, validator);
|
|
331
|
+
return validator;
|
|
332
|
+
};
|
|
333
|
+
const rawProposals = Array.isArray(payload.proposals) ? payload.proposals : [];
|
|
334
|
+
const rawRejected = Array.isArray(payload.rejected) ? payload.rejected : [];
|
|
335
|
+
const kept = [];
|
|
336
|
+
const rejected = [...rawRejected];
|
|
337
|
+
for (const raw of rawProposals) {
|
|
338
|
+
const entry = normalizeEntry(raw);
|
|
339
|
+
const hostIssues = [];
|
|
340
|
+
let offered = entry.offered;
|
|
341
|
+
const snapshotEntry = snapshot.tools.get(entry.name);
|
|
342
|
+
if (snapshotEntry === undefined) {
|
|
343
|
+
// A proposal outside the snapshot was never offered by the HOST, whatever the
|
|
344
|
+
// provider believed: the host's offer set is the snapshot by construction.
|
|
345
|
+
offered = false;
|
|
346
|
+
hostIssues.push(`the tool ${JSON.stringify(entry.name)} is not in the frozen host-approved snapshot`);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
const validate = validatorFor(snapshotEntry);
|
|
350
|
+
if (validate === undefined) {
|
|
351
|
+
hostIssues.push(`the frozen schema for ${JSON.stringify(entry.name)} does not compile, so no arguments can be validated`);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
hostIssues.push(...validate(entry.arguments));
|
|
355
|
+
}
|
|
356
|
+
if (hostIssues.length === 0 && !offeredNames.has(entry.name)) {
|
|
357
|
+
// F6: the OFFERED set is per-call. A snapshotted, schema-valid, still-registered
|
|
358
|
+
// proposal for a tool the caller never requested is still not a valid plan step:
|
|
359
|
+
// the provider had no business proposing it this turn.
|
|
360
|
+
offered = false;
|
|
361
|
+
hostIssues.push(`the tool ${JSON.stringify(entry.name)} was not offered for this turn`);
|
|
362
|
+
}
|
|
363
|
+
if (hostIssues.length === 0) {
|
|
364
|
+
const authorization = isCurrentlyAuthorized(entry.name);
|
|
365
|
+
if (authorization !== true) {
|
|
366
|
+
// NDL-03: fresh policy checks at execution time. The tool was authorized when the
|
|
367
|
+
// snapshot froze, but it is not callable NOW — withdrawn, deactivated or
|
|
368
|
+
// quarantined — so returning the proposal would present a tool that would fail
|
|
369
|
+
// admission as a valid plan step.
|
|
370
|
+
hostIssues.push(`the tool ${JSON.stringify(entry.name)} is no longer authorized: ${authorization}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (hostIssues.length === 0) {
|
|
375
|
+
kept.push(entry);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
// The bounded report shape: the provider's issues first, then the host's, capped at
|
|
379
|
+
// the shipped schema's own 32-item bound rather than grown without limit.
|
|
380
|
+
rejected.push({
|
|
381
|
+
name: entry.name,
|
|
382
|
+
arguments: entry.arguments,
|
|
383
|
+
offered,
|
|
384
|
+
valid: false,
|
|
385
|
+
issues: [...entry.issues, ...hostIssues].slice(0, 32),
|
|
386
|
+
});
|
|
387
|
+
warnings.push(`the host refused the ${entry.name} proposal: ${hostIssues[0] ?? 'invalid'}`);
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
payload: { ...payload, proposals: kept, rejected },
|
|
391
|
+
warnings,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* The input the HOST advertises and validates for `tools_plan` (NDL-02).
|
|
396
|
+
*
|
|
397
|
+
* The caller names tools as STRINGS — a requested eligible subset, never a grant and
|
|
398
|
+
* never full declarations. The extension's own shipped input schema (full tool objects)
|
|
399
|
+
* remains its provider contract; the host is the broker that turns requested names into
|
|
400
|
+
* snapshot entries, so a caller never supplies the schemas the planner reasons over.
|
|
401
|
+
* Bounds mirror the shipped extension input, so the advertised surface and the transport
|
|
402
|
+
* surface cannot admit different requests.
|
|
403
|
+
*/
|
|
404
|
+
export const PLANNING_TOOL_INPUT_SCHEMA = {
|
|
405
|
+
type: 'object',
|
|
406
|
+
additionalProperties: false,
|
|
407
|
+
required: ['request', 'tools'],
|
|
408
|
+
properties: {
|
|
409
|
+
request: {
|
|
410
|
+
type: 'string',
|
|
411
|
+
minLength: 1,
|
|
412
|
+
maxLength: 32_768,
|
|
413
|
+
description: 'The user request the plan is derived from, verbatim.',
|
|
414
|
+
},
|
|
415
|
+
tools: {
|
|
416
|
+
type: 'array',
|
|
417
|
+
minItems: 1,
|
|
418
|
+
maxItems: 64,
|
|
419
|
+
description: 'The tool names to plan over. A requested eligible subset, never a grant; unknown or excluded names refuse the call.',
|
|
420
|
+
items: { type: 'string', minLength: 1 },
|
|
421
|
+
},
|
|
422
|
+
system: {
|
|
423
|
+
type: 'string',
|
|
424
|
+
maxLength: 4_096,
|
|
425
|
+
description: 'Session facts the model should assume: the date, the locale, the device.',
|
|
426
|
+
},
|
|
427
|
+
max_new_tokens: {
|
|
428
|
+
type: 'integer',
|
|
429
|
+
minimum: 16,
|
|
430
|
+
maximum: 4_096,
|
|
431
|
+
description: 'Response-token budget for the planner.',
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
/**
|
|
436
|
+
* Whether a declared input schema has the planning shape (a `tools` array property).
|
|
437
|
+
*
|
|
438
|
+
* The NAME alone is not enough to treat a contribution as the planning contract: a
|
|
439
|
+
* package that named a tool `tools_plan` without the planning input would otherwise be
|
|
440
|
+
* served through the planning pipeline and fail in ways nobody could see. The name plus
|
|
441
|
+
* the shape is the contract; the host refuses the name without the shape.
|
|
442
|
+
* @param schema - the declared input schema.
|
|
443
|
+
* @returns whether the schema declares an array-valued `tools` property.
|
|
444
|
+
*/
|
|
445
|
+
export function hasPlanningInputShape(schema) {
|
|
446
|
+
if (schema === undefined)
|
|
447
|
+
return false;
|
|
448
|
+
const properties = schema.properties;
|
|
449
|
+
if (typeof properties !== 'object' || properties === null || Array.isArray(properties))
|
|
450
|
+
return false;
|
|
451
|
+
const tools = properties.tools;
|
|
452
|
+
if (typeof tools !== 'object' || tools === null || Array.isArray(tools))
|
|
453
|
+
return false;
|
|
454
|
+
const type = tools.type;
|
|
455
|
+
return type === 'array' || (Array.isArray(type) && type.includes('array'));
|
|
456
|
+
}
|
|
457
|
+
//# sourceMappingURL=planning.js.map
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host's prepared-assets path (RES-02/03/05, the last in-repo slice):
|
|
3
|
+
* lock reading → preparation plan → approval-gated, digest-verified acquisition
|
|
4
|
+
* into an isolated content-addressed cache → the prepared-cold readiness flip →
|
|
5
|
+
* the PreparedAssetMapV1 the provider routes consume.
|
|
6
|
+
*
|
|
7
|
+
* The verified components this module finally gives callers to:
|
|
8
|
+
*
|
|
9
|
+
* - `planPreparation`/`DeploymentIdentity` (deployment-coordinator.ts) — the
|
|
10
|
+
* pre-approval disclosure;
|
|
11
|
+
* - `acquireAsset` (asset-acquisition.ts) — digest+size verified, atomic,
|
|
12
|
+
* resumable acquisition with tamper refusal;
|
|
13
|
+
* - `createHttpSource` (http-acquisition.ts) — the default ranged source.
|
|
14
|
+
*
|
|
15
|
+
* Honesty rules carried through:
|
|
16
|
+
*
|
|
17
|
+
* - a lock with no digests REFUSES (no guessed values);
|
|
18
|
+
* - an executable asset WITHOUT the explicit executable-preparation approval
|
|
19
|
+
* refuses (RES-02);
|
|
20
|
+
* - a tampered final path is refused and never flips readiness;
|
|
21
|
+
* - the cache lives under the host's isolated state dir, never the repo or
|
|
22
|
+
* HOME; a REAL network run is opt-in (the default source), and the suite
|
|
23
|
+
* injects a local file source so it performs no network.
|
|
24
|
+
*
|
|
25
|
+
* @module @try-works/dsh-model-as-tool/prepared-assets
|
|
26
|
+
*/
|
|
27
|
+
import { type AcquisitionSource } from './asset-acquisition.ts';
|
|
28
|
+
import type { DeploymentIdentity, PlannedAsset, PreparationPlan } from './deployment-coordinator.ts';
|
|
29
|
+
/** One lock asset, read from an extension's models.lock.json. */
|
|
30
|
+
interface LockAsset {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly file: string;
|
|
33
|
+
readonly bytes: number;
|
|
34
|
+
readonly source: string;
|
|
35
|
+
readonly digest: string;
|
|
36
|
+
readonly executable: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** One lock group with its enabled modalities and assets. */
|
|
39
|
+
export interface LockGroup {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly assets: readonly LockAsset[];
|
|
42
|
+
}
|
|
43
|
+
/** One lock asset set, with the pinning state and license. */
|
|
44
|
+
interface LockAssetSet {
|
|
45
|
+
readonly groups: readonly LockGroup[];
|
|
46
|
+
readonly revision: string;
|
|
47
|
+
readonly license: {
|
|
48
|
+
readonly name: string;
|
|
49
|
+
readonly url: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** A parsed asset lock, ready to plan from. */
|
|
53
|
+
export interface ParsedAssetLock {
|
|
54
|
+
readonly revision: string;
|
|
55
|
+
readonly license: {
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly url: string;
|
|
58
|
+
};
|
|
59
|
+
readonly sets: Readonly<Record<string, LockAssetSet>>;
|
|
60
|
+
}
|
|
61
|
+
/** One verified asset FILE inside a prepared group (EXT-18). */
|
|
62
|
+
export interface PreparedAssetFile {
|
|
63
|
+
/** The lock's asset id, e.g. `needle-engine-win-x64`. */
|
|
64
|
+
readonly assetId: string;
|
|
65
|
+
/** The lock's package-relative file path, e.g. `needle.exe`. */
|
|
66
|
+
readonly file: string;
|
|
67
|
+
/** The absolute path the file was materialized at. */
|
|
68
|
+
readonly path: string;
|
|
69
|
+
/** The pinned digest the materialized bytes verified against. */
|
|
70
|
+
readonly digest: string;
|
|
71
|
+
}
|
|
72
|
+
/** The group directories one preparation materialized. */
|
|
73
|
+
export interface PreparedGroup {
|
|
74
|
+
/** The group id (text-backbone, vision-encoder, ...). */
|
|
75
|
+
readonly groupId: string;
|
|
76
|
+
/** The absolute directory holding the lock's per-asset file layout. */
|
|
77
|
+
readonly path: string;
|
|
78
|
+
/** The digests this group holds. */
|
|
79
|
+
readonly digests: readonly string[];
|
|
80
|
+
/**
|
|
81
|
+
* The individual verification handles of this group's assets.
|
|
82
|
+
*
|
|
83
|
+
* EXT-18's `PreparedAssetMapV1` is a mapping from asset ids to local handles, and
|
|
84
|
+
* a deployment may resolve an individual file by its lock asset id rather than a
|
|
85
|
+
* group directory (extension-needle does exactly that). Both projections are
|
|
86
|
+
* carried so neither consumer has to guess the other's convention.
|
|
87
|
+
*/
|
|
88
|
+
readonly files: readonly PreparedAssetFile[];
|
|
89
|
+
}
|
|
90
|
+
/** The outcome of one preparation. */
|
|
91
|
+
export type PreparationOutcome = {
|
|
92
|
+
readonly ok: true;
|
|
93
|
+
readonly groups: readonly PreparedGroup[];
|
|
94
|
+
} | {
|
|
95
|
+
readonly ok: false;
|
|
96
|
+
readonly reason: string;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Parse a models.lock.json document.
|
|
100
|
+
*
|
|
101
|
+
* A lock with no digest on any asset REFUSES: a preparation built on guessed
|
|
102
|
+
* bytes would verify nothing (RES-03).
|
|
103
|
+
* @param bytes - the lock document.
|
|
104
|
+
* @returns the parsed lock.
|
|
105
|
+
*/
|
|
106
|
+
export declare function parseAssetLock(bytes: string): ParsedAssetLock;
|
|
107
|
+
/** The identity the lock implies for one model deployment. */
|
|
108
|
+
export declare function identityFor(extensionId: string, modelId: string, lock: ParsedAssetLock): DeploymentIdentity;
|
|
109
|
+
/** The flat PlannedAsset list for one asset set. */
|
|
110
|
+
export declare function plannedAssets(lock: ParsedAssetLock, assetSetId: string): readonly PlannedAsset[];
|
|
111
|
+
/** The group map for one asset set, keyed by group id. */
|
|
112
|
+
export declare function lockGroups(lock: ParsedAssetLock, assetSetId: string): readonly LockGroup[];
|
|
113
|
+
/** The host-side prepared-assets registry (session-scoped). */
|
|
114
|
+
export interface PreparedAssetRegistry {
|
|
115
|
+
/** The plan the last extensions-prepare-plan built, per extension id. */
|
|
116
|
+
plans(): ReadonlyMap<string, PreparationPlan>;
|
|
117
|
+
/** The acquired group map per extension id (PreparedAssetMapV1 shapes). */
|
|
118
|
+
assetMapFor(extensionId: string): PreparedAssetMap | undefined;
|
|
119
|
+
/** All acquired group directories per extension id. */
|
|
120
|
+
groupsFor(extensionId: string): readonly PreparedGroup[];
|
|
121
|
+
/**
|
|
122
|
+
* Prepare one plan: acquire every asset (digest+size verified, atomic), then
|
|
123
|
+
* materialize the group directories. Idempotent: an already-acquired asset is
|
|
124
|
+
* a cache hit and its bytes are never re-fetched.
|
|
125
|
+
*/
|
|
126
|
+
prepare(extensionId: string, plan: PreparationPlan, groups: readonly LockGroup[], options: {
|
|
127
|
+
readonly source: AcquisitionSource;
|
|
128
|
+
readonly allowExecutable: boolean;
|
|
129
|
+
readonly signal: AbortSignal;
|
|
130
|
+
}): Promise<PreparationOutcome>;
|
|
131
|
+
}
|
|
132
|
+
/** The PreparedAssetMapV1 shape the provider routes consume. */
|
|
133
|
+
export interface PreparedAssetMap {
|
|
134
|
+
readonly assetIds: readonly string[];
|
|
135
|
+
get(assetId: string): {
|
|
136
|
+
readonly path: string;
|
|
137
|
+
readonly digest: string;
|
|
138
|
+
} | undefined;
|
|
139
|
+
}
|
|
140
|
+
/** Options for the registry factory. */
|
|
141
|
+
export interface PreparedAssetRegistryOptions {
|
|
142
|
+
/** The isolated cache root (the host's state dir, never the repo or HOME). */
|
|
143
|
+
readonly cacheRoot: string;
|
|
144
|
+
/** The default acquisition source (real network, opt-in). */
|
|
145
|
+
readonly defaultSource?: AcquisitionSource;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The management-channel surface the host wires over the registry: the plan
|
|
149
|
+
* disclosure (before approval) and the acquisition (the approval itself, with
|
|
150
|
+
* the executable gate). The plan cache lives in the caller so a disclosed plan
|
|
151
|
+
* is exactly what a later prepare executes.
|
|
152
|
+
*/
|
|
153
|
+
export interface PrepareAssetsSurface {
|
|
154
|
+
/** Build and cache the preparation plan for one installed extension. */
|
|
155
|
+
plan(extensionId: string): PreparationPlan;
|
|
156
|
+
/** The lock groups behind the cached plan (the prepare call needs the layout). */
|
|
157
|
+
groupsForPlan(extensionId: string): readonly LockGroup[];
|
|
158
|
+
/** The manifest model id the plan prepares. */
|
|
159
|
+
modelIdFor(extensionId: string): string;
|
|
160
|
+
/** Acquire every planned asset with the given source. */
|
|
161
|
+
prepare(extensionId: string, options: {
|
|
162
|
+
readonly source: AcquisitionSource;
|
|
163
|
+
readonly allowExecutable: boolean;
|
|
164
|
+
readonly signal: AbortSignal;
|
|
165
|
+
}): Promise<PreparationOutcome>;
|
|
166
|
+
/** The prepared map, or undefined before a successful prepare. */
|
|
167
|
+
assetMapFor(extensionId: string): PreparedAssetMap | undefined;
|
|
168
|
+
/** The underlying registry (diagnostics/tests). */
|
|
169
|
+
readonly registry: PreparedAssetRegistry;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Create the prepared-assets registry.
|
|
173
|
+
* @param options - the cache root and the default source.
|
|
174
|
+
* @returns the registry.
|
|
175
|
+
*/
|
|
176
|
+
export declare function createPreparedAssetRegistry(options: PreparedAssetRegistryOptions): PreparedAssetRegistry;
|
|
177
|
+
export {};
|
|
178
|
+
//# sourceMappingURL=prepared-assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepared-assets.d.ts","sourceRoot":"","sources":["../src/prepared-assets.ts"],"names":[],"mappings":"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"}
|