@mono-agent/agent-runtime 0.20.11 → 0.21.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.
Files changed (148) hide show
  1. package/ARCHITECTURE.md +50 -11
  2. package/MIGRATION.md +288 -26
  3. package/README.md +352 -477
  4. package/package.json +13 -44
  5. package/src/agent/tool-bloat.js +145 -9
  6. package/src/agent/tools/agent-tool.js +108 -9
  7. package/src/agent/tools/bash.js +11 -26
  8. package/src/agent/tools/codex-subscription-search.js +123 -29
  9. package/src/agent/tools/exec.js +10 -2
  10. package/src/agent/tools/index.js +7 -0
  11. package/src/agent/tools/monitor.js +149 -0
  12. package/src/agent/tools/pi-bridge.js +123 -19
  13. package/src/agent/tools/shared/bash-environment.js +31 -0
  14. package/src/agent/tools/shared/monitors.js +293 -0
  15. package/src/agent/tools/shared/path-resolver.js +25 -6
  16. package/src/agent/tools/shared/process-jobs.js +6 -1
  17. package/src/agent/tools/shared/process-runner.js +26 -6
  18. package/src/agent/tools/shared/tool-context.js +8 -0
  19. package/src/agent/tools/web-access-interstitial.js +70 -0
  20. package/src/agent/tools/web-browser-render.js +83 -58
  21. package/src/agent/tools/web-controller.js +112 -21
  22. package/src/agent/tools/web-document-extractor.js +379 -0
  23. package/src/agent/tools/web-fetch.js +271 -243
  24. package/src/agent/tools/web-request.js +65 -0
  25. package/src/agent/tools/web-search-output.js +165 -0
  26. package/src/agent/tools/web-search-state.js +75 -0
  27. package/src/agent/tools/web-search.js +532 -71
  28. package/src/ai/cost.js +13 -68
  29. package/src/ai/failure.js +3 -3
  30. package/src/ai/index.js +5 -17
  31. package/src/ai/observer.js +8 -0
  32. package/src/ai/pi-interop.js +221 -1
  33. package/src/ai/pi-oauth-compat.js +1 -1
  34. package/src/ai/provider-check.js +131 -0
  35. package/src/ai/providers/codex/app-server-client.js +592 -0
  36. package/src/ai/providers/pi-models.js +18 -10
  37. package/src/ai/providers/pi-native/compaction-driver.js +94 -42
  38. package/src/ai/providers/pi-native/compaction-summary.js +140 -0
  39. package/src/ai/providers/pi-native/harness-adapter.js +376 -0
  40. package/src/ai/providers/pi-native/prompt-cache-diagnostics.js +103 -0
  41. package/src/ai/providers/pi-native/provider-attribution.js +102 -0
  42. package/src/ai/providers/pi-native/result-builder.js +38 -14
  43. package/src/ai/providers/pi-native/session-lifecycle.js +253 -55
  44. package/src/ai/providers/pi-native/stream-subscriber.js +52 -6
  45. package/src/ai/providers/pi-native/terminal-recovery.js +40 -0
  46. package/src/ai/providers/pi-native/turn-runner.js +279 -28
  47. package/src/ai/providers/pi-native.js +206 -61
  48. package/src/ai/runtime/capabilities.js +11 -56
  49. package/src/ai/runtime/live-input-events.js +250 -54
  50. package/src/ai/runtime/model-refs.js +118 -153
  51. package/src/ai/runtime/registry.js +22 -56
  52. package/src/ai/runtime/router.js +76 -417
  53. package/src/ai/runtime/session-liveness.js +3 -4
  54. package/src/ai/runtime/sessions.js +4 -5
  55. package/src/ai/runtime/tool-policy.js +0 -2
  56. package/src/ai/tool-lifecycle.js +32 -18
  57. package/src/ai/types.js +37 -112
  58. package/src/index.js +0 -6
  59. package/src/runtime.js +29 -16
  60. package/types/agent/tool-bloat.d.ts +1 -1
  61. package/types/agent/tools/agent-tool.d.ts +4 -2
  62. package/types/agent/tools/bash.d.ts +5 -3
  63. package/types/agent/tools/codex-subscription-search.d.ts +7 -3
  64. package/types/agent/tools/exec.d.ts +5 -3
  65. package/types/agent/tools/index.d.ts +1 -0
  66. package/types/agent/tools/monitor.d.ts +47 -0
  67. package/types/agent/tools/pi-bridge.d.ts +7 -4
  68. package/types/agent/tools/shared/bash-environment.d.ts +4 -0
  69. package/types/agent/tools/shared/monitors.d.ts +98 -0
  70. package/types/agent/tools/shared/process-jobs.d.ts +5 -1
  71. package/types/agent/tools/shared/process-runner.d.ts +14 -4
  72. package/types/agent/tools/shared/tool-context.d.ts +2 -0
  73. package/types/agent/tools/web-access-interstitial.d.ts +23 -0
  74. package/types/agent/tools/web-browser-render.d.ts +4 -1
  75. package/types/agent/tools/web-controller.d.ts +4 -2
  76. package/types/agent/tools/web-document-extractor.d.ts +27 -0
  77. package/types/agent/tools/web-fetch.d.ts +19 -24
  78. package/types/agent/tools/web-request.d.ts +20 -0
  79. package/types/agent/tools/web-search-output.d.ts +31 -0
  80. package/types/agent/tools/web-search-state.d.ts +21 -0
  81. package/types/agent/tools/web-search.d.ts +10 -45
  82. package/types/ai/cost.d.ts +1 -2
  83. package/types/ai/index.d.ts +2 -4
  84. package/types/ai/observer.d.ts +6 -0
  85. package/types/ai/pi-interop.d.ts +81 -0
  86. package/types/ai/provider-check.d.ts +53 -0
  87. package/types/ai/providers/codex/app-server-client.d.ts +37 -0
  88. package/types/ai/providers/pi-native/compaction-driver.d.ts +2 -1
  89. package/types/ai/providers/pi-native/compaction-summary.d.ts +19 -0
  90. package/types/ai/providers/pi-native/harness-adapter.d.ts +58 -0
  91. package/types/ai/providers/pi-native/prompt-cache-diagnostics.d.ts +3 -0
  92. package/types/ai/providers/pi-native/provider-attribution.d.ts +26 -0
  93. package/types/ai/providers/pi-native/result-builder.d.ts +14 -4
  94. package/types/ai/providers/pi-native/session-lifecycle.d.ts +25 -6
  95. package/types/ai/providers/pi-native/stream-subscriber.d.ts +2 -2
  96. package/types/ai/providers/pi-native/terminal-recovery.d.ts +2 -0
  97. package/types/ai/providers/pi-native/turn-runner.d.ts +68 -10
  98. package/types/ai/providers/pi-native.d.ts +21 -4
  99. package/types/ai/runtime/capabilities.d.ts +21 -70
  100. package/types/ai/runtime/live-input-events.d.ts +32 -8
  101. package/types/ai/runtime/model-refs.d.ts +0 -24
  102. package/types/ai/runtime/router.d.ts +3 -10
  103. package/types/ai/runtime/tool-policy.d.ts +0 -2
  104. package/types/ai/tool-lifecycle.d.ts +4 -3
  105. package/types/ai/types.d.ts +162 -256
  106. package/types/index.d.ts +0 -1
  107. package/src/ai/providers/acp-client.js +0 -1149
  108. package/src/ai/providers/acp-privacy.js +0 -124
  109. package/src/ai/providers/acp-public.js +0 -21
  110. package/src/ai/providers/acp-session-tokens.js +0 -282
  111. package/src/ai/providers/acp-transport.js +0 -356
  112. package/src/ai/providers/acp.js +0 -543
  113. package/src/ai/providers/claude-cli.js +0 -883
  114. package/src/ai/providers/claude-sandbox.js +0 -71
  115. package/src/ai/providers/claude-sdk-discovery-worker.js +0 -53
  116. package/src/ai/providers/claude-sdk-discovery.js +0 -352
  117. package/src/ai/providers/claude-sdk.js +0 -1127
  118. package/src/ai/providers/claude-subagent-activity.js +0 -719
  119. package/src/ai/providers/claude-subagents.js +0 -88
  120. package/src/ai/providers/codex-app.js +0 -2946
  121. package/src/ai/providers/opencode-app.js +0 -1109
  122. package/src/ai/providers/opencode-discovery.js +0 -39
  123. package/src/ai/providers/opencode-server.js +0 -508
  124. package/src/ai/runtime/context-windows.js +0 -46
  125. package/src/ai/runtime/fast-mode.js +0 -8
  126. package/src/ai/streaming/codex-events.js +0 -146
  127. package/src/ai/streaming/opencode-events.js +0 -59
  128. package/types/ai/providers/acp-client.d.ts +0 -227
  129. package/types/ai/providers/acp-privacy.d.ts +0 -25
  130. package/types/ai/providers/acp-public.d.ts +0 -7
  131. package/types/ai/providers/acp-session-tokens.d.ts +0 -41
  132. package/types/ai/providers/acp-transport.d.ts +0 -45
  133. package/types/ai/providers/acp.d.ts +0 -93
  134. package/types/ai/providers/claude-cli.d.ts +0 -305
  135. package/types/ai/providers/claude-sandbox.d.ts +0 -79
  136. package/types/ai/providers/claude-sdk-discovery-worker.d.ts +0 -1
  137. package/types/ai/providers/claude-sdk-discovery.d.ts +0 -97
  138. package/types/ai/providers/claude-sdk.d.ts +0 -138
  139. package/types/ai/providers/claude-subagent-activity.d.ts +0 -53
  140. package/types/ai/providers/claude-subagents.d.ts +0 -18
  141. package/types/ai/providers/codex-app.d.ts +0 -151
  142. package/types/ai/providers/opencode-app.d.ts +0 -96
  143. package/types/ai/providers/opencode-discovery.d.ts +0 -4
  144. package/types/ai/providers/opencode-server.d.ts +0 -20
  145. package/types/ai/runtime/context-windows.d.ts +0 -9
  146. package/types/ai/runtime/fast-mode.d.ts +0 -2
  147. package/types/ai/streaming/codex-events.d.ts +0 -40
  148. package/types/ai/streaming/opencode-events.d.ts +0 -42
@@ -1,124 +0,0 @@
1
- // @ts-check
2
-
3
- const PRIVATE_PROTOCOL_KEYS = new Set(["_meta", "sessionId", "session_id"]);
4
- const REDACTED = "[redacted]";
5
- const MAX_HOST_VALUE_DEPTH = 32;
6
- const MAX_HOST_VALUE_NODES = 4_096;
7
- // ACP v1's SessionUpdate union is closed. Never carry an arbitrary peer
8
- // discriminator onto a host-facing event, even when the SDK boundary is
9
- // bypassed or becomes more permissive.
10
- const ACP_SESSION_UPDATE_KINDS = new Set([
11
- "user_message_chunk",
12
- "agent_message_chunk",
13
- "agent_thought_chunk",
14
- "tool_call",
15
- "tool_call_update",
16
- "plan",
17
- "plan_update",
18
- "plan_removed",
19
- "available_commands_update",
20
- "current_mode_update",
21
- "config_option_update",
22
- "session_info_update",
23
- "usage_update",
24
- ]);
25
-
26
- /**
27
- * ACP protocol session ids are connection state, not host-facing metadata.
28
- * Remove their canonical keys, discard extension metadata, and redact copies
29
- * embedded by an agent in otherwise public strings.
30
- *
31
- * @param {unknown} value
32
- * @param {ReadonlyArray<unknown>} [rawSecrets]
33
- * @returns {{value: any, truncated: boolean}}
34
- */
35
- export function sanitizeAcpHostValueWithStatus(value, rawSecrets = []) {
36
- const secrets = rawSecrets
37
- .filter((secret) => typeof secret === "string" && secret.length > 0)
38
- .sort((left, right) => /** @type {string} */ (right).length - /** @type {string} */ (left).length);
39
-
40
- /** @param {string} text */
41
- const redact = (text) => {
42
- let result = text;
43
- for (const secret of secrets) {
44
- result = result.replaceAll(/** @type {string} */ (secret), REDACTED);
45
- }
46
- return result;
47
- };
48
-
49
- let nodes = 0;
50
- let truncated = false;
51
-
52
- /** @param {unknown} item @param {WeakSet<object>} ancestors @param {number} depth @returns {any} */
53
- const visit = (item, ancestors, depth) => {
54
- nodes += 1;
55
- if (nodes > MAX_HOST_VALUE_NODES || depth > MAX_HOST_VALUE_DEPTH) {
56
- truncated = true;
57
- return null;
58
- }
59
- if (typeof item === "string") return redact(item);
60
- if (Array.isArray(item)) {
61
- if (ancestors.has(item)) {
62
- truncated = true;
63
- return null;
64
- }
65
- ancestors.add(item);
66
- const result = [];
67
- for (const entry of item) {
68
- if (nodes >= MAX_HOST_VALUE_NODES) {
69
- truncated = true;
70
- break;
71
- }
72
- result.push(visit(entry, ancestors, depth + 1));
73
- }
74
- ancestors.delete(item);
75
- return result;
76
- }
77
- if (!item || typeof item !== "object") return item;
78
- if (ancestors.has(item)) {
79
- truncated = true;
80
- return null;
81
- }
82
- ancestors.add(item);
83
- /** @type {Record<string, unknown>} */
84
- const result = Object.create(null);
85
- for (const [key, entry] of Object.entries(item)) {
86
- if (nodes >= MAX_HOST_VALUE_NODES) {
87
- truncated = true;
88
- break;
89
- }
90
- if (PRIVATE_PROTOCOL_KEYS.has(key)) continue;
91
- Object.defineProperty(result, redact(key), {
92
- value: visit(entry, ancestors, depth + 1),
93
- enumerable: true,
94
- configurable: true,
95
- writable: true,
96
- });
97
- }
98
- ancestors.delete(item);
99
- return result;
100
- };
101
-
102
- return { value: visit(value, new WeakSet(), 0), truncated };
103
- }
104
-
105
- /**
106
- * Compatibility wrapper for callback/list surfaces that intentionally accept
107
- * a bounded representation. Protocol normalization should use the status form
108
- * above so it can fail explicitly instead of consuming partial data.
109
- *
110
- * @param {unknown} value
111
- * @param {ReadonlyArray<unknown>} [rawSecrets]
112
- * @returns {any}
113
- */
114
- export function sanitizeAcpHostValue(value, rawSecrets = []) {
115
- return sanitizeAcpHostValueWithStatus(value, rawSecrets).value;
116
- }
117
-
118
- /** @param {unknown} value @returns {string|null} */
119
- export function ownAcpSessionUpdateKind(value) {
120
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
121
- if (!Object.hasOwn(value, "sessionUpdate")) return null;
122
- const kind = /** @type {{sessionUpdate?: unknown}} */ (value).sessionUpdate;
123
- return typeof kind === "string" && ACP_SESSION_UPDATE_KINDS.has(kind) ? kind : null;
124
- }
@@ -1,21 +0,0 @@
1
- // Public high-level ACP surface. The owned stdio connection and raw protocol
2
- // session state stay in acp-client.js and are intentionally not package exports.
3
-
4
- /** @typedef {import("./acp-client.js").AcpProfileDescriptor} AcpProfileDescriptor */
5
- /** @typedef {import("./acp-client.js").AcpCallbackContext} AcpCallbackContext */
6
- /** @typedef {import("./acp-client.js").AcpInteractionRequest} AcpInteractionRequest */
7
- /** @typedef {import("./acp-client.js").AcpClientHostOptions} AcpClientHostOptions */
8
- /** @typedef {import("./acp-client.js").AcpListedSession} AcpListedSession */
9
- /** @typedef {import("./acp-client.js").AcpSessionListResult} AcpSessionListResult */
10
-
11
- export {
12
- ACP_PROTOCOL_VERSION,
13
- AcpClientError,
14
- authenticateAcpProfile,
15
- deleteAcpSession,
16
- listAcpSessions,
17
- logoutAcpProfile,
18
- probeAcpProfile,
19
- validateAcpProfileId,
20
- validateAcpProviderSessionId,
21
- } from "./acp-client.js";
@@ -1,282 +0,0 @@
1
- // @ts-check
2
-
3
- import {
4
- createCipheriv,
5
- createDecipheriv,
6
- hkdfSync,
7
- randomBytes,
8
- } from "node:crypto";
9
-
10
- const PROFILE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
11
- const BASE64URL_RE = /^[A-Za-z0-9_-]+$/;
12
- const MAX_PROFILE_ID_LENGTH = 128;
13
- const MAX_RAW_TOKEN_BYTES = 4_096;
14
- const TOKEN_KEY_BYTES = 32;
15
- const NONCE_BYTES = 12;
16
- const AUTH_TAG_BYTES = 16;
17
- const MAX_SEALED_TOKEN_BYTES = NONCE_BYTES + MAX_RAW_TOKEN_BYTES + AUTH_TAG_BYTES;
18
- const MAX_ENCODED_TOKEN_LENGTH = Math.ceil(MAX_SEALED_TOKEN_BYTES * 4 / 3);
19
- const PROVIDER_SESSION_PREFIX = "acp:v2:";
20
- const SESSION_CURSOR_PREFIX = "acp-cursor:v2:";
21
- const MAX_PROVIDER_SESSION_ID_LENGTH = PROVIDER_SESSION_PREFIX.length
22
- + MAX_PROFILE_ID_LENGTH + 1 + MAX_ENCODED_TOKEN_LENGTH;
23
- const MAX_SESSION_CURSOR_LENGTH = SESSION_CURSOR_PREFIX.length
24
- + MAX_PROFILE_ID_LENGTH + 1 + MAX_ENCODED_TOKEN_LENGTH;
25
- const TOKEN_DOMAIN = Buffer.from("mono-agent/acp-session-token", "utf8");
26
- const TOKEN_VERSION = Buffer.from("v2", "utf8");
27
- const HKDF_SALT = Buffer.from("mono-agent/acp-session-token/v2/hkdf", "utf8");
28
- const ENCRYPTION_INFO = Buffer.from("mono-agent/acp-session-token/v2/encryption", "utf8");
29
-
30
- export class AcpClientError extends Error {
31
- /**
32
- * @param {string} code
33
- * @param {string} message
34
- * @param {Record<string, unknown>} [details]
35
- */
36
- constructor(code, message, details = {}) {
37
- super(message);
38
- this.name = "AcpClientError";
39
- this.code = code;
40
- this.details = { ...details, code };
41
- }
42
- }
43
-
44
- /** @param {string} profileId @returns {string} */
45
- export function validateAcpProfileId(profileId) {
46
- if (typeof profileId !== "string" || !PROFILE_ID_RE.test(profileId)) {
47
- throw new AcpClientError(
48
- "invalid_profile_id",
49
- "ACP profile id must use 1-128 ASCII letters, digits, dots, underscores, or hyphens and start alphanumeric.",
50
- );
51
- }
52
- return profileId;
53
- }
54
-
55
- /** @param {unknown} value @param {string} code @param {string} label */
56
- function requiredTokenString(value, code, label) {
57
- if (typeof value !== "string" || value.trim() !== value || value.length === 0 || value.includes("\0")) {
58
- throw new AcpClientError(code, `${label} must be a non-empty trimmed string without NUL bytes.`);
59
- }
60
- return value;
61
- }
62
-
63
- /**
64
- * The token key is binary on purpose: accepting textual secrets here would
65
- * make encoding, truncation, and cross-host persistence ambiguous.
66
- * @param {unknown} value
67
- */
68
- function sessionTokenKey(value) {
69
- if (!(value instanceof Uint8Array) || value.byteLength !== TOKEN_KEY_BYTES) {
70
- throw new AcpClientError(
71
- "invalid_token_key",
72
- `ACP session token key must be exactly ${TOKEN_KEY_BYTES} bytes.`,
73
- );
74
- }
75
- return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
76
- }
77
-
78
- /** @param {unknown} key @returns {Buffer} */
79
- export function validateAcpSessionTokenKey(key) {
80
- return Buffer.from(sessionTokenKey(key));
81
- }
82
-
83
- /** @param {ReadonlyArray<Uint8Array>} parts */
84
- function frame(parts) {
85
- const size = parts.reduce((total, part) => total + 4 + part.byteLength, 0);
86
- const result = Buffer.allocUnsafe(size);
87
- let offset = 0;
88
- for (const part of parts) {
89
- result.writeUInt32BE(part.byteLength, offset);
90
- offset += 4;
91
- Buffer.from(part.buffer, part.byteOffset, part.byteLength).copy(result, offset);
92
- offset += part.byteLength;
93
- }
94
- return result;
95
- }
96
-
97
- /** @param {"session"|"cursor"} kind @param {string} profileId */
98
- function tokenAad(kind, profileId) {
99
- return frame([
100
- TOKEN_DOMAIN,
101
- TOKEN_VERSION,
102
- Buffer.from(kind, "utf8"),
103
- Buffer.from(profileId, "utf8"),
104
- ]);
105
- }
106
-
107
- /** @param {Uint8Array} key */
108
- function encryptionKey(key) {
109
- const rawKey = Buffer.from(sessionTokenKey(key));
110
- try {
111
- return Buffer.from(hkdfSync("sha256", rawKey, HKDF_SALT, ENCRYPTION_INFO, TOKEN_KEY_BYTES));
112
- } finally {
113
- rawKey.fill(0);
114
- }
115
- }
116
-
117
- /** @param {string} profileId @param {string} raw @param {string} code @param {string} label */
118
- function rawTokenBytes(profileId, raw, code, label) {
119
- validateAcpProfileId(profileId);
120
- requiredTokenString(raw, code, label);
121
- const plaintext = Buffer.from(raw, "utf8");
122
- if (plaintext.byteLength > MAX_RAW_TOKEN_BYTES) {
123
- throw new AcpClientError(code, `${label} exceeds ${MAX_RAW_TOKEN_BYTES} bytes.`);
124
- }
125
- return plaintext;
126
- }
127
-
128
- /**
129
- * @param {"session"|"cursor"} kind
130
- * @param {string} profileId
131
- * @param {string} raw
132
- * @param {Uint8Array} key
133
- * @param {string} code
134
- * @param {string} label
135
- */
136
- function sealToken(kind, profileId, raw, key, code, label) {
137
- const plaintext = rawTokenBytes(profileId, raw, code, label);
138
- const aad = tokenAad(kind, profileId);
139
- const nonce = randomBytes(NONCE_BYTES);
140
- const derivedKey = encryptionKey(key);
141
- let cipher;
142
- try {
143
- cipher = createCipheriv("aes-256-gcm", derivedKey, nonce);
144
- } finally {
145
- derivedKey.fill(0);
146
- }
147
- cipher.setAAD(aad);
148
- const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
149
- return Buffer.concat([nonce, ciphertext, cipher.getAuthTag()]).toString("base64url");
150
- }
151
-
152
- /** @param {string} profileId @param {string} code @param {string} label */
153
- function tokenProfileId(profileId, code, label) {
154
- try {
155
- return validateAcpProfileId(profileId);
156
- } catch {
157
- throw new AcpClientError(code, `Invalid ${label}.`);
158
- }
159
- }
160
-
161
- /**
162
- * @param {string} encoded
163
- * @param {string} code
164
- * @param {string} label
165
- */
166
- function sealedTokenBytes(encoded, code, label) {
167
- if (!BASE64URL_RE.test(encoded)) throw new AcpClientError(code, `Invalid ${label}.`);
168
- const sealed = Buffer.from(encoded, "base64url");
169
- if (sealed.toString("base64url") !== encoded
170
- || sealed.byteLength <= NONCE_BYTES + AUTH_TAG_BYTES
171
- || sealed.byteLength > MAX_SEALED_TOKEN_BYTES) {
172
- throw new AcpClientError(code, `Invalid ${label}.`);
173
- }
174
- return sealed;
175
- }
176
-
177
- /**
178
- * @param {"session"|"cursor"} kind
179
- * @param {string} profileId
180
- * @param {string} encoded
181
- * @param {Uint8Array} key
182
- * @param {string} code
183
- * @param {string} label
184
- */
185
- function openToken(kind, profileId, encoded, key, code, label) {
186
- const sealed = sealedTokenBytes(encoded, code, label);
187
- const aad = tokenAad(kind, profileId);
188
- const nonce = sealed.subarray(0, NONCE_BYTES);
189
- const ciphertext = sealed.subarray(NONCE_BYTES, -AUTH_TAG_BYTES);
190
- const authTag = sealed.subarray(-AUTH_TAG_BYTES);
191
- const derivedKey = encryptionKey(key);
192
- let decipher;
193
- try {
194
- decipher = createDecipheriv("aes-256-gcm", derivedKey, nonce);
195
- } finally {
196
- derivedKey.fill(0);
197
- }
198
- let plaintext;
199
- try {
200
- decipher.setAAD(aad);
201
- decipher.setAuthTag(authTag);
202
- plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
203
- } catch {
204
- throw new AcpClientError(code, `Invalid ${label}.`);
205
- }
206
- try {
207
- return requiredTokenString(
208
- new TextDecoder("utf-8", { fatal: true }).decode(plaintext),
209
- code,
210
- label,
211
- );
212
- } catch (error) {
213
- if (error instanceof AcpClientError) throw error;
214
- throw new AcpClientError(code, `Invalid ${label}.`);
215
- }
216
- }
217
-
218
- /** @param {string} profileId @param {string} sessionId @param {Uint8Array} key */
219
- export function encodeAcpProviderSessionId(profileId, sessionId, key) {
220
- const encoded = sealToken("session", profileId, sessionId, key, "invalid_session_id", "ACP session id");
221
- return `${PROVIDER_SESSION_PREFIX}${profileId}:${encoded}`;
222
- }
223
-
224
- /**
225
- * Internal protocol-state decoder. This module is not a package export.
226
- * @param {string} providerSessionId
227
- * @param {Uint8Array} key
228
- */
229
- export function decodeAcpProviderSessionId(providerSessionId, key) {
230
- if (typeof providerSessionId !== "string" || providerSessionId.length > MAX_PROVIDER_SESSION_ID_LENGTH) {
231
- throw new AcpClientError("invalid_session_id", "Invalid ACP provider session id.");
232
- }
233
- const match = /^acp:v2:([^:]+):([^:]+)$/.exec(providerSessionId);
234
- if (!match) throw new AcpClientError("invalid_session_id", "Invalid ACP provider session id.");
235
- const profileId = tokenProfileId(match[1], "invalid_session_id", "ACP provider session id");
236
- const sessionId = openToken(
237
- "session",
238
- profileId,
239
- match[2],
240
- key,
241
- "invalid_session_id",
242
- "ACP provider session id",
243
- );
244
- return { profileId, sessionId };
245
- }
246
-
247
- /**
248
- * Validate an opaque provider-session handle and its profile binding without
249
- * exposing the remote agent's protocol session id.
250
- *
251
- * @param {string} providerSessionId
252
- * @param {string} expectedProfileId
253
- * @param {Uint8Array} key Host-owned 32-byte ACP session-token key.
254
- * @returns {string}
255
- */
256
- export function validateAcpProviderSessionId(providerSessionId, expectedProfileId, key) {
257
- const profileId = validateAcpProfileId(expectedProfileId);
258
- const decoded = decodeAcpProviderSessionId(providerSessionId, key);
259
- if (decoded.profileId !== profileId) {
260
- throw new AcpClientError("invalid_session_id", "ACP provider session belongs to a different profile.");
261
- }
262
- return providerSessionId;
263
- }
264
-
265
- /** @param {string} profileId @param {string} cursor @param {Uint8Array} key */
266
- export function encodeAcpSessionCursor(profileId, cursor, key) {
267
- const encoded = sealToken("cursor", profileId, cursor, key, "invalid_cursor", "ACP session cursor");
268
- return `${SESSION_CURSOR_PREFIX}${profileId}:${encoded}`;
269
- }
270
-
271
- /** @param {string} profileId @param {unknown} cursor @param {Uint8Array} key */
272
- export function decodeAcpSessionCursor(profileId, cursor, key) {
273
- validateAcpProfileId(profileId);
274
- if (typeof cursor !== "string" || cursor.length > MAX_SESSION_CURSOR_LENGTH) {
275
- throw new AcpClientError("invalid_cursor", "ACP session cursor must be an opaque cursor returned by listAcpSessions.");
276
- }
277
- const match = /^acp-cursor:v2:([^:]+):([^:]+)$/.exec(cursor);
278
- if (!match || tokenProfileId(match[1], "invalid_cursor", "ACP session cursor") !== profileId) {
279
- throw new AcpClientError("invalid_cursor", "ACP session cursor is invalid for this profile.");
280
- }
281
- return openToken("cursor", profileId, match[2], key, "invalid_cursor", "ACP session cursor");
282
- }