@ai-workbench/remote-agent 0.0.0-stage → 0.8.1

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 (46) hide show
  1. package/README.md +58 -2
  2. package/cordis.patch.yml +3 -0
  3. package/dist/acp.d.ts +54 -0
  4. package/dist/acp.js +341 -0
  5. package/dist/agents.d.ts +15 -0
  6. package/dist/agents.js +4 -0
  7. package/dist/broker.d.ts +67 -0
  8. package/dist/broker.js +397 -0
  9. package/dist/client.js +36 -0
  10. package/dist/connect-contract.d.ts +26 -0
  11. package/dist/connect-contract.js +1 -0
  12. package/dist/conversation-details.d.ts +47 -0
  13. package/dist/conversation-details.js +319 -0
  14. package/dist/host-layout.d.ts +8 -0
  15. package/dist/host-layout.js +9 -0
  16. package/dist/index.d.ts +114 -0
  17. package/dist/index.js +360 -0
  18. package/dist/input.d.ts +12 -0
  19. package/dist/input.js +49 -0
  20. package/dist/interaction.d.ts +19 -0
  21. package/dist/interaction.js +1 -0
  22. package/dist/job.d.ts +17 -0
  23. package/dist/job.js +57 -0
  24. package/dist/mapping.d.ts +46 -0
  25. package/dist/mapping.js +230 -0
  26. package/dist/oneshot.d.ts +13 -0
  27. package/dist/oneshot.js +78 -0
  28. package/dist/presentation.d.ts +6 -0
  29. package/dist/presentation.js +45 -0
  30. package/dist/provider-locale.d.ts +19 -0
  31. package/dist/provider-locale.js +73 -0
  32. package/dist/provider.d.ts +144 -0
  33. package/dist/provider.js +164 -0
  34. package/dist/readiness.d.ts +28 -0
  35. package/dist/readiness.js +192 -0
  36. package/dist/remote-details.d.ts +26 -0
  37. package/dist/remote-details.js +250 -0
  38. package/dist/remote.d.ts +34 -0
  39. package/dist/remote.js +51 -0
  40. package/dist/sdk-ssh-wrapper.d.ts +2 -0
  41. package/dist/sdk-ssh-wrapper.js +34 -0
  42. package/dist/workspace-notice.d.ts +47 -0
  43. package/dist/workspace-notice.js +273 -0
  44. package/locale/en.json +10 -0
  45. package/locale/zh.json +10 -0
  46. package/package.json +97 -4
@@ -0,0 +1,230 @@
1
+ import { NeedError } from "./remote.js";
2
+ import { sanitizeObservation } from "./conversation-details.js";
3
+ const usageCounters = ['inputTokens', 'outputTokens', 'cachedReadTokens', 'cachedWriteTokens', 'thoughtTokens', 'totalTokens'];
4
+ const optionalUsageCounters = ['cachedReadTokens', 'cachedWriteTokens', 'thoughtTokens', 'totalTokens'];
5
+ /** Only model text enters DSH blocks; remote operations remain diagnostic observations. */
6
+ export class StreamMapper {
7
+ index = 0;
8
+ active;
9
+ seen = new Map();
10
+ baseline;
11
+ observations = [];
12
+ onDiagnostic;
13
+ /** Omitted argument is exclusively for a known new session; pass null on unknown resume. */
14
+ constructor(previousUsage = { version: 1, kind: 'zero' }, onDiagnostic) {
15
+ if (previousUsage !== null && !validUsageSnapshot(previousUsage))
16
+ throw new NeedError('USAGE_BASELINE_INVALID', '已保存的远端累计用量基线无效,不能重复结算历史用量', '与原远端会话关联的有效用量快照');
17
+ this.baseline = previousUsage === null ? null : { ...previousUsage };
18
+ this.onDiagnostic = onDiagnostic;
19
+ }
20
+ get usageSnapshot() { return this.baseline === null ? null : { ...this.baseline }; }
21
+ get diagnostics() {
22
+ return Object.freeze(this.observations.map(observation => sanitizeObservation(observation)));
23
+ }
24
+ diagnostic(observation) {
25
+ const sanitized = sanitizeObservation(observation);
26
+ this.observations.push(sanitized);
27
+ this.onDiagnostic?.(sanitized);
28
+ return [];
29
+ }
30
+ usageWarning(summary) {
31
+ return this.diagnostic({ kind: 'usage', severity: 'warning', code: 'USAGE_UNAVAILABLE', summary });
32
+ }
33
+ text(text, reasoning = false) {
34
+ if (!text)
35
+ return [];
36
+ const type = reasoning ? 'reasoning' : 'text';
37
+ const chunks = this.active && this.active.type !== type ? this.flush() : [];
38
+ if (!this.active) {
39
+ this.active = { index: this.index++, type, text: '' };
40
+ chunks.push({ type: 'block-start', index: this.active.index, blockType: type });
41
+ }
42
+ const { index } = this.active;
43
+ this.active.text += text;
44
+ chunks.push(reasoning ? { type: 'reasoning-delta', index, text } : { type: 'text-delta', index, text });
45
+ return chunks;
46
+ }
47
+ /** Close the current text or reasoning block once, before the stream settles. */
48
+ flush() {
49
+ if (!this.active)
50
+ return [];
51
+ const { index, type, text } = this.active;
52
+ this.active = undefined;
53
+ return [{ type: 'block-end', index, block: { type, text } }];
54
+ }
55
+ acp(update) {
56
+ if (update.sessionUpdate === 'agent_message_chunk' || update.sessionUpdate === 'agent_thought_chunk') {
57
+ if (update.content.type === 'text' && typeof update.content.text === 'string') {
58
+ // The whole update preserves every extension layer without inventing a display mapping.
59
+ if (Object.keys(update).some(key => key !== 'sessionUpdate' && key !== 'content')
60
+ || Object.keys(update.content).some(key => key !== 'type' && key !== 'text')) {
61
+ this.diagnostic({ kind: 'extension', severity: 'info', code: 'ACP_MODEL_METADATA', summary: '远端模型消息扩展', method: 'session/update', payload: update });
62
+ }
63
+ return this.text(update.content.text, update.sessionUpdate === 'agent_thought_chunk');
64
+ }
65
+ return this.diagnostic({ kind: 'extension', severity: 'warning', code: 'ACP_CONTENT_UNSUPPORTED', summary: '远端非文本内容,需要对应的公开显示映射', method: 'session/update', payload: update });
66
+ }
67
+ let kind = 'session';
68
+ let code = 'ACP_SESSION_UPDATE';
69
+ let summary = '远端会话事件';
70
+ let severity = 'info';
71
+ switch (update.sessionUpdate) {
72
+ case 'available_commands_update':
73
+ kind = 'commands';
74
+ code = 'ACP_COMMANDS';
75
+ summary = '远端命令与技能目录';
76
+ break;
77
+ case 'tool_call':
78
+ case 'tool_call_update':
79
+ kind = 'tool';
80
+ code = 'ACP_TOOL';
81
+ summary = '远端工具事件';
82
+ break;
83
+ case 'usage_update':
84
+ kind = 'usage';
85
+ code = 'ACP_USAGE_CONTEXT';
86
+ summary = '远端上下文占用,不替代生成用量';
87
+ break;
88
+ case 'notice':
89
+ kind = 'notice';
90
+ code = 'ACP_NOTICE';
91
+ summary = '远端通知';
92
+ break;
93
+ case 'user_message_chunk':
94
+ case 'plan':
95
+ case 'plan_update':
96
+ case 'plan_removed':
97
+ case 'current_mode_update':
98
+ case 'config_option_update':
99
+ case 'session_info_update':
100
+ case 'compaction_update':
101
+ case 'compaction_summary_chunk':
102
+ case 'session_message_chunk':
103
+ case 'session_message': break;
104
+ default:
105
+ kind = 'extension';
106
+ code = 'ACP_EVENT_UNSUPPORTED';
107
+ severity = 'warning';
108
+ summary = '远端未知事件,需要对应的公开显示映射';
109
+ }
110
+ return this.diagnostic({ kind, severity, code, summary, method: 'session/update', payload: update });
111
+ }
112
+ /** ACP usage and the fixed Claude quota both accumulate across the session, not per turn. */
113
+ acpResponse(response) {
114
+ this.diagnostic({ kind: 'usage', severity: 'info', code: 'ACP_PROMPT_RESPONSE', summary: '远端响应与真实用量', method: 'session/prompt', payload: response });
115
+ const quota = record(response._meta?.quota);
116
+ const tokens = record(quota?.token_count);
117
+ const usage = response.usage;
118
+ let chunks;
119
+ if (usage) {
120
+ chunks = this.cumulativeUsage('usage', usage.inputTokens, usage.outputTokens, usage.cachedReadTokens, usage.cachedWriteTokens, usage.thoughtTokens, usage.totalTokens);
121
+ }
122
+ else if (tokens) {
123
+ // Fixed Claude adapter's quotaTokenCount: inputTokens excludes both
124
+ // cachedInputTokens (reads) and cachedWriteTokens (writes).
125
+ chunks = this.cumulativeUsage('quota', tokens.inputTokens, tokens.outputTokens, tokens.cachedInputTokens, tokens.cachedWriteTokens, undefined, tokens.totalTokens);
126
+ }
127
+ else {
128
+ this.usageGap();
129
+ chunks = this.usageWarning('\n[远端用量] 本轮生成用量未提供;上下文占用不能替代输入或输出计数。\n');
130
+ }
131
+ return chunks;
132
+ }
133
+ usageGap() { this.baseline = this.baseline?.kind === 'cumulative' ? { ...this.baseline, gap: true } : null; }
134
+ cumulativeUsage(source, input, output, read, write, thought, total) {
135
+ const values = [input, output, ...(read == null ? [] : [read]), ...(write == null ? [] : [write]), ...(thought == null ? [] : [thought]), ...(total == null ? [] : [total])];
136
+ if (!values.every(validCount)) {
137
+ this.usageGap();
138
+ return this.usageWarning('\n[远端用量] 远端累计计数无效,本轮未记录生成用量;需要有效的输入、输出与缓存计数。\n');
139
+ }
140
+ const current = { version: 1, kind: 'cumulative', source, inputTokens: input, outputTokens: output,
141
+ ...(read == null ? {} : { cachedReadTokens: read }), ...(write == null ? {} : { cachedWriteTokens: write }),
142
+ ...(thought == null ? {} : { thoughtTokens: thought }), ...(total == null ? {} : { totalTokens: total }) };
143
+ const previous = this.baseline;
144
+ if (previous?.kind === 'cumulative' && previous.source === source) {
145
+ if (usageCounters.some(key => current[key] !== undefined && previous[key] !== undefined && current[key] < previous[key])) {
146
+ this.usageGap();
147
+ return this.usageWarning('\n[远端用量] 远端累计计数发生回退,本轮未记录生成用量;已保留此前高水位,需要核对远端会话计数。\n');
148
+ }
149
+ if (optionalUsageCounters.some(key => previous[key] !== undefined && current[key] === undefined)) {
150
+ this.baseline = { ...previous, ...current, gap: true };
151
+ return this.usageWarning('\n[远端用量] 可选累计计数缺失,本轮未记录生成用量;未把缺失值补为零,等待完整计数重建基线。\n');
152
+ }
153
+ }
154
+ const sum = current.inputTokens + current.outputTokens + (current.cachedReadTokens ?? 0) + (current.cachedWriteTokens ?? 0);
155
+ if (!Number.isSafeInteger(sum) || (current.totalTokens !== undefined && current.totalTokens !== sum) || (current.thoughtTokens !== undefined && current.thoughtTokens > current.outputTokens)) {
156
+ this.usageGap();
157
+ return this.usageWarning('\n[远端用量] 输入、输出、缓存与合计计数不一致,本轮未记录生成用量;需要远端提供语义一致的真实计数。\n');
158
+ }
159
+ this.baseline = current;
160
+ if (previous === null || previous.kind === 'cumulative' && (previous.source !== source || previous.gap || optionalUsageCounters.some(key => previous[key] === undefined && current[key] !== undefined))) {
161
+ return this.usageWarning('\n[远端用量] 已建立当前累计用量基线;本轮未记录生成用量,避免把无法核对的历史累计重复结算。\n');
162
+ }
163
+ if (previous.kind === 'zero')
164
+ return this.usage(current.inputTokens, current.outputTokens, current.cachedReadTokens, current.cachedWriteTokens, current.thoughtTokens, current.totalTokens);
165
+ const delta = (key) => current[key] === undefined ? undefined : current[key] - previous[key];
166
+ if (!usageCounters.some(key => (delta(key) ?? 0) !== 0))
167
+ return [];
168
+ return this.usage(delta('inputTokens'), delta('outputTokens'), delta('cachedReadTokens'), delta('cachedWriteTokens'), delta('thoughtTokens'), delta('totalTokens'));
169
+ }
170
+ usage(input, output, read, write, thought, total) {
171
+ const values = [input, output, ...(read == null ? [] : [read]), ...(write == null ? [] : [write]), ...(thought == null ? [] : [thought]), ...(total == null ? [] : [total])];
172
+ if (!values.every(n => typeof n === 'number' && Number.isSafeInteger(n) && n >= 0)) {
173
+ return this.usageWarning('\n[远端用量] 远端真实计数无效,本轮未记录生成用量;需要有效的输入、输出与缓存计数。\n');
174
+ }
175
+ const sum = input + output + (read ?? 0) + (write ?? 0);
176
+ if ((total != null && total !== sum) || (thought != null && thought > output)) {
177
+ return this.usageWarning('\n[远端用量] 输入、输出、缓存与合计计数不一致,本轮未记录生成用量;需要远端提供语义一致的真实计数。\n');
178
+ }
179
+ return [{ type: 'usage', usage: { inputTokens: input, outputTokens: output,
180
+ ...(read == null ? {} : { cacheReadTokens: read }), ...(write == null ? {} : { cacheWriteTokens: write }),
181
+ ...(thought == null ? {} : { reasoningTokens: thought }) } }];
182
+ }
183
+ sdk(event) {
184
+ if (event.type === 'item.started' || event.type === 'item.updated' || event.type === 'item.completed') {
185
+ const item = event.item;
186
+ if ((item.type === 'agent_message' || item.type === 'reasoning') && typeof item.text === 'string') {
187
+ if (Object.keys(event).some(key => key !== 'type' && key !== 'item')
188
+ || Object.keys(item).some(key => key !== 'id' && key !== 'type' && key !== 'text')) {
189
+ this.diagnostic({ kind: 'extension', severity: 'info', code: 'SDK_MODEL_METADATA', summary: '远端模型消息扩展', method: event.type, payload: event });
190
+ }
191
+ const previous = this.seen.get(item.id) ?? '';
192
+ if (!item.text.startsWith(previous))
193
+ throw new NeedError('EVENT_REWRITE_UNSUPPORTED', '远端更改了已输出的文本,当前流不能无损替换', '支持文本替换的展示适配器');
194
+ this.seen.set(item.id, item.text);
195
+ return this.text(item.text.slice(previous.length), item.type === 'reasoning');
196
+ }
197
+ const tool = item.type === 'command_execution' || item.type === 'file_change' || item.type === 'mcp_tool_call';
198
+ this.diagnostic({ kind: tool ? 'tool' : 'session', severity: item.type === 'error' ? 'warning' : 'info', code: tool ? 'SDK_TOOL' : 'SDK_ITEM', summary: '远端非模型事件', method: event.type, payload: event });
199
+ if (item.type === 'error')
200
+ throw new NeedError('ONESHOT_FAILED', 'Codex 返回了错误,根因未查明', '远端诊断');
201
+ return [];
202
+ }
203
+ this.diagnostic({ kind: event.type === 'turn.completed' ? 'usage' : 'session', severity: event.type === 'error' || event.type === 'turn.failed' ? 'warning' : 'info', code: 'SDK_EVENT', summary: '远端会话事件', method: event.type, payload: event });
204
+ if (event.type === 'turn.completed') {
205
+ const u = event.usage;
206
+ const write = u.cache_write_input_tokens ?? 0;
207
+ const uncached = u.input_tokens - u.cached_input_tokens - write;
208
+ if (![u.input_tokens, u.cached_input_tokens, write, u.output_tokens, uncached].every(n => Number.isSafeInteger(n) && n >= 0))
209
+ throw new NeedError('USAGE_INVALID', '远端用量计数不一致', '有效的 Codex 用量');
210
+ return [{ type: 'usage', usage: { inputTokens: uncached, cacheReadTokens: u.cached_input_tokens, outputTokens: u.output_tokens,
211
+ ...(u.cache_write_input_tokens !== undefined ? { cacheWriteTokens: write } : {}),
212
+ ...(u.reasoning_output_tokens !== undefined ? { reasoningTokens: u.reasoning_output_tokens } : {}) } }];
213
+ }
214
+ return [];
215
+ }
216
+ }
217
+ function validCount(value) { return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0; }
218
+ function validUsageSnapshot(value) {
219
+ const snapshot = record(value);
220
+ if (snapshot?.version !== 1)
221
+ return false;
222
+ if (snapshot.kind === 'zero')
223
+ return Object.keys(snapshot).every(key => key === 'kind' || key === 'version');
224
+ if (snapshot.kind !== 'cumulative' || (snapshot.source !== 'usage' && snapshot.source !== 'quota') || (snapshot.gap !== undefined && snapshot.gap !== true)
225
+ || !validCount(snapshot.inputTokens) || !validCount(snapshot.outputTokens) || optionalUsageCounters.some(key => snapshot[key] !== undefined && !validCount(snapshot[key])))
226
+ return false;
227
+ const sum = snapshot.inputTokens + snapshot.outputTokens + (snapshot.cachedReadTokens ?? 0) + (snapshot.cachedWriteTokens ?? 0);
228
+ return snapshot.gap === true || Number.isSafeInteger(sum) && (snapshot.totalTokens === undefined || snapshot.totalTokens === sum) && (snapshot.thoughtTokens === undefined || snapshot.thoughtTokens <= snapshot.outputTokens);
229
+ }
230
+ function record(value) { return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined; }
@@ -0,0 +1,13 @@
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>;
@@ -0,0 +1,78 @@
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
+ }
@@ -0,0 +1,6 @@
1
+ /** Functional wording from connect-api appendix D1-D3 (L0.5.1 candidate). */
2
+ export declare const REMOTE_CODEX_NAME = "Codex\uFF08\u8FDC\u7A0B\u4E3B\u673A\uFF09";
3
+ export declare const CONNECTION_REQUIRED = "\u8FDC\u7A0B\u4E3B\u673A\u8FDE\u63A5\u7EC4\u4EF6\u6CA1\u6709\u88C5\u597D\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002";
4
+ export declare const TEST_HOST_NOTICE = "\u8FDC\u7A0B\u4E3B\u673A\u6B63\u5728\u4F7F\u7528\u6D4B\u8BD5\u914D\u7F6E\uFF0C\u9047\u5230\u95EE\u9898\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002";
5
+ /** Employee failures use business language; diagnostic codes remain separately available. */
6
+ export declare function employeeFailure(code: string): string;
@@ -0,0 +1,45 @@
1
+ /** Functional wording from connect-api appendix D1-D3 (L0.5.1 candidate). */
2
+ export const REMOTE_CODEX_NAME = 'Codex(远程主机)';
3
+ export const CONNECTION_REQUIRED = '远程主机连接组件没有装好,请联系管理员。';
4
+ export const TEST_HOST_NOTICE = '远程主机正在使用测试配置,遇到问题请联系管理员。';
5
+ /** Employee failures use business language; diagnostic codes remain separately available. */
6
+ export function employeeFailure(code) {
7
+ const messages = {
8
+ TECHNICAL_DETAILS_UNAVAILABLE: '技术详情暂不可用,请更新远程主机连接组件。',
9
+ REMOTE_DETAIL_STORAGE_FAILED: '技术详情保存失败,本轮已停止,请联系管理员检查。',
10
+ REMOTE_DETAIL_DELIVERY_UNKNOWN: '技术详情的持久结果无法核对,本轮已停止。',
11
+ REMOTE_DETAIL_TOO_LARGE: '技术详情超过已配置的单事件容量,本轮已停止;请联系管理员调整conversationDetails.maxEventBytes。',
12
+ DEPENDENCY_REQUIRED: CONNECTION_REQUIRED,
13
+ LOGIN_REQUIRED: '需要登录,请到设置 → 远程主机连接 点登录',
14
+ LOGIN_COMPLETED: '登录已完成,请重新发送本轮内容。',
15
+ LOGIN_FAILED: '登录未完成,请到设置 → 远程主机连接 点登录;如仍有问题,请联系管理员。',
16
+ AUTH_STATUS_INVALID: '登录检查没有返回有效结果,请重新检查;如仍有问题,请联系管理员。',
17
+ HOST_OFFLINE: '暂时无法连接远程主机,请联系管理员。',
18
+ CONNECTION_LOST: '远程主机连接已中断,请检查网络后重新连接。',
19
+ SETTINGS_REQUIRED: '远程主机连接尚未设置好,请联系管理员。',
20
+ CONNECT_API_MISMATCH: '远程主机连接组件需要更新,请联系管理员。',
21
+ ENROLLMENT_REQUIRED: '我需要连接资料,请联系管理员发放。',
22
+ NETWORK_KEY_INVALID: '连接资料已经失效,请联系管理员核对并重新发放。',
23
+ EMPLOYEE_MISSING: '远程主机账号还没有开通,请联系管理员。',
24
+ DISPOSED: '远程主机连接已经关闭,请重新连接远程主机。',
25
+ ENROLLMENT_INVALID: '远程主机开通信息需要更新,请联系管理员。',
26
+ HOST_KEY_MISMATCH: '远程主机连接信息需要管理员核对。',
27
+ HOST_TRUST_REQUIRED: '远程主机连接信息还未准备好,请联系管理员。',
28
+ POLICY_DENIED: '这个账号暂时不能使用,请联系公司账号管理员。',
29
+ SESSION_CONTEXT_MISMATCH: '远程主机连接已改变,请重新开始本次检查。',
30
+ ABORTED: '本次检查已取消。',
31
+ CONFIRMATION_REQUIRED: '请先选择是否打开授权窗口。',
32
+ CONTEXT_UNSUPPORTED: '远程 Codex 暂不支持这类会话内容,请联系管理员。',
33
+ CONTENT_UNSUPPORTED: '远程 Codex 暂不支持这类附件,请联系管理员。',
34
+ SYSTEM_PROMPT_UPDATE_PENDING: 'Claude原会话无法保证立即应用已变化的系统或开发者要求,本轮未发送;请新建会话应用新要求。',
35
+ SYSTEM_PROMPT_UNSUPPORTED: '远端没有声明系统与开发者上下文能力,本轮未发送;需要管理员提供支持该能力的远端适配器。',
36
+ SYSTEM_PROMPT_UPDATE_UNSUPPORTED: '远端不能更新原会话的系统与开发者要求,本轮未发送;请新建会话。',
37
+ PROMPT_CONTENT_UNSUPPORTED: '远端没有声明这类输入能力,本轮未发送;请使用受支持的内容或联系管理员。',
38
+ ATTACHMENT_REQUIRED: '我需要官方附件组件读取这张图片,请联系管理员检查组件。',
39
+ ATTACHMENT_INVALID: '图片附件没有返回匹配的已核验内容,本轮未发送;请重新添加图片或联系管理员。',
40
+ ACP_MESSAGE_TOO_LARGE: '本轮输入超过会话消息容量,未截断或发送;请联系管理员调整acpMaxMessageBytes。',
41
+ ACP_CLIENT_LOST: '我需要管理员核对并结束遗留的主机会话;当前保留原进程,没有代答旧审批。',
42
+ ONESHOT_CONTEXT_REQUIRED: '首版单次模式不支持图片或系统与开发者上下文,请改用会话模式。',
43
+ };
44
+ return messages[code] ?? `远程主机暂时无法完成这次操作,请联系管理员。技术详情:诊断代码 ${code}`;
45
+ }
@@ -0,0 +1,19 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
3
+ /** Matches the official locale service's English fallback before a client reports. */
4
+ export declare const PROVIDER_NAMES: Readonly<{
5
+ en: "Codex (Remote Host)";
6
+ zh: "Codex(远程主机)";
7
+ }>;
8
+ export type ProviderLanguage = 'en' | 'zh';
9
+ /** Local client presentation only: no host login, model call or locale preference write. */
10
+ export declare class ProviderLocaleRemote extends TypertRemoteService {
11
+ private readonly update;
12
+ private language;
13
+ private disposed;
14
+ constructor(ctx: Context, update: (language: ProviderLanguage) => void);
15
+ setLocale(language: unknown, signal: AbortSignal): Promise<{
16
+ language: ProviderLanguage;
17
+ name: string;
18
+ }>;
19
+ }
@@ -0,0 +1,73 @@
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 { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
36
+ import { NeedError } from "./remote.js";
37
+ /** Matches the official locale service's English fallback before a client reports. */
38
+ export const PROVIDER_NAMES = Object.freeze({ en: 'Codex (Remote Host)', zh: 'Codex(远程主机)' });
39
+ /** Local client presentation only: no host login, model call or locale preference write. */
40
+ let ProviderLocaleRemote = (() => {
41
+ let _classSuper = TypertRemoteService;
42
+ let _instanceExtraInitializers = [];
43
+ let _setLocale_decorators;
44
+ return class ProviderLocaleRemote extends _classSuper {
45
+ static {
46
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
47
+ _setLocale_decorators = [Remote];
48
+ __esDecorate(this, null, _setLocale_decorators, { kind: "method", name: "setLocale", static: false, private: false, access: { has: obj => "setLocale" in obj, get: obj => obj.setLocale }, metadata: _metadata }, null, _instanceExtraInitializers);
49
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
50
+ }
51
+ update = __runInitializers(this, _instanceExtraInitializers);
52
+ language = 'en';
53
+ disposed = false;
54
+ constructor(ctx, update) {
55
+ super(ctx, 'awCodexPresentation');
56
+ this.update = update;
57
+ ctx.effect(() => () => { this.disposed = true; });
58
+ }
59
+ async setLocale(language, signal) {
60
+ signal.throwIfAborted();
61
+ if (this.disposed)
62
+ throw new NeedError('DISPOSED', 'Codex presentation registration is disposed', 'active plugin');
63
+ if (language !== 'en' && language !== 'zh')
64
+ throw new NeedError('INVALID_REQUEST', 'Unsupported presentation language', 'official en/zh locale');
65
+ if (language !== this.language) {
66
+ this.update(language);
67
+ this.language = language;
68
+ }
69
+ return { language, name: PROVIDER_NAMES[language] };
70
+ }
71
+ };
72
+ })();
73
+ export { ProviderLocaleRemote };
@@ -0,0 +1,144 @@
1
+ import type { AgentApp } from '@agentclientprotocol/sdk';
2
+ import type { AgentApp as ExtendedAgentApp } from '@agentclientprotocol/sdk/unstable';
3
+ import type { ConnectState, ConnectApi as LConnectApi, ConnectionState, LoginPromptState } from '@ai-workbench/connect/api';
4
+ export type { ConnectionState, LoginPromptState } from '@ai-workbench/connect/api';
5
+ import type { OneShotRuntime } from './job.ts';
6
+ import type { BrokerTransport, BrokerSettings } from './broker.ts';
7
+ import type { Interaction, Invocation } from './interaction.ts';
8
+ import { type AgentDefinition } from './agents.ts';
9
+ import type { NativeWorkspaceApi, SessionProjectTarget, WorkspaceNotice, LoginProvider } from './connect-contract.ts';
10
+ import type { DetailChannel } from './remote-details.ts';
11
+ import type { WorkspaceNoticePresentation } from './workspace-notice.ts';
12
+ export interface Runtime {
13
+ identity: string;
14
+ cwd: string;
15
+ acpTransport?: {
16
+ writable: WritableStream<Uint8Array>;
17
+ readable: ReadableStream<Uint8Array>;
18
+ } | AgentApp | ExtendedAgentApp;
19
+ broker?: BrokerTransport;
20
+ oneShot?: OneShotRuntime;
21
+ loginRequired?: () => Promise<void>;
22
+ dispose?: () => Promise<void>;
23
+ binding?: {
24
+ bindingId: string;
25
+ workspaceId: string;
26
+ revision: number;
27
+ };
28
+ connection?: ConnectionState;
29
+ workspaceNotice?: WorkspaceNoticePresentation;
30
+ details?: DetailChannel;
31
+ detailContext?: () => {
32
+ turn: string | number | null;
33
+ step: string | number | null;
34
+ };
35
+ }
36
+ export interface RuntimeRequest extends Invocation {
37
+ mode: 'session' | 'one-shot';
38
+ cwd?: string;
39
+ identity?: string;
40
+ bindingId?: string;
41
+ workspaceId?: string;
42
+ agent?: AgentDefinition;
43
+ retainProcess?: boolean;
44
+ brokerSettings?: Readonly<BrokerSettings>;
45
+ }
46
+ export type RuntimeProvider = ((request: RuntimeRequest) => Promise<Runtime>) & {
47
+ preflight?: (request: RuntimeRequest) => Promise<void>;
48
+ };
49
+ export interface ConnectTarget {
50
+ hostContext?: {
51
+ enforcement: 'production' | 'test';
52
+ checkExitCode: 0 | 1;
53
+ failedChecks: {
54
+ id: string;
55
+ status: string;
56
+ }[];
57
+ unverifiedChecks: {
58
+ id: string;
59
+ status: string;
60
+ }[];
61
+ };
62
+ host: string;
63
+ username: string;
64
+ port: number;
65
+ proxyCommand: string;
66
+ projectRoot: string;
67
+ knownHostsFile: string;
68
+ session: {
69
+ command: string;
70
+ root: string;
71
+ settingsPath: string;
72
+ protocolVersion: 1;
73
+ };
74
+ }
75
+ export interface Project {
76
+ name: string;
77
+ path: string;
78
+ }
79
+ type Result<T> = {
80
+ ok: true;
81
+ value: T;
82
+ } | {
83
+ ok: false;
84
+ error: {
85
+ code: string;
86
+ message: string;
87
+ requires: string;
88
+ };
89
+ };
90
+ /** 消费正式connect-api 0.7公开子集;X不响应L登录票据、不直接调用登录。 */
91
+ export interface ConnectApi extends Pick<LConnectApi, 'getConnectionState' | 'getLoginPromptState' | 'requestLoginPrompt' | 'refreshLoginPrompts'>, Partial<NativeWorkspaceApi>, Partial<Pick<LConnectApi, 'appendRemoteConversationDetail' | 'getRemoteConversationDetailReceipt' | 'listRemoteConversationDetails'>> {
92
+ getState?(): ConnectState;
93
+ getSshTarget(): Promise<Result<ConnectTarget>>;
94
+ checkLogin(provider: LoginProvider): Promise<Result<{
95
+ provider: string;
96
+ loggedIn: boolean;
97
+ }>>;
98
+ login(provider: LoginProvider): Promise<Result<{
99
+ provider: string;
100
+ loggedIn: boolean;
101
+ }>>;
102
+ listProjects(): Promise<Result<{
103
+ projects: Project[];
104
+ unavailable: {
105
+ name: string;
106
+ reason: string;
107
+ }[];
108
+ }>>;
109
+ createProject(name: string): Promise<Result<Project>>;
110
+ }
111
+ export declare function requireLoginCoordinator(api: ConnectApi): void;
112
+ export declare function requireLoginState(state: LoginPromptState): void;
113
+ declare module '@deepseek-ai/cordis' {
114
+ interface Context {
115
+ aiWorkbenchConnect: ConnectApi;
116
+ }
117
+ interface Events {
118
+ 'ai-workbench/connection-state': (state: ConnectionState) => void;
119
+ 'ai-workbench/login-prompt-state': (state: LoginPromptState) => void;
120
+ }
121
+ }
122
+ /** Cancel only this consumer's wait; L owns the shared flight and its lifetime. */
123
+ export declare function awaitL<T>(operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
124
+ export declare function unwrap<T>(result: Result<T>): T;
125
+ export declare function validateTarget(t: ConnectTarget): void;
126
+ /** Only the declared public check identifiers/statuses, never provider evidence. */
127
+ export declare function hostContextNotice(t: ConnectTarget): string | undefined;
128
+ export declare function identity(t: ConnectTarget): string;
129
+ /** Reject draft.3 and legacy runtime rather than inventing workspace bindings. */
130
+ export declare function requireNativeWorkspace(api: ConnectApi): asserts api is ConnectApi & NativeWorkspaceApi;
131
+ /** Authorization stays in L; ensureReady only checks and records the need. */
132
+ export declare function offerLogin(connect: ConnectApi, request: Invocation, currentApi?: () => ConnectApi | undefined, agent?: AgentDefinition): Promise<void>;
133
+ export interface NoticeConsumer {
134
+ reconcile(request: RuntimeRequest, api: ConnectApi): Promise<void>;
135
+ deliver(notice: WorkspaceNotice, resolved: SessionProjectTarget, request: RuntimeRequest, api: ConnectApi): Promise<WorkspaceNoticePresentation | void>;
136
+ }
137
+ export declare function sameTarget(a: ConnectTarget, b: ConnectTarget): boolean;
138
+ export declare function connectProvider(get: () => ConnectApi | undefined, options?: {
139
+ agent?: AgentDefinition;
140
+ ask?: Interaction;
141
+ notice?: NoticeConsumer;
142
+ details?: (request: RuntimeRequest, api: ConnectApi, connection: ConnectionState, target: ConnectTarget) => Promise<DetailChannel>;
143
+ open?: (target: ConnectTarget, project: Project, request: RuntimeRequest) => Promise<Runtime>;
144
+ }): RuntimeProvider;