@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 @@
|
|
|
1
|
+
5471a0070ddbb69dca34dee6da550de4581e8e1a40e22e0f97873d539793bf4e v1.json
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// The yamux profile carried inside the records: one frame per record, phone-opened streams only, 2 MiB windows.
|
|
2
|
+
// Wire spec: companion/src/e2e/README.md, "Multiplexing". This module does no I/O, so every rule is testable.
|
|
3
|
+
|
|
4
|
+
export const VERSION = 0;
|
|
5
|
+
export const TYPE = { data: 0, windowUpdate: 1, ping: 2, goAway: 3 } as const;
|
|
6
|
+
export const FLAG = { syn: 0x1, ack: 0x2, fin: 0x4, rst: 0x8 } as const;
|
|
7
|
+
const KNOWN_FLAGS = 0xf;
|
|
8
|
+
/** `removed` goes only from the computer to the phone: this device was removed, and the close that follows is 4001. It
|
|
9
|
+
* is inside the encryption, so unlike a bare 4001 close code a relay can't forge it. */
|
|
10
|
+
export const GO_AWAY = { normal: 0, protocol: 1, internal: 2, removed: 3 } as const;
|
|
11
|
+
|
|
12
|
+
export const HEADER_BYTES = 12;
|
|
13
|
+
export const MAX_DATA_PAYLOAD = 65_507;
|
|
14
|
+
export const INITIAL_WINDOW = 256 * 1024;
|
|
15
|
+
export const STREAM_WINDOW = 2 * 1024 * 1024;
|
|
16
|
+
export const OPEN_WINDOW_DELTA = STREAM_WINDOW - INITIAL_WINDOW;
|
|
17
|
+
export const WINDOW_UPDATE_AFTER = 1024 * 1024;
|
|
18
|
+
export const MAX_STREAMS = 64;
|
|
19
|
+
export const MAX_STREAM_ID = 2 ** 31 - 1;
|
|
20
|
+
export const PING_INTERVAL_MS = 20_000;
|
|
21
|
+
export const PING_TIMEOUT_MS = 10_000;
|
|
22
|
+
|
|
23
|
+
export type FrameType = (typeof TYPE)[keyof typeof TYPE];
|
|
24
|
+
|
|
25
|
+
export interface Frame {
|
|
26
|
+
type: FrameType;
|
|
27
|
+
flags: number;
|
|
28
|
+
streamId: number;
|
|
29
|
+
/** Data: the payload size. WindowUpdate: the window delta. Ping: the opaque value. GoAway: the code. */
|
|
30
|
+
length: number;
|
|
31
|
+
data?: Uint8Array;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class YamuxError extends Error {
|
|
35
|
+
readonly reason: string;
|
|
36
|
+
constructor(reason: string) {
|
|
37
|
+
super(`yamux: ${reason}`);
|
|
38
|
+
this.reason = reason;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function encodeFrame(frame: Frame): Buffer {
|
|
43
|
+
const payload = frame.type === TYPE.data ? frame.data ?? new Uint8Array(0) : new Uint8Array(0);
|
|
44
|
+
if (frame.type === TYPE.data && payload.length !== frame.length) throw new YamuxError("data_length");
|
|
45
|
+
if (payload.length > MAX_DATA_PAYLOAD) throw new YamuxError("data_too_large");
|
|
46
|
+
const out = Buffer.alloc(HEADER_BYTES + payload.length);
|
|
47
|
+
out.writeUInt8(VERSION, 0);
|
|
48
|
+
out.writeUInt8(frame.type, 1);
|
|
49
|
+
out.writeUInt16BE(frame.flags, 2);
|
|
50
|
+
out.writeUInt32BE(frame.streamId, 4);
|
|
51
|
+
out.writeUInt32BE(frame.length, 8);
|
|
52
|
+
out.set(payload, HEADER_BYTES);
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** One record's plaintext is exactly one frame. Everything that can be checked without stream state is
|
|
57
|
+
* checked here. */
|
|
58
|
+
export function decodeFrame(bytes: Uint8Array): Frame {
|
|
59
|
+
if (bytes.length < HEADER_BYTES) throw new YamuxError("short_frame");
|
|
60
|
+
const view = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
61
|
+
if (view.readUInt8(0) !== VERSION) throw new YamuxError("version");
|
|
62
|
+
const type = view.readUInt8(1);
|
|
63
|
+
if (type > TYPE.goAway) throw new YamuxError("type");
|
|
64
|
+
const flags = view.readUInt16BE(2);
|
|
65
|
+
if ((flags & ~KNOWN_FLAGS) !== 0) throw new YamuxError("flags");
|
|
66
|
+
const streamId = view.readUInt32BE(4);
|
|
67
|
+
const length = view.readUInt32BE(8);
|
|
68
|
+
if (type === TYPE.data) {
|
|
69
|
+
if (length > MAX_DATA_PAYLOAD) throw new YamuxError("data_too_large");
|
|
70
|
+
if (bytes.length !== HEADER_BYTES + length) throw new YamuxError("data_length");
|
|
71
|
+
if (streamId === 0) throw new YamuxError("stream_zero");
|
|
72
|
+
return { type, flags, streamId, length, data: view.subarray(HEADER_BYTES) };
|
|
73
|
+
}
|
|
74
|
+
if (bytes.length !== HEADER_BYTES) throw new YamuxError("trailing_bytes");
|
|
75
|
+
if (type === TYPE.windowUpdate) {
|
|
76
|
+
if (streamId === 0) throw new YamuxError("stream_zero");
|
|
77
|
+
} else {
|
|
78
|
+
if (streamId !== 0) throw new YamuxError("session_frame_stream");
|
|
79
|
+
if (type === TYPE.ping && flags !== FLAG.syn && flags !== FLAG.ack) throw new YamuxError("ping_flags");
|
|
80
|
+
if (type === TYPE.goAway && flags !== 0) throw new YamuxError("goaway_flags");
|
|
81
|
+
}
|
|
82
|
+
return { type: type as FrameType, flags, streamId, length };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const openFrame = (streamId: number): Frame => ({ type: TYPE.windowUpdate, flags: FLAG.syn, streamId, length: OPEN_WINDOW_DELTA });
|
|
86
|
+
export const acceptFrame = (streamId: number): Frame => ({ type: TYPE.windowUpdate, flags: FLAG.ack, streamId, length: OPEN_WINDOW_DELTA });
|
|
87
|
+
export const resetFrame = (streamId: number): Frame => ({ type: TYPE.windowUpdate, flags: FLAG.rst, streamId, length: 0 });
|
|
88
|
+
export const finFrame = (streamId: number): Frame => ({ type: TYPE.windowUpdate, flags: FLAG.fin, streamId, length: 0 });
|
|
89
|
+
export const windowFrame = (streamId: number, delta: number): Frame => ({ type: TYPE.windowUpdate, flags: 0, streamId, length: delta });
|
|
90
|
+
export const dataFrame = (streamId: number, data: Uint8Array, flags = 0): Frame => ({ type: TYPE.data, flags, streamId, length: data.length, data });
|
|
91
|
+
export const pingFrame = (value: number, flags: number = FLAG.syn): Frame => ({ type: TYPE.ping, flags, streamId: 0, length: value >>> 0 });
|
|
92
|
+
export const goAwayFrame = (code: number): Frame => ({ type: TYPE.goAway, flags: 0, streamId: 0, length: code });
|
|
93
|
+
|
|
94
|
+
/** The phone opens every stream; the computer only accepts. */
|
|
95
|
+
export type Role = "phone" | "computer";
|
|
96
|
+
|
|
97
|
+
interface StreamState {
|
|
98
|
+
/** The phone side: false until the computer's ACK arrives. The computer side: false until accept(). */
|
|
99
|
+
established: boolean;
|
|
100
|
+
/** What the peer may still send before this side grants more. */
|
|
101
|
+
receiveWindow: number;
|
|
102
|
+
/** What this side may still send. */
|
|
103
|
+
sendWindow: number;
|
|
104
|
+
/** Consumed but not yet granted back. */
|
|
105
|
+
uncredited: number;
|
|
106
|
+
localFin: boolean;
|
|
107
|
+
remoteFin: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface MuxEvents {
|
|
111
|
+
/** Every frame to seal and send, in order. */
|
|
112
|
+
send(frame: Frame): void;
|
|
113
|
+
/** The computer side: a new stream. Answer with accept() or reset(); data may arrive first. */
|
|
114
|
+
opened?(streamId: number): void;
|
|
115
|
+
/** The phone side: the computer accepted. */
|
|
116
|
+
accepted?(streamId: number): void;
|
|
117
|
+
data(streamId: number, data: Uint8Array): void;
|
|
118
|
+
/** The peer may send no more (FIN). */
|
|
119
|
+
ended(streamId: number): void;
|
|
120
|
+
/** Ended by RST from either side, or the session closing. */
|
|
121
|
+
reset(streamId: number): void;
|
|
122
|
+
/** The peer granted more send window. */
|
|
123
|
+
writable(streamId: number): void;
|
|
124
|
+
goAway?(code: number): void;
|
|
125
|
+
pong?(value: number): void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** A frame that breaks the profile throws YamuxError. The caller then sends GoAway(1) through
|
|
129
|
+
* `protocolError()` and closes the WebSocket with 4000. */
|
|
130
|
+
export class Mux {
|
|
131
|
+
readonly role: Role;
|
|
132
|
+
readonly #events: MuxEvents;
|
|
133
|
+
readonly #streams = new Map<number, StreamState>();
|
|
134
|
+
/** The highest ID the phone has opened. */
|
|
135
|
+
#highestId = 0;
|
|
136
|
+
#peerGoneAway = false;
|
|
137
|
+
#localGoneAway = false;
|
|
138
|
+
|
|
139
|
+
constructor(role: Role, events: MuxEvents) {
|
|
140
|
+
this.role = role;
|
|
141
|
+
this.#events = events;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
get activeStreams(): number {
|
|
145
|
+
return this.#streams.size;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get peerGoneAway(): boolean {
|
|
149
|
+
return this.#peerGoneAway;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
has(streamId: number): boolean {
|
|
153
|
+
return this.#streams.has(streamId);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
sendWindow(streamId: number): number {
|
|
157
|
+
return this.#streams.get(streamId)?.sendWindow ?? 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The phone side. Data may follow at once, within the first 256 KiB. */
|
|
161
|
+
open(): number {
|
|
162
|
+
if (this.role !== "phone") throw new YamuxError("computer_open");
|
|
163
|
+
if (this.#localGoneAway || this.#peerGoneAway) throw new YamuxError("gone_away");
|
|
164
|
+
if (this.#streams.size >= MAX_STREAMS) throw new YamuxError("too_many_streams");
|
|
165
|
+
const streamId = this.#highestId === 0 ? 1 : this.#highestId + 2;
|
|
166
|
+
if (streamId > MAX_STREAM_ID) throw new YamuxError("ids_exhausted");
|
|
167
|
+
this.#highestId = streamId;
|
|
168
|
+
this.#streams.set(streamId, {
|
|
169
|
+
established: false,
|
|
170
|
+
receiveWindow: INITIAL_WINDOW + OPEN_WINDOW_DELTA,
|
|
171
|
+
sendWindow: INITIAL_WINDOW,
|
|
172
|
+
uncredited: 0,
|
|
173
|
+
localFin: false,
|
|
174
|
+
remoteFin: false,
|
|
175
|
+
});
|
|
176
|
+
this.#events.send(openFrame(streamId));
|
|
177
|
+
return streamId;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** The computer side, once the origin socket is connected. */
|
|
181
|
+
accept(streamId: number): void {
|
|
182
|
+
const stream = this.#streams.get(streamId);
|
|
183
|
+
if (!stream || stream.established || this.role !== "computer") return;
|
|
184
|
+
stream.established = true;
|
|
185
|
+
stream.receiveWindow += OPEN_WINDOW_DELTA;
|
|
186
|
+
this.#events.send(acceptFrame(streamId));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Sends up to the window and the frame cap; returns the bytes taken. */
|
|
190
|
+
write(streamId: number, data: Uint8Array): number {
|
|
191
|
+
const stream = this.#streams.get(streamId);
|
|
192
|
+
if (!stream || stream.localFin || (this.role === "computer" && !stream.established)) return 0;
|
|
193
|
+
const size = Math.min(data.length, stream.sendWindow, MAX_DATA_PAYLOAD);
|
|
194
|
+
if (size === 0) return 0;
|
|
195
|
+
stream.sendWindow -= size;
|
|
196
|
+
this.#events.send(dataFrame(streamId, data.subarray(0, size)));
|
|
197
|
+
return size;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
finish(streamId: number): void {
|
|
201
|
+
const stream = this.#streams.get(streamId);
|
|
202
|
+
if (!stream || stream.localFin) return;
|
|
203
|
+
stream.localFin = true;
|
|
204
|
+
this.#events.send(finFrame(streamId));
|
|
205
|
+
this.#closeIfDone(streamId, stream);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
reset(streamId: number): void {
|
|
209
|
+
if (!this.#streams.delete(streamId)) return;
|
|
210
|
+
this.#events.send(resetFrame(streamId));
|
|
211
|
+
this.#events.reset(streamId);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** The receiver handed `bytes` on (to the origin socket, or the HTTP stack). Grants them back in 1 MiB steps. */
|
|
215
|
+
consumed(streamId: number, bytes: number): void {
|
|
216
|
+
const stream = this.#streams.get(streamId);
|
|
217
|
+
if (!stream || stream.remoteFin) return;
|
|
218
|
+
stream.uncredited += bytes;
|
|
219
|
+
if (stream.uncredited < WINDOW_UPDATE_AFTER) return;
|
|
220
|
+
const delta = stream.uncredited;
|
|
221
|
+
stream.uncredited = 0;
|
|
222
|
+
stream.receiveWindow += delta;
|
|
223
|
+
this.#events.send(windowFrame(streamId, delta));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
ping(value: number): void {
|
|
227
|
+
this.#events.send(pingFrame(value));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
goAway(code: number = GO_AWAY.normal): void {
|
|
231
|
+
this.#localGoneAway = true;
|
|
232
|
+
this.#events.send(goAwayFrame(code));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Sends GoAway(1); the caller closes with 4000. */
|
|
236
|
+
protocolError(): void {
|
|
237
|
+
this.goAway(GO_AWAY.protocol);
|
|
238
|
+
this.closeAll();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
closeAll(): void {
|
|
242
|
+
const ids = [...this.#streams.keys()];
|
|
243
|
+
this.#streams.clear();
|
|
244
|
+
for (const id of ids) this.#events.reset(id);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
receive(frame: Frame): void {
|
|
248
|
+
if (frame.type === TYPE.ping) {
|
|
249
|
+
if (frame.flags === FLAG.syn) this.#events.send(pingFrame(frame.length, FLAG.ack));
|
|
250
|
+
else this.#events.pong?.(frame.length);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (frame.type === TYPE.goAway) {
|
|
254
|
+
this.#peerGoneAway = true;
|
|
255
|
+
this.#events.goAway?.(frame.length);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (frame.flags & FLAG.syn) {
|
|
259
|
+
this.#receiveOpen(frame);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (frame.flags & FLAG.ack) {
|
|
263
|
+
this.#receiveAck(frame);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const stream = this.#known(frame.streamId);
|
|
267
|
+
if (!stream) return;
|
|
268
|
+
this.#receiveOnStream(frame.streamId, stream, frame);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** A stream that ended is ignored: an RST and the peer's last frames cross. An ID never opened is an error. */
|
|
272
|
+
#known(streamId: number): StreamState | null {
|
|
273
|
+
if (streamId % 2 === 0 || streamId > this.#highestId) throw new YamuxError("unknown_stream");
|
|
274
|
+
return this.#streams.get(streamId) ?? null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
#receiveOpen(frame: Frame): void {
|
|
278
|
+
if (this.role === "phone") throw new YamuxError("computer_opened_stream");
|
|
279
|
+
if (frame.type !== TYPE.windowUpdate || frame.flags !== FLAG.syn) throw new YamuxError("open_flags");
|
|
280
|
+
if (frame.streamId % 2 === 0) throw new YamuxError("even_stream");
|
|
281
|
+
if (frame.streamId <= this.#highestId) throw new YamuxError("stream_reused");
|
|
282
|
+
this.#highestId = frame.streamId;
|
|
283
|
+
if (this.#localGoneAway || this.#streams.size >= MAX_STREAMS) {
|
|
284
|
+
this.#events.send(resetFrame(frame.streamId));
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
this.#streams.set(frame.streamId, {
|
|
288
|
+
established: false,
|
|
289
|
+
receiveWindow: INITIAL_WINDOW,
|
|
290
|
+
sendWindow: INITIAL_WINDOW + frame.length,
|
|
291
|
+
uncredited: 0,
|
|
292
|
+
localFin: false,
|
|
293
|
+
remoteFin: false,
|
|
294
|
+
});
|
|
295
|
+
this.#events.opened?.(frame.streamId);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#receiveAck(frame: Frame): void {
|
|
299
|
+
if (this.role === "computer") throw new YamuxError("phone_ack");
|
|
300
|
+
if (frame.type !== TYPE.windowUpdate || (frame.flags & ~(FLAG.ack | FLAG.fin | FLAG.rst)) !== 0) throw new YamuxError("ack_flags");
|
|
301
|
+
const stream = this.#known(frame.streamId);
|
|
302
|
+
if (!stream) return;
|
|
303
|
+
if (stream.established) throw new YamuxError("duplicate_ack");
|
|
304
|
+
stream.established = true;
|
|
305
|
+
this.#events.accepted?.(frame.streamId);
|
|
306
|
+
this.#receiveOnStream(frame.streamId, stream, { ...frame, flags: frame.flags & ~FLAG.ack });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
#receiveOnStream(streamId: number, stream: StreamState, frame: Frame): void {
|
|
310
|
+
if (frame.flags & FLAG.rst) {
|
|
311
|
+
this.#streams.delete(streamId);
|
|
312
|
+
this.#events.reset(streamId);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (frame.type === TYPE.data) {
|
|
316
|
+
if (stream.remoteFin && (frame.length > 0 || frame.flags & FLAG.fin)) throw new YamuxError("data_after_fin");
|
|
317
|
+
if (frame.length > stream.receiveWindow) throw new YamuxError("window_exceeded");
|
|
318
|
+
stream.receiveWindow -= frame.length;
|
|
319
|
+
if (frame.length > 0) this.#events.data(streamId, frame.data as Uint8Array);
|
|
320
|
+
} else if (frame.length > 0) {
|
|
321
|
+
stream.sendWindow += frame.length;
|
|
322
|
+
this.#events.writable(streamId);
|
|
323
|
+
}
|
|
324
|
+
if (frame.flags & FLAG.fin) {
|
|
325
|
+
if (stream.remoteFin) throw new YamuxError("data_after_fin");
|
|
326
|
+
stream.remoteFin = true;
|
|
327
|
+
this.#events.ended(streamId);
|
|
328
|
+
this.#closeIfDone(streamId, stream);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
#closeIfDone(streamId: number, stream: StreamState): void {
|
|
333
|
+
if (stream.localFin && stream.remoteFin) this.#streams.delete(streamId);
|
|
334
|
+
}
|
|
335
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ljwei-stak/dsh-model-route-bot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.67.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DSH Bot workspace with integrated model routing, official tools and remote computers",
|
|
6
6
|
"repository": {
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"modules/bot-runtime/vendor/ds-bot/office.js",
|
|
52
52
|
"modules/bot-runtime/vendor/ds-bot/test/harness-resolve.mjs",
|
|
53
53
|
"modules/bot-runtime/web-search.mjs",
|
|
54
|
+
"modules/openmaus-protocol/",
|
|
54
55
|
"packages/orchestration/src/bot-runtime-config.mjs",
|
|
55
56
|
".dsh-plugin/index.mjs",
|
|
56
57
|
".dsh-plugin/official-tools-remote-service.mjs",
|
|
@@ -256,6 +257,7 @@
|
|
|
256
257
|
"packages/orchestration/src/sqlite-router-state.mjs",
|
|
257
258
|
"packages/orchestration/src/task-contract.mjs",
|
|
258
259
|
"packages/orchestration/src/bot-settings.mjs",
|
|
260
|
+
"packages/orchestration/src/main-model-routes.mjs",
|
|
259
261
|
"packages/orchestration/src/funding-accounts.mjs",
|
|
260
262
|
"packages/orchestration/src/model-accounting.mjs",
|
|
261
263
|
"packages/capability-broker/src/cli-bootstrap.mjs",
|
|
@@ -323,6 +325,7 @@
|
|
|
323
325
|
"packages/orchestration/src/machine-heartbeat.mjs",
|
|
324
326
|
"remote-computer-agent/start.cmd",
|
|
325
327
|
"remote-computer-agent/setup.ps1",
|
|
328
|
+
"remote-computer-agent/pair-device.ps1",
|
|
326
329
|
"remote-computer-agent/launch-agent.ps1",
|
|
327
330
|
"native/windows-desktop/",
|
|
328
331
|
"remote-computer-agent/build.mjs",
|
|
@@ -338,11 +341,15 @@
|
|
|
338
341
|
"check:client": "node scripts/build-client.mjs --check",
|
|
339
342
|
"check:contracts": "node scripts/check-contracts.mjs",
|
|
340
343
|
"contracts:write": "node scripts/check-contracts.mjs --write",
|
|
341
|
-
"build:agent": "node remote-computer-agent/build.mjs",
|
|
344
|
+
"build:agent": "node scripts/build-openmaus-protocol.mjs && node remote-computer-agent/build.mjs",
|
|
342
345
|
"check:source": "node scripts/integration-product-source.mjs",
|
|
343
346
|
"pretest": "node scripts/prepare-product-tests.mjs",
|
|
344
347
|
"test": "node scripts/run-tests.mjs",
|
|
345
|
-
"
|
|
348
|
+
"verify:independent": "node scripts/verify-bot-independent.mjs",
|
|
349
|
+
"verify:independent-chat": "node scripts/verify-independent-chat-browser.mjs && node scripts/verify-independent-chat-rich-browser.mjs && node scripts/verify-independent-chat-native-browser.mjs",
|
|
350
|
+
"prepack": "node scripts/build-openmaus-protocol.mjs && node remote-computer-agent/build.mjs && node scripts/prepack.mjs",
|
|
351
|
+
"prepare:native": "node scripts/prepare-native-driver.mjs",
|
|
352
|
+
"check:github-math": "node scripts/check-github-math.mjs"
|
|
346
353
|
},
|
|
347
354
|
"license": "MIT",
|
|
348
355
|
"engines": {
|
|
@@ -354,12 +361,12 @@
|
|
|
354
361
|
"@deepseek-ai/dsh-commands": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3",
|
|
355
362
|
"@deepseek-ai/dsh-llm": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3",
|
|
356
363
|
"@deepseek-ai/dsh-sandbox": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3",
|
|
364
|
+
"@deepseek-ai/dsh-scope": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3",
|
|
357
365
|
"@deepseek-ai/dsh-settings": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3",
|
|
358
366
|
"@deepseek-ai/dsh-tools": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3",
|
|
359
367
|
"@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3",
|
|
360
368
|
"@deepseek-ai/dsh-web": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3",
|
|
361
|
-
"@deepseek-ai/schemastery": "~3.18.4"
|
|
362
|
-
"@deepseek-ai/dsh-scope": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-rc.1 || 0.2.2-rc.2 || 0.2.2-rc.3"
|
|
369
|
+
"@deepseek-ai/schemastery": "~3.18.4"
|
|
363
370
|
},
|
|
364
371
|
"optionalDependencies": {
|
|
365
372
|
"@lydell/node-pty": "1.2.0-beta.15",
|
|
@@ -373,6 +380,7 @@
|
|
|
373
380
|
"@deepseek-ai/cordis-plugin-timer": "1.1.7",
|
|
374
381
|
"@deepseek-ai/dsh-agent": "0.2.1-rc.3 || 0.2.1-rc.4",
|
|
375
382
|
"@deepseek-ai/dsh-agent-default-model": "0.2.1-rc.3 || 0.2.1-rc.4",
|
|
383
|
+
"@deepseek-ai/dsh-agent-loop": "0.2.1-rc.4",
|
|
376
384
|
"@deepseek-ai/dsh-agent-preset-registry": "0.2.1-rc.3 || 0.2.1-rc.4",
|
|
377
385
|
"@deepseek-ai/dsh-api-gateway": "0.2.1-rc.3 || 0.2.1-rc.4",
|
|
378
386
|
"@deepseek-ai/dsh-api-remotes": "0.2.1-rc.3 || 0.2.1-rc.4",
|
|
@@ -437,6 +445,12 @@
|
|
|
437
445
|
"dependencies": {
|
|
438
446
|
"@modelcontextprotocol/server": "2.4.0",
|
|
439
447
|
"better-sqlite3": "13.0.3",
|
|
440
|
-
"deep-view": "0.1.0"
|
|
448
|
+
"deep-view": "0.1.0",
|
|
449
|
+
"jsqr": "1.4.0",
|
|
450
|
+
"qrcode": "1.5.4",
|
|
451
|
+
"ws": "8.22.1"
|
|
452
|
+
},
|
|
453
|
+
"overrides": {
|
|
454
|
+
"zod": "4.6.6"
|
|
441
455
|
}
|
|
442
456
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# packages/bot-acp
|
|
2
|
+
|
|
3
|
+
The generic ACP (Agent Client Protocol) session core for the Bot runtime: a strictly
|
|
4
|
+
bounded JSON-RPC 2.0 stdio transport, multi-turn session management, fail-closed
|
|
5
|
+
permissions, and a Host-bound adapter the product composition can wire.
|
|
6
|
+
|
|
7
|
+
Ported and re-grounded from OpenMausBot `server/drivers/acp/*` at pin
|
|
8
|
+
`1bc4749aa8307d7d6998bf86de4ef6478460dd93` (Apache-2.0); see `licenses/NOTICE.md`
|
|
9
|
+
for the exact upstream paths and the preserved license text.
|
|
10
|
+
|
|
11
|
+
> **Status.** This module is a core, not a product feature. It is **not wired into
|
|
12
|
+
> product composition**, and no ACP CLI is claimed to be verified end-to-end here.
|
|
13
|
+
> Presence in the tree is not availability: the parent composition still has to
|
|
14
|
+
> register a pinned entry, a grant scope and the funding/ledger hooks.
|
|
15
|
+
|
|
16
|
+
## Modules
|
|
17
|
+
|
|
18
|
+
| File | Purpose |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `src/rpc-codec.mjs` | Pure codec: newline framing with UTF-8 reassembly and a hard frame cap, message classification, id validation, wire-error mapping, client/agent capability schema |
|
|
21
|
+
| `src/stdio-rpc.mjs` | Bounded transport: `createChildPipe` (real stdio) and `createStdioRpc` (id-mapped requests, notifications, responses, pending/deadline/idle/backlog limits, duplicate/unknown/late response accounting, accurate pending rejection on real process exit) |
|
|
22
|
+
| `src/acp-host.mjs` | One agent process and the sessions in it: sessionId routing, single-flight `initialize`/`authenticate`, session/lifetime/draining bounds, fail-closed unrouted handler |
|
|
23
|
+
| `src/session-core.mjs` | Multi-turn session: handshake, `session/new`, capability-gated `session/load`, serial turns with explicit busy, text-only prompts, `session/update` merging, stopReason classification, advertised-only model/config/mode control, an `onDispatched` dispatch marker and `acpNotDispatched`-tagged pre-dispatch refusals |
|
|
24
|
+
| `src/permission-policy.mjs` | `session/request_permission` decided by committed Tool/Task/operation authority; every unknown, duplicate, expired, revoked, timed-out or closing case denies. `createClientMethodGuard` refuses `fs/*` and `terminal/*` unless the Host enables them with a submitted root and IO callbacks |
|
|
25
|
+
| `src/native-launch.mjs` | `pinAcpEntry` (absolute path + digest + real version proof), `createSupervisorPipe` (native supervisor stdio relay), `launchAcpProcess` — all reusing `packages/cli-runtime/src/native-process.mjs` |
|
|
26
|
+
| `src/acp-adapter.mjs` | `createAcpAdapter` — the public surface: identity, Host hooks, the one-slot turn fence, settlement ordering, cancellation, usage/cost evidence, stop evidence |
|
|
27
|
+
## Public API
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import { createAcpAdapter } from './packages/bot-acp/src/acp-adapter.mjs'
|
|
31
|
+
|
|
32
|
+
const adapter = await createAcpAdapter({
|
|
33
|
+
authority, // capability-broker authority: { describe, assertModel, assertTool }
|
|
34
|
+
subject, // registered authority subject
|
|
35
|
+
binding, // { taskId, attemptId, machineId, workspaceId, location }
|
|
36
|
+
operationId, // durable operation identity
|
|
37
|
+
entry, // { executable, sha256, version, script?, scriptSha256? } — Host-pinned
|
|
38
|
+
spawnArgs, // Host-constructed argv after the executable (e.g. ['acp'])
|
|
39
|
+
runtimeRoot, // isolated absolute runtime root (private HOME)
|
|
40
|
+
workspace, // agent cwd
|
|
41
|
+
model, // { catalogProvider, catalogModel }
|
|
42
|
+
signal, // AbortSignal
|
|
43
|
+
hooks, // { onBeforeRequest, onObserve, onSettle, onClose } — the only ledger recipients
|
|
44
|
+
onPermissionRequest, // Host permission callback; absent denies every ask
|
|
45
|
+
operationTool, // (identity) => committed tool name | null
|
|
46
|
+
resumeSessionId, // only honoured when the agent advertises loadSession
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
await adapter.start() // initialize → authenticate → session/new (or load)
|
|
50
|
+
const turn = await adapter.submitTurn({ text }) // text only
|
|
51
|
+
adapter.cancel() // a request, never a stop confirmation
|
|
52
|
+
await adapter.stop('reason') // real process stop + evidence
|
|
53
|
+
adapter.snapshot() // evidence snapshot
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
A turn result carries `{ ok, outcome, stopReason, sessionId, text, usage, costUsd,
|
|
57
|
+
costStatus, elapsedMs, cancelRequestedAt, updates, errorText, dispatched }`.
|
|
58
|
+
|
|
59
|
+
`outcome` is one of `succeeded`, `cancelled`, `failed`, `not-run` or `unknown`.
|
|
60
|
+
`not-run` means the prompt provably never reached the agent (a refused admission, a
|
|
61
|
+
failed launch or session handshake, a pre-dispatch refusal, a cancelled
|
|
62
|
+
reservation); `unknown` means the prompt was issued and the agent may have acted
|
|
63
|
+
on it — an unknown outcome is reported verbatim and is never replayed
|
|
64
|
+
automatically. `dispatched` records whether the prompt was really issued, and
|
|
65
|
+
`snapshot().turnsDispatched` counts dispatches, never completions.
|
|
66
|
+
|
|
67
|
+
`submitTurn` rejects only for a request that never became a turn (a malformed
|
|
68
|
+
payload, a Host admission refusal, a denied grant). Once a turn exists it always
|
|
69
|
+
ends in exactly one settlement, delivered to `hooks.onSettle` before the returned
|
|
70
|
+
promise resolves.
|
|
71
|
+
|
|
72
|
+
## Policy invariants
|
|
73
|
+
|
|
74
|
+
- **Authority is Host state.** Model routes, tools, executable paths, digests,
|
|
75
|
+
environment and credentials never arrive through a turn payload: `submitTurn`
|
|
76
|
+
accepts `{ text }` and nothing else. A driver request naming an executable,
|
|
77
|
+
endpoint or credential is an error (`HOST_OWNED_ENGINE_INPUT` upstream, and the
|
|
78
|
+
same discipline here). Request JSON that claims a `botId`, `owner` or `config`
|
|
79
|
+
is never authority.
|
|
80
|
+
- **One turn slot, reserved before the first await.** `submitTurn` takes its slot
|
|
81
|
+
synchronously, so a second call in the same tick is refused with
|
|
82
|
+
`ACP_TURN_IN_PROGRESS` and performs no admission, no launch and no prompt.
|
|
83
|
+
Admission, session start, dispatch, the peer's result and the Host settlement
|
|
84
|
+
all belong to that one slot, and the Host grant is re-judged after every
|
|
85
|
+
asynchronous boundary. A turn settled underneath a pending call (cancel, stop,
|
|
86
|
+
revocation, peer exit) is never resumed: the late continuation refuses with
|
|
87
|
+
`ACP_TURN_NOT_DISPATCHED`.
|
|
88
|
+
- **Settlement is durable before it is public.** `hooks.onSettle` is the only
|
|
89
|
+
settlement path, and it runs before the success is published and before the
|
|
90
|
+
slot is released; a slow hook keeps the turn observably pending
|
|
91
|
+
(`busy === true`, `snapshot().settlement.inFlight`). A hook that refuses
|
|
92
|
+
refuses the success: `submitTurn` rejects with `ACP_SETTLEMENT_FAILED` carrying
|
|
93
|
+
the original native result and its unknown funding evidence, and the adapter
|
|
94
|
+
retires instead of letting a new request run on an unrecorded turn. Nothing is
|
|
95
|
+
caught and swallowed into a success, no cost or usage figure is invented, and
|
|
96
|
+
no second ledger is opened.
|
|
97
|
+
- **Not-run and unknown are different answers.** A refused admission releases the
|
|
98
|
+
slot so the caller may call again and records no settlement. After admission,
|
|
99
|
+
a failure before the prompt is issued settles once as `not-run`; a prompt whose
|
|
100
|
+
write may have reached the agent settles as `unknown`. `turnsDispatched` counts
|
|
101
|
+
the dispatch moment, not completions.
|
|
102
|
+
- **Session and model identity are fenced by a pending turn.** `start`,
|
|
103
|
+
`startFresh`, `selectModel` and `setMode` refuse while a turn is pending. The
|
|
104
|
+
model a turn runs under is the Host-committed `catalogModel`: a peer advertising
|
|
105
|
+
another model is not authority, and selecting one anyway would make the turn
|
|
106
|
+
evidence and the funded grant disagree. A different binding needs a rebuilt,
|
|
107
|
+
re-authorized adapter (`ACP_MODEL_BINDING_CHANGED`), never owner or permission
|
|
108
|
+
data smuggled through request JSON.
|
|
109
|
+
- **Stop and cancel work at every stage.** `stop` settles a pending turn first
|
|
110
|
+
(not-run before dispatch, unknown afterwards), awaits a launch that raced it so
|
|
111
|
+
no late spawn outlives the stop, really stops the native tree, and only then
|
|
112
|
+
waits for in-flight settlements before reporting `onClose`. `cancel` during a
|
|
113
|
+
pending admission settles the reservation as not-run; after dispatch it sends
|
|
114
|
+
`session/cancel`, and an unanswered cancel ends in a real process stop. Only the
|
|
115
|
+
launcher's own accounting (Windows job object) confirms a tree stop; a cancel
|
|
116
|
+
request is never a stop confirmation, and a stop without one is reported
|
|
117
|
+
`unconfirmed`, not `confirmed`.- **Fail-closed permissions.** Only an `allow`-kind option the agent itself offered
|
|
118
|
+
can approve, and only after the Host handler answers against a committed grant.
|
|
119
|
+
Missing handler, unknown option, duplicate ask, timeout, close, revocation and
|
|
120
|
+
abort all deny. Session-scoped "allow always" is deliberately not implemented:
|
|
121
|
+
every ask is decided by the Host.
|
|
122
|
+
- **Client file and terminal access is off by default.** `fs/read_text_file`,
|
|
123
|
+
`fs/write_text_file` and `terminal/*` are advertised `false` and refused with
|
|
124
|
+
method-not-found. Enabling them requires a Host-submitted root plus Host IO
|
|
125
|
+
callbacks; a hand-written instruction in a prompt is never the boundary.
|
|
126
|
+
- **Unknown is not success.** An unknown `stopReason` is reported verbatim;
|
|
127
|
+
`end_turn` with no output is a lost turn; a refused resume never opens a new
|
|
128
|
+
session and replays the old turn.
|
|
129
|
+
- **Cancel is a request, not a confirmation.** `session/cancel` and the process
|
|
130
|
+
tree-stop are recorded separately; only the native supervisor's own accounting
|
|
131
|
+
(Windows job object) confirms a tree stop.
|
|
132
|
+
- **No second ledger.** ACP reports token/context usage and no dollar figure, so
|
|
133
|
+
`costUsd` stays `null` and `costStatus` stays `unknown`; evidence flows to the
|
|
134
|
+
Host hooks only.
|
|
135
|
+
- **`onObserve` is diagnostic, `onSettle` is the record.** `onObserve` reports
|
|
136
|
+
that a session exists; it is never a settlement and never carries money. Only
|
|
137
|
+
`onSettle` records a turn, and only `onClose` reports the stop evidence.
|
|
138
|
+
|
|
139
|
+
## Tests
|
|
140
|
+
|
|
141
|
+
| File | Runs |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `tests/bot-acp-codec.test.mjs` | fast, offline: framing, UTF-8 splits, oversize, malformed, duplicate/unknown ids, exit rejection, real-child pipe |
|
|
144
|
+
| `tests/bot-acp-permissions.test.mjs` | fast, offline: every deny path, client-method guard, path containment |
|
|
145
|
+
| `tests/bot-acp-session-state.test.mjs` | fast, in-process: session lifecycle, turn rules, model/mode gating, permission routing |
|
|
146
|
+
| `tests/bot-acp-turn-fence.test.mjs` | fast, in-process: the turn fence — same-tick submits, slow/refused admission, slow/refused settlement, stop during a launch, pin/launch/dispatch failures, late and retired-process traffic, cancel/revocation at every stage, identity fences — against a controlled launcher seam |
|
|
147
|
+
| `tests/bot-acp-session.native.mjs` | **serial** (`node --test --test-concurrency=1`): real offline child through the real native supervisor, including the turn-fence suite |
|
|
148
|
+
|
|
149
|
+
`scripts/verify-bot-acp.mjs` runs all five in an isolated `tmp/integration/bot-acp/`
|
|
150
|
+
directory and writes `report.json`. No agent CLI, network, user profile, credential
|
|
151
|
+
or desktop is touched.
|
|
152
|
+
|
|
153
|
+
The seam suite proves the *timing* of the fence deterministically; the native
|
|
154
|
+
suite proves the same rules against a real offline Node child and a real native
|
|
155
|
+
supervisor, which is the only real-machine evidence. Neither is a substitute for
|
|
156
|
+
the other.
|