@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.
Files changed (195) hide show
  1. package/README.md +9 -1
  2. package/dist/capabilities.d.ts +2 -0
  3. package/dist/capabilities.js +15 -4
  4. package/dist/claude-agent-sdk.d.ts +4 -16
  5. package/dist/claude-agent-sdk.js +18 -79
  6. package/dist/claude-channel.js +0 -1
  7. package/dist/codex-app-server.d.ts +5 -0
  8. package/dist/codex-app-server.js +26 -5
  9. package/dist/config.d.ts +3 -0
  10. package/dist/config.js +18 -2
  11. package/dist/connector/node-connector-options.d.ts +26 -0
  12. package/dist/connector/node-connector-options.js +1 -0
  13. package/dist/connector/node-operation-router.d.ts +9 -0
  14. package/dist/connector/node-operation-router.js +22 -0
  15. package/dist/connector.d.ts +62 -356
  16. package/dist/connector.js +714 -6176
  17. package/dist/control-outbound-scheduler.d.ts +26 -0
  18. package/dist/control-outbound-scheduler.js +121 -0
  19. package/dist/database.d.ts +4 -0
  20. package/dist/database.js +15 -10
  21. package/dist/event-buffer.js +0 -1
  22. package/dist/fake-runner.js +0 -1
  23. package/dist/health.js +0 -1
  24. package/dist/main.js +6 -1
  25. package/dist/migrations/v001.js +0 -1
  26. package/dist/migrations/v002.js +0 -1
  27. package/dist/migrations/v003.d.ts +4 -0
  28. package/dist/migrations/v003.js +44 -0
  29. package/dist/native-jsonl-reader.d.ts +14 -0
  30. package/dist/native-jsonl-reader.js +97 -0
  31. package/dist/native-session-history.d.ts +41 -4
  32. package/dist/native-session-history.js +340 -159
  33. package/dist/opencode-runtime.d.ts +33 -0
  34. package/dist/opencode-runtime.js +80 -0
  35. package/dist/opencode-server.d.ts +47 -0
  36. package/dist/opencode-server.js +250 -0
  37. package/dist/operational.js +10 -3
  38. package/dist/process-tree.js +0 -1
  39. package/dist/runner/abstract-runner.d.ts +153 -0
  40. package/dist/runner/abstract-runner.js +154 -0
  41. package/dist/runner/claude/managed-run-controller.d.ts +52 -0
  42. package/dist/runner/claude/managed-run-controller.js +326 -0
  43. package/dist/runner/claude-code-runner.d.ts +70 -0
  44. package/dist/runner/claude-code-runner.js +286 -0
  45. package/dist/runner/codex/conversation-parser.d.ts +59 -0
  46. package/dist/runner/codex/conversation-parser.js +816 -0
  47. package/dist/runner/codex/managed-run-controller.d.ts +80 -0
  48. package/dist/runner/codex/managed-run-controller.js +388 -0
  49. package/dist/runner/codex-runner.d.ts +85 -0
  50. package/dist/runner/codex-runner.js +365 -0
  51. package/dist/runner/fake-test-runner.d.ts +26 -0
  52. package/dist/runner/fake-test-runner.js +47 -0
  53. package/dist/runner/opencode/conversation-parser.d.ts +27 -0
  54. package/dist/runner/opencode/conversation-parser.js +217 -0
  55. package/dist/runner/opencode/managed-run-controller.d.ts +76 -0
  56. package/dist/runner/opencode/managed-run-controller.js +451 -0
  57. package/dist/runner/opencode-runner.d.ts +60 -0
  58. package/dist/runner/opencode-runner.js +330 -0
  59. package/dist/runner/runner-registry.d.ts +19 -0
  60. package/dist/runner/runner-registry.js +75 -0
  61. package/dist/runner-command-engine.js +0 -1
  62. package/dist/runner-profiles.d.ts +4 -0
  63. package/dist/runner-profiles.js +76 -3
  64. package/dist/runner-usage.d.ts +2 -2
  65. package/dist/runner-usage.js +3 -6
  66. package/dist/runtime-state.js +26 -14
  67. package/dist/service/attachment-domain-state.d.ts +5 -0
  68. package/dist/service/attachment-domain-state.js +5 -0
  69. package/dist/service/conversation-history-service.d.ts +34 -0
  70. package/dist/service/conversation-history-service.js +158 -0
  71. package/dist/service/external-session-resume-service.d.ts +32 -0
  72. package/dist/service/external-session-resume-service.js +98 -0
  73. package/dist/service/fake-managed-run-service.d.ts +29 -0
  74. package/dist/service/fake-managed-run-service.js +62 -0
  75. package/dist/service/managed-runner-session-service.d.ts +12 -0
  76. package/dist/service/managed-runner-session-service.js +35 -0
  77. package/dist/service/native-session-projection-service.d.ts +51 -0
  78. package/dist/service/native-session-projection-service.js +271 -0
  79. package/dist/service/native-session-watch-service.d.ts +37 -0
  80. package/dist/service/native-session-watch-service.js +150 -0
  81. package/dist/service/node-connection-lifecycle-service.d.ts +37 -0
  82. package/dist/service/node-connection-lifecycle-service.js +85 -0
  83. package/dist/service/node-control-channel.d.ts +32 -0
  84. package/dist/service/node-control-channel.js +85 -0
  85. package/dist/service/node-control-message-service.d.ts +28 -0
  86. package/dist/service/node-control-message-service.js +104 -0
  87. package/dist/service/node-request-service.d.ts +14 -0
  88. package/dist/service/node-request-service.js +50 -0
  89. package/dist/service/node-run-event-bridge.d.ts +40 -0
  90. package/dist/service/node-run-event-bridge.js +83 -0
  91. package/dist/service/node-terminal-channel.d.ts +29 -0
  92. package/dist/service/node-terminal-channel.js +182 -0
  93. package/dist/service/node-upgrade-coordinator.d.ts +29 -0
  94. package/dist/service/node-upgrade-coordinator.js +137 -0
  95. package/dist/service/node-workspace-coordinator.d.ts +32 -0
  96. package/dist/service/node-workspace-coordinator.js +80 -0
  97. package/dist/service/run-attachment-service.d.ts +37 -0
  98. package/dist/service/run-attachment-service.js +160 -0
  99. package/dist/service/run-domain-state.d.ts +4 -0
  100. package/dist/service/run-domain-state.js +4 -0
  101. package/dist/service/run-event-service.d.ts +31 -0
  102. package/dist/service/run-event-service.js +102 -0
  103. package/dist/service/run-workbench-service.d.ts +27 -0
  104. package/dist/service/run-workbench-service.js +113 -0
  105. package/dist/service/runner-channel-message-service.d.ts +13 -0
  106. package/dist/service/runner-channel-message-service.js +20 -0
  107. package/dist/service/runner-interaction-service.d.ts +40 -0
  108. package/dist/service/runner-interaction-service.js +246 -0
  109. package/dist/service/runner-service.d.ts +44 -0
  110. package/dist/service/runner-service.js +68 -0
  111. package/dist/service/session-catalog-service.d.ts +45 -0
  112. package/dist/service/session-catalog-service.js +224 -0
  113. package/dist/service/session-command-service.d.ts +23 -0
  114. package/dist/service/session-command-service.js +69 -0
  115. package/dist/service/session-context-service.d.ts +34 -0
  116. package/dist/service/session-context-service.js +87 -0
  117. package/dist/service/session-domain-state.d.ts +8 -0
  118. package/dist/service/session-domain-state.js +8 -0
  119. package/dist/service/session-identity-service.d.ts +24 -0
  120. package/dist/service/session-identity-service.js +86 -0
  121. package/dist/service/session-lifecycle-service.d.ts +31 -0
  122. package/dist/service/session-lifecycle-service.js +242 -0
  123. package/dist/service/session-message-service.d.ts +33 -0
  124. package/dist/service/session-message-service.js +157 -0
  125. package/dist/service/session-presentation-service.d.ts +33 -0
  126. package/dist/service/session-presentation-service.js +36 -0
  127. package/dist/service/session-query-service.d.ts +31 -0
  128. package/dist/service/session-query-service.js +77 -0
  129. package/dist/service/terminal-relay-state.d.ts +6 -0
  130. package/dist/service/terminal-relay-state.js +3 -0
  131. package/dist/service/terminal-service.d.ts +12 -0
  132. package/dist/service/terminal-service.js +52 -0
  133. package/dist/service/workbench-event-service.d.ts +21 -0
  134. package/dist/service/workbench-event-service.js +53 -0
  135. package/dist/service/workbench-manifest-service.d.ts +89 -0
  136. package/dist/service/workbench-manifest-service.js +70 -0
  137. package/dist/service/workspace-change-service.d.ts +9 -0
  138. package/dist/service/workspace-change-service.js +86 -0
  139. package/dist/service/workspace-content-search-service.d.ts +14 -0
  140. package/dist/service/workspace-content-search-service.js +477 -0
  141. package/dist/service/workspace-domain-state.d.ts +75 -0
  142. package/dist/service/workspace-domain-state.js +40 -0
  143. package/dist/service/workspace-file-service.d.ts +17 -0
  144. package/dist/service/workspace-file-service.js +109 -0
  145. package/dist/service/workspace-queue-workbench-service.d.ts +55 -0
  146. package/dist/service/workspace-queue-workbench-service.js +226 -0
  147. package/dist/service/workspace-service.d.ts +15 -0
  148. package/dist/service/workspace-service.js +67 -0
  149. package/dist/service/workspace-session-service.d.ts +23 -0
  150. package/dist/service/workspace-session-service.js +139 -0
  151. package/dist/service/workspace-upload-service.d.ts +37 -0
  152. package/dist/service/workspace-upload-service.js +240 -0
  153. package/dist/service/workspace-watch-service.d.ts +18 -0
  154. package/dist/service/workspace-watch-service.js +120 -0
  155. package/dist/service/workspace-workbench-service.d.ts +47 -0
  156. package/dist/service/workspace-workbench-service.js +223 -0
  157. package/dist/service.js +2 -3
  158. package/dist/storage.js +0 -1
  159. package/dist/supervisor.d.ts +1 -0
  160. package/dist/supervisor.js +154 -0
  161. package/dist/terminal.d.ts +1 -0
  162. package/dist/terminal.js +1 -2
  163. package/dist/util/node-operation-parsers.d.ts +92 -0
  164. package/dist/util/node-operation-parsers.js +382 -0
  165. package/dist/util/runner-native-session-parsers.d.ts +72 -0
  166. package/dist/util/runner-native-session-parsers.js +381 -0
  167. package/dist/util/secret-environment.d.ts +1 -0
  168. package/dist/util/secret-environment.js +25 -0
  169. package/dist/workspace.d.ts +62 -2
  170. package/dist/workspace.js +539 -57
  171. package/package.json +3 -2
  172. package/dist/capabilities.js.map +0 -1
  173. package/dist/claude-agent-sdk.js.map +0 -1
  174. package/dist/claude-channel.js.map +0 -1
  175. package/dist/codex-app-server.js.map +0 -1
  176. package/dist/config.js.map +0 -1
  177. package/dist/connector.js.map +0 -1
  178. package/dist/database.js.map +0 -1
  179. package/dist/event-buffer.js.map +0 -1
  180. package/dist/fake-runner.js.map +0 -1
  181. package/dist/health.js.map +0 -1
  182. package/dist/main.js.map +0 -1
  183. package/dist/migrations/v001.js.map +0 -1
  184. package/dist/migrations/v002.js.map +0 -1
  185. package/dist/native-session-history.js.map +0 -1
  186. package/dist/operational.js.map +0 -1
  187. package/dist/process-tree.js.map +0 -1
  188. package/dist/runner-command-engine.js.map +0 -1
  189. package/dist/runner-profiles.js.map +0 -1
  190. package/dist/runner-usage.js.map +0 -1
  191. package/dist/runtime-state.js.map +0 -1
  192. package/dist/service.js.map +0 -1
  193. package/dist/storage.js.map +0 -1
  194. package/dist/terminal.js.map +0 -1
  195. package/dist/workspace.js.map +0 -1
@@ -0,0 +1,80 @@
1
+ import type { NodeEnvelope, RunnerCommandDescriptor, RunnerCommandState } from '@myagentroam/protocol';
2
+ import type { NodeAgentSession, NodeMessageAttachmentInput } from '../../database.js';
3
+ import type { CodexComposerInput } from '../../codex-app-server.js';
4
+ import type { FakeRunnerStatus } from '../../fake-runner.js';
5
+ import { CodexRunner } from '../codex-runner.js';
6
+ type CodexImageInput = Extract<CodexComposerInput, {
7
+ readonly type: 'localImage';
8
+ }>;
9
+ export interface CodexManagedRunHost<TAttachment> {
10
+ readonly available: () => boolean;
11
+ readonly intercepted: () => boolean;
12
+ readonly active: ReadonlySet<string>;
13
+ addActive(runId: string): void;
14
+ removeActive(runId: string): void;
15
+ adopt(input: {
16
+ runId: string;
17
+ sessionId: string;
18
+ runner: 'codex';
19
+ cwd: string;
20
+ externalSessionId?: string;
21
+ }): void;
22
+ session(sessionId: string): NodeAgentSession | undefined;
23
+ runStatus(runId: string): string | undefined;
24
+ run(runId: string): unknown;
25
+ interrupted(runId: string): boolean;
26
+ parseAttachments(value: unknown): readonly TAttachment[];
27
+ materialize(runId: string, attachments: readonly TAttachment[]): Promise<readonly CodexImageInput[]>;
28
+ completeCancelled(runId: string, cwd: string): boolean;
29
+ promoteSession(sessionId: string, nativeSessionId: string): void;
30
+ send(type: string, payload: unknown): void;
31
+ emit(runId: string, eventType: string, payload: unknown, status?: FakeRunnerStatus): void;
32
+ cancel(runId: string, threadId: string, turnId: string, cwd: string, status: 'INTERRUPTED' | 'CANCELLED'): Promise<void>;
33
+ createApproval(input: {
34
+ runId: string;
35
+ approvalId: string;
36
+ payload: unknown;
37
+ expiresAt: number;
38
+ }): void;
39
+ publishApproval(input: {
40
+ runId: string;
41
+ approvalId: string;
42
+ payload: unknown;
43
+ expiresAt: number;
44
+ }): void;
45
+ emitItem(runId: string, item: {
46
+ readonly itemId: string;
47
+ readonly kind: 'reasoning_summary' | 'tool_call' | 'command_execution' | 'user_input_request' | 'plan' | 'context_compaction' | 'file_change' | 'unknown';
48
+ readonly status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'DECLINED';
49
+ readonly payload: Record<string, unknown>;
50
+ readonly merge?: boolean;
51
+ readonly append?: 'sections' | 'outputPreview' | 'outputSummary';
52
+ }): void;
53
+ appendImages(runId: string, images: readonly NodeMessageAttachmentInput[]): readonly Record<string, unknown>[];
54
+ captureSnapshot(runId: string, cwd: string): Promise<void>;
55
+ syncTitle(sessionId: string, nativeSessionId: string, cwd: string): Promise<void>;
56
+ commandState(sessionId: string, commandId: string): RunnerCommandState | undefined;
57
+ setCommandState(sessionId: string, state: RunnerCommandState): void;
58
+ clearCommandState(sessionId: string, commandId: string): void;
59
+ commandStates(sessionId: string): readonly RunnerCommandState[];
60
+ fast(input: string): Promise<unknown>;
61
+ }
62
+ export declare class CodexManagedRunController<TAttachment> {
63
+ private readonly runner;
64
+ private readonly host;
65
+ constructor(runner: CodexRunner, host: CodexManagedRunHost<TAttachment>);
66
+ executeCommand(session: NodeAgentSession, command: RunnerCommandDescriptor, input: string, context: {
67
+ readonly secretEnvironment: Readonly<Record<string, string>>;
68
+ }): Promise<unknown>;
69
+ private compact;
70
+ handleNotification(notification: {
71
+ readonly method: string;
72
+ readonly params: unknown;
73
+ readonly requestId?: string | number;
74
+ }): void;
75
+ private requestApproval;
76
+ private requestUserInput;
77
+ start(envelope: NodeEnvelope): void;
78
+ private run;
79
+ }
80
+ export {};
@@ -0,0 +1,388 @@
1
+ import { extractId } from '../../util/runner-native-session-parsers.js';
2
+ import { parseSecretEnvironment } from '../../util/secret-environment.js';
3
+ import { boundedConversationItemId, codexImageAttachments, codexLiveConversationItem, codexUserInputQuestions, compactRunnerText, isPlainRecord } from './conversation-parser.js';
4
+ export class CodexManagedRunController {
5
+ runner;
6
+ host;
7
+ constructor(runner, host) {
8
+ this.runner = runner;
9
+ this.host = host;
10
+ }
11
+ async executeCommand(session, command, input, context) {
12
+ if (command.id === 'plan') {
13
+ const environmentLeaseId = crypto.randomUUID();
14
+ this.runner.prepareEnvironment(context.secretEnvironment, environmentLeaseId);
15
+ let modes;
16
+ try {
17
+ await this.runner.start();
18
+ modes = await this.runner.listCollaborationModes();
19
+ }
20
+ finally {
21
+ this.runner.releaseEnvironment(environmentLeaseId);
22
+ }
23
+ if (!isPlainRecord(modes) ||
24
+ !Array.isArray(modes.data) ||
25
+ !modes.data.some((mode) => isPlainRecord(mode) && mode.mode === 'plan'))
26
+ throw new Error('RUNNER_COMMAND_UNAVAILABLE');
27
+ const current = this.host.commandState(session.id, 'plan');
28
+ if (current?.active)
29
+ this.host.clearCommandState(session.id, 'plan');
30
+ else
31
+ this.host.setCommandState(session.id, {
32
+ commandId: 'plan',
33
+ active: true,
34
+ label: 'Plan Mode',
35
+ detail: '后续消息将以协作规划模式运行。',
36
+ closable: true,
37
+ closeInput: '/plan',
38
+ continuation: { label: 'Implement', prompt: '请根据上述计划开始实施。' }
39
+ });
40
+ return { command: command.id, states: this.host.commandStates(session.id) };
41
+ }
42
+ const threadId = session.externalSessionId;
43
+ if (threadId === null || session.nativeControl !== 'MAR_MANAGED')
44
+ throw new Error('RUNNER_COMMAND_UNAVAILABLE');
45
+ if (command.id === 'compact') {
46
+ const runId = crypto.randomUUID();
47
+ this.runner.prepareEnvironment(context.secretEnvironment, runId);
48
+ try {
49
+ await this.runner.start();
50
+ return await this.compact(session, threadId, runId);
51
+ }
52
+ catch (error) {
53
+ this.runner.releaseEnvironment(runId);
54
+ throw error;
55
+ }
56
+ }
57
+ if (command.id === 'fast')
58
+ return this.host.fast(input);
59
+ throw new Error('RUNNER_COMMAND_UNAVAILABLE');
60
+ }
61
+ async compact(session, threadId, runId) {
62
+ this.host.adopt({
63
+ runId,
64
+ sessionId: session.id,
65
+ runner: 'codex',
66
+ cwd: session.cwd,
67
+ externalSessionId: threadId
68
+ });
69
+ this.host.addActive(runId);
70
+ this.runner.execution.runs.set(runId, {
71
+ sessionId: session.id,
72
+ threadId,
73
+ cwd: session.cwd,
74
+ refreshTitle: false
75
+ });
76
+ this.host.emit(runId, 'run.started', { command: 'compact' }, 'STARTING');
77
+ try {
78
+ await this.runner.request('thread/compact/start', { threadId });
79
+ return {
80
+ command: 'compact',
81
+ run: this.host.run(runId),
82
+ states: this.host.commandStates(session.id)
83
+ };
84
+ }
85
+ catch (error) {
86
+ this.host.emit(runId, 'run.failed', { code: error instanceof Error ? error.message : 'CODEX_COMPACT_FAILED' }, 'FAILED');
87
+ this.runner.execution.runs.delete(runId);
88
+ this.runner.releaseEnvironment(runId);
89
+ this.host.removeActive(runId);
90
+ throw error;
91
+ }
92
+ }
93
+ handleNotification(notification) {
94
+ const params = isPlainRecord(notification.params) ? notification.params : {};
95
+ const turn = isPlainRecord(params.turn) ? params.turn : undefined;
96
+ const turnId = typeof params.turnId === 'string'
97
+ ? params.turnId
98
+ : typeof turn?.id === 'string'
99
+ ? turn.id
100
+ : undefined;
101
+ const threadId = typeof params.threadId === 'string' ? params.threadId : undefined;
102
+ if (turnId === undefined && threadId === undefined)
103
+ return;
104
+ const run = [...this.runner.execution.runs.entries()].find(([, value]) => (turnId !== undefined && value.turnId === turnId) ||
105
+ (threadId !== undefined && value.threadId === threadId));
106
+ if (run === undefined)
107
+ return;
108
+ const [runId] = run;
109
+ if (notification.method === 'turn/started' && turnId !== undefined) {
110
+ const active = this.runner.execution.runs.get(runId);
111
+ if (active !== undefined)
112
+ this.runner.execution.runs.set(runId, { ...active, turnId });
113
+ this.host.emit(runId, 'run.running', { threadId, turnId }, 'RUNNING');
114
+ return;
115
+ }
116
+ if (notification.requestId !== undefined &&
117
+ (notification.method === 'item/commandExecution/requestApproval' ||
118
+ notification.method === 'item/fileChange/requestApproval')) {
119
+ this.requestApproval(runId, {
120
+ ...notification,
121
+ requestId: notification.requestId
122
+ });
123
+ return;
124
+ }
125
+ if (notification.requestId !== undefined &&
126
+ notification.method === 'item/tool/requestUserInput') {
127
+ this.requestUserInput(runId, notification.requestId, params);
128
+ return;
129
+ }
130
+ if (notification.method === 'item/agentMessage/delta' && typeof params.delta === 'string') {
131
+ this.host.emit(runId, 'text.delta', { text: params.delta });
132
+ return;
133
+ }
134
+ if (notification.method === 'item/reasoning/summaryTextDelta' &&
135
+ typeof params.itemId === 'string' &&
136
+ typeof params.delta === 'string') {
137
+ const index = typeof params.summaryIndex === 'number' && Number.isInteger(params.summaryIndex)
138
+ ? params.summaryIndex
139
+ : 0;
140
+ this.host.emitItem(runId, {
141
+ itemId: boundedConversationItemId('codex', params.itemId),
142
+ kind: 'reasoning_summary',
143
+ status: 'IN_PROGRESS',
144
+ payload: { sections: [{ index, text: compactRunnerText(params.delta, 10_000) }] },
145
+ merge: true,
146
+ append: 'sections'
147
+ });
148
+ return;
149
+ }
150
+ if (notification.method === 'item/commandExecution/outputDelta' &&
151
+ typeof params.itemId === 'string' &&
152
+ typeof params.delta === 'string') {
153
+ this.host.emitItem(runId, {
154
+ itemId: boundedConversationItemId('codex', params.itemId),
155
+ kind: 'command_execution',
156
+ status: 'IN_PROGRESS',
157
+ payload: { outputPreview: compactRunnerText(params.delta, 10_000) },
158
+ merge: true,
159
+ append: 'outputPreview'
160
+ });
161
+ return;
162
+ }
163
+ if (notification.method === 'item/mcpToolCall/progress' &&
164
+ typeof params.itemId === 'string' &&
165
+ typeof params.message === 'string') {
166
+ this.host.emitItem(runId, {
167
+ itemId: boundedConversationItemId('codex', params.itemId),
168
+ kind: 'tool_call',
169
+ status: 'IN_PROGRESS',
170
+ payload: { progressLabel: compactRunnerText(params.message, 500) },
171
+ merge: true
172
+ });
173
+ return;
174
+ }
175
+ if ((notification.method === 'item/started' || notification.method === 'item/completed') &&
176
+ isPlainRecord(params.item)) {
177
+ const attachments = this.host.appendImages(runId, codexImageAttachments(params.item));
178
+ const item = codexLiveConversationItem(params.item, notification.method === 'item/completed', attachments);
179
+ if (item !== undefined)
180
+ this.host.emitItem(runId, item);
181
+ return;
182
+ }
183
+ if (notification.method === 'turn/completed') {
184
+ const status = turn?.status;
185
+ const terminal = status === 'completed' ? 'SUCCEEDED' : status === 'interrupted' ? 'INTERRUPTED' : 'FAILED';
186
+ this.host.emit(runId, 'run.completed', { status: status ?? 'unknown' }, terminal);
187
+ const active = this.runner.execution.runs.get(runId);
188
+ const titleRefresh = active === undefined ||
189
+ active.refreshTitle === false ||
190
+ this.host.session(active.sessionId)?.titleSource !== 'AUTO'
191
+ ? undefined
192
+ : this.host.syncTitle(active.sessionId, active.threadId, active.cwd);
193
+ if (active !== undefined) {
194
+ void this.host.captureSnapshot(runId, active.cwd);
195
+ }
196
+ this.runner.execution.runs.delete(runId);
197
+ if (titleRefresh === undefined)
198
+ this.runner.releaseEnvironment(runId);
199
+ else
200
+ void titleRefresh.finally(() => this.runner.releaseEnvironment(runId));
201
+ this.host.removeActive(runId);
202
+ }
203
+ }
204
+ requestApproval(runId, notification) {
205
+ const approvalId = `codex:${notification.requestId}`;
206
+ const expiresAt = Date.now() + 10 * 60_000;
207
+ this.runner.execution.approvals.set(approvalId, notification.requestId);
208
+ try {
209
+ this.host.createApproval({
210
+ runId,
211
+ approvalId,
212
+ payload: { method: notification.method, params: notification.params },
213
+ expiresAt
214
+ });
215
+ }
216
+ catch {
217
+ this.runner.execution.approvals.delete(approvalId);
218
+ return;
219
+ }
220
+ this.host.publishApproval({
221
+ runId,
222
+ approvalId,
223
+ payload: { method: notification.method, params: notification.params },
224
+ expiresAt
225
+ });
226
+ }
227
+ requestUserInput(runId, nativeRequestId, params) {
228
+ const questions = codexUserInputQuestions(params.questions);
229
+ if (questions.length === 0) {
230
+ try {
231
+ this.runner.respond(nativeRequestId, { answers: {} });
232
+ }
233
+ catch {
234
+ // A disconnected App Server cannot receive a malformed-request response.
235
+ }
236
+ return;
237
+ }
238
+ const requestId = String(nativeRequestId);
239
+ this.runner.execution.userInputs.set(requestId, {
240
+ runId,
241
+ requestId: nativeRequestId,
242
+ questions: questions.map((question) => ({
243
+ id: question.id,
244
+ options: question.options.map((option) => option.value),
245
+ allowOther: question.allowOther
246
+ }))
247
+ });
248
+ this.host.emitItem(runId, {
249
+ itemId: boundedConversationItemId('codex-input', requestId),
250
+ kind: 'user_input_request',
251
+ status: 'PENDING',
252
+ payload: {
253
+ requestId,
254
+ questions: questions.map((question) => ({
255
+ id: question.id,
256
+ prompt: question.prompt,
257
+ input: question.options.length === 0 ? 'SHORT_TEXT' : 'SINGLE_SELECT',
258
+ options: question.options.map((option) => ({
259
+ value: option.value,
260
+ label: option.label,
261
+ description: option.description
262
+ })),
263
+ allowOther: question.allowOther
264
+ }))
265
+ }
266
+ });
267
+ }
268
+ start(envelope) {
269
+ const payload = envelope.payload;
270
+ if (payload.runner !== 'codex' ||
271
+ typeof payload.runId !== 'string' ||
272
+ typeof payload.sessionId !== 'string' ||
273
+ typeof payload.input !== 'string' ||
274
+ typeof payload.cwd !== 'string' ||
275
+ this.host.intercepted() ||
276
+ this.host.active.has(payload.runId))
277
+ return;
278
+ const { runId, sessionId, input, cwd } = payload;
279
+ let attachments;
280
+ let secretEnvironment;
281
+ try {
282
+ attachments = this.host.parseAttachments(payload.attachments);
283
+ secretEnvironment = parseSecretEnvironment(payload.secretEnvironment);
284
+ }
285
+ catch (error) {
286
+ this.host.emit(runId, 'run.rejected', { code: error instanceof Error ? error.message : 'MESSAGE_ATTACHMENTS_INVALID' }, 'FAILED');
287
+ return;
288
+ }
289
+ this.host.adopt({
290
+ runId,
291
+ sessionId,
292
+ runner: 'codex',
293
+ cwd,
294
+ ...(typeof payload.externalSessionId === 'string'
295
+ ? { externalSessionId: payload.externalSessionId }
296
+ : {})
297
+ });
298
+ if (this.host.completeCancelled(runId, cwd))
299
+ return;
300
+ const session = this.host.session(sessionId);
301
+ if (!this.host.available()) {
302
+ this.host.emit(runId, 'run.rejected', { code: 'CODEX_UNAVAILABLE' }, 'FAILED');
303
+ return;
304
+ }
305
+ if (typeof payload.externalSessionId === 'string' &&
306
+ (session === undefined || session.nativeControl !== 'MAR_MANAGED')) {
307
+ this.host.emit(runId, 'run.rejected', { code: 'EXTERNAL_THREAD_OBSERVE_ONLY' }, 'FAILED');
308
+ return;
309
+ }
310
+ try {
311
+ this.runner.prepareEnvironment(secretEnvironment, runId);
312
+ }
313
+ catch (error) {
314
+ this.host.emit(runId, 'run.rejected', { code: error instanceof Error ? error.message : 'SECRET_ENVIRONMENT_INVALID' }, 'FAILED');
315
+ return;
316
+ }
317
+ this.host.addActive(runId);
318
+ void this.run({
319
+ runId,
320
+ sessionId,
321
+ input,
322
+ cwd,
323
+ externalSessionId: payload.externalSessionId,
324
+ model: payload.model,
325
+ effort: payload.effort,
326
+ access: payload.access,
327
+ collaborationMode: payload.collaborationMode,
328
+ serviceTier: payload.serviceTier
329
+ }, attachments);
330
+ }
331
+ async run(payload, attachments) {
332
+ const { runId, sessionId, input, cwd } = payload;
333
+ try {
334
+ await this.runner.start();
335
+ if (this.host.completeCancelled(runId, cwd)) {
336
+ this.runner.releaseEnvironment(runId);
337
+ return;
338
+ }
339
+ this.host.emit(runId, 'run.started', {}, 'STARTING');
340
+ const configuration = {
341
+ ...(typeof payload.model === 'string' ? { model: payload.model } : {}),
342
+ ...(typeof payload.effort === 'string' ? { effort: payload.effort } : {}),
343
+ ...(typeof payload.access === 'string' ? { access: payload.access } : {})
344
+ };
345
+ const threadResult = typeof payload.externalSessionId === 'string'
346
+ ? await this.runner.resumeThread(payload.externalSessionId, cwd, configuration)
347
+ : await this.runner.startThread(cwd, configuration);
348
+ if (this.host.completeCancelled(runId, cwd)) {
349
+ this.runner.releaseEnvironment(runId);
350
+ return;
351
+ }
352
+ const threadId = extractId(threadResult, 'thread');
353
+ let publicSessionId = sessionId;
354
+ if (typeof payload.externalSessionId !== 'string') {
355
+ this.host.promoteSession(sessionId, threadId);
356
+ publicSessionId = threadId;
357
+ this.host.send('session.external-id', { sessionId, externalSessionId: threadId });
358
+ }
359
+ this.runner.execution.runs.set(runId, { sessionId: publicSessionId, threadId, cwd });
360
+ const turnResult = await this.runner.startTurn(threadId, input, cwd, {
361
+ ...configuration,
362
+ ...(payload.collaborationMode === 'plan' || payload.collaborationMode === 'default'
363
+ ? { collaborationMode: payload.collaborationMode }
364
+ : {}),
365
+ ...(payload.serviceTier === 'fast' ? { serviceTier: 'fast' } : {})
366
+ }, await this.host.materialize(runId, attachments));
367
+ const turnId = extractId(turnResult, 'turn');
368
+ this.runner.execution.runs.set(runId, { sessionId: publicSessionId, threadId, turnId, cwd });
369
+ this.runner.execution.managedTurns.set(turnId, { sessionId: publicSessionId, runId });
370
+ if (this.host.runStatus(runId) === 'CANCELLING') {
371
+ await this.host.cancel(runId, threadId, turnId, cwd, this.host.interrupted(runId) ? 'INTERRUPTED' : 'CANCELLED');
372
+ this.runner.releaseEnvironment(runId);
373
+ return;
374
+ }
375
+ this.host.emit(runId, 'run.running', { threadId, turnId }, 'RUNNING');
376
+ }
377
+ catch (error) {
378
+ if (this.host.completeCancelled(runId, cwd)) {
379
+ this.runner.releaseEnvironment(runId);
380
+ return;
381
+ }
382
+ this.host.emit(runId, 'run.failed', { code: error instanceof Error ? error.message : 'CODEX_RUN_FAILED' }, 'FAILED');
383
+ this.runner.execution.runs.delete(runId);
384
+ this.runner.releaseEnvironment(runId);
385
+ this.host.removeActive(runId);
386
+ }
387
+ }
388
+ }
@@ -0,0 +1,85 @@
1
+ import { CodexAppServerClient, type CodexComposerInput, type CodexRunConfiguration, type JsonRpcNotification } from '../codex-app-server.js';
2
+ import { AbstractRunner, type ExternalResumeContext, type ExternalResumeResult, type RunnerDiscoveryContext, type RunnerSessionPresentationContext, type RunnerSessionPresentation, type RunnerProjectionTitleInput } from './abstract-runner.js';
3
+ import type { NodeCapabilities, RunnerDefaultConfiguration, RunnerProfile } from '@myagentroam/protocol';
4
+ import type { NodeAgentSession, NodeDatabase } from '../database.js';
5
+ import type { NodeWorkspace } from '../database.js';
6
+ export declare class CodexRunner extends AbstractRunner<'codex'> {
7
+ private readonly client;
8
+ readonly name: "codex";
9
+ readonly discoveryScope: "ALL_WORKSPACES";
10
+ readonly execution: CodexExecutionState;
11
+ private fastEnabled;
12
+ private environmentFingerprint;
13
+ private readonly environmentRuns;
14
+ constructor(client?: CodexAppServerClient);
15
+ profile(capabilities: NodeCapabilities): RunnerProfile;
16
+ available(capabilities: NodeCapabilities): boolean;
17
+ executeGlobalCommand(commandId: string, input: string): unknown;
18
+ serviceTier(): 'fast' | undefined;
19
+ usesRunnerTitleForRename(): boolean;
20
+ renameNative(session: NodeAgentSession, input: {
21
+ readonly title?: string | null;
22
+ readonly pinned?: boolean;
23
+ }, database: NodeDatabase): Promise<void>;
24
+ projectedRunnerTitle(metadata: ReturnType<NodeDatabase['getNativeSessionMetadata']>, input: RunnerProjectionTitleInput, database: NodeDatabase | undefined): string | null;
25
+ readOfficialConversation(session: NodeAgentSession, input: {
26
+ readonly cursor?: string;
27
+ readonly limit?: number;
28
+ }, managedTurn: (nativeTurnId: string) => import('../database.js').NodeConversationTurn | undefined): Promise<{
29
+ readonly turns: readonly import("../database.js").NodeConversationTurn[];
30
+ readonly nextCursor: string | null;
31
+ } | undefined>;
32
+ readExternalActivity(session: NodeAgentSession): Promise<"UNAVAILABLE" | "MANAGED_ACTIVE" | "EXTERNAL_ACTIVE" | "IDLE" | undefined>;
33
+ acceptsTruncatedNativeHistory(): boolean;
34
+ readManagedRunnerTitle(session: NodeAgentSession, nativeSessionId: string): Promise<string | undefined>;
35
+ nativeImageRoot(session: NodeAgentSession): string | undefined;
36
+ managedRunForNativeTurn(nativeTurnId: string): string | undefined;
37
+ protected profileForValidation(): RunnerProfile;
38
+ defaultConfiguration(): RunnerDefaultConfiguration;
39
+ resumeExternal(external: NodeAgentSession, context: ExternalResumeContext, force?: boolean): Promise<ExternalResumeResult>;
40
+ discoverSessions(context: RunnerDiscoveryContext, workspace?: NodeWorkspace): Promise<readonly NodeAgentSession[]>;
41
+ readContextUsage(session: NodeAgentSession): Promise<import("../native-session-history.js").NativeCodexContextUsage | undefined>;
42
+ presentSession(session: NodeAgentSession, capabilities: NodeCapabilities, context: RunnerSessionPresentationContext): RunnerSessionPresentation;
43
+ onNotification(listener: (notification: JsonRpcNotification) => void): () => void;
44
+ start(): Promise<void>;
45
+ prepareEnvironment(environment: Readonly<Record<string, string>>, runId: string): void;
46
+ releaseEnvironment(runId: string): void;
47
+ stop(): void;
48
+ request(method: string, params: unknown): Promise<unknown>;
49
+ respond(requestId: string | number, result: unknown): void;
50
+ listThreads(): Promise<unknown>;
51
+ readThread(threadId: string, includeTurns?: boolean): Promise<unknown>;
52
+ startThread(cwd: string, configuration?: CodexRunConfiguration): Promise<unknown>;
53
+ forkThread(threadId: string, cwd: string, lastTurnId: string, configuration?: CodexRunConfiguration): Promise<unknown>;
54
+ forkNative(session: NodeAgentSession, boundary: string | null, title: string): Promise<string>;
55
+ resumeThread(threadId: string, cwd: string, configuration?: CodexRunConfiguration): Promise<unknown>;
56
+ startTurn(threadId: string, text: string, cwd: string, configuration?: CodexRunConfiguration, attachments?: readonly Extract<CodexComposerInput, {
57
+ readonly type: 'localImage';
58
+ }>[]): Promise<unknown>;
59
+ interruptTurn(threadId: string, turnId: string): Promise<unknown>;
60
+ setThreadName(threadId: string, name: string): Promise<unknown>;
61
+ listCollaborationModes(): Promise<unknown>;
62
+ }
63
+ export declare class CodexExecutionState {
64
+ readonly runs: Map<string, {
65
+ sessionId: string;
66
+ threadId: string;
67
+ turnId?: string;
68
+ cwd: string;
69
+ refreshTitle?: boolean;
70
+ }>;
71
+ readonly managedTurns: Map<string, {
72
+ sessionId: string;
73
+ runId: string;
74
+ }>;
75
+ readonly approvals: Map<string, string | number>;
76
+ readonly userInputs: Map<string, {
77
+ readonly runId: string;
78
+ readonly requestId: string | number;
79
+ readonly questions: readonly {
80
+ readonly id: string;
81
+ readonly options: readonly string[];
82
+ readonly allowOther: boolean;
83
+ }[];
84
+ }>;
85
+ }