@ljwei-stak/dsh-model-route-bot 0.66.3 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dsh-plugin/bot-product.mjs +81 -5
- package/.dsh-plugin/client.js +17490 -1791
- package/README.i18n.yaml +4 -4
- package/README.md +409 -364
- package/README.zh.md +409 -364
- package/apps/integration-client/README.md +31 -7
- package/apps/integration-client/api.mjs +14 -3
- package/apps/integration-client/binding-editor.jsx +308 -109
- package/apps/integration-client/binding-model.mjs +262 -0
- package/apps/integration-client/binding.css +144 -0
- package/apps/integration-client/bot-model-status.mjs +18 -0
- package/apps/integration-client/bot-profile-identity.jsx +197 -0
- package/apps/integration-client/bot-profile.css +57 -0
- package/apps/integration-client/bot-profile.mjs +97 -0
- package/apps/integration-client/bot-settings-dialog.css +18 -0
- package/apps/integration-client/bot-settings-dialog.jsx +111 -0
- package/apps/integration-client/bot-thread-header-bridge.css +20 -0
- package/apps/integration-client/bot-thread-header.jsx +278 -0
- package/apps/integration-client/bot-threads-panel.jsx +378 -0
- package/apps/integration-client/bot-threads.css +169 -0
- package/apps/integration-client/chat-design.mjs +250 -0
- package/apps/integration-client/chat-ui.jsx +175 -0
- package/apps/integration-client/chat.css +477 -0
- package/apps/integration-client/composer-draft-binding.mjs +50 -0
- package/apps/integration-client/composer-draft-bound.jsx +14 -0
- package/apps/integration-client/composer-draft-host.jsx +142 -0
- package/apps/integration-client/composer-draft-session.jsx +287 -0
- package/apps/integration-client/composer-draft-witness.mjs +115 -0
- package/apps/integration-client/composer-draft.css +4 -0
- package/apps/integration-client/composer-draft.mjs +322 -0
- package/apps/integration-client/contact-attention.jsx +54 -0
- package/apps/integration-client/data-panel.jsx +439 -0
- package/apps/integration-client/data.css +69 -0
- package/apps/integration-client/desktop-bridge.mjs +1 -0
- package/apps/integration-client/desktop-http-socket.mjs +49 -0
- package/apps/integration-client/desktop-keyboard.mjs +32 -0
- package/apps/integration-client/desktop-pane.jsx +99 -30
- package/apps/integration-client/desktop-stream.mjs +13 -3
- package/apps/integration-client/desktop-tools.jsx +19 -0
- package/apps/integration-client/desktop-touch.mjs +27 -0
- package/apps/integration-client/desktop.css +41 -0
- package/apps/integration-client/independent-chat-controller.mjs +224 -0
- package/apps/integration-client/independent-chat-entry.jsx +61 -0
- package/apps/integration-client/independent-chat-navigation.mjs +21 -0
- package/apps/integration-client/independent-chat-page.jsx +148 -0
- package/apps/integration-client/independent-chat-rich.jsx +64 -0
- package/apps/integration-client/independent-chat-rich.mjs +30 -0
- package/apps/integration-client/independent-chat.css +73 -0
- package/apps/integration-client/index.jsx +24 -16
- package/apps/integration-client/library-panel.jsx +4 -1
- package/apps/integration-client/licenses/jsqr-1.4.0-LICENSE.txt +202 -0
- package/apps/integration-client/main-route-editor.jsx +94 -0
- package/apps/integration-client/main-route-model.mjs +100 -0
- package/apps/integration-client/model-picker.css +37 -0
- package/apps/integration-client/model-picker.jsx +83 -0
- package/apps/integration-client/native-activity.css +141 -0
- package/apps/integration-client/native-activity.jsx +276 -0
- package/apps/integration-client/native-activity.mjs +384 -0
- package/apps/integration-client/notifications-panel.jsx +186 -0
- package/apps/integration-client/notifications.css +126 -0
- package/apps/integration-client/pairing-qr.jsx +79 -0
- package/apps/integration-client/panels.jsx +126 -115
- package/apps/integration-client/product-slots.jsx +46 -0
- package/apps/integration-client/routine-recorder.mjs +23 -2
- package/apps/integration-client/secure-pairing.jsx +84 -0
- package/apps/integration-client/tasks-panel.jsx +32 -10
- package/apps/integration-client/ui-icons.jsx +17 -0
- package/apps/integration-client/usage-explorer.jsx +43 -0
- package/apps/integration-client/usage-report.mjs +71 -0
- package/apps/integration-client/usage.css +34 -0
- package/apps/integration-client/web-panel-model.mjs +140 -0
- package/apps/integration-client/web-panel-page.jsx +221 -0
- package/apps/integration-client/web-panel-pins-live.jsx +28 -0
- package/apps/integration-client/web-panel.css +139 -0
- package/apps/integration-client/workspace-model.mjs +67 -0
- package/apps/integration-client/workspace-navigation.mjs +56 -0
- package/apps/integration-client/workspace-ui.jsx +173 -0
- package/apps/integration-client/workspace.css +332 -0
- package/modules/bot-runtime/SOURCE.json +176 -50
- package/modules/bot-runtime/index.mjs +169 -13
- package/modules/bot-runtime/product-overlay/OVERLAY.json +128 -2
- package/modules/bot-runtime/product-overlay/base/src/client/header.js +244 -0
- package/modules/bot-runtime/product-overlay/base/src/client/learning-card.js +202 -0
- package/modules/bot-runtime/product-overlay/base/src/client/model-menu.js +371 -0
- package/modules/bot-runtime/product-overlay/base/src/client/secret-card.js +363 -0
- package/modules/bot-runtime/product-overlay/base/src/client/sources.js +68 -0
- package/modules/bot-runtime/product-overlay/base/src/client/turn-error.js +260 -0
- package/modules/bot-runtime/product-overlay/base/src/host/channel.js +647 -0
- package/modules/bot-runtime/product-overlay/base/src/host/memory.js +2050 -0
- package/modules/bot-runtime/product-overlay/base/src/host/models.js +314 -0
- package/modules/bot-runtime/product-overlay/base/src/host/parts.js +1473 -0
- package/modules/bot-runtime/product-overlay/base/src/host/privacy.js +368 -0
- package/modules/bot-runtime/product-overlay/base/src/host/roster.js +776 -0
- package/modules/bot-runtime/product-overlay/base/src/host/store.js +512 -0
- package/modules/bot-runtime/product-overlay/base/src/host/usage.js +653 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/details-drawer.js.patch +48 -2
- package/modules/bot-runtime/product-overlay/patches/src/client/header.js.patch +41 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/index.js.patch +33 -1
- package/modules/bot-runtime/product-overlay/patches/src/client/learning-card.js.patch +20 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/model-menu.js.patch +24 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/secret-card.js.patch +25 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/sources.js.patch +52 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/turn-error.js.patch +23 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/channel.js.patch +46 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/memory.js.patch +11 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/models.js.patch +16 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/parts.js.patch +95 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/privacy.js.patch +11 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/roster.js.patch +145 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/store.js.patch +68 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/usage.js.patch +11 -0
- package/modules/bot-runtime/read-model.mjs +19 -4
- package/modules/bot-runtime/vendor/ds-bot/src/client/details-drawer.js +15 -5
- package/modules/bot-runtime/vendor/ds-bot/src/client/header.js +9 -2
- package/modules/bot-runtime/vendor/ds-bot/src/client/index.js +9 -2
- package/modules/bot-runtime/vendor/ds-bot/src/client/learning-card.js +2 -2
- package/modules/bot-runtime/vendor/ds-bot/src/client/model-menu.js +5 -3
- package/modules/bot-runtime/vendor/ds-bot/src/client/secret-card.js +3 -3
- package/modules/bot-runtime/vendor/ds-bot/src/client/sources.js +25 -2
- package/modules/bot-runtime/vendor/ds-bot/src/client/turn-error.js +3 -2
- package/modules/bot-runtime/vendor/ds-bot/src/host/channel.js +14 -2
- package/modules/bot-runtime/vendor/ds-bot/src/host/memory.js +1 -1
- package/modules/bot-runtime/vendor/ds-bot/src/host/models.js +7 -0
- package/modules/bot-runtime/vendor/ds-bot/src/host/parts.js +26 -9
- package/modules/bot-runtime/vendor/ds-bot/src/host/privacy.js +1 -1
- package/modules/bot-runtime/vendor/ds-bot/src/host/roster.js +112 -3
- package/modules/bot-runtime/vendor/ds-bot/src/host/store.js +34 -1
- package/modules/bot-runtime/vendor/ds-bot/src/host/usage.js +1 -1
- package/modules/independent-runtime/index.mjs +152 -62
- package/modules/independent-runtime/remote-tools.mjs +8 -5
- package/modules/openmaus-protocol/NOTICE +10 -0
- package/modules/openmaus-protocol/README.md +15 -0
- package/modules/openmaus-protocol/SOURCE.json +68 -0
- package/modules/openmaus-protocol/dist/protocol.mjs +1203 -0
- package/modules/openmaus-protocol/index.mjs +1 -0
- package/modules/openmaus-protocol/transport.mjs +77 -0
- package/modules/openmaus-protocol/vendor/LICENSE +202 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/README.md +405 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/credentials.ts +128 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/descriptor.ts +84 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/noise.ts +456 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/phone.ts +104 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/session.ts +417 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/vectors/v1.json +2565 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/vectors/v1.sha256 +1 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/yamux.ts +335 -0
- package/package.json +20 -6
- package/packages/bot-acp/README.md +156 -0
- package/packages/bot-acp/licenses/LICENSE-OpenMausBot +202 -0
- package/packages/bot-acp/licenses/NOTICE.md +27 -0
- package/packages/bot-acp/src/acp-adapter.mjs +795 -0
- package/packages/bot-acp/src/acp-host.mjs +148 -0
- package/packages/bot-acp/src/native-launch.mjs +121 -0
- package/packages/bot-acp/src/permission-policy.mjs +283 -0
- package/packages/bot-acp/src/rpc-codec.mjs +265 -0
- package/packages/bot-acp/src/session-core.mjs +581 -0
- package/packages/bot-acp/src/stdio-rpc.mjs +329 -0
- package/packages/bot-engines/README.md +281 -0
- package/packages/bot-engines/licenses/LICENSE-OpenMausBot +202 -0
- package/packages/bot-engines/licenses/NOTICE.md +32 -0
- package/packages/bot-engines/src/claude-engine.mjs +772 -0
- package/packages/bot-engines/src/claude-llm-adapter.mjs +797 -0
- package/packages/bot-engines/src/claude-llm-turn.mjs +292 -0
- package/packages/bot-engines/src/claude-permission-broker.mjs +130 -0
- package/packages/bot-engines/src/claude-permission-proxy.mjs +87 -0
- package/packages/bot-engines/src/claude-protocol.mjs +177 -0
- package/packages/bot-engines/src/driver-catalog.mjs +334 -0
- package/packages/bot-engines/src/host-loop-request.mjs +95 -0
- package/packages/bot-engines/src/host-registrations.mjs +105 -0
- package/packages/capability-broker/src/cli-bootstrap.mjs +86 -16
- package/packages/capability-broker/src/cli-bridge-response.mjs +80 -0
- package/packages/capability-broker/src/cli-stdio-bridge.mjs +21 -8
- package/packages/cli-runtime/src/codex-session-core.mjs +4 -0
- package/packages/cli-runtime/src/native-cli-output.mjs +57 -0
- package/packages/cli-runtime/src/native-peer.ps1 +51 -3
- package/packages/cli-runtime/src/native-process.mjs +77 -14
- package/packages/model-router-plugin/README.md +3 -3
- package/packages/model-router-plugin/package.json +1 -1
- package/packages/orchestration/src/binding-product.mjs +19 -0
- package/packages/orchestration/src/bot-data-actions.mjs +45 -0
- package/packages/orchestration/src/bot-data-chart.mjs +109 -0
- package/packages/orchestration/src/bot-data-context.mjs +44 -0
- package/packages/orchestration/src/bot-data-engine.mjs +809 -0
- package/packages/orchestration/src/bot-data-io.mjs +186 -0
- package/packages/orchestration/src/bot-data-sql.mjs +195 -0
- package/packages/orchestration/src/bot-data-surface.mjs +144 -0
- package/packages/orchestration/src/bot-data-tools.mjs +68 -0
- package/packages/orchestration/src/bot-data-worker.mjs +228 -0
- package/packages/orchestration/src/bot-independent-execution.mjs +195 -0
- package/packages/orchestration/src/bot-independent-stream.mjs +74 -0
- package/packages/orchestration/src/bot-loop.mjs +152 -19
- package/packages/orchestration/src/bot-notifications.mjs +159 -0
- package/packages/orchestration/src/bot-policy.mjs +43 -4
- package/packages/orchestration/src/bot-profile.mjs +171 -0
- package/packages/orchestration/src/bot-routine.mjs +4 -4
- package/packages/orchestration/src/bot-runtime-assembly.mjs +255 -8
- package/packages/orchestration/src/bot-runtime-switch.mjs +13 -2
- package/packages/orchestration/src/bot-serving.mjs +164 -0
- package/packages/orchestration/src/bot-session-store.mjs +105 -9
- package/packages/orchestration/src/bot-settings.mjs +50 -11
- package/packages/orchestration/src/bot-thread-admission.mjs +84 -0
- package/packages/orchestration/src/bot-thread-host.mjs +187 -0
- package/packages/orchestration/src/bot-threads.mjs +790 -0
- package/packages/orchestration/src/data-product-access.mjs +37 -0
- package/packages/orchestration/src/data-tool-contract.mjs +291 -0
- package/packages/orchestration/src/desktop-http-carrier.mjs +120 -0
- package/packages/orchestration/src/desktop-proxy.mjs +311 -0
- package/packages/orchestration/src/desktop-sessions.mjs +43 -9
- package/packages/orchestration/src/ds-lifecycle.mjs +31 -4
- package/packages/orchestration/src/grant-refresh-queue.mjs +17 -0
- package/packages/orchestration/src/independent-chat-page.mjs +39 -0
- package/packages/orchestration/src/independent-chat-service.mjs +308 -0
- package/packages/orchestration/src/independent-context-state.mjs +44 -0
- package/packages/orchestration/src/independent-message-wire.mjs +84 -0
- package/packages/orchestration/src/independent-tool-catalog.mjs +175 -0
- package/packages/orchestration/src/independent-tool-families.mjs +73 -0
- package/packages/orchestration/src/independent-tool-host.mjs +344 -0
- package/packages/orchestration/src/main-bot-grant.mjs +15 -0
- package/packages/orchestration/src/main-model-routes.mjs +208 -0
- package/packages/orchestration/src/model-accounting.mjs +99 -35
- package/packages/orchestration/src/model-auxiliary-dispatch.mjs +187 -0
- package/packages/orchestration/src/model-loop-frame.mjs +179 -0
- package/packages/orchestration/src/model-prepared-generation.mjs +175 -0
- package/packages/orchestration/src/model-request-bounds.mjs +111 -0
- package/packages/orchestration/src/native-activity-contract.mjs +491 -0
- package/packages/orchestration/src/native-activity-service.mjs +102 -0
- package/packages/orchestration/src/native-activity-store.mjs +249 -0
- package/packages/orchestration/src/native-conversation-tools.mjs +78 -0
- package/packages/orchestration/src/native-data-tools.mjs +100 -0
- package/packages/orchestration/src/native-remote-actions.mjs +742 -0
- package/packages/orchestration/src/native-team-tools.mjs +73 -0
- package/packages/orchestration/src/pairing-invite.mjs +37 -0
- package/packages/orchestration/src/product-composition.mjs +1559 -1001
- package/packages/orchestration/src/remote-machines.mjs +82 -14
- package/packages/orchestration/src/routine-execution.mjs +5 -3
- package/packages/orchestration/src/routine-session.mjs +19 -13
- package/packages/orchestration/src/secure-pairing-host.mjs +112 -0
- package/packages/orchestration/src/task-journal.mjs +29 -0
- package/packages/orchestration/src/task-product.mjs +17 -0
- package/packages/orchestration/src/team-assignment-store.mjs +120 -0
- package/packages/orchestration/src/team-bridge.mjs +282 -0
- package/packages/orchestration/src/team-scheduler.mjs +83 -0
- package/packages/orchestration/src/team-sdk-tools.mjs +84 -0
- package/packages/orchestration/src/team-tool-contract.mjs +284 -0
- package/packages/orchestration/src/ui-host-namespace.mjs +23 -0
- package/packages/orchestration/src/web-panel-actions.mjs +103 -0
- package/packages/orchestration/src/web-panel-framing.mjs +227 -0
- package/packages/orchestration/src/web-panel-store.mjs +206 -0
- package/remote-computer-agent/README.md +39 -29
- package/remote-computer-agent/build.mjs +3 -0
- package/remote-computer-agent/dist/OPENMAUSBOT_LICENSE +202 -0
- package/remote-computer-agent/dist/OPENMAUSBOT_NOTICE +10 -0
- package/remote-computer-agent/dist/OPENMAUSBOT_SOURCE.json +68 -0
- package/remote-computer-agent/dist/cli-stdio-bridge.mjs +215 -25
- package/remote-computer-agent/dist/cli-stdio-bridge.mjs.map +4 -4
- package/remote-computer-agent/dist/cli.mjs +1688 -168
- package/remote-computer-agent/dist/cli.mjs.map +4 -4
- package/remote-computer-agent/dist/index.mjs +1579 -122
- package/remote-computer-agent/dist/index.mjs.map +4 -4
- package/remote-computer-agent/dist/native-peer.ps1 +51 -3
- package/remote-computer-agent/package-lock.json +308 -2
- package/remote-computer-agent/package.json +2 -1
- package/remote-computer-agent/pair-device.ps1 +20 -0
- package/remote-computer-agent/setup.ps1 +38 -16
- package/remote-computer-agent/src/cli.ts +24 -3
- package/remote-computer-agent/src/desktop-session.ts +11 -3
- package/remote-computer-agent/src/e2e-server.ts +81 -0
- package/remote-computer-agent/src/pairing-page.ts +35 -0
- package/remote-computer-agent/src/protocol.ts +25 -25
- package/remote-computer-agent/src/secure-pairing.ts +105 -0
- package/remote-computer-agent/src/store.ts +20 -5
- package/remote-computer-agent/src/transport.ts +39 -15
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// One prepared model generation, admitted and dispatched as the same generation.
|
|
2
|
+
//
|
|
3
|
+
// The installed SDK's `LlmRuntime.prepareCall(config, signal, configure)` is the only
|
|
4
|
+
// public seam that binds one adapter registration's metadata to its own dispatch: it
|
|
5
|
+
// captures the registration and the adapter's `prepareCall` result at prepare time, and
|
|
6
|
+
// its returned one-shot `stream(options)` dispatches against exactly that capture, failing
|
|
7
|
+
// `INVALID_PREPARED_CALL` on a second dispatch or a call-config change. A Host that reads
|
|
8
|
+
// `resolveModelInfo` at admission and then lets the waterfall's `next()` re-resolve the
|
|
9
|
+
// model at dispatch can price one generation and dispatch another when a registration is
|
|
10
|
+
// replaced in between — the race `model-request-bounds` documents and cannot close, because
|
|
11
|
+
// its admission wraps the waterfall `next()`, not a prepared call.
|
|
12
|
+
//
|
|
13
|
+
// This module moves the independent path's admission onto that fence. A prepared call is
|
|
14
|
+
// made once, its exact metadata and defaults are captured into a Host-owned frame keyed by
|
|
15
|
+
// the request's own signal, and the accounting admits against that frame instead of reading
|
|
16
|
+
// the current registration again. The clamp happens in `configure`, before the config is
|
|
17
|
+
// frozen, so the dispatched config, the receipt hash and the estimate all describe the one
|
|
18
|
+
// generation that actually streams. Nothing here holds money, issues a receipt, or bypasses
|
|
19
|
+
// the policy, the secret rewrite or the `llm/stream` waterfall: it only decides the bound
|
|
20
|
+
// and hands the product's own listener the same generation it admitted.
|
|
21
|
+
//
|
|
22
|
+
// Each dispatch derives its own signal, linked to cancellation but used only to find
|
|
23
|
+
// captured capacity, never as Bot authentication. The product bridge redacts through
|
|
24
|
+
// the Host before preparation and refuses a later request-object replacement: an
|
|
25
|
+
// ordinary llm.stream re-entry would lose the SDK's prepared dispatch. The immutable
|
|
26
|
+
// frame therefore includes both the exact route and its original request identity.
|
|
27
|
+
|
|
28
|
+
const fail = (code, detail) => { throw Object.assign(new Error(code), { code, ...detail }) }
|
|
29
|
+
const positive = value => Number.isSafeInteger(value) && value > 0 ? value : null
|
|
30
|
+
|
|
31
|
+
/** The call-config fields the SDK compares for a prepared dispatch (`callConfigEquals`). */
|
|
32
|
+
const CALL_CONFIG_FIELDS = Object.freeze(['maxTokens', 'reasoningEffort', 'temperature', 'stop'])
|
|
33
|
+
|
|
34
|
+
/** Only the call-config fields — never `signal`, `messages`, `tools` or `system`. */
|
|
35
|
+
function callConfigOf(options) {
|
|
36
|
+
const config = { provider: options.provider, model: options.model }
|
|
37
|
+
for (const field of CALL_CONFIG_FIELDS) if (options[field] !== undefined) config[field] = options[field]
|
|
38
|
+
return config
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The resolved call-config fields to restate onto the dispatched request. */
|
|
42
|
+
function resolvedFields(config) {
|
|
43
|
+
const fields = {}
|
|
44
|
+
for (const field of CALL_CONFIG_FIELDS) if (config[field] !== undefined) fields[field] = config[field]
|
|
45
|
+
return fields
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The output bound one prepared call will dispatch with, decided before the config freezes.
|
|
50
|
+
*
|
|
51
|
+
* A configured allowance is a price, not a capacity; the pinned adapter's `defaultMaxTokens`
|
|
52
|
+
* is the per-request cap the SDK materializes into a request that declares none. The bound
|
|
53
|
+
* the provider sees is the smaller of what the request or the adapter declares and the
|
|
54
|
+
* allowance. Because `configure` works on a detached clone and returns new controls, the
|
|
55
|
+
* clamp always lands — a request frozen above the allowance is lowered to the allowance
|
|
56
|
+
* here, which is the correct recovery (lower the dispatched `maxTokens`), not a refusal and
|
|
57
|
+
* never a lowered allowance.
|
|
58
|
+
*
|
|
59
|
+
* @param requested - the request's own `maxTokens`; `null` when it declares none.
|
|
60
|
+
* @param allowance - the priced output allowance; `null` when the funding is not priced
|
|
61
|
+
* per token (a subscription), in which case nothing is clamped and the adapter default
|
|
62
|
+
* stands.
|
|
63
|
+
* @param adapterDefault - the pinned adapter's declared per-request cap; `null` when none.
|
|
64
|
+
* @returns `{ok:true,maxTokens,clamp}`, or `{ok:false,code}` when the request's own bound is
|
|
65
|
+
* present but invalid.
|
|
66
|
+
*/
|
|
67
|
+
export function planConfiguredOutput({ requested = null, allowance = null, adapterDefault = null } = {}) {
|
|
68
|
+
if (requested !== null && !(Number.isSafeInteger(requested) && requested >= 1)) return { ok: false, code: 'MODEL_OUTPUT_BOUNDARY_REQUIRED' }
|
|
69
|
+
if (allowance === null) return { ok: true, maxTokens: requested ?? null, clamp: false }
|
|
70
|
+
const known = requested ?? adapterDefault ?? null
|
|
71
|
+
const maxTokens = known === null ? allowance : Math.min(known, allowance)
|
|
72
|
+
return { ok: true, maxTokens, clamp: known === null || known > allowance }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The Host-owned frame store. Keyed by the request signal so a rebuilt request (the secret
|
|
77
|
+
* rewrite) keeps its frame, and unforgeable because a request cannot insert into a WeakMap
|
|
78
|
+
* the Host owns. Nothing is cached across calls: the frame lives exactly as long as the one
|
|
79
|
+
* dispatch that set it.
|
|
80
|
+
*/
|
|
81
|
+
export function createModelFrameStore() {
|
|
82
|
+
const frames = new WeakMap()
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
set: (signal, frame) => { if (signal) frames.set(signal, frame) },
|
|
85
|
+
read: signal => (signal ? frames.get(signal) ?? null : null),
|
|
86
|
+
delete: signal => { if (signal) frames.delete(signal) },
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Apply the Host's public string redactor before binding an adapter generation. */
|
|
91
|
+
export function redactPreparedModelRequest(options, redact) {
|
|
92
|
+
if (typeof redact !== 'function') return options
|
|
93
|
+
const walk = value => {
|
|
94
|
+
if (typeof value === 'string') return redact(value)
|
|
95
|
+
if (Array.isArray(value)) {
|
|
96
|
+
const next = value.map(walk)
|
|
97
|
+
return next.some((item, index) => item !== value[index]) ? next : value
|
|
98
|
+
}
|
|
99
|
+
if (value && Object.getPrototypeOf(value) === Object.prototype) {
|
|
100
|
+
const next = Object.fromEntries(Object.entries(value).map(([key, item]) => [key, walk(item)]))
|
|
101
|
+
return Object.keys(next).some(key => next[key] !== value[key]) ? next : value
|
|
102
|
+
}
|
|
103
|
+
return value
|
|
104
|
+
}
|
|
105
|
+
const messages = walk(options.messages), system = walk(options.system)
|
|
106
|
+
return messages === options.messages && system === options.system ? options : { ...options, messages, ...(system === undefined ? {} : { system }) }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The prepared-dispatch seam for the independent path.
|
|
111
|
+
*
|
|
112
|
+
* @param llm - the live `llm` service, or a function returning it.
|
|
113
|
+
* @param frames - the shared {@link createModelFrameStore} the accounting reads.
|
|
114
|
+
* @param fundingFor - `({botId, provider, model}) => funding | null`, the Host's committed
|
|
115
|
+
* funding read. Only its priced output allowance is used, and only as a capacity to clamp
|
|
116
|
+
* against — never as a token count or a cost. A throw or a null is treated as unpriced, so
|
|
117
|
+
* the accounting stays the single authority over money and refuses in its own coded way.
|
|
118
|
+
* @returns `{dispatch}`, where `dispatch(options, {botId})` is an async iterable that prepares
|
|
119
|
+
* the call once, records the frame, and streams the captured generation.
|
|
120
|
+
*/
|
|
121
|
+
export function createPreparedModelDispatch({ llm = null, frames = null, fundingFor = null } = {}) {
|
|
122
|
+
if (!frames || typeof frames.set !== 'function') fail('MODEL_FRAME_STORE_REQUIRED')
|
|
123
|
+
const service = () => (typeof llm === 'function' ? llm() : llm)
|
|
124
|
+
|
|
125
|
+
async function* dispatch(options, context = {}) {
|
|
126
|
+
if (options.maxTokens !== undefined && positive(options.maxTokens) === null) fail('MODEL_OUTPUT_BOUNDARY_REQUIRED')
|
|
127
|
+
const runtime = service()
|
|
128
|
+
if (!runtime || typeof runtime.prepareCall !== 'function') fail('MODEL_PREPARED_UNAVAILABLE')
|
|
129
|
+
const { botId } = context
|
|
130
|
+
let funding = null
|
|
131
|
+
if (botId && typeof fundingFor === 'function') {
|
|
132
|
+
try { funding = await fundingFor({ botId, provider: options.provider, model: options.model }) } catch { funding = null }
|
|
133
|
+
}
|
|
134
|
+
const allowance = funding && funding.funding === 'api' ? positive(funding.maxOutputTokens) : null
|
|
135
|
+
|
|
136
|
+
// `configure` is pure and synchronous: it decides the bound from the captured metadata
|
|
137
|
+
// and the already-resolved allowance, and records the capacities the accounting admits on.
|
|
138
|
+
let contextWindow = null, defaultMaxTokens = null
|
|
139
|
+
const configure = (controls, info) => {
|
|
140
|
+
contextWindow = positive(info.context?.contextWindow)
|
|
141
|
+
defaultMaxTokens = positive(info.defaultMaxTokens)
|
|
142
|
+
const plan = planConfiguredOutput({ requested: controls.maxTokens ?? null, allowance, adapterDefault: defaultMaxTokens })
|
|
143
|
+
if (!plan.ok) fail(plan.code)
|
|
144
|
+
// Unpriced (a subscription), or already within the bound: leave the controls untouched,
|
|
145
|
+
// so the SDK materializes the adapter's own default exactly as it would with no Host.
|
|
146
|
+
if (allowance === null || !plan.clamp) return controls
|
|
147
|
+
// Above the allowance, or nothing declared at all: lower the dispatched bound to the
|
|
148
|
+
// allowance. This is the only safe dispatch, and it lands because `configure` returns
|
|
149
|
+
// new controls on a detached clone regardless of how the caller froze its request.
|
|
150
|
+
return { ...controls, maxTokens: plan.maxTokens }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const signal = options.signal ? AbortSignal.any([options.signal]) : new AbortController().signal
|
|
154
|
+
const prepared = await runtime.prepareCall(callConfigOf(options), signal, configure)
|
|
155
|
+
// Restate the resolved call-config so the dispatched request equals the prepared one:
|
|
156
|
+
// `prepared.stream` refuses `INVALID_PREPARED_CALL` on any call-config mismatch.
|
|
157
|
+
const dispatchOptions = { ...options, ...resolvedFields(prepared.config), signal }
|
|
158
|
+
const frame = Object.freeze({ provider: options.provider, model: options.model, fundingRevision: funding?.fundingRevision ?? null,
|
|
159
|
+
bounds: Object.freeze({ contextWindow, defaultMaxTokens }), request: dispatchOptions })
|
|
160
|
+
frames.set(signal, frame)
|
|
161
|
+
try {
|
|
162
|
+
// A Host gate between binding the generation and dispatching it. The frame is already
|
|
163
|
+
// recorded, so the accounting that runs inside the stream admits against this capture;
|
|
164
|
+
// anything that swaps the current registration here cannot reach the one-shot stream,
|
|
165
|
+
// which dispatches the generation it was prepared with. The gate may throw to refuse
|
|
166
|
+
// before any dispatch, and it runs with no receipt, reservation or provider call yet.
|
|
167
|
+
if (typeof context.beforeDispatch === 'function') await context.beforeDispatch({ prepared, frame, options: dispatchOptions })
|
|
168
|
+
yield* prepared.stream(dispatchOptions)
|
|
169
|
+
} finally {
|
|
170
|
+
frames.delete(signal)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return Object.freeze({ dispatch })
|
|
175
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// The bounds a Host can prove about one model request, and the ones it cannot.
|
|
2
|
+
//
|
|
3
|
+
// A configured allowance is a price, not a capacity: it says what the Host is
|
|
4
|
+
// willing to pay for, never what the model accepts. This module reads the two
|
|
5
|
+
// capacities that actually decide what a provider request is, from the pinned
|
|
6
|
+
// adapter that owns the exact route, through the installed SDK's own public
|
|
7
|
+
// metadata seam (`LlmRuntime.resolveModelInfo`):
|
|
8
|
+
//
|
|
9
|
+
// * `context.contextWindow` — the whole window the route accepts, input and
|
|
10
|
+
// output together. It is the honest input bound: it already accounts for
|
|
11
|
+
// images, tool declarations and adapter framing, so no tokenizer and no
|
|
12
|
+
// source-length estimate stands in for it.
|
|
13
|
+
// * `defaultMaxTokens` — the per-request output cap the adapter materializes
|
|
14
|
+
// into a request that declares none. The SDK's own call-config resolution
|
|
15
|
+
// does exactly that before dispatch, so this is the bound the provider
|
|
16
|
+
// receives for an unbounded request, not a guess about one.
|
|
17
|
+
//
|
|
18
|
+
// Everything here fails closed with a coded error. A missing context window is
|
|
19
|
+
// a missing bound, never a convenient number.
|
|
20
|
+
|
|
21
|
+
const fail=(code,detail)=>{throw Object.assign(new Error(code),{code,...detail})}
|
|
22
|
+
const positive=value=>Number.isSafeInteger(value)&&value>0?value:null
|
|
23
|
+
const routeName=(value,what)=>{
|
|
24
|
+
if(typeof value!=='string'||value.length===0||value.length>512)fail('MODEL_ROUTE_INVALID',{what})
|
|
25
|
+
return value
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the pinned adapter's declared capacity for one exact provider/model
|
|
30
|
+
* route. Nothing is cached: an adapter registration can be replaced under a
|
|
31
|
+
* live Host, and one generation's window must never be combined with another
|
|
32
|
+
* generation's endpoint.
|
|
33
|
+
*
|
|
34
|
+
* @param llm - the live `llm` service, or a function returning it.
|
|
35
|
+
* @param provider - provider route; a route name never carries a namespace.
|
|
36
|
+
* @param model - exact model id; a model id may carry one (`vendor/two`).
|
|
37
|
+
* @param signal - cancellation for the adapter-owned lookup.
|
|
38
|
+
* @returns frozen `{provider,model,contextWindow,defaultMaxTokens}`, where a
|
|
39
|
+
* missing `defaultMaxTokens` is `null` rather than an invented cap.
|
|
40
|
+
*/
|
|
41
|
+
export async function resolveModelRouteBounds({llm,provider,model,signal=null}){
|
|
42
|
+
const route={provider:routeName(provider,'provider'),model:routeName(model,'model')}
|
|
43
|
+
// The Host splits a saved route at its first slash, so a provider name that
|
|
44
|
+
// carried one would resolve a different route's model and price the wrong one.
|
|
45
|
+
if(route.provider.includes('/'))fail('MODEL_ROUTE_INVALID',{what:'provider',reason:'namespace'})
|
|
46
|
+
if(!llm||typeof llm.resolveModelInfo!=='function')fail('MODEL_BOUND_SOURCE_REQUIRED')
|
|
47
|
+
if(signal?.aborted)fail('MODEL_BOUND_UNAVAILABLE',{reason:'aborted'})
|
|
48
|
+
let info
|
|
49
|
+
try{info=await llm.resolveModelInfo(route.provider,route.model,signal??undefined)}
|
|
50
|
+
catch(error){fail('MODEL_BOUND_UNAVAILABLE',{reason:typeof error?.code==='string'?error.code:'unavailable'})}
|
|
51
|
+
if(!info||typeof info!=='object')fail('MODEL_BOUND_UNAVAILABLE',{reason:'empty'})
|
|
52
|
+
// An adapter that answers about another route would price another model.
|
|
53
|
+
if(info.provider!==route.provider||info.id!==route.model)fail('MODEL_BOUND_MISMATCH',{provider:route.provider,model:route.model})
|
|
54
|
+
const contextWindow=positive(info.context?.contextWindow)
|
|
55
|
+
if(contextWindow===null)fail('MODEL_INPUT_BOUND_REQUIRED',{provider:route.provider,model:route.model})
|
|
56
|
+
return Object.freeze({provider:route.provider,model:route.model,contextWindow,defaultMaxTokens:positive(info.defaultMaxTokens)})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The reusable Host resolver. `bounds` answers both capacities for one route;
|
|
61
|
+
* `inputBound` answers only the trusted input upper bound, which is the whole
|
|
62
|
+
* declared window.
|
|
63
|
+
*/
|
|
64
|
+
export function createModelBoundResolver({llm=null}={}){
|
|
65
|
+
const service=()=>typeof llm==='function'?llm():llm
|
|
66
|
+
const bounds=async({provider,model,signal=null})=>resolveModelRouteBounds({llm:service(),provider,model,signal})
|
|
67
|
+
return Object.freeze({bounds,inputBound:async({provider,model,signal=null})=>(await bounds({provider,model,signal})).contextWindow})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The output bound one request will actually dispatch with, or the refusal that
|
|
72
|
+
* keeps it out of the ledger.
|
|
73
|
+
*
|
|
74
|
+
* @param requested - the request's own `maxTokens`; `null` when it declares none.
|
|
75
|
+
* @param allowance - the Host-configured priced output allowance.
|
|
76
|
+
* @param adapterDefault - the pinned adapter's declared per-request output cap.
|
|
77
|
+
* @returns `{ok:true,maxTokens,source}` with `source` one of `request`,
|
|
78
|
+
* `adapter-default` or `clamp`, or `{ok:false,code}`. A `clamp` result also
|
|
79
|
+
* carries `refusal`: the code to fail with when this request object cannot
|
|
80
|
+
* carry the clamp.
|
|
81
|
+
*/
|
|
82
|
+
export function planOutputBound({requested=null,allowance,adapterDefault=null}){
|
|
83
|
+
const cap=positive(allowance)
|
|
84
|
+
if(cap===null)return{ok:false,code:'MODEL_OUTPUT_ALLOWANCE_REQUIRED'}
|
|
85
|
+
const declared=requested===undefined?null:requested
|
|
86
|
+
if(declared!==null&&positive(declared)===null)return{ok:false,code:'MODEL_OUTPUT_BOUNDARY_REQUIRED'}
|
|
87
|
+
const known=declared!==null?declared:positive(adapterDefault)
|
|
88
|
+
if(known!==null&&known<=cap)return{ok:true,maxTokens:known,source:declared!==null?'request':'adapter-default'}
|
|
89
|
+
// The bound the provider will see is above the allowance, or is the provider's
|
|
90
|
+
// own default that nothing declared. Either way the only safe dispatch is the
|
|
91
|
+
// one this request can be made to carry; whether it can is the caller's
|
|
92
|
+
// question, and the refusal names which of the two it was.
|
|
93
|
+
return{ok:true,maxTokens:cap,source:'clamp',refusal:known===null?'MODEL_OUTPUT_BOUND_UNBOUNDED':'MODEL_OUTPUT_BOUND_EXCEEDED'}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Whether one field of a request object can carry a value the Host writes.
|
|
98
|
+
*
|
|
99
|
+
* Mutability is per field, and it is independent of every other field's: a
|
|
100
|
+
* request whose signal is read-only may still carry a writable `maxTokens`, and
|
|
101
|
+
* the reverse. Only a plain writable data property — present, or addable on an
|
|
102
|
+
* extensible object — counts as writable; an accessor is never written, because
|
|
103
|
+
* restoring one exactly is not something a defineProperty round-trip promises.
|
|
104
|
+
*
|
|
105
|
+
* @returns frozen `{present,canWrite,descriptor}`; `descriptor` is the own
|
|
106
|
+
* property descriptor to restore, or `null` when the field was absent.
|
|
107
|
+
*/
|
|
108
|
+
export function requestFieldAccess(options,field){
|
|
109
|
+
const descriptor=Object.getOwnPropertyDescriptor(options,field)
|
|
110
|
+
return Object.freeze({present:Boolean(descriptor),canWrite:descriptor?descriptor.writable===true:Object.isExtensible(options),descriptor:descriptor??null})
|
|
111
|
+
}
|