@ai-workbench/remote-agent 0.8.21 → 0.8.24

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 (96) hide show
  1. package/README.md +28 -155
  2. package/dist/acp.d.ts +20 -15
  3. package/dist/acp.js +197 -224
  4. package/dist/agents.d.ts +0 -1
  5. package/dist/agents.js +2 -2
  6. package/dist/broker-runtime.d.ts +3 -5
  7. package/dist/broker-runtime.js +45 -342
  8. package/dist/broker.d.ts +6 -31
  9. package/dist/broker.js +42 -113
  10. package/dist/catalog-ssh.d.ts +2 -31
  11. package/dist/catalog-ssh.js +9 -39
  12. package/dist/claude-local.d.ts +1 -1
  13. package/dist/claude-local.js +40 -105
  14. package/dist/client.js +34 -127
  15. package/dist/close-diagnostics.d.ts +6 -20
  16. package/dist/close-diagnostics.js +13 -57
  17. package/dist/connect-contract.d.ts +4 -8
  18. package/dist/conversation-details.d.ts +0 -29
  19. package/dist/conversation-details.js +8 -99
  20. package/dist/employee-endpoint.d.ts +1 -3
  21. package/dist/employee-endpoint.js +8 -39
  22. package/dist/employee-exec.d.ts +4 -3
  23. package/dist/employee-exec.js +25 -13
  24. package/dist/employee-info.d.ts +5 -1
  25. package/dist/employee-info.js +16 -5
  26. package/dist/employee-location.js +3 -4
  27. package/dist/employee-mcp.js +44 -69
  28. package/dist/employee-native.d.ts +1 -3
  29. package/dist/employee-native.js +6 -27
  30. package/dist/employee-presentation.d.ts +0 -5
  31. package/dist/employee-presentation.js +0 -15
  32. package/dist/employee-provider.d.ts +3 -11
  33. package/dist/employee-provider.js +69 -205
  34. package/dist/employee-reasons.js +1 -5
  35. package/dist/employee-shell.d.ts +2 -1
  36. package/dist/employee-shell.js +10 -13
  37. package/dist/employee-ssh.d.ts +1 -3
  38. package/dist/employee-ssh.js +13 -16
  39. package/dist/employee-tools.d.ts +5 -5
  40. package/dist/failure-evidence.d.ts +2 -10
  41. package/dist/failure-evidence.js +12 -61
  42. package/dist/host-workspace.d.ts +4 -2
  43. package/dist/host-workspace.js +1 -1
  44. package/dist/index.d.ts +12 -42
  45. package/dist/index.js +203 -471
  46. package/dist/input.js +2 -3
  47. package/dist/local-user-turn.js +5 -6
  48. package/dist/mapping.d.ts +0 -3
  49. package/dist/mapping.js +0 -34
  50. package/dist/model-catalog.d.ts +2 -41
  51. package/dist/model-catalog.js +24 -282
  52. package/dist/model-configuration.d.ts +0 -3
  53. package/dist/model-configuration.js +8 -47
  54. package/dist/model-presentation.d.ts +1 -0
  55. package/dist/model-presentation.js +2 -2
  56. package/dist/permissions.d.ts +15 -0
  57. package/dist/permissions.js +30 -0
  58. package/dist/preflight-diagnostic.js +11 -22
  59. package/dist/preflight-payload.d.ts +1 -1
  60. package/dist/preflight-payload.js +2 -2
  61. package/dist/presentation.d.ts +0 -1
  62. package/dist/presentation.js +21 -42
  63. package/dist/provider.d.ts +12 -48
  64. package/dist/provider.js +18 -106
  65. package/dist/readiness.js +1 -7
  66. package/dist/remote-details.d.ts +41 -17
  67. package/dist/remote-details.js +84 -381
  68. package/dist/workspace-source.d.ts +1 -25
  69. package/dist/workspace-source.js +1 -138
  70. package/package.json +11 -12
  71. package/dist/catalog-diagnostics.d.ts +0 -16
  72. package/dist/catalog-diagnostics.js +0 -108
  73. package/dist/host-layout.d.ts +0 -8
  74. package/dist/host-layout.js +0 -9
  75. package/dist/job.d.ts +0 -17
  76. package/dist/job.js +0 -57
  77. package/dist/model-defaults.d.ts +0 -29
  78. package/dist/model-defaults.js +0 -62
  79. package/dist/oneshot.d.ts +0 -13
  80. package/dist/oneshot.js +0 -78
  81. package/dist/protocol-boundary.d.ts +0 -13
  82. package/dist/protocol-boundary.js +0 -49
  83. package/dist/provider-locale.d.ts +0 -19
  84. package/dist/provider-locale.js +0 -73
  85. package/dist/recovery-checkpoint.d.ts +0 -6
  86. package/dist/recovery-checkpoint.js +0 -71
  87. package/dist/recovery.d.ts +0 -51
  88. package/dist/recovery.js +0 -61
  89. package/dist/sdk-ssh-wrapper.d.ts +0 -2
  90. package/dist/sdk-ssh-wrapper.js +0 -34
  91. package/dist/stage-timing.d.ts +0 -31
  92. package/dist/stage-timing.js +0 -61
  93. package/dist/workspace-notice.d.ts +0 -47
  94. package/dist/workspace-notice.js +0 -273
  95. package/dist/workspace-source-confirmation.d.ts +0 -4
  96. package/dist/workspace-source-confirmation.js +0 -12
@@ -1,138 +1 @@
1
- import { withFailureEvidence } from "./failure-evidence.js";
2
- import { connectNeedError } from "./connect-need.js";
3
- import { isDeepStrictEqual } from 'node:util';
4
- import { sanitizeDisplayLabel } from "./conversation-details.js";
5
- import { NeedError } from "./remote.js";
6
- const fail = (code) => new NeedError(code, '工作区来源尚未确认,本轮未执行;你的原话仍保留。', '当前工作区来源及原始员工输入');
7
- const codeOf = (value) => value instanceof NeedError ? value.code : 'WORKSPACE_SOURCE_CONFIRMATION_FAILED';
8
- const diagnosticFailure = (error, diagnostic) => withFailureEvidence(error, [diagnostic], 'diagnostic', undefined, codeOf(error));
9
- /** Explicit human source transition only. No source selection is inferred from a folder path. */
10
- export async function confirmLegacyWorkspaceSource(get, api, request, hooks) {
11
- let initialContext = { workspaceId: null, bindingId: null, source: null, status: null };
12
- let diagnosticAttempted = false;
13
- try {
14
- request.signal?.throwIfAborted();
15
- if (api.getContractInfo?.().workspaceLocations !== 1 || typeof api.getWorkspaceLocationState !== 'function')
16
- return;
17
- if (!api.getWorkspaceLocationState().items.some(item => item.source === 'legacy-host' && item.status === 'legacy-confirmation-required'))
18
- return;
19
- const workspace = { ...await hooks.workspace(request) };
20
- request.signal?.throwIfAborted();
21
- const snapshot = api.getWorkspaceLocationState(), rows = snapshot.items.filter(item => item.workspaceId === workspace.id), row = rows[0];
22
- if (rows.length !== 1 || row?.status !== 'legacy-confirmation-required' || row.source !== 'legacy-host')
23
- return;
24
- const frozen = structuredClone(row);
25
- initialContext = { workspaceId: frozen.workspaceId, bindingId: frozen.bindingId, source: frozen.source, status: frozen.status };
26
- const connection = api.getConnectionState(), turn = { ...hooks.currentUserTurn(request) };
27
- const name = sanitizeDisplayLabel(row.workspaceName).replace(/[\u0000-\u001f\u007f\u202a-\u202e\u2066-\u2069]/g, '').trim();
28
- if (!name || name.includes('[已脱敏:'))
29
- throw fail('WORKSPACE_SOURCE_CONFIRMATION_UNAVAILABLE');
30
- const question = '这个工作区以前用的是主机上的文件夹。要改用本机的『' + name + '』文件夹吗?';
31
- const record = (code, outcome, current = frozen, stage = 'source-confirmation') => hooks.record(request, { kind: 'need', severity: outcome === 'confirmed' ? 'info' : 'action-required', code, summary: outcome === 'confirmation-required' ? question : outcome === 'confirmed' ? '已确认改用本机文件夹,继续处理本轮原始请求。' : '本轮工作区来源确认未完成;原始请求保留,未执行。', payload: { phase: 'workspace-source-admission', stage, workspaceId: workspace.id, bindingId: current.bindingId, source: current.source, status: current.status, outcome } });
32
- // Persistence precedes the question; a failed diagnostic is not retried in this path.
33
- diagnosticAttempted = true;
34
- await record('WORKSPACE_SOURCE_CONFIRMATION_REQUIRED', 'confirmation-required');
35
- let terminalDiagnostic = false;
36
- async function stable(requireOldSource) {
37
- request.signal?.throwIfAborted();
38
- if (get() !== api || api.getContractInfo?.().workspaceLocations !== 1 || typeof api.getWorkspaceLocationState !== 'function')
39
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
40
- const now = api.getConnectionState(), location = api.getWorkspaceLocationState();
41
- if (now.phase !== 'connected' || connection.phase !== 'connected' || now.instanceId !== connection.instanceId || now.epoch !== connection.epoch || location.phase !== 'ready' || location.instanceId !== connection.instanceId || location.epoch !== connection.epoch || snapshot.phase !== 'ready' || snapshot.instanceId !== connection.instanceId || snapshot.epoch !== connection.epoch)
42
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
43
- const currentWorkspace = await hooks.workspace(request);
44
- request.signal?.throwIfAborted();
45
- if (!isDeepStrictEqual(currentWorkspace, workspace))
46
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
47
- if (!isDeepStrictEqual(hooks.currentUserTurn(request), turn))
48
- throw fail('LOCAL_USER_TURN_CHANGED');
49
- // Re-read after awaits. Only the explicit successful L selection may replace the frozen binding.
50
- if (get() !== api)
51
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
52
- const after = api.getConnectionState(), state = api.getWorkspaceLocationState();
53
- if (after.phase !== 'connected' || after.instanceId !== connection.instanceId || after.epoch !== connection.epoch || state.phase !== 'ready' || state.instanceId !== connection.instanceId || state.epoch !== connection.epoch)
54
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
55
- const current = state.items.filter(item => item.workspaceId === workspace.id);
56
- if (current.length !== 1 || current[0].localPath !== workspace.path || requireOldSource && (current[0].bindingId !== frozen.bindingId || current[0].source !== frozen.source || current[0].status !== frozen.status))
57
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
58
- return current[0];
59
- }
60
- try {
61
- await stable(true);
62
- if (typeof api.selectWorkspaceLocation !== 'function')
63
- throw fail('DEPENDENCY_REQUIRED');
64
- const decision = await hooks.confirm({ question, action: '改用本机文件夹' }, request);
65
- request.signal?.throwIfAborted();
66
- if (decision !== 'allowed-once') {
67
- const code = decision === 'rejected' ? 'WORKSPACE_SOURCE_CONFIRMATION_DECLINED' : decision === 'cancelled' ? 'ABORTED' : 'WORKSPACE_SOURCE_CONFIRMATION_UNAVAILABLE';
68
- terminalDiagnostic = true;
69
- await record(code, decision === 'rejected' ? 'declined' : decision === 'cancelled' ? 'cancelled' : 'failed');
70
- throw fail(code);
71
- }
72
- await stable(true);
73
- // The await continuation itself is a boundary: already-observed cancellation,
74
- // replacement input or source withdrawal must not start the L mutation.
75
- request.signal?.throwIfAborted();
76
- if (get() !== api || typeof api.getWorkspaceLocationState !== 'function')
77
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
78
- const atChoice = api.getConnectionState(), choiceState = api.getWorkspaceLocationState(), choiceRows = choiceState.items.filter(item => item.workspaceId === workspace.id);
79
- if (atChoice.phase !== 'connected' || atChoice.instanceId !== connection.instanceId || atChoice.epoch !== connection.epoch || choiceState.phase !== 'ready' || choiceState.instanceId !== connection.instanceId || choiceState.epoch !== connection.epoch || choiceRows.length !== 1 || choiceRows[0].bindingId !== frozen.bindingId || choiceRows[0].source !== frozen.source || choiceRows[0].status !== frozen.status || choiceRows[0].localPath !== workspace.path)
80
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
81
- if (!isDeepStrictEqual(hooks.currentUserTurn(request), turn))
82
- throw fail('LOCAL_USER_TURN_CHANGED');
83
- const selected = await api.selectWorkspaceLocation({ instanceId: connection.instanceId, epoch: connection.epoch, workspaceId: workspace.id, expectedBindingId: frozen.bindingId, source: 'local' });
84
- request.signal?.throwIfAborted();
85
- if (!selected.ok)
86
- throw connectNeedError(selected.error);
87
- const current = await stable(false), value = selected.value, returned = value.items.filter(item => item.workspaceId === workspace.id);
88
- if (value.phase !== 'ready' || value.instanceId !== connection.instanceId || value.epoch !== connection.epoch || returned.length !== 1 || returned[0].source !== 'local' || returned[0].status !== 'selected' || !returned[0].bindingId || returned[0].bindingId === frozen.bindingId || returned[0].localPath !== workspace.path || current.source !== 'local' || current.status !== 'selected' || current.bindingId !== returned[0].bindingId)
89
- throw fail('WORKSPACE_SOURCE_SELECTION_UNCONFIRMED');
90
- const selectedBinding = returned[0].bindingId;
91
- const assertSelected = () => {
92
- request.signal?.throwIfAborted();
93
- if (get() !== api || typeof api.getWorkspaceLocationState !== 'function')
94
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
95
- const c = api.getConnectionState(), s = api.getWorkspaceLocationState(), items = s.items.filter(item => item.workspaceId === workspace.id);
96
- if (c.phase !== 'connected' || c.instanceId !== connection.instanceId || c.epoch !== connection.epoch || s.phase !== 'ready' || s.instanceId !== connection.instanceId || s.epoch !== connection.epoch || items.length !== 1 || items[0].source !== 'local' || items[0].status !== 'selected' || items[0].bindingId !== selectedBinding || items[0].localPath !== workspace.path)
97
- throw fail('EMPLOYEE_CONTEXT_CHANGED');
98
- if (!isDeepStrictEqual(hooks.currentUserTurn(request), turn))
99
- throw fail('LOCAL_USER_TURN_CHANGED');
100
- };
101
- terminalDiagnostic = true;
102
- await record('WORKSPACE_SOURCE_CONFIRMED', 'confirmed', current);
103
- terminalDiagnostic = false;
104
- await stable(false);
105
- assertSelected();
106
- return { assertCurrent: assertSelected, async recordFailure(error, stage) {
107
- try {
108
- await record(request.signal?.aborted ? 'ABORTED' : codeOf(error), request.signal?.aborted ? 'cancelled' : 'failed', current, stage);
109
- }
110
- catch (diagnostic) {
111
- throw diagnosticFailure(error, diagnostic);
112
- }
113
- } };
114
- }
115
- catch (error) {
116
- if (!terminalDiagnostic) {
117
- try {
118
- await record(request.signal?.aborted ? 'ABORTED' : codeOf(error), request.signal?.aborted ? 'cancelled' : 'failed');
119
- }
120
- catch (diagnostic) {
121
- throw diagnosticFailure(error, diagnostic);
122
- }
123
- }
124
- throw error;
125
- }
126
- }
127
- catch (error) {
128
- if (!diagnosticAttempted) {
129
- try {
130
- await hooks.record(request, { kind: 'need', severity: 'action-required', code: request.signal?.aborted ? 'ABORTED' : codeOf(error), summary: '无法核对本轮工作区来源,已停止;原话保留。', payload: { phase: 'workspace-source-admission', stage: 'source-context', ...initialContext, outcome: request.signal?.aborted ? 'cancelled' : 'failed' } });
131
- }
132
- catch (diagnostic) {
133
- throw diagnosticFailure(error, diagnostic);
134
- }
135
- }
136
- throw error;
137
- }
138
- }
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-workbench/remote-agent",
3
- "version": "0.8.21",
3
+ "version": "0.8.24",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,11 +22,9 @@
22
22
  "scripts": {
23
23
  "test": "npm run build && node --test --test-concurrency=1 lib/test/*.test.js",
24
24
  "typecheck": "node node_modules/typescript/bin/tsc --noEmit",
25
- "build": "node node_modules/typescript/bin/tsc && node node_modules/typescript/bin/tsc -p tsconfig.build.json && chmod 755 lib/src/sdk-ssh-wrapper.js dist/sdk-ssh-wrapper.js && node tools/build-client.mjs",
25
+ "build": "node node_modules/typescript/bin/tsc && node node_modules/typescript/bin/tsc -p tsconfig.build.json && node tools/build-client.mjs",
26
26
  "probe:host": "node tools/probe-host.ts",
27
- "probe:broker": "node tools/probe-broker.ts",
28
- "pack:artifact": "node tools/pack-artifact.mjs",
29
- "probe:formal": "node tools/probe-formal.mjs"
27
+ "pack:artifact": "node tools/pack-artifact.mjs"
30
28
  },
31
29
  "engines": {
32
30
  "node": ">=24.13.1"
@@ -35,12 +33,11 @@
35
33
  "@agentclientprotocol/sdk": "2.0.0",
36
34
  "@modelcontextprotocol/sdk": "1.32.1",
37
35
  "@openai/codex": "0.160.1",
38
- "@openai/codex-sdk": "0.163.0",
39
36
  "zod": "4.6.7"
40
37
  },
41
38
  "devDependencies": {
42
- "@ai-workbench/connect": "file:test/fixtures/ai-workbench-connect-0.7.20.tgz",
43
- "@ai-workbench/network": "file:test/fixtures/ai-workbench-network-0.4.4.tgz",
39
+ "@ai-workbench/connect": "file:test/fixtures/ai-workbench-connect-0.7.27.tgz",
40
+ "@ai-workbench/network": "file:test/fixtures/ai-workbench-network-0.4.9.tgz",
44
41
  "@deepseek-ai/cordis": "4.0.4",
45
42
  "@deepseek-ai/cordis-plugin-group": "1.0.4",
46
43
  "@deepseek-ai/cordis-plugin-include": "1.0.9",
@@ -65,11 +62,12 @@
65
62
  "react": "18.3.1",
66
63
  "react-dom": "18.3.1",
67
64
  "typescript": "5.9.3",
68
- "zustand": "4.4.7"
65
+ "zustand": "4.4.7",
66
+ "@deepseek-ai/dsh-sandbox-policy": "0.2.0-rc.2"
69
67
  },
70
68
  "peerDependencies": {
71
- "@ai-workbench/connect": "~0.7.20",
72
- "@ai-workbench/network": "~0.4.4",
69
+ "@ai-workbench/connect": "~0.7.27",
70
+ "@ai-workbench/network": "~0.4.9",
73
71
  "@deepseek-ai/cordis": "4.0.4",
74
72
  "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
75
73
  "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
@@ -77,7 +75,8 @@
77
75
  "@deepseek-ai/dsh-session": "0.2.0-rc.2",
78
76
  "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
79
77
  "@deepseek-ai/dsh-user-approval": "0.2.0-rc.2",
80
- "@deepseek-ai/schemastery": "3.18.4"
78
+ "@deepseek-ai/schemastery": "3.18.4",
79
+ "@deepseek-ai/dsh-sandbox-policy": "0.2.0-rc.2"
81
80
  },
82
81
  "dsh": {
83
82
  "bundle": {
@@ -1,16 +0,0 @@
1
- import { Context } from '@deepseek-ai/cordis';
2
- import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
3
- import type { CatalogSshEvent } from './catalog-ssh.ts';
4
- export declare function createCatalogDiagnostics(profile: () => string | undefined): {
5
- record(event: Readonly<CatalogSshEvent>): Promise<void>;
6
- snapshot(): Promise<{
7
- storageError: string | null;
8
- records: CatalogSshEvent[];
9
- }>;
10
- };
11
- export declare class CatalogDiagnosticsRemote extends TypertRemoteService {
12
- private readonly snapshot;
13
- private disposed;
14
- constructor(ctx: Context, snapshot: () => Promise<unknown>);
15
- read(request: unknown, signal: AbortSignal): Promise<unknown>;
16
- }
@@ -1,108 +0,0 @@
1
- var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
- var useValue = arguments.length > 2;
3
- for (var i = 0; i < initializers.length; i++) {
4
- value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
- }
6
- return useValue ? value : void 0;
7
- };
8
- var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
- function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
- var _, done = false;
14
- for (var i = decorators.length - 1; i >= 0; i--) {
15
- var context = {};
16
- for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
- for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
- context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
- var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
- if (kind === "accessor") {
21
- if (result === void 0) continue;
22
- if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
- if (_ = accept(result.get)) descriptor.get = _;
24
- if (_ = accept(result.set)) descriptor.set = _;
25
- if (_ = accept(result.init)) initializers.unshift(_);
26
- }
27
- else if (_ = accept(result)) {
28
- if (kind === "field") initializers.unshift(_);
29
- else descriptor[key] = _;
30
- }
31
- }
32
- if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
- done = true;
34
- };
35
- import { join } from 'node:path';
36
- import { open, rename, lstat, realpath } from 'node:fs/promises';
37
- import { randomUUID } from 'node:crypto';
38
- import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
39
- import { privateDirectory, durableWrite, readJournal } from "./remote-details.js";
40
- export function createCatalogDiagnostics(profile) {
41
- let tail = Promise.resolve();
42
- const errors = new Map();
43
- const latest = new Map();
44
- const dir = () => { const p = profile(); if (!p)
45
- throw Error('PROFILE_UNAVAILABLE'); return join(p, 'ai-workbench-remote-agent', 'catalog-diagnostics'); };
46
- const prepare = async () => { const root = dir(); await privateDirectory(join(root, '..')); await privateDirectory(root); return root; };
47
- return {
48
- record(event) {
49
- const saved = structuredClone(event);
50
- if (event.stage === 'started' || !latest.has(event.provider) || latest.get(event.provider)?.eventId === event.eventId)
51
- latest.set(event.provider, saved);
52
- const work = tail.then(async () => { const root = await prepare(); await durableWrite(join(root, event.eventId + '-' + event.stage + '.json'), saved); if (latest.get(event.provider)?.eventId !== event.eventId)
53
- return; const temp = join(root, randomUUID() + '.current'); await durableWrite(temp, saved); await rename(temp, join(root, event.provider + '-latest.json')); const d = await open(root, 'r'); try {
54
- await d.sync();
55
- }
56
- finally {
57
- await d.close();
58
- } errors.delete(event.provider); });
59
- tail = work.catch(() => { errors.set(event.provider, '本机目录诊断未能写入磁盘,本次记录仅在当前客户端内可见;请联系管理员检查本机诊断目录。'); });
60
- return tail;
61
- },
62
- async snapshot() {
63
- await tail;
64
- for (const brand of ['claude', 'codex'])
65
- if (!latest.has(brand)) {
66
- try {
67
- const root = dir(), info = await lstat(root);
68
- if (!info.isDirectory() || info.isSymbolicLink() || await realpath(root) !== root || (info.mode & 0o077) !== 0 || process.getuid && info.uid !== process.getuid())
69
- throw Error('CATALOG_DIAGNOSTIC_INVALID');
70
- const value = await readJournal(join(root, brand + '-latest.json'));
71
- if (value?.version !== 1 || value.provider !== brand || !['started', 'closed'].includes(value.stage) || !/^[-a-f0-9]{36}$/.test(value.eventId))
72
- throw Error('CATALOG_DIAGNOSTIC_INVALID');
73
- if (!latest.has(brand))
74
- latest.set(brand, value);
75
- }
76
- catch (error) {
77
- if (error.code !== 'ENOENT')
78
- errors.set(brand, '本机已保存的目录诊断暂不可读;未将它当作当前状态,请联系管理员检查本机诊断目录。');
79
- }
80
- }
81
- return { storageError: errors.size ? [...new Set(errors.values())].join('\n') : null, records: [...latest.values()].map(v => structuredClone(v)) };
82
- }
83
- };
84
- }
85
- let CatalogDiagnosticsRemote = (() => {
86
- let _classSuper = TypertRemoteService;
87
- let _instanceExtraInitializers = [];
88
- let _read_decorators;
89
- return class CatalogDiagnosticsRemote extends _classSuper {
90
- static {
91
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
92
- _read_decorators = [Remote];
93
- __esDecorate(this, null, _read_decorators, { kind: "method", name: "read", static: false, private: false, access: { has: obj => "read" in obj, get: obj => obj.read }, metadata: _metadata }, null, _instanceExtraInitializers);
94
- if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
95
- }
96
- snapshot = __runInitializers(this, _instanceExtraInitializers);
97
- disposed = false;
98
- constructor(ctx, snapshot) {
99
- super(ctx, 'awRemoteModelDiagnostics');
100
- this.snapshot = snapshot;
101
- ctx.effect(() => () => { this.disposed = true; });
102
- }
103
- async read(request, signal) { signal.throwIfAborted(); if (this.disposed)
104
- throw Error('DISPOSED'); const value = await this.snapshot(); signal.throwIfAborted(); if (this.disposed)
105
- throw Error('DISPOSED'); return value; }
106
- };
107
- })();
108
- export { CatalogDiagnosticsRemote };
@@ -1,8 +0,0 @@
1
- import { type ConnectTarget } from './provider.ts';
2
- /** S host-layout 0.3.4 fixed executable conventions, not employee policy defaults. */
3
- export declare function protectedOneShotLayout(target: ConnectTarget): {
4
- runner: string;
5
- runnerKind: "executable";
6
- root: string;
7
- codex: string;
8
- };
@@ -1,9 +0,0 @@
1
- import { validateTarget } from "./provider.js";
2
- import { NeedError } from "./remote.js";
3
- /** S host-layout 0.3.4 fixed executable conventions, not employee policy defaults. */
4
- export function protectedOneShotLayout(target) {
5
- validateTarget(target);
6
- if (!/^emp-[a-z0-9][a-z0-9-]*$/.test(target.username) || target.session.root !== `/Users/${target.username}/.aw/s`)
7
- throw new NeedError('HOST_LAYOUT_MISMATCH', '远程主机的运行位置需要管理员核对', 'S声明的同一员工HOME/.aw/s与HOME/.aw/oneshot布局');
8
- return { runner: '/usr/local/aw-host/bin/aw-codex-once', runnerKind: 'executable', root: `/Users/${target.username}/.aw/oneshot`, codex: '/opt/homebrew/bin/codex' };
9
- }
package/dist/job.d.ts DELETED
@@ -1,17 +0,0 @@
1
- import { type ChildProcess } from 'node:child_process';
2
- import { type DevelopmentSshTarget } from './remote.ts';
3
- import { type ConnectTarget } from './provider.ts';
4
- export interface JobSpec {
5
- target: DevelopmentSshTarget | ConnectTarget;
6
- cwd: string;
7
- runner: string;
8
- runnerKind: 'executable' | 'node-source';
9
- root: string;
10
- codex: string;
11
- id: string;
12
- /** Test/programmatic transport seam only; never an employee setting. */
13
- sshExecutable?: string;
14
- }
15
- export type OneShotRuntime = Omit<JobSpec, 'id' | 'sshExecutable'>;
16
- export declare function jobCommand(spec: JobSpec, op: 'prepare' | 'stage' | 'run' | 'kill'): string;
17
- export declare function executeJob(spec: JobSpec, op: 'prepare' | 'stage' | 'run' | 'kill', input?: string, onSpawn?: (child: ChildProcess) => void, inheritOutput?: boolean, signal?: AbortSignal): Promise<number | null>;
package/dist/job.js DELETED
@@ -1,57 +0,0 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
- import { spawn } from 'node:child_process';
10
- import { shellQuote, sshArgs, NeedError } from "./remote.js";
11
- import { validateTarget } from "./provider.js";
12
- import { protectedOneShotLayout } from "./host-layout.js";
13
- export function jobCommand(spec, op) {
14
- if (!/^[a-f0-9]{32}$/.test(spec.id) || !['executable', 'node-source'].includes(spec.runnerKind) || ![spec.cwd, spec.root, spec.runner, spec.codex].every(p => p.startsWith('/') && !p.includes('\0'))) {
15
- throw new NeedError('INVALID_RUNTIME', '我需要主机提供有效的单次运行上下文', '主机程序与工作目录');
16
- }
17
- if (!('kind' in spec.target)) {
18
- const layout = protectedOneShotLayout(spec.target);
19
- if (spec.runnerKind !== layout.runnerKind || spec.runner !== layout.runner || spec.root !== layout.root || spec.codex !== layout.codex)
20
- throw new NeedError('INVALID_RUNTIME', '远程主机的单次运行位置需要管理员核对', 'S安装的受保护单次运行路径');
21
- }
22
- return [...(spec.runnerKind === 'node-source' ? ['node'] : []), spec.runner, op, spec.root, spec.id].map(shellQuote).join(' ');
23
- }
24
- export async function executeJob(spec, op, input, onSpawn, inheritOutput = false, signal) {
25
- signal?.throwIfAborted();
26
- const command = jobCommand(spec, op);
27
- let args;
28
- if ('kind' in spec.target) {
29
- args = sshArgs(spec.target, command, op !== 'stage');
30
- }
31
- else {
32
- validateTarget(spec.target);
33
- if (spec.runnerKind !== 'executable')
34
- throw new NeedError('INVALID_RUNTIME', '主机单次助手必须使用管理员安装的启动器', 'host-layout 单次启动器');
35
- let supplied;
36
- try {
37
- const moduleName = '@ai-workbench/connect';
38
- supplied = await import(__rewriteRelativeImportExtension(moduleName));
39
- if (typeof supplied.sshArgs !== 'function')
40
- throw Error('missing sshArgs');
41
- }
42
- catch {
43
- throw new NeedError('DEPENDENCY_REQUIRED', '我需要连接组件提供可信连接', '@ai-workbench/connect sshArgs');
44
- }
45
- args = [...(op === 'stage' ? [] : ['-n']), ...supplied.sshArgs(spec.target), command];
46
- }
47
- signal?.throwIfAborted();
48
- const child = spawn(spec.sshExecutable ?? 'ssh', args, { stdio: ['pipe', inheritOutput ? 'inherit' : 'ignore', 'ignore'], signal });
49
- onSpawn?.(child);
50
- child.stdin.on('error', () => { }); // Exit status owns transport failure; EPIPE cannot mean success.
51
- child.stdin.end(input);
52
- return new Promise((resolve, reject) => {
53
- let failed = false;
54
- child.on('error', () => { failed = true; });
55
- child.on('close', code => failed ? reject(new NeedError('SSH_FAILED', 'SSH 启动失败或已取消,根因未查明', '可用的员工连接')) : resolve(code));
56
- });
57
- }
@@ -1,29 +0,0 @@
1
- import type { AgentDefinition } from './agents.ts';
2
- import { type HostModel } from './model-catalog.ts';
3
- export interface DefaultSelection {
4
- provider: string;
5
- model: string;
6
- reasoningEffort?: string;
7
- }
8
- /** Public agentDefaultModel service; writes below use the atomic config editor. */
9
- export interface DefaultModelService {
10
- currentSelection(): DefaultSelection;
11
- }
12
- interface DefaultEntry {
13
- options: {
14
- name?: string;
15
- };
16
- }
17
- /** Public rc.2 configEditor.entries/edit subset; no ownerContext/private config access. */
18
- export interface DefaultConfigEditor {
19
- entries(): readonly DefaultEntry[];
20
- edit(entry: DefaultEntry, change: (current: Record<string, unknown>, inherited: Record<string, unknown>) => Record<string, unknown>): Promise<void>;
21
- }
22
- export type DefaultSelectionWriter = (expected: DefaultSelection, next: DefaultSelection, assertCurrent: () => void) => Promise<void>;
23
- /** The editor invokes the guard synchronously under its profile lock after reconciliation. */
24
- export declare function createDefaultSelectionWriter(service: DefaultModelService, getEditor: () => DefaultConfigEditor | undefined): DefaultSelectionWriter;
25
- export declare function createModelDefaults(service: DefaultModelService, report: (provider: string, message: string | undefined) => void, write?: DefaultSelectionWriter): {
26
- observe(agent: AgentDefinition, models: readonly HostModel[], assertCurrent?: () => void): Promise<void>;
27
- dispose(): void;
28
- };
29
- export {};
@@ -1,62 +0,0 @@
1
- import { declaredDefault } from "./model-catalog.js";
2
- const same = (a, b) => a.provider === b.provider && a.model === b.model && a.reasoningEffort === b.reasoningEffort;
3
- /** The editor invokes the guard synchronously under its profile lock after reconciliation. */
4
- export function createDefaultSelectionWriter(service, getEditor) {
5
- return async (expected, next, assertCurrent) => {
6
- const editor = getEditor();
7
- const entries = editor?.entries().filter(entry => entry.options.name === '@deepseek-ai/dsh-agent-default-model');
8
- if (!editor || entries?.length !== 1)
9
- throw Error('缺少唯一可原子编辑的官方默认模型设置');
10
- await editor.edit(entries[0], current => {
11
- assertCurrent();
12
- if (!same(expected, service.currentSelection()))
13
- throw Error('员工已改变模型选择,本次默认更新已撤回');
14
- const updated = { ...current, provider: next.provider, model: next.model };
15
- Reflect.deleteProperty(updated, 'reasoningEffort');
16
- if (next.reasoningEffort !== undefined)
17
- Object.assign(updated, { reasoningEffort: next.reasoningEffort });
18
- return updated;
19
- });
20
- };
21
- }
22
- export function createModelDefaults(service, report, write) {
23
- let disposed = false, queue = Promise.resolve();
24
- return {
25
- observe(agent, models, assertCurrent = () => { }) {
26
- const snapshot = { ...service.currentSelection() };
27
- const job = queue.then(async () => {
28
- if (disposed || snapshot.provider !== agent.id || !same(snapshot, service.currentSelection()))
29
- return;
30
- if (snapshot.model !== 'remote' && models.some(model => model.id === snapshot.model)) {
31
- report(agent.id, undefined);
32
- return;
33
- }
34
- const model = declaredDefault(agent, models);
35
- const notice = model ? undefined : '提供方没有声明唯一的默认模型,请先选择模型。(诊断代码:MODEL_DEFAULT_UNAVAILABLE)';
36
- const next = { provider: agent.id, model: model?.id ?? '', ...(model?.reasoning.defaultKnown && model.reasoning.defaultValue !== null ? { reasoningEffort: model.reasoning.defaultValue } : {}) };
37
- try {
38
- const guard = () => { assertCurrent(); if (disposed)
39
- throw Error('默认设置消费已撤回'); };
40
- guard();
41
- if (!same(snapshot, next)) {
42
- if (!write)
43
- throw Error('缺少原子配置能力');
44
- await write(snapshot, next, guard);
45
- }
46
- if (disposed)
47
- return;
48
- if (!same(next, service.currentSelection()))
49
- throw Error('未确认官方默认模型设置已保存');
50
- report(agent.id, notice);
51
- }
52
- catch {
53
- if (!disposed)
54
- report(agent.id, (notice ? notice + '\n' : '') + '未能确认默认模型设置已更新,员工新选择不会被迟到的自动更新覆盖;请在模型菜单选择当前目录中的模型。(诊断代码:MODEL_DEFAULT_SAVE_FAILED)');
55
- }
56
- });
57
- queue = job;
58
- return job;
59
- },
60
- dispose() { disposed = true; },
61
- };
62
- }
package/dist/oneshot.d.ts DELETED
@@ -1,13 +0,0 @@
1
- import { type ThreadEvent, type SandboxMode } from '@openai/codex-sdk';
2
- import { type OneShotRuntime } from './job.ts';
3
- export type { OneShotRuntime } from './job.ts';
4
- export interface OneShotOptions {
5
- wrapperPath: string;
6
- sshExecutable?: string;
7
- sandboxMode?: SandboxMode;
8
- approvalPolicy?: 'never';
9
- /** Independent disposal signal for control/cleanup; not the already-cancelled turn signal. */
10
- controlSignal?: AbortSignal;
11
- onTiming?: (phase: string, elapsedMs: number) => void;
12
- }
13
- export declare function runOneShot(runtime: OneShotRuntime, options: OneShotOptions, prompt: string, resumeId?: string, signal?: AbortSignal): AsyncGenerator<ThreadEvent>;
package/dist/oneshot.js DELETED
@@ -1,78 +0,0 @@
1
- import { Codex } from '@openai/codex-sdk';
2
- import { randomUUID } from 'node:crypto';
3
- import { mkdtemp, writeFile, rm } from 'node:fs/promises';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
- import { executeJob } from "./job.js";
7
- import { NeedError } from "./remote.js";
8
- export async function* runOneShot(runtime, options, prompt, resumeId, signal) {
9
- if (!options.sandboxMode || options.approvalPolicy !== 'never')
10
- throw new NeedError('SETTINGS_REQUIRED', '我需要管理员明确单次模式的沙箱及无交互审批设置', 'oneShotSandbox / oneShotApproval(待 owner 确认)');
11
- signal?.throwIfAborted();
12
- const started = performance.now();
13
- const timing = (phase) => options.onTiming?.(phase, Math.round(performance.now() - started));
14
- const dir = await mkdtemp(join(tmpdir(), 'aw-x-job-'));
15
- const spec = { ...runtime, id: randomUUID().replaceAll('-', ''), sshExecutable: options.sshExecutable };
16
- try {
17
- timing('prepare-start');
18
- const prepareSignal = signal && options.controlSignal ? AbortSignal.any([signal, options.controlSignal]) : signal ?? options.controlSignal;
19
- if (await executeJob(spec, 'prepare', undefined, undefined, false, prepareSignal) !== 0)
20
- throw new NeedError('ONESHOT_FAILED', '主机单次运行准备失败,根因未查明', '已安装的主机单次运行助手');
21
- timing('prepare-complete');
22
- signal?.throwIfAborted();
23
- const file = join(dir, 'job.json');
24
- await writeFile(file, JSON.stringify(spec), { mode: 0o600 });
25
- const codex = new Codex({ codexPathOverride: options.wrapperPath, env: { PATH: process.env.PATH ?? '', AW_X_JOB_FILE: file } });
26
- const threadOptions = { workingDirectory: runtime.cwd, sandboxMode: options.sandboxMode, approvalPolicy: options.approvalPolicy };
27
- const thread = resumeId ? codex.resumeThread(resumeId, threadOptions) : codex.startThread(threadOptions);
28
- timing('sdk-start');
29
- const { events } = await thread.runStreamed(prompt, { signal });
30
- let first = true;
31
- let firstModel = true;
32
- let completed = false;
33
- for await (const event of events) {
34
- const lifecycle = event;
35
- if (lifecycle.type === 'aw.remote.lifecycle') {
36
- if (lifecycle.phase === 'remote-run-ready' || lifecycle.phase === 'remote-codex-spawned')
37
- timing(lifecycle.phase);
38
- continue;
39
- }
40
- if (first) {
41
- timing('first-event');
42
- first = false;
43
- }
44
- if (firstModel && (event.type === 'item.started' || event.type === 'item.updated' || event.type === 'item.completed') && (event.item.type === 'agent_message' || event.item.type === 'reasoning')) {
45
- firstModel = false;
46
- timing('first-model-output');
47
- }
48
- if (event.type === 'turn.failed' || event.type === 'error')
49
- throw new NeedError('ONESHOT_FAILED', 'Codex 单次运行返回失败,根因未查明', '可用的主机 Codex 会话');
50
- if (event.type === 'turn.completed')
51
- completed = true;
52
- yield event;
53
- }
54
- if (!completed)
55
- throw new NeedError('ONESHOT_INCOMPLETE', '连接结束前未收到 turn.completed,结果不完整', '确认远端运行状态');
56
- }
57
- catch (error) {
58
- if (signal?.aborted)
59
- throw new NeedError('ABORTED', '本轮单次运行已取消', '重新开始或恢复已保存的线程');
60
- if (error instanceof NeedError)
61
- throw error;
62
- throw new NeedError('ONESHOT_FAILED', 'SSH / Codex 单次运行失败,根因未查明', '有效的远端运行连接');
63
- }
64
- finally {
65
- timing('cleanup-start');
66
- let cleanupOk = false;
67
- try {
68
- cleanupOk = (await executeJob(spec, 'kill', undefined, undefined, false, options.controlSignal)) === 0;
69
- }
70
- catch { /* No fallback: every failed control attempt below reports cleanup unconfirmed. */ }
71
- finally {
72
- await rm(dir, { recursive: true });
73
- }
74
- timing('cleanup-complete');
75
- if (!cleanupOk)
76
- throw new NeedError('REMOTE_CLEANUP_UNCONFIRMED', '连接中断,无法确认远端进程已结束;需要管理员确认此轮状态', '恢复连接并结束对应运行');
77
- }
78
- }
@@ -1,13 +0,0 @@
1
- /** Byte fragments remain only in memory. A cursor is clean only after SDK consumption. */
2
- export declare class ProtocolBoundary {
3
- private readonly maxBytes;
4
- private partial;
5
- private pending;
6
- private requests;
7
- constructor(maxBytes: number);
8
- receive(bytes: Uint8Array, gate?: (message: Record<string, unknown>) => void): void;
9
- consumed(message: Record<string, unknown>): void;
10
- responded(id: unknown): void;
11
- get complete(): boolean;
12
- get wholeLine(): boolean;
13
- }