@myagentroam/node 0.1.3 → 0.1.6
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.
- package/README.md +9 -1
- package/dist/capabilities.d.ts +2 -0
- package/dist/capabilities.js +15 -4
- package/dist/claude-agent-sdk.d.ts +4 -16
- package/dist/claude-agent-sdk.js +18 -79
- package/dist/claude-channel.js +0 -1
- package/dist/codex-app-server.d.ts +5 -0
- package/dist/codex-app-server.js +26 -5
- package/dist/config.d.ts +3 -0
- package/dist/config.js +18 -2
- package/dist/connector/node-connector-options.d.ts +26 -0
- package/dist/connector/node-connector-options.js +1 -0
- package/dist/connector/node-operation-router.d.ts +9 -0
- package/dist/connector/node-operation-router.js +22 -0
- package/dist/connector.d.ts +62 -356
- package/dist/connector.js +714 -6176
- package/dist/control-outbound-scheduler.d.ts +26 -0
- package/dist/control-outbound-scheduler.js +121 -0
- package/dist/database.d.ts +4 -0
- package/dist/database.js +15 -10
- package/dist/event-buffer.js +0 -1
- package/dist/fake-runner.js +0 -1
- package/dist/health.js +0 -1
- package/dist/main.js +6 -1
- package/dist/migrations/v001.js +0 -1
- package/dist/migrations/v002.js +0 -1
- package/dist/migrations/v003.d.ts +4 -0
- package/dist/migrations/v003.js +44 -0
- package/dist/native-jsonl-reader.d.ts +14 -0
- package/dist/native-jsonl-reader.js +97 -0
- package/dist/native-session-history.d.ts +41 -4
- package/dist/native-session-history.js +340 -159
- package/dist/opencode-runtime.d.ts +33 -0
- package/dist/opencode-runtime.js +80 -0
- package/dist/opencode-server.d.ts +47 -0
- package/dist/opencode-server.js +250 -0
- package/dist/operational.js +10 -3
- package/dist/process-tree.js +0 -1
- package/dist/runner/abstract-runner.d.ts +153 -0
- package/dist/runner/abstract-runner.js +154 -0
- package/dist/runner/claude/managed-run-controller.d.ts +52 -0
- package/dist/runner/claude/managed-run-controller.js +326 -0
- package/dist/runner/claude-code-runner.d.ts +70 -0
- package/dist/runner/claude-code-runner.js +286 -0
- package/dist/runner/codex/conversation-parser.d.ts +59 -0
- package/dist/runner/codex/conversation-parser.js +816 -0
- package/dist/runner/codex/managed-run-controller.d.ts +80 -0
- package/dist/runner/codex/managed-run-controller.js +388 -0
- package/dist/runner/codex-runner.d.ts +85 -0
- package/dist/runner/codex-runner.js +365 -0
- package/dist/runner/fake-test-runner.d.ts +26 -0
- package/dist/runner/fake-test-runner.js +47 -0
- package/dist/runner/opencode/conversation-parser.d.ts +27 -0
- package/dist/runner/opencode/conversation-parser.js +217 -0
- package/dist/runner/opencode/managed-run-controller.d.ts +76 -0
- package/dist/runner/opencode/managed-run-controller.js +451 -0
- package/dist/runner/opencode-runner.d.ts +60 -0
- package/dist/runner/opencode-runner.js +330 -0
- package/dist/runner/runner-registry.d.ts +19 -0
- package/dist/runner/runner-registry.js +75 -0
- package/dist/runner-command-engine.js +0 -1
- package/dist/runner-profiles.d.ts +4 -0
- package/dist/runner-profiles.js +76 -3
- package/dist/runner-usage.d.ts +2 -2
- package/dist/runner-usage.js +3 -6
- package/dist/runtime-state.js +26 -14
- package/dist/service/attachment-domain-state.d.ts +5 -0
- package/dist/service/attachment-domain-state.js +5 -0
- package/dist/service/conversation-history-service.d.ts +34 -0
- package/dist/service/conversation-history-service.js +158 -0
- package/dist/service/external-session-resume-service.d.ts +32 -0
- package/dist/service/external-session-resume-service.js +98 -0
- package/dist/service/fake-managed-run-service.d.ts +29 -0
- package/dist/service/fake-managed-run-service.js +62 -0
- package/dist/service/managed-runner-session-service.d.ts +12 -0
- package/dist/service/managed-runner-session-service.js +35 -0
- package/dist/service/native-session-projection-service.d.ts +51 -0
- package/dist/service/native-session-projection-service.js +271 -0
- package/dist/service/native-session-watch-service.d.ts +37 -0
- package/dist/service/native-session-watch-service.js +150 -0
- package/dist/service/node-connection-lifecycle-service.d.ts +37 -0
- package/dist/service/node-connection-lifecycle-service.js +85 -0
- package/dist/service/node-control-channel.d.ts +32 -0
- package/dist/service/node-control-channel.js +85 -0
- package/dist/service/node-control-message-service.d.ts +28 -0
- package/dist/service/node-control-message-service.js +104 -0
- package/dist/service/node-request-service.d.ts +14 -0
- package/dist/service/node-request-service.js +50 -0
- package/dist/service/node-run-event-bridge.d.ts +40 -0
- package/dist/service/node-run-event-bridge.js +83 -0
- package/dist/service/node-terminal-channel.d.ts +29 -0
- package/dist/service/node-terminal-channel.js +182 -0
- package/dist/service/node-upgrade-coordinator.d.ts +29 -0
- package/dist/service/node-upgrade-coordinator.js +137 -0
- package/dist/service/node-workspace-coordinator.d.ts +32 -0
- package/dist/service/node-workspace-coordinator.js +80 -0
- package/dist/service/run-attachment-service.d.ts +37 -0
- package/dist/service/run-attachment-service.js +160 -0
- package/dist/service/run-domain-state.d.ts +4 -0
- package/dist/service/run-domain-state.js +4 -0
- package/dist/service/run-event-service.d.ts +31 -0
- package/dist/service/run-event-service.js +102 -0
- package/dist/service/run-workbench-service.d.ts +27 -0
- package/dist/service/run-workbench-service.js +113 -0
- package/dist/service/runner-channel-message-service.d.ts +13 -0
- package/dist/service/runner-channel-message-service.js +20 -0
- package/dist/service/runner-interaction-service.d.ts +40 -0
- package/dist/service/runner-interaction-service.js +246 -0
- package/dist/service/runner-service.d.ts +44 -0
- package/dist/service/runner-service.js +68 -0
- package/dist/service/session-catalog-service.d.ts +45 -0
- package/dist/service/session-catalog-service.js +224 -0
- package/dist/service/session-command-service.d.ts +23 -0
- package/dist/service/session-command-service.js +69 -0
- package/dist/service/session-context-service.d.ts +34 -0
- package/dist/service/session-context-service.js +87 -0
- package/dist/service/session-domain-state.d.ts +8 -0
- package/dist/service/session-domain-state.js +8 -0
- package/dist/service/session-identity-service.d.ts +24 -0
- package/dist/service/session-identity-service.js +86 -0
- package/dist/service/session-lifecycle-service.d.ts +31 -0
- package/dist/service/session-lifecycle-service.js +242 -0
- package/dist/service/session-message-service.d.ts +33 -0
- package/dist/service/session-message-service.js +157 -0
- package/dist/service/session-presentation-service.d.ts +33 -0
- package/dist/service/session-presentation-service.js +36 -0
- package/dist/service/session-query-service.d.ts +31 -0
- package/dist/service/session-query-service.js +77 -0
- package/dist/service/terminal-relay-state.d.ts +6 -0
- package/dist/service/terminal-relay-state.js +3 -0
- package/dist/service/terminal-service.d.ts +12 -0
- package/dist/service/terminal-service.js +52 -0
- package/dist/service/workbench-event-service.d.ts +21 -0
- package/dist/service/workbench-event-service.js +53 -0
- package/dist/service/workbench-manifest-service.d.ts +89 -0
- package/dist/service/workbench-manifest-service.js +70 -0
- package/dist/service/workspace-change-service.d.ts +9 -0
- package/dist/service/workspace-change-service.js +86 -0
- package/dist/service/workspace-content-search-service.d.ts +14 -0
- package/dist/service/workspace-content-search-service.js +477 -0
- package/dist/service/workspace-domain-state.d.ts +75 -0
- package/dist/service/workspace-domain-state.js +40 -0
- package/dist/service/workspace-file-service.d.ts +17 -0
- package/dist/service/workspace-file-service.js +109 -0
- package/dist/service/workspace-queue-workbench-service.d.ts +55 -0
- package/dist/service/workspace-queue-workbench-service.js +226 -0
- package/dist/service/workspace-service.d.ts +15 -0
- package/dist/service/workspace-service.js +67 -0
- package/dist/service/workspace-session-service.d.ts +23 -0
- package/dist/service/workspace-session-service.js +139 -0
- package/dist/service/workspace-upload-service.d.ts +37 -0
- package/dist/service/workspace-upload-service.js +240 -0
- package/dist/service/workspace-watch-service.d.ts +18 -0
- package/dist/service/workspace-watch-service.js +120 -0
- package/dist/service/workspace-workbench-service.d.ts +47 -0
- package/dist/service/workspace-workbench-service.js +223 -0
- package/dist/service.js +2 -3
- package/dist/storage.js +0 -1
- package/dist/supervisor.d.ts +1 -0
- package/dist/supervisor.js +154 -0
- package/dist/terminal.d.ts +1 -0
- package/dist/terminal.js +1 -2
- package/dist/util/node-operation-parsers.d.ts +92 -0
- package/dist/util/node-operation-parsers.js +382 -0
- package/dist/util/runner-native-session-parsers.d.ts +72 -0
- package/dist/util/runner-native-session-parsers.js +381 -0
- package/dist/util/secret-environment.d.ts +1 -0
- package/dist/util/secret-environment.js +25 -0
- package/dist/workspace.d.ts +62 -2
- package/dist/workspace.js +539 -57
- package/package.json +3 -2
- package/dist/capabilities.js.map +0 -1
- package/dist/claude-agent-sdk.js.map +0 -1
- package/dist/claude-channel.js.map +0 -1
- package/dist/codex-app-server.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/connector.js.map +0 -1
- package/dist/database.js.map +0 -1
- package/dist/event-buffer.js.map +0 -1
- package/dist/fake-runner.js.map +0 -1
- package/dist/health.js.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/migrations/v001.js.map +0 -1
- package/dist/migrations/v002.js.map +0 -1
- package/dist/native-session-history.js.map +0 -1
- package/dist/operational.js.map +0 -1
- package/dist/process-tree.js.map +0 -1
- package/dist/runner-command-engine.js.map +0 -1
- package/dist/runner-profiles.js.map +0 -1
- package/dist/runner-usage.js.map +0 -1
- package/dist/runtime-state.js.map +0 -1
- package/dist/service.js.map +0 -1
- package/dist/storage.js.map +0 -1
- package/dist/terminal.js.map +0 -1
- package/dist/workspace.js.map +0 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { SessionActivityState } from '@myagentroam/protocol';
|
|
2
|
+
import type { NodeAgentSession, NodeConversationTurn } from '../database.js';
|
|
3
|
+
import type { NativeSessionHistory, NativeTranscriptImageAttachment } from '../native-session-history.js';
|
|
4
|
+
export interface SessionContextUsage {
|
|
5
|
+
readonly model: string | null;
|
|
6
|
+
readonly usedTokens: number;
|
|
7
|
+
readonly maxTokens: number;
|
|
8
|
+
readonly percentage: number;
|
|
9
|
+
}
|
|
10
|
+
export type ConversationHistorySource = 'official' | 'native' | 'runtime';
|
|
11
|
+
export interface ConversationHistoryPage {
|
|
12
|
+
readonly turns: readonly NodeConversationTurn[];
|
|
13
|
+
readonly nextCursor: string | null;
|
|
14
|
+
readonly snapshotSequence: number;
|
|
15
|
+
readonly source: ConversationHistorySource;
|
|
16
|
+
readonly readAt: number;
|
|
17
|
+
readonly latestVisibleAt: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function extractId(result: unknown, key: 'thread' | 'turn'): string;
|
|
20
|
+
export declare function settleWithin<T>(promise: Promise<T>, milliseconds: number): Promise<T | undefined>;
|
|
21
|
+
export declare function extractCodexThreads(result: unknown): readonly {
|
|
22
|
+
readonly id: string;
|
|
23
|
+
readonly source: string | undefined;
|
|
24
|
+
readonly cwd: unknown;
|
|
25
|
+
readonly title: string | undefined;
|
|
26
|
+
}[];
|
|
27
|
+
export declare function codexThreadActivity(result: unknown): SessionActivityState;
|
|
28
|
+
export declare function externalResumeFailureDetail(error: unknown): string;
|
|
29
|
+
export declare function codexActiveTurnId(result: unknown): string | undefined;
|
|
30
|
+
export declare function codexThreadCwd(value: unknown): string;
|
|
31
|
+
export declare function isWithinWorkspace(cwd: string, workspacePath: string): boolean;
|
|
32
|
+
export declare function claudeDiscoveredSession(value: unknown, fallbackCwd: string): {
|
|
33
|
+
readonly externalSessionId: string;
|
|
34
|
+
readonly cwd: string;
|
|
35
|
+
readonly title?: string;
|
|
36
|
+
} | undefined;
|
|
37
|
+
export declare function latestNativeActivity(history: NativeSessionHistory): number;
|
|
38
|
+
export declare function sameContextUsage(left: SessionContextUsage, right: SessionContextUsage): boolean;
|
|
39
|
+
export declare function isNativeHistory(value: unknown): value is NativeSessionHistory;
|
|
40
|
+
export declare function deduplicateDirectSessions<T extends NodeAgentSession>(sessions: readonly T[]): readonly T[];
|
|
41
|
+
export declare function nativeConversationPage(session: NodeAgentSession, history: NativeSessionHistory, input: {
|
|
42
|
+
readonly cursor?: string;
|
|
43
|
+
readonly limit?: number;
|
|
44
|
+
}, runtimeTurns?: readonly NodeConversationTurn[], registerImages?: (images: readonly NativeTranscriptImageAttachment[]) => readonly Record<string, unknown>[]): {
|
|
45
|
+
readonly turns: readonly NodeConversationTurn[];
|
|
46
|
+
readonly nextCursor: string | null;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Claude JSONL 不保存浏览器请求 ID。Node 进程仍存活时,以正文和生成时刻
|
|
50
|
+
* 对齐当前运行态的用户消息,让 native 历史页替换同一乐观 Turn,而不是重复一条。
|
|
51
|
+
*/
|
|
52
|
+
export declare function nativeUserClientMessageId(entry: Extract<NativeSessionHistory['items'][number], {
|
|
53
|
+
readonly kind: 'message';
|
|
54
|
+
}>, createdAt: number, runtimeTurns: readonly NodeConversationTurn[]): string | null;
|
|
55
|
+
/**
|
|
56
|
+
* Normalize the documented App Server `thread/read(includeTurns: true)`
|
|
57
|
+
* result. Only items with a user-visible Workbench representation are kept;
|
|
58
|
+
* raw protocol envelopes are deliberately never exposed to Web.
|
|
59
|
+
*/
|
|
60
|
+
export declare function claudePlanText(text: string): string | undefined;
|
|
61
|
+
export declare function claudeUserInputQuestions(value: Record<string, unknown>): readonly {
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly prompt: string;
|
|
64
|
+
readonly input: 'SINGLE_SELECT' | 'MULTI_SELECT' | 'SHORT_TEXT';
|
|
65
|
+
readonly options: readonly {
|
|
66
|
+
readonly value: string;
|
|
67
|
+
readonly label: string;
|
|
68
|
+
readonly description: string | null;
|
|
69
|
+
}[];
|
|
70
|
+
readonly allowOther: boolean;
|
|
71
|
+
readonly multiSelect: boolean;
|
|
72
|
+
}[];
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { stripClaudePlanTag } from '../runner-command-engine.js';
|
|
3
|
+
import { compactRunnerText, isClaudeCommandTool, isPlainRecord, nativeClaudeCommand, nativePageEnd } from './node-operation-parsers.js';
|
|
4
|
+
export function extractId(result, key) {
|
|
5
|
+
if (typeof result === 'object' &&
|
|
6
|
+
result !== null &&
|
|
7
|
+
key in result &&
|
|
8
|
+
typeof result[key] === 'object' &&
|
|
9
|
+
result[key] !== null &&
|
|
10
|
+
typeof result[key]['id'] === 'string') {
|
|
11
|
+
return result[key]['id'];
|
|
12
|
+
}
|
|
13
|
+
throw new Error(`CODEX_${key.toUpperCase()}_ID_MISSING`);
|
|
14
|
+
}
|
|
15
|
+
export async function settleWithin(promise, milliseconds) {
|
|
16
|
+
let timer;
|
|
17
|
+
try {
|
|
18
|
+
return await Promise.race([
|
|
19
|
+
promise,
|
|
20
|
+
new Promise((resolve) => {
|
|
21
|
+
timer = setTimeout(() => resolve(undefined), milliseconds);
|
|
22
|
+
timer.unref();
|
|
23
|
+
})
|
|
24
|
+
]);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
if (timer !== undefined)
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function extractCodexThreads(result) {
|
|
32
|
+
if (typeof result !== 'object' || result === null || !('data' in result))
|
|
33
|
+
return [];
|
|
34
|
+
const data = result.data;
|
|
35
|
+
if (!Array.isArray(data))
|
|
36
|
+
return [];
|
|
37
|
+
return data.flatMap((entry) => {
|
|
38
|
+
if (typeof entry !== 'object' || entry === null)
|
|
39
|
+
return [];
|
|
40
|
+
const value = entry;
|
|
41
|
+
if (typeof value.id !== 'string')
|
|
42
|
+
return [];
|
|
43
|
+
const source = typeof value.sourceKind === 'string'
|
|
44
|
+
? value.sourceKind
|
|
45
|
+
: typeof value.source === 'string'
|
|
46
|
+
? value.source
|
|
47
|
+
: typeof value.source === 'object' &&
|
|
48
|
+
value.source !== null &&
|
|
49
|
+
typeof value.source.kind === 'string'
|
|
50
|
+
? value.source.kind
|
|
51
|
+
: undefined;
|
|
52
|
+
const title = typeof value.name === 'string' && value.name.trim().length > 0
|
|
53
|
+
? value.name.trim()
|
|
54
|
+
: typeof value.preview === 'string' && value.preview.trim().length > 0
|
|
55
|
+
? value.preview.trim()
|
|
56
|
+
: undefined;
|
|
57
|
+
return [{ id: value.id, source, cwd: value.cwd, title }];
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export function codexThreadActivity(result) {
|
|
61
|
+
if (!isPlainRecord(result) || !isPlainRecord(result.thread))
|
|
62
|
+
return 'UNAVAILABLE';
|
|
63
|
+
const status = result.thread.status;
|
|
64
|
+
if (!isPlainRecord(status) || typeof status.type !== 'string')
|
|
65
|
+
return 'UNAVAILABLE';
|
|
66
|
+
if (status.type === 'active')
|
|
67
|
+
return 'EXTERNAL_ACTIVE';
|
|
68
|
+
if (status.type === 'idle' || status.type === 'notLoaded')
|
|
69
|
+
return 'IDLE';
|
|
70
|
+
return 'UNAVAILABLE';
|
|
71
|
+
}
|
|
72
|
+
export function externalResumeFailureDetail(error) {
|
|
73
|
+
const message = error instanceof Error ? error.message : 'SESSION_RESUME_REJECTED';
|
|
74
|
+
return [...message]
|
|
75
|
+
.map((character) => (character <= '\u001f' || character === '\u007f' ? ' ' : character))
|
|
76
|
+
.join('')
|
|
77
|
+
.replace(/\s+/g, ' ')
|
|
78
|
+
.trim()
|
|
79
|
+
.slice(0, 240);
|
|
80
|
+
}
|
|
81
|
+
export function codexActiveTurnId(result) {
|
|
82
|
+
if (!isPlainRecord(result) ||
|
|
83
|
+
!isPlainRecord(result.thread) ||
|
|
84
|
+
!Array.isArray(result.thread.turns))
|
|
85
|
+
return undefined;
|
|
86
|
+
const active = result.thread.turns.find((turn) => isPlainRecord(turn) &&
|
|
87
|
+
typeof turn.id === 'string' &&
|
|
88
|
+
(turn.status === 'active' || turn.status === 'inProgress'));
|
|
89
|
+
return isPlainRecord(active) && typeof active.id === 'string' ? active.id : undefined;
|
|
90
|
+
}
|
|
91
|
+
export function codexThreadCwd(value) {
|
|
92
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
93
|
+
throw new Error('CODEX_THREAD_CWD_INVALID');
|
|
94
|
+
if (!value.startsWith('file:'))
|
|
95
|
+
return value;
|
|
96
|
+
try {
|
|
97
|
+
return fileURLToPath(value);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw new Error('CODEX_THREAD_CWD_INVALID');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function isWithinWorkspace(cwd, workspacePath) {
|
|
104
|
+
const normalize = (value) => {
|
|
105
|
+
const compact = value.replace(/[\\/]+/g, '/').replace(/\/$/, '');
|
|
106
|
+
return process.platform === 'win32' ? compact.toLowerCase() : compact;
|
|
107
|
+
};
|
|
108
|
+
const normalizedCwd = normalize(cwd);
|
|
109
|
+
const normalizedWorkspace = normalize(workspacePath);
|
|
110
|
+
if (normalizedCwd === normalizedWorkspace)
|
|
111
|
+
return true;
|
|
112
|
+
return normalizedCwd.startsWith(`${normalizedWorkspace}/`);
|
|
113
|
+
}
|
|
114
|
+
export function claudeDiscoveredSession(value, fallbackCwd) {
|
|
115
|
+
if (typeof value !== 'object' || value === null)
|
|
116
|
+
return undefined;
|
|
117
|
+
const record = value;
|
|
118
|
+
const externalSessionId = typeof record.sessionId === 'string'
|
|
119
|
+
? record.sessionId
|
|
120
|
+
: typeof record.id === 'string'
|
|
121
|
+
? record.id
|
|
122
|
+
: undefined;
|
|
123
|
+
if (externalSessionId === undefined || externalSessionId.length === 0)
|
|
124
|
+
return undefined;
|
|
125
|
+
const title = typeof record.summary === 'string'
|
|
126
|
+
? record.summary
|
|
127
|
+
: typeof record.title === 'string'
|
|
128
|
+
? record.title
|
|
129
|
+
: undefined;
|
|
130
|
+
const cwd = typeof record.cwd === 'string' && record.cwd.length > 0 ? record.cwd : fallbackCwd;
|
|
131
|
+
return { externalSessionId, cwd, ...(title === undefined ? {} : { title: title.slice(0, 80) }) };
|
|
132
|
+
}
|
|
133
|
+
export function latestNativeActivity(history) {
|
|
134
|
+
return history.lastActivityAt;
|
|
135
|
+
}
|
|
136
|
+
export function sameContextUsage(left, right) {
|
|
137
|
+
return (left.model === right.model &&
|
|
138
|
+
left.usedTokens === right.usedTokens &&
|
|
139
|
+
left.maxTokens === right.maxTokens);
|
|
140
|
+
}
|
|
141
|
+
export function isNativeHistory(value) {
|
|
142
|
+
return (typeof value === 'object' &&
|
|
143
|
+
value !== null &&
|
|
144
|
+
Array.isArray(value.items));
|
|
145
|
+
}
|
|
146
|
+
export function deduplicateDirectSessions(sessions) {
|
|
147
|
+
const values = new Map();
|
|
148
|
+
for (const session of sessions) {
|
|
149
|
+
const existing = values.get(session.id);
|
|
150
|
+
if (existing === undefined || existing.lastActivityAt < session.lastActivityAt)
|
|
151
|
+
values.set(session.id, session);
|
|
152
|
+
}
|
|
153
|
+
return [...values.values()].sort((left, right) => right.lastActivityAt - left.lastActivityAt);
|
|
154
|
+
}
|
|
155
|
+
export function nativeConversationPage(session, history, input, runtimeTurns = [], registerImages = () => []) {
|
|
156
|
+
const limit = Math.min(Math.max(input.limit ?? 30, 1), 500);
|
|
157
|
+
const nativeTurns = nativeTranscriptTurns(history.items);
|
|
158
|
+
const end = nativePageEnd(input.cursor, session.id, nativeTurns.length);
|
|
159
|
+
const start = Math.max(0, end - limit);
|
|
160
|
+
const turns = nativeTurns
|
|
161
|
+
.slice(start, end)
|
|
162
|
+
.map((entries, relativeIndex) => {
|
|
163
|
+
const firstIndex = entries[0]?.index ?? 0;
|
|
164
|
+
const turnId = `${session.id}:native:${firstIndex}`;
|
|
165
|
+
const items = entries.map(({ entry, index }) => nativeTranscriptConversationItem(session, entry, index, turnId, runtimeTurns, registerImages));
|
|
166
|
+
const startedAt = items[0]?.startedAt ?? firstIndex;
|
|
167
|
+
const completedAt = items.at(-1)?.completedAt ?? startedAt;
|
|
168
|
+
const after = lastNativeItemId(entries);
|
|
169
|
+
return {
|
|
170
|
+
id: turnId,
|
|
171
|
+
sessionId: session.id,
|
|
172
|
+
runId: null,
|
|
173
|
+
userItemId: items.find((item) => item.kind === 'user_message')?.id ?? null,
|
|
174
|
+
status: 'SUCCEEDED',
|
|
175
|
+
model: null,
|
|
176
|
+
effort: null,
|
|
177
|
+
access: null,
|
|
178
|
+
...(after === undefined || session.runner !== 'claude-code'
|
|
179
|
+
? {}
|
|
180
|
+
: {
|
|
181
|
+
rewind: {
|
|
182
|
+
before: lastNativeItemId(nativeTurns[start + relativeIndex - 1] ?? []) ?? null,
|
|
183
|
+
after
|
|
184
|
+
}
|
|
185
|
+
}),
|
|
186
|
+
items,
|
|
187
|
+
startedAt,
|
|
188
|
+
completedAt
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
return {
|
|
192
|
+
turns,
|
|
193
|
+
nextCursor: start > 0
|
|
194
|
+
? Buffer.from(JSON.stringify({ sessionId: session.id, end: start })).toString('base64url')
|
|
195
|
+
: null
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function lastNativeItemId(entries) {
|
|
199
|
+
return entries.findLast(({ entry }) => entry.nativeId !== undefined)?.entry.nativeId;
|
|
200
|
+
}
|
|
201
|
+
function nativeTranscriptTurns(items) {
|
|
202
|
+
const turns = [];
|
|
203
|
+
let current = [];
|
|
204
|
+
for (const [index, entry] of items.entries()) {
|
|
205
|
+
if (entry.kind === 'message' && entry.role === 'USER' && current.length > 0) {
|
|
206
|
+
turns.push(current);
|
|
207
|
+
current = [];
|
|
208
|
+
}
|
|
209
|
+
current.push({ entry, index });
|
|
210
|
+
}
|
|
211
|
+
if (current.length > 0)
|
|
212
|
+
turns.push(current);
|
|
213
|
+
return turns;
|
|
214
|
+
}
|
|
215
|
+
function nativeTranscriptConversationItem(session, entry, index, turnId, runtimeTurns, registerImages) {
|
|
216
|
+
const time = entry.createdAt ?? index;
|
|
217
|
+
const isToolCall = entry.kind === 'tool_call';
|
|
218
|
+
const isUnknown = entry.kind === 'unknown';
|
|
219
|
+
const isReasoning = entry.kind === 'reasoning_summary';
|
|
220
|
+
const isCommand = isToolCall && isClaudeCommandTool(entry.toolName);
|
|
221
|
+
const userInputQuestions = session.runner === 'claude-code' &&
|
|
222
|
+
isToolCall &&
|
|
223
|
+
entry.toolName === 'AskUserQuestion' &&
|
|
224
|
+
isPlainRecord(entry.input)
|
|
225
|
+
? claudeUserInputQuestions(entry.input)
|
|
226
|
+
: [];
|
|
227
|
+
const isUserInputRequest = userInputQuestions.length > 0;
|
|
228
|
+
const clientMessageId = entry.kind === 'message' && entry.role === 'USER'
|
|
229
|
+
? nativeUserClientMessageId(entry, time, runtimeTurns)
|
|
230
|
+
: null;
|
|
231
|
+
// Claude 标签式 Plan Mode:用户正文剥离注入的规划指令标签,
|
|
232
|
+
// 完整的 <proposed_plan> Agent 文本归一化为计划卡。
|
|
233
|
+
const entryText = entry.kind === 'message'
|
|
234
|
+
? entry.role === 'USER'
|
|
235
|
+
? stripClaudePlanTag(entry.text)
|
|
236
|
+
: entry.text
|
|
237
|
+
: '';
|
|
238
|
+
const planText = entry.kind === 'message' && entry.role !== 'USER' ? claudePlanText(entryText) : undefined;
|
|
239
|
+
const item = {
|
|
240
|
+
id: `${turnId}:item:${index}`,
|
|
241
|
+
sessionId: session.id,
|
|
242
|
+
turnId,
|
|
243
|
+
runId: null,
|
|
244
|
+
parentItemId: null,
|
|
245
|
+
sourceSequence: index,
|
|
246
|
+
revision: 0,
|
|
247
|
+
kind: isUserInputRequest
|
|
248
|
+
? 'user_input_request'
|
|
249
|
+
: isReasoning
|
|
250
|
+
? 'reasoning_summary'
|
|
251
|
+
: isToolCall
|
|
252
|
+
? isCommand
|
|
253
|
+
? 'command_execution'
|
|
254
|
+
: 'tool_call'
|
|
255
|
+
: isUnknown
|
|
256
|
+
? 'unknown'
|
|
257
|
+
: entry.kind === 'message' && entry.role === 'USER'
|
|
258
|
+
? 'user_message'
|
|
259
|
+
: planText !== undefined
|
|
260
|
+
? 'plan'
|
|
261
|
+
: 'assistant_message',
|
|
262
|
+
status: 'COMPLETED',
|
|
263
|
+
payload: isUserInputRequest
|
|
264
|
+
? {
|
|
265
|
+
requestId: `native:${entry.kind === 'tool_call' ? entry.toolUseId : `item-${index}`}`,
|
|
266
|
+
questions: userInputQuestions
|
|
267
|
+
}
|
|
268
|
+
: isReasoning
|
|
269
|
+
? { sections: entry.sections }
|
|
270
|
+
: isToolCall
|
|
271
|
+
? isCommand
|
|
272
|
+
? {
|
|
273
|
+
command: nativeClaudeCommand(entry),
|
|
274
|
+
cwd: null,
|
|
275
|
+
outputPreview: entry.outputSummary ?? '',
|
|
276
|
+
outputRef: null,
|
|
277
|
+
exitCode: null,
|
|
278
|
+
durationMs: null,
|
|
279
|
+
truncated: entry.inputTruncated || entry.outputTruncated
|
|
280
|
+
}
|
|
281
|
+
: {
|
|
282
|
+
namespace: session.runner,
|
|
283
|
+
toolName: entry.toolName,
|
|
284
|
+
title: entry.toolName,
|
|
285
|
+
inputSummary: entry.inputSummary,
|
|
286
|
+
outputSummary: entry.outputSummary,
|
|
287
|
+
progressLabel: null,
|
|
288
|
+
errorCode: entry.failed ? 'TOOL_EXECUTION_FAILED' : null,
|
|
289
|
+
truncated: entry.inputTruncated || entry.outputTruncated,
|
|
290
|
+
...(entry.imageAttachments === undefined
|
|
291
|
+
? {}
|
|
292
|
+
: { attachments: registerImages(entry.imageAttachments) })
|
|
293
|
+
}
|
|
294
|
+
: isUnknown
|
|
295
|
+
? { sourceEventType: entry.sourceEventType, label: entry.label }
|
|
296
|
+
: entry.kind === 'message' && entry.role === 'USER'
|
|
297
|
+
? { clientMessageId, text: entryText, contexts: [], delivery: 'ACCEPTED' }
|
|
298
|
+
: planText !== undefined
|
|
299
|
+
? { explanation: compactRunnerText(planText, 20_000), steps: [] }
|
|
300
|
+
: { text: entryText, format: 'markdown', source: 'native-file' },
|
|
301
|
+
startedAt: time,
|
|
302
|
+
completedAt: time
|
|
303
|
+
};
|
|
304
|
+
return item;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Claude JSONL 不保存浏览器请求 ID。Node 进程仍存活时,以正文和生成时刻
|
|
308
|
+
* 对齐当前运行态的用户消息,让 native 历史页替换同一乐观 Turn,而不是重复一条。
|
|
309
|
+
*/
|
|
310
|
+
export function nativeUserClientMessageId(entry, createdAt, runtimeTurns) {
|
|
311
|
+
for (const turn of runtimeTurns) {
|
|
312
|
+
const user = turn.items.find((item) => item.kind === 'user_message');
|
|
313
|
+
if (user === undefined || !isPlainRecord(user.payload))
|
|
314
|
+
continue;
|
|
315
|
+
const payload = user.payload;
|
|
316
|
+
const clientMessageId = typeof payload.clientMessageId === 'string' ? payload.clientMessageId : null;
|
|
317
|
+
// 标签式 Plan Mode 的指令标签只存在于原生 transcript,运行态气泡始终是原文。
|
|
318
|
+
if (clientMessageId === null || payload.text !== stripClaudePlanTag(entry.text))
|
|
319
|
+
continue;
|
|
320
|
+
const runtimeCreatedAt = user.startedAt ?? turn.startedAt;
|
|
321
|
+
if (runtimeCreatedAt !== null &&
|
|
322
|
+
runtimeCreatedAt !== undefined &&
|
|
323
|
+
Math.abs(createdAt - runtimeCreatedAt) > 60_000)
|
|
324
|
+
continue;
|
|
325
|
+
return clientMessageId;
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Normalize the documented App Server `thread/read(includeTurns: true)`
|
|
331
|
+
* result. Only items with a user-visible Workbench representation are kept;
|
|
332
|
+
* raw protocol envelopes are deliberately never exposed to Web.
|
|
333
|
+
*/
|
|
334
|
+
export function claudePlanText(text) {
|
|
335
|
+
const trimmed = text.trim();
|
|
336
|
+
if (!trimmed.startsWith('<proposed_plan>') || !/<\/proposed_plan>\s*$/.test(trimmed))
|
|
337
|
+
return undefined;
|
|
338
|
+
const plan = trimmed
|
|
339
|
+
.slice('<proposed_plan>'.length)
|
|
340
|
+
.replace(/<\/proposed_plan>\s*$/, '')
|
|
341
|
+
.trim();
|
|
342
|
+
return plan.length > 0 ? plan : undefined;
|
|
343
|
+
}
|
|
344
|
+
export function claudeUserInputQuestions(value) {
|
|
345
|
+
if (!Array.isArray(value.questions) || value.questions.length === 0 || value.questions.length > 4)
|
|
346
|
+
return [];
|
|
347
|
+
const questions = value.questions.map((raw, index) => {
|
|
348
|
+
if (!isPlainRecord(raw) || typeof raw.question !== 'string')
|
|
349
|
+
return undefined;
|
|
350
|
+
const options = Array.isArray(raw.options)
|
|
351
|
+
? raw.options.flatMap((option) => {
|
|
352
|
+
if (!isPlainRecord(option) || typeof option.label !== 'string')
|
|
353
|
+
return [];
|
|
354
|
+
const label = compactRunnerText(option.label, 500);
|
|
355
|
+
return label.length === 0
|
|
356
|
+
? []
|
|
357
|
+
: [
|
|
358
|
+
{
|
|
359
|
+
value: label,
|
|
360
|
+
label,
|
|
361
|
+
description: typeof option.description === 'string'
|
|
362
|
+
? compactRunnerText(option.description, 1_000)
|
|
363
|
+
: null
|
|
364
|
+
}
|
|
365
|
+
];
|
|
366
|
+
})
|
|
367
|
+
: [];
|
|
368
|
+
const multiSelect = raw.multiSelect === true;
|
|
369
|
+
return {
|
|
370
|
+
id: `claude-question-${index}`,
|
|
371
|
+
prompt: compactRunnerText(raw.question, 5_000),
|
|
372
|
+
input: options.length === 0 ? 'SHORT_TEXT' : multiSelect ? 'MULTI_SELECT' : 'SINGLE_SELECT',
|
|
373
|
+
options,
|
|
374
|
+
allowOther: true,
|
|
375
|
+
multiSelect
|
|
376
|
+
};
|
|
377
|
+
});
|
|
378
|
+
return questions.some((question) => question === undefined)
|
|
379
|
+
? []
|
|
380
|
+
: questions;
|
|
381
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseSecretEnvironment(value: unknown): Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const ENVIRONMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
2
|
+
const MAX_SECRET_COUNT = 128;
|
|
3
|
+
const MAX_SECRET_VALUE_BYTES = 16 * 1024;
|
|
4
|
+
const MAX_SECRET_TOTAL_BYTES = 64 * 1024;
|
|
5
|
+
export function parseSecretEnvironment(value) {
|
|
6
|
+
if (value === undefined)
|
|
7
|
+
return {};
|
|
8
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
9
|
+
throw new Error('SECRET_ENVIRONMENT_INVALID');
|
|
10
|
+
const entries = Object.entries(value);
|
|
11
|
+
if (entries.length > MAX_SECRET_COUNT)
|
|
12
|
+
throw new Error('SECRET_ENVIRONMENT_LIMIT_EXCEEDED');
|
|
13
|
+
const environment = {};
|
|
14
|
+
let bytes = 0;
|
|
15
|
+
for (const [name, secret] of entries) {
|
|
16
|
+
if (!ENVIRONMENT_NAME.test(name) || typeof secret !== 'string' || secret.includes('\0'))
|
|
17
|
+
throw new Error('SECRET_ENVIRONMENT_INVALID');
|
|
18
|
+
const valueBytes = Buffer.byteLength(secret);
|
|
19
|
+
bytes += Buffer.byteLength(name) + valueBytes;
|
|
20
|
+
if (valueBytes > MAX_SECRET_VALUE_BYTES || bytes > MAX_SECRET_TOTAL_BYTES)
|
|
21
|
+
throw new Error('SECRET_ENVIRONMENT_LIMIT_EXCEEDED');
|
|
22
|
+
environment[name] = secret;
|
|
23
|
+
}
|
|
24
|
+
return environment;
|
|
25
|
+
}
|
package/dist/workspace.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export interface WorkspaceDirectoryListing {
|
|
|
41
41
|
readonly entries: readonly WorkspaceDirectoryEntry[];
|
|
42
42
|
readonly truncated: boolean;
|
|
43
43
|
}
|
|
44
|
+
export declare function createWorkspaceDirectory(parentPath: string, directoryName: string, allowedRoots: readonly string[]): Promise<string>;
|
|
44
45
|
/** A per-workspace, process-local index for the Quick Open file picker. */
|
|
45
46
|
export declare class WorkspaceFileIndex {
|
|
46
47
|
private readonly workspaceRoot;
|
|
@@ -61,8 +62,12 @@ export declare class WorkspaceFileIndex {
|
|
|
61
62
|
});
|
|
62
63
|
/** Starts/renews the lightweight index observer without exposing file data. */
|
|
63
64
|
observe(): Promise<void>;
|
|
65
|
+
contentCandidates(): Promise<{
|
|
66
|
+
readonly entries: readonly WorkspaceFileEntry[];
|
|
67
|
+
readonly truncated: boolean;
|
|
68
|
+
}>;
|
|
64
69
|
dispose(): void;
|
|
65
|
-
search(query: string, cursor?: string, limit?: number): Promise<WorkspaceFileSearchPage>;
|
|
70
|
+
search(query: string, cursor?: string, limit?: number, includeDirectories?: boolean): Promise<WorkspaceFileSearchPage>;
|
|
66
71
|
private ensureLoaded;
|
|
67
72
|
private touch;
|
|
68
73
|
private rebuild;
|
|
@@ -77,6 +82,24 @@ export declare class WorkspaceFileIndex {
|
|
|
77
82
|
*/
|
|
78
83
|
export declare function listWorkspaceDirectories(requestedPath: string | undefined, allowedRoots: readonly string[], homePath?: string): Promise<WorkspaceDirectoryListing>;
|
|
79
84
|
export declare function listWorkspaceFiles(workspaceRoot: string, requestedPath?: string, cursor?: string, limit?: number): Promise<WorkspaceFilePage>;
|
|
85
|
+
export type WorkspaceFileMutation = {
|
|
86
|
+
readonly action: 'CREATE_FILE';
|
|
87
|
+
readonly path: string;
|
|
88
|
+
} | {
|
|
89
|
+
readonly action: 'CREATE_DIRECTORY';
|
|
90
|
+
readonly path: string;
|
|
91
|
+
} | {
|
|
92
|
+
readonly action: 'MOVE';
|
|
93
|
+
readonly path: string;
|
|
94
|
+
readonly destination: string;
|
|
95
|
+
} | {
|
|
96
|
+
readonly action: 'DELETE';
|
|
97
|
+
readonly path: string;
|
|
98
|
+
};
|
|
99
|
+
export declare function mutateWorkspaceFile(workspaceRoot: string, mutation: WorkspaceFileMutation): Promise<{
|
|
100
|
+
readonly path?: string;
|
|
101
|
+
readonly deleted?: true;
|
|
102
|
+
}>;
|
|
80
103
|
/** Validates an upload destination without following an existing target symlink. */
|
|
81
104
|
export declare function preflightWorkspaceUpload(workspaceRoot: string, requestedDirectory: string, name: string): Promise<WorkspaceUploadTarget>;
|
|
82
105
|
export declare function workspaceUploadTemporaryName(uploadId: string): string;
|
|
@@ -98,7 +121,7 @@ export declare function readAllowedTextFile(requestedPath: string, allowedRoots:
|
|
|
98
121
|
readonly nextOffset: number | null;
|
|
99
122
|
}>;
|
|
100
123
|
/** Searches file names only, so an exploratory request cannot exfiltrate file contents. */
|
|
101
|
-
export declare function searchWorkspaceFiles(workspaceRoot: string, query: string, cursor?: string, limit?: number): Promise<WorkspaceFileSearchPage>;
|
|
124
|
+
export declare function searchWorkspaceFiles(workspaceRoot: string, query: string, cursor?: string, limit?: number, includeDirectories?: boolean): Promise<WorkspaceFileSearchPage>;
|
|
102
125
|
export declare function normalizeWorkspacePath(input: string, platform?: NodeJS.Platform): string;
|
|
103
126
|
export declare function isWithinAllowedRoot(target: string, roots: readonly string[], platform?: NodeJS.Platform): boolean;
|
|
104
127
|
/** Resolving both candidate and roots rejects Linux symlinks which point outside a root. */
|
|
@@ -128,6 +151,35 @@ export declare function readCurrentChangesSummary(workspacePath: string): Promis
|
|
|
128
151
|
}>;
|
|
129
152
|
export declare function listInitializedSubmodulePaths(workspacePath: string): Promise<readonly string[]>;
|
|
130
153
|
export declare function resolveGitRepository(workspacePath: string, repositoryPath: string): Promise<string>;
|
|
154
|
+
export interface GitHistoryRefEntry {
|
|
155
|
+
readonly name: string;
|
|
156
|
+
readonly displayName: string;
|
|
157
|
+
readonly kind: 'LOCAL' | 'REMOTE';
|
|
158
|
+
readonly hash: string;
|
|
159
|
+
readonly current: boolean;
|
|
160
|
+
}
|
|
161
|
+
export declare function listGitHistoryRefs(repository: string): Promise<readonly GitHistoryRefEntry[]>;
|
|
162
|
+
export declare function readGitHistory(repository: string, input: {
|
|
163
|
+
readonly refs?: readonly string[];
|
|
164
|
+
readonly cursor?: string;
|
|
165
|
+
readonly limit?: number;
|
|
166
|
+
}): Promise<{
|
|
167
|
+
readonly commits: readonly object[];
|
|
168
|
+
readonly nextCursor: string | null;
|
|
169
|
+
}>;
|
|
170
|
+
export declare function readGitHistoryFiles(repository: string, commit: string, refs?: readonly string[]): Promise<readonly {
|
|
171
|
+
path: string;
|
|
172
|
+
previousPath?: string;
|
|
173
|
+
change: ReturnType<typeof gitChange>;
|
|
174
|
+
}[]>;
|
|
175
|
+
export declare function readGitHistoryFileDiff(repository: string, commit: string, requestedPath: string, refs?: readonly string[], requestedPreviousPath?: string): Promise<{
|
|
176
|
+
readonly binary: boolean;
|
|
177
|
+
readonly text?: string;
|
|
178
|
+
readonly summary?: string;
|
|
179
|
+
readonly truncated: boolean;
|
|
180
|
+
readonly beforePreview?: BinaryDiffPreview;
|
|
181
|
+
readonly afterPreview?: BinaryDiffPreview;
|
|
182
|
+
}>;
|
|
131
183
|
export declare function readCurrentChanges(workspacePath: string): Promise<readonly {
|
|
132
184
|
readonly path: string;
|
|
133
185
|
readonly state: 'STAGED' | 'UNSTAGED' | 'UNTRACKED';
|
|
@@ -140,12 +192,20 @@ export declare function readCurrentChangeDiff(workspacePath: string, requestedPa
|
|
|
140
192
|
readonly truncated: boolean;
|
|
141
193
|
readonly afterText?: string;
|
|
142
194
|
readonly afterTruncated?: boolean;
|
|
195
|
+
readonly beforePreview?: BinaryDiffPreview;
|
|
196
|
+
readonly afterPreview?: BinaryDiffPreview;
|
|
143
197
|
}>;
|
|
144
198
|
/** Current Changes must not expose an external link. */
|
|
145
199
|
export declare function isWorkspaceChangeVisible(workspacePath: string, requestedPath: string): Promise<boolean>;
|
|
200
|
+
interface BinaryDiffPreview {
|
|
201
|
+
readonly contentBase64?: string;
|
|
202
|
+
readonly truncated: boolean;
|
|
203
|
+
}
|
|
146
204
|
export declare function restoreCurrentChange(workspacePath: string, requestedPath: string, state: 'STAGED' | 'UNSTAGED' | 'UNTRACKED'): Promise<void>;
|
|
205
|
+
declare function gitChange(status: string): 'ADDED' | 'MODIFIED' | 'DELETED' | 'RENAMED' | 'COPIED' | 'TYPE_CHANGED';
|
|
147
206
|
export declare class GitCommandError extends Error {
|
|
148
207
|
readonly code: number;
|
|
149
208
|
readonly stderr: string;
|
|
150
209
|
constructor(code: number, stderr: string);
|
|
151
210
|
}
|
|
211
|
+
export {};
|