@ai-workbench/remote-agent 0.8.2 → 0.8.10

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 (194) hide show
  1. package/README.md +42 -7
  2. package/dist/acp.d.ts +46 -1
  3. package/dist/acp.js +296 -36
  4. package/dist/approval-label.d.ts +6 -0
  5. package/dist/approval-label.js +57 -0
  6. package/dist/broker-runtime.d.ts +10 -0
  7. package/dist/broker-runtime.js +334 -0
  8. package/dist/broker.d.ts +29 -6
  9. package/dist/broker.js +146 -142
  10. package/dist/claude-local.d.ts +37 -0
  11. package/dist/claude-local.js +207 -0
  12. package/dist/client.js +71 -2
  13. package/dist/conversation-details.d.ts +1 -1
  14. package/dist/conversation-details.js +88 -8
  15. package/dist/employee-endpoint.d.ts +91 -0
  16. package/dist/employee-endpoint.js +141 -0
  17. package/dist/employee-exec.d.ts +53 -0
  18. package/dist/employee-exec.js +423 -0
  19. package/dist/employee-location.d.ts +66 -0
  20. package/dist/employee-location.js +51 -0
  21. package/dist/employee-mcp.d.ts +42 -0
  22. package/dist/employee-mcp.js +181 -0
  23. package/dist/employee-native.d.ts +18 -0
  24. package/dist/employee-native.js +38 -0
  25. package/dist/employee-provider.d.ts +42 -0
  26. package/dist/employee-provider.js +326 -0
  27. package/dist/employee-relay.d.ts +18 -0
  28. package/dist/employee-relay.js +49 -0
  29. package/dist/employee-shell.d.ts +26 -0
  30. package/dist/employee-shell.js +89 -0
  31. package/dist/employee-ssh.d.ts +22 -0
  32. package/dist/employee-ssh.js +123 -0
  33. package/dist/employee-tools.d.ts +117 -0
  34. package/dist/employee-tools.js +292 -0
  35. package/dist/employee-workspace.d.ts +26 -0
  36. package/dist/employee-workspace.js +61 -0
  37. package/dist/index.d.ts +31 -14
  38. package/dist/index.js +335 -42
  39. package/dist/input.d.ts +15 -0
  40. package/dist/input.js +65 -24
  41. package/dist/local-user-turn.d.ts +24 -0
  42. package/dist/local-user-turn.js +103 -0
  43. package/dist/model-catalog.d.ts +55 -0
  44. package/dist/model-catalog.js +254 -0
  45. package/dist/model-configuration.d.ts +51 -0
  46. package/dist/model-configuration.js +212 -0
  47. package/dist/presentation.d.ts +1 -1
  48. package/dist/presentation.js +50 -2
  49. package/dist/protocol-boundary.d.ts +13 -0
  50. package/dist/protocol-boundary.js +49 -0
  51. package/dist/provider.d.ts +54 -2
  52. package/dist/provider.js +92 -29
  53. package/dist/readiness.js +1 -1
  54. package/dist/recovery-checkpoint.d.ts +6 -0
  55. package/dist/recovery-checkpoint.js +71 -0
  56. package/dist/recovery.d.ts +47 -0
  57. package/dist/recovery.js +61 -0
  58. package/dist/remote-details.d.ts +10 -0
  59. package/dist/remote-details.js +14 -4
  60. package/dist/rpc-namespace.d.ts +3 -0
  61. package/dist/rpc-namespace.js +50 -0
  62. package/dist/stage-timing.d.ts +27 -0
  63. package/dist/stage-timing.js +39 -0
  64. package/helpers/ripgrep/LICENSE-vscode +13 -0
  65. package/helpers/ripgrep/PROVENANCE.md +9 -0
  66. package/helpers/ripgrep/darwin-arm64/rg +0 -0
  67. package/helpers/ripgrep/darwin-x64/rg +0 -0
  68. package/helpers/ripgrep/manifest.json +56 -0
  69. package/helpers/ripgrep/notices/COPYING-ripgrep +3 -0
  70. package/helpers/ripgrep/notices/DEPENDENCIES.json +946 -0
  71. package/helpers/ripgrep/notices/LICENCE-PCRE2.md +103 -0
  72. package/helpers/ripgrep/notices/LICENSE-MIT-ripgrep +21 -0
  73. package/helpers/ripgrep/notices/UNLICENSE-ripgrep +24 -0
  74. package/helpers/ripgrep/notices/aho-corasick-1.1.3/COPYING +3 -0
  75. package/helpers/ripgrep/notices/aho-corasick-1.1.3/LICENSE-MIT +21 -0
  76. package/helpers/ripgrep/notices/aho-corasick-1.1.3/UNLICENSE +24 -0
  77. package/helpers/ripgrep/notices/anyhow-1.0.100/LICENSE-APACHE +176 -0
  78. package/helpers/ripgrep/notices/anyhow-1.0.100/LICENSE-MIT +23 -0
  79. package/helpers/ripgrep/notices/arbitrary-1.4.2/LICENSE-APACHE +201 -0
  80. package/helpers/ripgrep/notices/arbitrary-1.4.2/LICENSE-MIT +27 -0
  81. package/helpers/ripgrep/notices/bstr-1.12.0/COPYING +8 -0
  82. package/helpers/ripgrep/notices/bstr-1.12.0/LICENSE-APACHE +201 -0
  83. package/helpers/ripgrep/notices/bstr-1.12.0/LICENSE-MIT +21 -0
  84. package/helpers/ripgrep/notices/cc-1.2.41/LICENSE-APACHE +201 -0
  85. package/helpers/ripgrep/notices/cc-1.2.41/LICENSE-MIT +25 -0
  86. package/helpers/ripgrep/notices/cfg-if-1.0.4/LICENSE-APACHE +201 -0
  87. package/helpers/ripgrep/notices/cfg-if-1.0.4/LICENSE-MIT +25 -0
  88. package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-APACHE +201 -0
  89. package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-MIT +27 -0
  90. package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-THIRD-PARTY +593 -0
  91. package/helpers/ripgrep/notices/crossbeam-deque-0.8.6/LICENSE-APACHE +201 -0
  92. package/helpers/ripgrep/notices/crossbeam-deque-0.8.6/LICENSE-MIT +27 -0
  93. package/helpers/ripgrep/notices/crossbeam-epoch-0.9.18/LICENSE-APACHE +201 -0
  94. package/helpers/ripgrep/notices/crossbeam-epoch-0.9.18/LICENSE-MIT +27 -0
  95. package/helpers/ripgrep/notices/crossbeam-utils-0.8.21/LICENSE-APACHE +201 -0
  96. package/helpers/ripgrep/notices/crossbeam-utils-0.8.21/LICENSE-MIT +27 -0
  97. package/helpers/ripgrep/notices/derive_arbitrary-1.4.2/LICENSE-APACHE +201 -0
  98. package/helpers/ripgrep/notices/derive_arbitrary-1.4.2/LICENSE-MIT +27 -0
  99. package/helpers/ripgrep/notices/encoding_rs-0.8.35/COPYRIGHT +17 -0
  100. package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-APACHE +202 -0
  101. package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-MIT +25 -0
  102. package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-WHATWG +26 -0
  103. package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/COPYING +8 -0
  104. package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/LICENSE-APACHE +201 -0
  105. package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/LICENSE-MIT +21 -0
  106. package/helpers/ripgrep/notices/find-msvc-tools-0.1.4/LICENSE-APACHE +201 -0
  107. package/helpers/ripgrep/notices/find-msvc-tools-0.1.4/LICENSE-MIT +25 -0
  108. package/helpers/ripgrep/notices/getrandom-0.3.4/LICENSE-APACHE +201 -0
  109. package/helpers/ripgrep/notices/getrandom-0.3.4/LICENSE-MIT +26 -0
  110. package/helpers/ripgrep/notices/glob-0.3.3/LICENSE-APACHE +201 -0
  111. package/helpers/ripgrep/notices/glob-0.3.3/LICENSE-MIT +25 -0
  112. package/helpers/ripgrep/notices/itoa-1.0.15/LICENSE-APACHE +176 -0
  113. package/helpers/ripgrep/notices/itoa-1.0.15/LICENSE-MIT +23 -0
  114. package/helpers/ripgrep/notices/jobserver-0.1.34/LICENSE-APACHE +201 -0
  115. package/helpers/ripgrep/notices/jobserver-0.1.34/LICENSE-MIT +25 -0
  116. package/helpers/ripgrep/notices/lexopt-0.3.1/LICENSE +19 -0
  117. package/helpers/ripgrep/notices/libc-0.2.177/LICENSE-APACHE +176 -0
  118. package/helpers/ripgrep/notices/libc-0.2.177/LICENSE-MIT +25 -0
  119. package/helpers/ripgrep/notices/log-0.4.28/LICENSE-APACHE +201 -0
  120. package/helpers/ripgrep/notices/log-0.4.28/LICENSE-MIT +25 -0
  121. package/helpers/ripgrep/notices/memchr-2.7.6/COPYING +3 -0
  122. package/helpers/ripgrep/notices/memchr-2.7.6/LICENSE-MIT +21 -0
  123. package/helpers/ripgrep/notices/memchr-2.7.6/UNLICENSE +24 -0
  124. package/helpers/ripgrep/notices/memmap2-0.9.8/LICENSE-APACHE +201 -0
  125. package/helpers/ripgrep/notices/memmap2-0.9.8/LICENSE-MIT +26 -0
  126. package/helpers/ripgrep/notices/msvc_spectre_libs-0.1.3/LICENSE +21 -0
  127. package/helpers/ripgrep/notices/pcre2-0.2.11/COPYING +3 -0
  128. package/helpers/ripgrep/notices/pcre2-0.2.11/LICENSE-MIT +21 -0
  129. package/helpers/ripgrep/notices/pcre2-0.2.11/UNLICENSE +24 -0
  130. package/helpers/ripgrep/notices/pcre2-sys-0.2.10/COPYING +3 -0
  131. package/helpers/ripgrep/notices/pcre2-sys-0.2.10/LICENSE-MIT +21 -0
  132. package/helpers/ripgrep/notices/pcre2-sys-0.2.10/UNLICENSE +24 -0
  133. package/helpers/ripgrep/notices/pkg-config-0.3.32/LICENSE-APACHE +201 -0
  134. package/helpers/ripgrep/notices/pkg-config-0.3.32/LICENSE-MIT +25 -0
  135. package/helpers/ripgrep/notices/proc-macro2-1.0.101/LICENSE-APACHE +176 -0
  136. package/helpers/ripgrep/notices/proc-macro2-1.0.101/LICENSE-MIT +23 -0
  137. package/helpers/ripgrep/notices/quote-1.0.41/LICENSE-APACHE +176 -0
  138. package/helpers/ripgrep/notices/quote-1.0.41/LICENSE-MIT +23 -0
  139. package/helpers/ripgrep/notices/r-efi-5.3.0/AUTHORS +74 -0
  140. package/helpers/ripgrep/notices/regex-1.12.2/LICENSE-APACHE +201 -0
  141. package/helpers/ripgrep/notices/regex-1.12.2/LICENSE-MIT +25 -0
  142. package/helpers/ripgrep/notices/regex-automata-0.4.13/LICENSE-APACHE +201 -0
  143. package/helpers/ripgrep/notices/regex-automata-0.4.13/LICENSE-MIT +25 -0
  144. package/helpers/ripgrep/notices/regex-syntax-0.8.8/LICENSE-APACHE +201 -0
  145. package/helpers/ripgrep/notices/regex-syntax-0.8.8/LICENSE-MIT +25 -0
  146. package/helpers/ripgrep/notices/regex-syntax-0.8.8/src/unicode_tables/LICENSE-UNICODE +57 -0
  147. package/helpers/ripgrep/notices/ryu-1.0.20/LICENSE-APACHE +176 -0
  148. package/helpers/ripgrep/notices/ryu-1.0.20/LICENSE-BOOST +23 -0
  149. package/helpers/ripgrep/notices/same-file-1.0.6/COPYING +3 -0
  150. package/helpers/ripgrep/notices/same-file-1.0.6/LICENSE-MIT +21 -0
  151. package/helpers/ripgrep/notices/same-file-1.0.6/UNLICENSE +24 -0
  152. package/helpers/ripgrep/notices/serde-1.0.228/LICENSE-APACHE +176 -0
  153. package/helpers/ripgrep/notices/serde-1.0.228/LICENSE-MIT +23 -0
  154. package/helpers/ripgrep/notices/serde_core-1.0.228/LICENSE-APACHE +176 -0
  155. package/helpers/ripgrep/notices/serde_core-1.0.228/LICENSE-MIT +23 -0
  156. package/helpers/ripgrep/notices/serde_derive-1.0.228/LICENSE-APACHE +176 -0
  157. package/helpers/ripgrep/notices/serde_derive-1.0.228/LICENSE-MIT +23 -0
  158. package/helpers/ripgrep/notices/serde_json-1.0.145/LICENSE-APACHE +176 -0
  159. package/helpers/ripgrep/notices/serde_json-1.0.145/LICENSE-MIT +23 -0
  160. package/helpers/ripgrep/notices/shlex-1.3.0/LICENSE-APACHE +13 -0
  161. package/helpers/ripgrep/notices/shlex-1.3.0/LICENSE-MIT +21 -0
  162. package/helpers/ripgrep/notices/syn-2.0.106/LICENSE-APACHE +176 -0
  163. package/helpers/ripgrep/notices/syn-2.0.106/LICENSE-MIT +23 -0
  164. package/helpers/ripgrep/notices/termcolor-1.4.1/COPYING +3 -0
  165. package/helpers/ripgrep/notices/termcolor-1.4.1/LICENSE-MIT +21 -0
  166. package/helpers/ripgrep/notices/termcolor-1.4.1/UNLICENSE +24 -0
  167. package/helpers/ripgrep/notices/textwrap-0.16.2/LICENSE +21 -0
  168. package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/LICENSE-APACHE +201 -0
  169. package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/LICENSE-MIT +25 -0
  170. package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/jemalloc/COPYING +27 -0
  171. package/helpers/ripgrep/notices/tikv-jemallocator-0.6.0/LICENSE-APACHE +201 -0
  172. package/helpers/ripgrep/notices/tikv-jemallocator-0.6.0/LICENSE-MIT +25 -0
  173. package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-APACHE +176 -0
  174. package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-MIT +23 -0
  175. package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-UNICODE +39 -0
  176. package/helpers/ripgrep/notices/walkdir-2.5.0/COPYING +3 -0
  177. package/helpers/ripgrep/notices/walkdir-2.5.0/LICENSE-MIT +21 -0
  178. package/helpers/ripgrep/notices/walkdir-2.5.0/UNLICENSE +24 -0
  179. package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-APACHE +201 -0
  180. package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  181. package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-MIT +23 -0
  182. package/helpers/ripgrep/notices/winapi-util-0.1.11/COPYING +3 -0
  183. package/helpers/ripgrep/notices/winapi-util-0.1.11/LICENSE-MIT +21 -0
  184. package/helpers/ripgrep/notices/winapi-util-0.1.11/UNLICENSE +24 -0
  185. package/helpers/ripgrep/notices/windows-link-0.2.1/license-apache-2.0 +201 -0
  186. package/helpers/ripgrep/notices/windows-link-0.2.1/license-mit +21 -0
  187. package/helpers/ripgrep/notices/windows-sys-0.61.2/license-apache-2.0 +201 -0
  188. package/helpers/ripgrep/notices/windows-sys-0.61.2/license-mit +21 -0
  189. package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-APACHE +201 -0
  190. package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  191. package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-MIT +23 -0
  192. package/locale/en.json +1 -1
  193. package/locale/zh.json +2 -2
  194. package/package.json +30 -12
@@ -0,0 +1,6 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { RuntimeRequest } from './provider.ts';
3
+ /** A step's accepted batch follows step/start in the official event contract. */
4
+ export declare function currentInputIds(ctx: Context, request: RuntimeRequest): Promise<readonly string[]>;
5
+ /** Read only the official persistence API after its awaited durability checkpoint. */
6
+ export declare function verifyRecoveryCheckpoint(ctx: Context, request: RuntimeRequest, provider: string): Promise<boolean>;
@@ -0,0 +1,71 @@
1
+ import { SessionId } from '@deepseek-ai/dsh-session';
2
+ import { BlockAssembler, expandAssistantStream } from '@deepseek-ai/dsh-llm';
3
+ import { isDeepStrictEqual } from 'node:util';
4
+ /** A step's accepted batch follows step/start in the official event contract. */
5
+ export async function currentInputIds(ctx, request) {
6
+ if (!request.sessionId)
7
+ return [];
8
+ const id = SessionId(request.sessionId), store = ctx.get('sessions'), session = store?.get(id);
9
+ const persistence = ctx.get('sessionPersistence');
10
+ if (!session || !store || !persistence || !await store.flush(session))
11
+ return [];
12
+ const handle = await persistence.open(id, 'read');
13
+ try {
14
+ if (handle.id !== id || handle.header?.id !== id || handle.access !== 'read')
15
+ return [];
16
+ const { events } = await handle.read();
17
+ if (!Array.isArray(events))
18
+ return [];
19
+ let step;
20
+ let ids = [];
21
+ for (const event of events) {
22
+ if (event.type === 'step/start') {
23
+ step = event;
24
+ ids = [];
25
+ }
26
+ else if (event.type === 'turn/start' || event.type === 'step/end' || event.type === 'turn/end') {
27
+ step = undefined;
28
+ ids = [];
29
+ }
30
+ else if (event.type === 'user/message' && step)
31
+ ids.push(event.data.id);
32
+ }
33
+ return ids;
34
+ }
35
+ finally {
36
+ await handle.close();
37
+ }
38
+ }
39
+ /** Read only the official persistence API after its awaited durability checkpoint. */
40
+ export async function verifyRecoveryCheckpoint(ctx, request, provider) {
41
+ if (!request.sessionId || !request.recoveryCheckpointId || !request.resumeSessionId)
42
+ return false;
43
+ const id = SessionId(request.sessionId), store = ctx.get('sessions'), session = store?.get(id);
44
+ const persistence = ctx.get('sessionPersistence');
45
+ if (!session || typeof store?.flush !== 'function' || typeof persistence?.open !== 'function')
46
+ return false;
47
+ if (!await store.flush(session))
48
+ return false;
49
+ const handle = await persistence.open(id, 'read');
50
+ try {
51
+ if (handle?.id !== id || handle.header?.id !== id || handle.access !== 'read')
52
+ return false;
53
+ const { events } = await handle.read();
54
+ if (!Array.isArray(events))
55
+ return false;
56
+ const final = events.findLast(e => e.type === 'assistant/message' && e.surfaceOp === 'append');
57
+ const live = session.deriveMessages().findLast(m => m.role === 'assistant');
58
+ if (final?.type !== 'assistant/message' || final.data.interrupted || !live || live.id !== final.data.message.id || !isDeepStrictEqual(live, final.data.message))
59
+ return false;
60
+ const message = final.data.message, pointer = message.source.replayState?.response;
61
+ if (message.source.provider !== provider || pointer?.agent !== provider || pointer.dshSessionId !== id || pointer.sessionId !== request.resumeSessionId || pointer.recoveryCheckpointId !== request.recoveryCheckpointId)
62
+ return false;
63
+ const assembled = new BlockAssembler();
64
+ for (const part of expandAssistantStream(final.data.stream))
65
+ assembled.push(part.chunk);
66
+ return ['stop', 'max-tokens'].includes(assembled.finish.kind) && isDeepStrictEqual(assembled.replayState, message.source.replayState) && isDeepStrictEqual(assembled.blocks(), message.content);
67
+ }
68
+ finally {
69
+ await handle.close();
70
+ }
71
+ }
@@ -0,0 +1,47 @@
1
+ import type { AgentCapabilities, SessionConfigOption } from '@agentclientprotocol/sdk/unstable';
2
+ export interface AcpRecovery {
3
+ sessionId: string;
4
+ capabilities: AgentCapabilities;
5
+ modelConfiguration?: {
6
+ configOptions: SessionConfigOption[];
7
+ resolvedEvidence?: {
8
+ modelValue: string;
9
+ resolvedModel: string;
10
+ };
11
+ };
12
+ configUpdates?: SessionConfigOption[][];
13
+ }
14
+ export interface RecoveryState {
15
+ version: 1;
16
+ phase: 'dirty' | 'clean' | 'ended';
17
+ scope: string;
18
+ brokerId: string;
19
+ instanceId?: string;
20
+ pid?: number;
21
+ cursor?: number;
22
+ checkpointId?: string;
23
+ acp?: AcpRecovery;
24
+ }
25
+ export interface RecoveryJournal {
26
+ read(): Promise<RecoveryState | undefined>;
27
+ write(state: RecoveryState): Promise<void>;
28
+ }
29
+ export declare function validateRecovery(value: unknown): RecoveryState;
30
+ export declare const codexContextPolicy: Readonly<{
31
+ version: 1;
32
+ scope: "new-session";
33
+ mode: "append";
34
+ content: "text";
35
+ resume: "omit-only";
36
+ changed: "new-session-required";
37
+ mapping: "developerInstructions";
38
+ nativeCliUnmodified: true;
39
+ }>;
40
+ export declare function supportsCodexContextPolicy(capabilities: AgentCapabilities): boolean;
41
+ /** Known capabilities plus the exact Codex policy whitelist; no arbitrary metadata. */
42
+ export declare function recoveryCapabilities(c: AgentCapabilities): AgentCapabilities;
43
+ export declare function recoveryKey(value: unknown): string;
44
+ /** Preserve legacy lookup bytes; each employee endpoint scope owns a separate journal. */
45
+ export declare function runtimeRecoveryKey(identity: string, provider: string, sessionId: string | undefined, employeeScope?: string): string;
46
+ /** History is immutable; current is atomically replaced only after fsync. No expiry. */
47
+ export declare function recoveryJournal(profileDir: string, key: string): RecoveryJournal;
@@ -0,0 +1,61 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { join } from 'node:path';
3
+ import { open, rename } from 'node:fs/promises';
4
+ import { constants } from 'node:fs';
5
+ import { privateDirectory, durableWrite, readJournal } from "./remote-details.js";
6
+ import { NeedError } from "./remote.js";
7
+ const invalid = () => new NeedError('RECOVERY_STORAGE_INVALID', '会话恢复记录无效,本轮尚未发送', '有效的私有会话恢复记录');
8
+ export function validateRecovery(value) {
9
+ const s = value;
10
+ if (!s || s.version !== 1 || !['dirty', 'clean', 'ended'].includes(s.phase) || !/^[a-f0-9]{64}$/.test(s.scope) || !/^r[a-f0-9]{23}$/.test(s.brokerId))
11
+ throw invalid();
12
+ if (s.instanceId !== undefined && !/^[a-f0-9-]{36}$/.test(s.instanceId) || s.pid !== undefined && (!Number.isSafeInteger(s.pid) || s.pid < 1))
13
+ throw invalid();
14
+ if (s.phase === 'clean' && (!s.instanceId || !Number.isSafeInteger(s.cursor) || s.cursor < 0 || typeof s.checkpointId !== 'string' || !s.checkpointId || !s.acp || typeof s.acp.sessionId !== 'string' || !s.acp.sessionId || !s.acp.capabilities || typeof s.acp.capabilities !== 'object' || Array.isArray(s.acp.capabilities)))
15
+ throw invalid();
16
+ return structuredClone(s);
17
+ }
18
+ export const codexContextPolicy = Object.freeze({ version: 1, scope: 'new-session', mode: 'append', content: 'text', resume: 'omit-only', changed: 'new-session-required', mapping: 'developerInstructions', nativeCliUnmodified: true });
19
+ export function supportsCodexContextPolicy(capabilities) {
20
+ const policy = capabilities._meta?.aiWorkbenchSystemPrompt;
21
+ return !!policy && typeof policy === 'object' && !Array.isArray(policy) && Object.keys(policy).length === Object.keys(codexContextPolicy).length && Object.entries(codexContextPolicy).every(([key, value]) => policy[key] === value);
22
+ }
23
+ /** Known capabilities plus the exact Codex policy whitelist; no arbitrary metadata. */
24
+ export function recoveryCapabilities(c) {
25
+ return { loadSession: c.loadSession === true, promptCapabilities: { image: c.promptCapabilities?.image === true, audio: c.promptCapabilities?.audio === true, embeddedContext: c.promptCapabilities?.embeddedContext === true }, sessionCapabilities: { ...(c.sessionCapabilities?.resume ? { resume: {} } : {}), ...(c.sessionCapabilities?.systemPrompt ? { systemPrompt: {} } : {}) }, ...(supportsCodexContextPolicy(c) ? { _meta: { aiWorkbenchSystemPrompt: { ...codexContextPolicy } } } : {}) };
26
+ }
27
+ export function recoveryKey(value) { return createHash('sha256').update(JSON.stringify(value)).digest('hex'); }
28
+ /** Preserve legacy lookup bytes; each employee endpoint scope owns a separate journal. */
29
+ export function runtimeRecoveryKey(identity, provider, sessionId, employeeScope) {
30
+ return recoveryKey([identity, provider, sessionId, ...(employeeScope ? ['employee', employeeScope] : [])]);
31
+ }
32
+ /** History is immutable; current is atomically replaced only after fsync. No expiry. */
33
+ export function recoveryJournal(profileDir, key) {
34
+ if (!/^[a-f0-9]{64}$/.test(key))
35
+ throw invalid();
36
+ const parent = join(profileDir, 'ai-workbench-remote-agent'), dir = join(parent, 'recovery', key), current = join(dir, 'current.json');
37
+ let tail = Promise.resolve();
38
+ const prepare = async () => { await privateDirectory(parent); await privateDirectory(join(parent, 'recovery')); await privateDirectory(dir); };
39
+ return {
40
+ async read() { await tail; await prepare(); try {
41
+ return validateRecovery(await readJournal(current));
42
+ }
43
+ catch (error) {
44
+ if (error?.code === 'ENOENT')
45
+ return;
46
+ throw error;
47
+ } },
48
+ write(state) { const value = validateRecovery(state); const task = tail.then(async () => { await prepare(); const id = randomUUID(); await durableWrite(join(dir, id + '.json'), value); const temp = join(dir, id + '.current'); const file = await open(temp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600); try {
49
+ await file.writeFile(JSON.stringify(value) + '\n');
50
+ await file.sync();
51
+ }
52
+ finally {
53
+ await file.close();
54
+ } await rename(temp, current); const directory = await open(dir, 'r'); try {
55
+ await directory.sync();
56
+ }
57
+ finally {
58
+ await directory.close();
59
+ } }); tail = task; return task; },
60
+ };
61
+ }
@@ -18,11 +18,21 @@ export interface DetailRequest {
18
18
  provider: 'claude' | 'codex';
19
19
  identity: string;
20
20
  connection: Connection;
21
+ readonly localAcceptanceId?: string;
21
22
  }
23
+ export declare function privateDirectory(path: string): Promise<void>;
24
+ export declare function durableWrite(path: string, value: unknown): Promise<void>;
25
+ export declare function readJournal(path: string): Promise<unknown>;
22
26
  export declare function createRemoteDetails(options: {
23
27
  profileDir: string;
24
28
  getApi: () => RemoteDetailsApi | undefined;
25
29
  }): {
30
+ recordLocalFailure(request: {
31
+ sessionId?: string;
32
+ provider: "claude" | "codex";
33
+ code: string;
34
+ observation?: DetailObservation;
35
+ }): Promise<void>;
26
36
  recordLoginFailure(request: {
27
37
  sessionId?: string;
28
38
  code: string;
@@ -23,7 +23,7 @@ function kind(observation) {
23
23
  return observation.severity === 'action-required' ? 'action-required' : 'unknown';
24
24
  return { extension: 'extensions', tool: 'tools', need: 'action-required', notice: 'extensions', session: 'extensions' }[observation.kind] ?? observation.kind;
25
25
  }
26
- async function privateDirectory(path) {
26
+ export async function privateDirectory(path) {
27
27
  if (!isAbsolute(path))
28
28
  throw failure('REMOTE_DETAIL_STORAGE_FAILED');
29
29
  // Existing parent/profile ownership is the host's responsibility; reject symlinks
@@ -57,7 +57,7 @@ async function privateDirectory(path) {
57
57
  if ((info.mode & 0o077) !== 0 || (process.getuid && info.uid !== process.getuid()))
58
58
  throw failure('REMOTE_DETAIL_STORAGE_FAILED');
59
59
  }
60
- async function durableWrite(path, value) {
60
+ export async function durableWrite(path, value) {
61
61
  const file = await open(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
62
62
  try {
63
63
  await file.writeFile(JSON.stringify(value) + '\n');
@@ -74,7 +74,7 @@ async function durableWrite(path, value) {
74
74
  await parent.close();
75
75
  }
76
76
  }
77
- async function readJournal(path) {
77
+ export async function readJournal(path) {
78
78
  const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
79
79
  try {
80
80
  const info = await file.stat();
@@ -91,6 +91,12 @@ export function createRemoteDetails(options) {
91
91
  const scopes = new Map(), loginScopes = new Map();
92
92
  const initialize = async () => { await privateDirectory(join(options.profileDir, 'ai-workbench-remote-agent')); await privateDirectory(root); };
93
93
  return {
94
+ async recordLocalFailure(request) {
95
+ await initialize();
96
+ if (!/^[A-Z][A-Z0-9_]*$/.test(request.code))
97
+ throw failure('REMOTE_DETAIL_STORAGE_FAILED');
98
+ await durableWrite(join(root, randomUUID() + '.json'), { version: 1, kind: 'local-admission', code: request.code, provider: request.provider, sessionHash: request.sessionId ? hash(request.sessionId) : null, ...request.observation ? { observation: sanitizeObservation(request.observation, true) } : {} });
99
+ },
94
100
  async recordLoginFailure(request) {
95
101
  await initialize();
96
102
  const eventId = randomUUID(), diagnostic = request.diagnostic;
@@ -221,6 +227,10 @@ export function createRemoteDetails(options) {
221
227
  return { eventId, recorded: true };
222
228
  },
223
229
  async open(request) {
230
+ request = structuredClone(request);
231
+ if (request.localAcceptanceId !== undefined && (typeof request.localAcceptanceId !== 'string' || !request.localAcceptanceId.trim()))
232
+ throw failure('REMOTE_DETAIL_SCOPE_INVALID');
233
+ const localScope = request.localAcceptanceId === undefined ? undefined : Object.freeze({ localAcceptanceId: request.localAcceptanceId });
224
234
  if (!request.sessionId || !request.identity || !request.connection.instanceId || !Number.isSafeInteger(request.connection.epoch) || request.connection.epoch < 0 || !['claude', 'codex'].includes(request.provider))
225
235
  throw failure('REMOTE_DETAIL_SCOPE_INVALID');
226
236
  try {
@@ -348,7 +358,7 @@ export function createRemoteDetails(options) {
348
358
  stable();
349
359
  let response;
350
360
  try {
351
- response = await api.appendRemoteConversationDetail(structuredClone(detail));
361
+ response = await (localScope === undefined ? api.appendRemoteConversationDetail(structuredClone(detail)) : api.appendRemoteConversationDetail(structuredClone(detail), localScope));
352
362
  }
353
363
  catch {
354
364
  stable();
@@ -0,0 +1,3 @@
1
+ import type { Stream } from '@agentclientprotocol/sdk/unstable';
2
+ /** Public Stream only. Server request/approval IDs are never changed. */
3
+ export declare function isolateRpcIds(stream: Stream): Stream;
@@ -0,0 +1,50 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { NeedError } from "./remote.js";
3
+ /** Public Stream only. Server request/approval IDs are never changed. */
4
+ export function isolateRpcIds(stream) {
5
+ const prefix = randomUUID() + '/';
6
+ const pending = new Map();
7
+ const readable = stream.readable.pipeThrough(new TransformStream({ transform(message, controller) {
8
+ const raw = message;
9
+ if (!raw.method && Object.hasOwn(raw, 'id')) {
10
+ if (typeof raw.id !== 'string' || !pending.has(raw.id))
11
+ throw new NeedError('ACP_FOREIGN_RESPONSE', '收到不属于当前客户端的协议响应', '原会话的完整客户端状态');
12
+ const id = pending.get(raw.id);
13
+ pending.delete(raw.id);
14
+ controller.enqueue({ ...raw, id });
15
+ }
16
+ else
17
+ controller.enqueue(message);
18
+ } }));
19
+ const writable = new WritableStream({ async write(message) {
20
+ const raw = message;
21
+ let outgoing = raw;
22
+ if (typeof raw.method === 'string' && Object.hasOwn(raw, 'id')) {
23
+ const id = prefix + JSON.stringify(raw.id);
24
+ pending.set(id, raw.id);
25
+ outgoing = { ...raw, id };
26
+ }
27
+ else if (raw.method === '$/cancel_request' && raw.params && typeof raw.params === 'object') {
28
+ const params = raw.params;
29
+ outgoing = { ...raw, params: { ...params, requestId: prefix + JSON.stringify(params.requestId) } };
30
+ }
31
+ const writer = stream.writable.getWriter();
32
+ try {
33
+ await writer.write(outgoing);
34
+ }
35
+ finally {
36
+ writer.releaseLock();
37
+ }
38
+ }, async close() { const writer = stream.writable.getWriter(); try {
39
+ await writer.close();
40
+ }
41
+ finally {
42
+ writer.releaseLock();
43
+ } }, async abort(reason) { const writer = stream.writable.getWriter(); try {
44
+ await writer.abort(reason);
45
+ }
46
+ finally {
47
+ writer.releaseLock();
48
+ } } });
49
+ return { readable, writable };
50
+ }
@@ -0,0 +1,27 @@
1
+ import type { DetailObservation } from './conversation-details.ts';
2
+ export declare const STAGES: Readonly<{
3
+ readonly 'login-ready': "登录就绪检查";
4
+ readonly 'connection-confirm': "核对当前连接状态";
5
+ readonly 'input-prepare': "准备本轮输入";
6
+ readonly 'workspace-resolve': "解析工作区";
7
+ readonly 'details-open': "打开技术详情";
8
+ readonly 'details-flush': "等待技术详情持久确认";
9
+ readonly 'workspace-notice': "记录工作区说明";
10
+ readonly 'broker-list': "查询主机会话";
11
+ readonly 'broker-start': "启动主机会话";
12
+ readonly 'broker-attach': "连接主机会话";
13
+ readonly 'acp-initialize': "初始化远程模型";
14
+ readonly 'acp-session-new': "新建模型会话";
15
+ readonly 'acp-session-load': "加载模型会话";
16
+ readonly 'acp-session-resume': "恢复模型会话";
17
+ readonly 'acp-prompt': "模型处理本轮(含等待审批)";
18
+ }>;
19
+ export type Stage = keyof typeof STAGES;
20
+ /** Passive client observations; no thresholds, retries, or remote-clock claims. */
21
+ export declare class StageTiming {
22
+ private readonly started;
23
+ private pending;
24
+ private sink?;
25
+ bind(sink: (detail: DetailObservation) => void): void;
26
+ run<T>(phase: Stage, operation: () => Promise<T>): Promise<T>;
27
+ }
@@ -0,0 +1,39 @@
1
+ export const STAGES = Object.freeze({
2
+ 'login-ready': '登录就绪检查', 'connection-confirm': '核对当前连接状态', 'input-prepare': '准备本轮输入', 'workspace-resolve': '解析工作区',
3
+ 'details-open': '打开技术详情', 'details-flush': '等待技术详情持久确认', 'workspace-notice': '记录工作区说明',
4
+ 'broker-list': '查询主机会话', 'broker-start': '启动主机会话', 'broker-attach': '连接主机会话',
5
+ 'acp-initialize': '初始化远程模型', 'acp-session-new': '新建模型会话',
6
+ 'acp-session-load': '加载模型会话', 'acp-session-resume': '恢复模型会话',
7
+ 'acp-prompt': '模型处理本轮(含等待审批)',
8
+ });
9
+ /** Passive client observations; no thresholds, retries, or remote-clock claims. */
10
+ export class StageTiming {
11
+ started = performance.now();
12
+ pending = [];
13
+ sink;
14
+ bind(sink) {
15
+ this.sink = sink;
16
+ const pending = this.pending;
17
+ this.pending = [];
18
+ for (const detail of pending)
19
+ sink(detail);
20
+ }
21
+ async run(phase, operation) {
22
+ const started = performance.now();
23
+ let outcome = 'failed';
24
+ try {
25
+ const result = await operation();
26
+ outcome = 'ok';
27
+ return result;
28
+ }
29
+ finally {
30
+ const ended = performance.now();
31
+ const durationMs = ended - started, elapsedMs = ended - this.started;
32
+ const detail = { kind: 'session', severity: outcome === 'ok' ? 'info' : 'warning', code: 'REMOTE_STAGE_TIMING', summary: STAGES[phase] + ':' + durationMs.toFixed(1) + '毫秒', payload: { phase, durationMs, elapsedMs, outcome, clock: 'client-monotonic' } };
33
+ if (this.sink)
34
+ this.sink(detail);
35
+ else
36
+ this.pending.push(detail);
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,13 @@
1
+ vscode-ripgrep
2
+
3
+ Copyright (c) Microsoft Corporation
4
+
5
+ All rights reserved.
6
+
7
+ MIT License
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ # Bundled Mac search component
2
+
3
+ Original unmodified binaries from Microsoft @vscode/ripgrep-universal 1.18.0. The npm integrity/SHA256, per-file SHA/bytes and upstream release archives are in manifest.json. Both npm binaries were compared byte for byte with the pinned Microsoft release assets (v15.0.1). No employee download/install hook or PATH lookup.
4
+
5
+ Microsoft packaging tag v1.18.0 commit d22dec46c67fc8c5d307c9b18dba2792efd1f016 locks ripgrep-prebuilt v15.0.1 commit 67202aaafb17aecd9b5b7046d5b7baa92b05237a. Its config pins upstream ripgrep15.0.0 plus the retained Microsoft patch (Windows/ARM-musl adjustments); reported binaries are ripgrep15.0.0 with PCRE2 10.45. Upstream source: https://github.com/BurntSushi/ripgrep/tree/15.0.0 ; vendor source/config: https://github.com/microsoft/ripgrep-prebuilt/tree/v15.0.1 ; npm wrapper: https://github.com/microsoft/vscode-ripgrep/tree/v1.18.0 . Source-input hashes are retained in manifest.json. This is vendor artifact provenance, not a local reproducible build attestation; exact vendor compiler invocation/runtime std build was not independently attested.
6
+
7
+ LICENSE-vscode retains Microsoft packaging MIT text. notices/ contains ripgrep MIT/Unlicense/COPYING, PCRE2 licence, and a conservative full upstream Cargo.lock dependency superset plus Microsoft's Windows-only msvc_spectre_libs. Every crate archive matches its Cargo checksum; DEPENDENCIES.json identifies original package, version, licence and retained texts. r-efi licence is embedded in AUTHORS. wasip2 texts omitted from its crate were retrieved at its .cargo_vcs_info.json exact source commit. Superset inclusion is not a claim all non-Mac/build/test crates are linked into these Mac binaries. prepare-ripgrep-notices.py regenerates notices from the retained pinned inputs.
8
+
9
+ Runtime validates shipped rg size/SHA256, executable regular file, macOS arm64/x64 only. No strip/sign/byte alteration by X. Arm64 actual PCRE2 operation is fixture-tested; x64 is byte/inventory verified, not executed on this arm64 Mac. macOS SDK/system libraries are supplied by the OS; binaries have no Homebrew dylib dependency. The plugin message/output bound uses named acpMaxMessageBytes. Other platforms fail explicitly rather than host fallback.
Binary file
Binary file
@@ -0,0 +1,56 @@
1
+ {
2
+ "npm": "@vscode/ripgrep-universal@1.18.0",
3
+ "source": "https://registry.npmjs.org/@vscode/ripgrep-universal/-/ripgrep-universal-1.18.0.tgz",
4
+ "archiveSha256": "76a03f429dc13a90f53b60995211a54308ab4f67b2521bac00f9c4be953dff52",
5
+ "integrity": "sha512-kgrAVy1L/v/8erAjo+I720ifTf7o4rvYUwgsuEu97XAEBtPT9sk3HhcSgFpxMydvNmwdJtXo9/AVee7JjocTrg==",
6
+ "files": {
7
+ "darwin-arm64": {
8
+ "bytes": 4528512,
9
+ "sha256": "6ef40346bf31fcce79d9614c7745c198542925a0c7d4911e1ffe794c53392ac1"
10
+ },
11
+ "darwin-x64": {
12
+ "bytes": 4742676,
13
+ "sha256": "f999495980a5e6f1e7d26461ef5768b4013a62df610ed7d77a8b2de247a5b228"
14
+ }
15
+ },
16
+ "vendorRelease": "microsoft/ripgrep-prebuilt@v15.0.1",
17
+ "vendorCommit": "67202aaafb17aecd9b5b7046d5b7baa92b05237a",
18
+ "sourceRipgrep": "BurntSushi/ripgrep@15.0.0",
19
+ "reportedVersion": "ripgrep 15.0.0 / PCRE2 10.45",
20
+ "sourceInputs": [
21
+ {
22
+ "file": "vscode-build.json",
23
+ "sha256": "fcf6b245175385854a06785ae990546fe0d197ceb65ec9ca58860a5817f07abd"
24
+ },
25
+ {
26
+ "file": "vscode-binaries.lock.json",
27
+ "sha256": "0bd421f46e3ee2f127290f67e73adb240e1311162d6d33c25121849e5f2beadc"
28
+ },
29
+ {
30
+ "file": "prebuilt-config.json",
31
+ "sha256": "71bc7b3ecdd5442001f74896a738eafb3b831ee5a183e128067fe6b6d6b64ea7"
32
+ },
33
+ {
34
+ "file": "prebuilt-build-build.sh",
35
+ "sha256": "cf0d97cafca064c763d3ceeebe8b2f7647f5b4b35710b37c74804a4d3e1b3929"
36
+ },
37
+ {
38
+ "file": "prebuilt-patches-0001-resolve-binskim-issues.patch",
39
+ "sha256": "45f8b58bba714c446de407f93120f557736b45fad6f43ea283077201646a1be7"
40
+ },
41
+ {
42
+ "file": "ripgrep-lock",
43
+ "sha256": "257bfa1eeacfa4d8222e2c50f9d2f8d15ef07e96adee46eaa87839f3b6cfa3b4"
44
+ }
45
+ ],
46
+ "vendorArchives": {
47
+ "darwin-arm64": {
48
+ "sha256": "2fa16464fd8638588a67c7fc172d3c4b57fbdc65dff366e10b0b0e90734628a6",
49
+ "url": "https://github.com/microsoft/ripgrep-prebuilt/releases/download/v15.0.1/ripgrep-v15.0.1-aarch64-apple-darwin.tar.gz"
50
+ },
51
+ "darwin-x64": {
52
+ "sha256": "591c693e80bb444ef1907b2a906feb9c77bcafe1cdf509107cc75dcf0e875bd2",
53
+ "url": "https://github.com/microsoft/ripgrep-prebuilt/releases/download/v15.0.1/ripgrep-v15.0.1-x86_64-apple-darwin.tar.gz"
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,3 @@
1
+ This project is dual-licensed under the Unlicense and MIT licenses.
2
+
3
+ You may use this code under the terms of either license.