@ai-workbench/remote-agent 0.8.19 → 0.8.23

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 (95) hide show
  1. package/README.md +27 -134
  2. package/dist/acp.d.ts +23 -16
  3. package/dist/acp.js +209 -226
  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 +41 -105
  14. package/dist/client.js +34 -126
  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 -1
  21. package/dist/employee-endpoint.js +21 -19
  22. package/dist/employee-exec.d.ts +4 -3
  23. package/dist/employee-exec.js +25 -13
  24. package/dist/employee-info.d.ts +7 -1
  25. package/dist/employee-info.js +24 -3
  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 +6 -2
  44. package/dist/index.d.ts +13 -43
  45. package/dist/index.js +192 -484
  46. package/dist/input.js +2 -3
  47. package/dist/local-user-turn.js +5 -6
  48. package/dist/mapping.d.ts +5 -3
  49. package/dist/mapping.js +31 -36
  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.js +21 -41
  62. package/dist/provider.d.ts +12 -34
  63. package/dist/provider.js +17 -92
  64. package/dist/readiness.js +1 -7
  65. package/dist/remote-details.d.ts +41 -17
  66. package/dist/remote-details.js +84 -381
  67. package/dist/workspace-source.d.ts +1 -25
  68. package/dist/workspace-source.js +1 -138
  69. package/package.json +11 -12
  70. package/dist/catalog-diagnostics.d.ts +0 -16
  71. package/dist/catalog-diagnostics.js +0 -108
  72. package/dist/host-layout.d.ts +0 -8
  73. package/dist/host-layout.js +0 -9
  74. package/dist/job.d.ts +0 -17
  75. package/dist/job.js +0 -57
  76. package/dist/model-defaults.d.ts +0 -29
  77. package/dist/model-defaults.js +0 -62
  78. package/dist/oneshot.d.ts +0 -13
  79. package/dist/oneshot.js +0 -78
  80. package/dist/protocol-boundary.d.ts +0 -13
  81. package/dist/protocol-boundary.js +0 -49
  82. package/dist/provider-locale.d.ts +0 -19
  83. package/dist/provider-locale.js +0 -73
  84. package/dist/recovery-checkpoint.d.ts +0 -6
  85. package/dist/recovery-checkpoint.js +0 -71
  86. package/dist/recovery.d.ts +0 -51
  87. package/dist/recovery.js +0 -61
  88. package/dist/sdk-ssh-wrapper.d.ts +0 -2
  89. package/dist/sdk-ssh-wrapper.js +0 -34
  90. package/dist/stage-timing.d.ts +0 -31
  91. package/dist/stage-timing.js +0 -61
  92. package/dist/workspace-notice.d.ts +0 -47
  93. package/dist/workspace-notice.js +0 -273
  94. package/dist/workspace-source-confirmation.d.ts +0 -4
  95. package/dist/workspace-source-confirmation.js +0 -12
@@ -1,49 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { NeedError } from "./remote.js";
3
- const fingerprint = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
4
- /** Byte fragments remain only in memory. A cursor is clean only after SDK consumption. */
5
- export class ProtocolBoundary {
6
- maxBytes;
7
- partial = Buffer.alloc(0);
8
- pending = [];
9
- requests = new Set();
10
- constructor(maxBytes) {
11
- this.maxBytes = maxBytes;
12
- }
13
- receive(bytes, gate) {
14
- this.partial = Buffer.concat([this.partial, bytes]);
15
- let end;
16
- while ((end = this.partial.indexOf(10)) >= 0) {
17
- if (end > this.maxBytes)
18
- throw new NeedError('ACP_MESSAGE_TOO_LARGE', '主机消息超过具名会话容量', 'acpMaxMessageBytes');
19
- const line = this.partial.subarray(0, end).toString('utf8').trim();
20
- this.partial = this.partial.subarray(end + 1);
21
- if (!line)
22
- continue;
23
- let message;
24
- try {
25
- message = JSON.parse(line);
26
- }
27
- catch {
28
- throw new NeedError('ACP_OUTPUT_INVALID', '主机消息不是有效JSON', '完整ACP消息');
29
- }
30
- if (!message || typeof message !== 'object' || Array.isArray(message))
31
- throw new NeedError('ACP_OUTPUT_INVALID', '主机消息结构无效', '完整ACP消息');
32
- if (gate)
33
- gate(message);
34
- else
35
- this.pending.push(fingerprint(message));
36
- }
37
- if (this.partial.length > this.maxBytes)
38
- throw new NeedError('ACP_MESSAGE_TOO_LARGE', '主机消息超过具名会话容量', 'acpMaxMessageBytes');
39
- }
40
- consumed(message) {
41
- if (this.pending.shift() !== fingerprint(message))
42
- throw new NeedError('ACP_BOUNDARY_UNCONFIRMED', '主机消息处理位置无法确认', '完整按序的ACP消息');
43
- if (typeof message.method === 'string' && Object.hasOwn(message, 'id'))
44
- this.requests.add(JSON.stringify(message.id));
45
- }
46
- responded(id) { this.requests.delete(JSON.stringify(id)); }
47
- get complete() { return this.partial.length === 0 && this.pending.length === 0 && this.requests.size === 0; }
48
- get wholeLine() { return this.partial.length === 0; }
49
- }
@@ -1,19 +0,0 @@
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
- }
@@ -1,73 +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 { 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 };
@@ -1,6 +0,0 @@
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>;
@@ -1,71 +0,0 @@
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
- }
@@ -1,51 +0,0 @@
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
- selectorRoles?: {
8
- model?: string;
9
- effort?: string;
10
- };
11
- resolvedEvidence?: {
12
- modelValue: string;
13
- resolvedModel: string;
14
- };
15
- };
16
- configUpdates?: SessionConfigOption[][];
17
- }
18
- export interface RecoveryState {
19
- version: 1;
20
- phase: 'dirty' | 'clean' | 'ended';
21
- scope: string;
22
- brokerId: string;
23
- instanceId?: string;
24
- pid?: number;
25
- cursor?: number;
26
- checkpointId?: string;
27
- acp?: AcpRecovery;
28
- }
29
- export interface RecoveryJournal {
30
- read(): Promise<RecoveryState | undefined>;
31
- write(state: RecoveryState): Promise<void>;
32
- }
33
- export declare function validateRecovery(value: unknown): RecoveryState;
34
- export declare const codexContextPolicy: Readonly<{
35
- version: 1;
36
- scope: "new-session";
37
- mode: "append";
38
- content: "text";
39
- resume: "omit-only";
40
- changed: "new-session-required";
41
- mapping: "developerInstructions";
42
- nativeCliUnmodified: true;
43
- }>;
44
- export declare function supportsCodexContextPolicy(capabilities: AgentCapabilities): boolean;
45
- /** Known capabilities plus the exact Codex policy whitelist; no arbitrary metadata. */
46
- export declare function recoveryCapabilities(c: AgentCapabilities): AgentCapabilities;
47
- export declare function recoveryKey(value: unknown): string;
48
- /** Preserve legacy lookup bytes; each employee endpoint scope owns a separate journal. */
49
- export declare function runtimeRecoveryKey(identity: string, provider: string, sessionId: string | undefined, employeeScope?: string): string;
50
- /** History is immutable; current is atomically replaced only after fsync. No expiry. */
51
- export declare function recoveryJournal(profileDir: string, key: string): RecoveryJournal;
package/dist/recovery.js DELETED
@@ -1,61 +0,0 @@
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
- }
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env node
2
- /** Codex SDK executable override. Prompt travels through stdin, never SSH argv. */
3
- import { readFile, lstat } from 'node:fs/promises';
4
- import { executeJob } from "./job.js";
5
- let child;
6
- let stopped = false;
7
- const lifetime = new AbortController();
8
- for (const sig of ['SIGTERM', 'SIGINT'])
9
- process.on(sig, () => { stopped = true; lifetime.abort(); child?.kill('SIGTERM'); });
10
- try {
11
- const path = process.env.AW_X_JOB_FILE;
12
- if (!path)
13
- throw new Error('job missing');
14
- const meta = await lstat(path);
15
- if (!meta.isFile() || meta.isSymbolicLink() || meta.uid !== process.getuid?.() || (meta.mode & 0o077) !== 0)
16
- throw new Error('job permissions');
17
- const spec = JSON.parse(await readFile(path, 'utf8'));
18
- let input = '';
19
- for await (const chunk of process.stdin)
20
- input += chunk;
21
- if (!stopped) {
22
- const code = await executeJob(spec, 'stage', JSON.stringify({ argv: process.argv.slice(2), input, cwd: spec.cwd, codex: spec.codex }), c => { child = c; }, false, lifetime.signal);
23
- if (code !== 0)
24
- throw new Error('stage failed');
25
- }
26
- if (!stopped)
27
- process.exitCode = (await executeJob(spec, 'run', undefined, c => { child = c; }, true, lifetime.signal)) ?? 1;
28
- else
29
- process.exitCode = 143;
30
- }
31
- catch {
32
- process.stderr.write('远端单次运行失败;需要连接诊断,根因未查明。\n');
33
- process.exitCode = 1;
34
- }
@@ -1,31 +0,0 @@
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
- private confirmations;
26
- private emit;
27
- /** Presentation aggregation only: no duration threshold and no new execution gate. */
28
- summarize(): void;
29
- bind(sink: (detail: DetailObservation) => void): void;
30
- run<T>(phase: Stage, operation: () => Promise<T>): Promise<T>;
31
- }
@@ -1,61 +0,0 @@
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
- confirmations = { count: 0, total: 0, min: Infinity, max: 0, elapsed: 0 };
15
- emit(detail) { if (this.sink)
16
- this.sink(detail);
17
- else
18
- this.pending.push(detail); }
19
- /** Presentation aggregation only: no duration threshold and no new execution gate. */
20
- summarize() {
21
- const current = this.confirmations;
22
- if (!current.count)
23
- return;
24
- this.confirmations = { count: 0, total: 0, min: Infinity, max: 0, elapsed: 0 };
25
- this.emit({ kind: 'session', severity: 'info', code: 'REMOTE_STAGE_TIMING', summary: STAGES['connection-confirm'] + ':' + current.count + '次,共' + current.total.toFixed(1) + '毫秒,最大' + current.max.toFixed(1) + '毫秒', payload: { phase: 'connection-confirm', count: current.count, totalDurationMs: current.total, minDurationMs: current.min, maxDurationMs: current.max, elapsedMs: current.elapsed, outcome: 'ok', clock: 'client-monotonic' } });
26
- }
27
- bind(sink) {
28
- this.sink = sink;
29
- const pending = this.pending;
30
- this.pending = [];
31
- for (const detail of pending)
32
- sink(detail);
33
- }
34
- async run(phase, operation) {
35
- const started = performance.now();
36
- let outcome = 'failed';
37
- try {
38
- const result = await operation();
39
- outcome = 'ok';
40
- return result;
41
- }
42
- finally {
43
- const ended = performance.now();
44
- const durationMs = ended - started, elapsedMs = ended - this.started;
45
- if (phase === 'connection-confirm' && outcome === 'ok') {
46
- const current = this.confirmations;
47
- current.count++;
48
- current.total += durationMs;
49
- current.min = Math.min(current.min, durationMs);
50
- current.max = Math.max(current.max, durationMs);
51
- current.elapsed = elapsedMs;
52
- }
53
- else {
54
- if (phase !== 'connection-confirm')
55
- this.summarize();
56
- 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' } };
57
- this.emit(detail);
58
- }
59
- }
60
- }
61
- }
@@ -1,47 +0,0 @@
1
- import type { Context } from '@deepseek-ai/cordis';
2
- import { type RequestMessage } from '@deepseek-ai/dsh-llm';
3
- import { type AgentDefinition } from './agents.ts';
4
- import type { ConnectApi, RuntimeRequest } from './provider.ts';
5
- import type { WorkspaceNotice, SessionProjectTarget } from './connect-contract.ts';
6
- /** X-owned adapter metadata; this is not a DSH renderer acknowledgement. */
7
- export interface WorkspaceNoticeMarker {
8
- readonly kind: 'workspace-notice';
9
- readonly version: 1;
10
- readonly provider: string;
11
- readonly loginProvider: 'claude' | 'codex';
12
- readonly sessionId: string;
13
- readonly noticeId: string;
14
- readonly claimId: string;
15
- readonly deliveryId: string;
16
- readonly messageId: string;
17
- readonly workspaceName: string;
18
- readonly bindingId: string;
19
- readonly workspaceId: string;
20
- }
21
- export declare function parseWorkspaceNoticeMarker(message: unknown): WorkspaceNoticeMarker | undefined;
22
- /** Only this plugin's complete, source-bound notice may be excluded from remote model history. */
23
- export declare function isWorkspaceNoticeMessage(message: unknown): boolean;
24
- /** A verified first block in the actual model reply, never a second final message. */
25
- export interface WorkspaceNoticePresentation {
26
- readonly kind: 'workspace-notice';
27
- readonly version: 2;
28
- readonly provider: string;
29
- readonly loginProvider: 'claude' | 'codex';
30
- readonly sessionId: string;
31
- readonly noticeId: string;
32
- readonly claimId: string;
33
- readonly workspaceName: string;
34
- readonly bindingId: string;
35
- readonly workspaceId: string;
36
- readonly turn: number;
37
- readonly step: number;
38
- readonly blockIndex: 0;
39
- readonly text: string;
40
- }
41
- export declare function workspaceNoticeFromMessage(message: unknown): WorkspaceNoticePresentation | undefined;
42
- /** Preserve the model answer and exact remote replay pointer; ordinary text is never searched. */
43
- export declare function withoutWorkspaceNotice<T extends RequestMessage>(message: T): T;
44
- export declare function createWorkspaceNoticeConsumer(ctx: Context, agent: AgentDefinition): {
45
- reconcile: (request: RuntimeRequest, api: ConnectApi) => Promise<void>;
46
- deliver: (notice: WorkspaceNotice, resolved: SessionProjectTarget, request: RuntimeRequest, api: ConnectApi) => Promise<WorkspaceNoticePresentation | undefined>;
47
- };