@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,242 @@
1
+ import { validSessionEffort, validSessionOption, validSessionTitle } from '../util/node-operation-parsers.js';
2
+ import { isPlainRecord } from '../util/node-operation-parsers.js';
3
+ export class SessionLifecycleService {
4
+ options;
5
+ starts = new Map();
6
+ constructor(options) {
7
+ this.options = options;
8
+ }
9
+ operations() {
10
+ return {
11
+ 'session.metadata.update': (data) => this.updateMetadata(record(data)),
12
+ 'session.pin.move': (data) => this.movePin(record(data)),
13
+ 'session.create': (data) => this.create(record(data)),
14
+ 'session.start': (data) => this.start(record(data)),
15
+ 'session.config.update': (data) => this.updateConfiguration(record(data))
16
+ };
17
+ }
18
+ clear() {
19
+ this.starts.clear();
20
+ }
21
+ async updateMetadata(input) {
22
+ if (typeof input.sessionId !== 'string' ||
23
+ (input.customTitle !== undefined &&
24
+ input.customTitle !== null &&
25
+ typeof input.customTitle !== 'string') ||
26
+ (input.pinned !== undefined && typeof input.pinned !== 'boolean'))
27
+ throw new Error('SESSION_INVALID');
28
+ if (input.customTitle === null || input.customTitle?.trim().length === 0)
29
+ throw new Error('SESSION_TITLE_REQUIRED');
30
+ if (input.customTitle !== undefined && input.customTitle.trim().length > 160)
31
+ throw new Error('SESSION_TITLE_INVALID');
32
+ const current = this.options.runtime.getAgentSession(input.sessionId) ??
33
+ (await this.options.projection.createMetadataProjection(input.sessionId));
34
+ if (current === undefined)
35
+ throw new Error('SESSION_NOT_FOUND');
36
+ const nativeCodexRename = this.options.runners.require(current.runner).usesRunnerTitleForRename() &&
37
+ current.externalSessionId !== null &&
38
+ input.customTitle !== undefined;
39
+ await this.options.projection.rename(current, {
40
+ ...(input.customTitle === undefined ? {} : { title: input.customTitle }),
41
+ ...(input.pinned === undefined ? {} : { pinned: input.pinned })
42
+ });
43
+ const metadataTitle = nativeCodexRename ? null : input.customTitle;
44
+ const updated = this.options.runtime.updateSessionMetadata({
45
+ sessionId: current.id,
46
+ ...(metadataTitle === undefined ? {} : { customTitle: metadataTitle }),
47
+ ...(input.pinned === undefined ? {} : { pinned: input.pinned })
48
+ });
49
+ return {
50
+ session: this.options.present(nativeCodexRename
51
+ ? this.options.runtime.setRunnerTitleFromNative(updated.id, input.customTitle ?? '')
52
+ : updated)
53
+ };
54
+ }
55
+ movePin(input) {
56
+ if (typeof input.sessionId !== 'string' ||
57
+ (input.direction !== 'up' && input.direction !== 'down'))
58
+ throw new Error('SESSION_PIN_MOVE_INVALID');
59
+ this.options.database().movePinnedSession(input.sessionId, input.direction);
60
+ return { moved: true };
61
+ }
62
+ create(input) {
63
+ const runner = this.options.runners.product(input.runner);
64
+ if (typeof input.workspaceId !== 'string' ||
65
+ runner === undefined ||
66
+ typeof input.cwd !== 'string' ||
67
+ input.cwd.length === 0 ||
68
+ (input.externalSessionId !== undefined && typeof input.externalSessionId !== 'string') ||
69
+ !validSessionTitle(input.title) ||
70
+ !validSessionOption(input.model) ||
71
+ !validSessionEffort(input.effort) ||
72
+ !validSessionOption(input.access))
73
+ throw new Error('SESSION_INVALID');
74
+ const database = this.options.database();
75
+ const workspace = database.getWorkspace(input.workspaceId);
76
+ if (workspace === undefined)
77
+ throw new Error('WORKSPACE_NOT_FOUND');
78
+ const saved = database
79
+ .getRunnerDefaults()
80
+ .find((value) => value.runner === runner.name &&
81
+ this.options.runners.supportsConfiguration(value, workspace));
82
+ const fallback = runner.defaultConfiguration(workspace);
83
+ const configuration = {
84
+ runner: runner.name,
85
+ model: typeof input.model === 'string' ? input.model : (saved?.model ?? fallback.model),
86
+ effort: typeof input.effort === 'string' ? input.effort : (saved?.effort ?? fallback.effort),
87
+ access: typeof input.access === 'string' ? input.access : (saved?.access ?? fallback.access)
88
+ };
89
+ if (!this.options.runners.supportsConfiguration(configuration, workspace))
90
+ throw new Error('RUNNER_CONFIGURATION_UNSUPPORTED');
91
+ const session = this.options.runtime.createAgentSession({
92
+ workspaceId: input.workspaceId,
93
+ runner: runner.name,
94
+ cwd: input.cwd,
95
+ ...(typeof input.externalSessionId === 'string'
96
+ ? { externalSessionId: input.externalSessionId }
97
+ : {}),
98
+ model: configuration.model,
99
+ effort: configuration.effort,
100
+ access: configuration.access,
101
+ ...(typeof input.title === 'string' ? { title: input.title } : {})
102
+ });
103
+ if (input.bootstrapOnly === true)
104
+ this.options.identity.markBootstrap(session.id);
105
+ database.saveRunnerDefaults(configuration);
106
+ if (input.bootstrapOnly !== true)
107
+ this.options.emitSession(session);
108
+ return { session: this.options.present(session) };
109
+ }
110
+ async updateConfiguration(input) {
111
+ if (typeof input.sessionId !== 'string' ||
112
+ typeof input.model !== 'string' ||
113
+ typeof input.effort !== 'string' ||
114
+ !validSessionEffort(input.effort) ||
115
+ typeof input.access !== 'string')
116
+ throw new Error('SESSION_INVALID');
117
+ const current = this.options.runtime.getAgentSession(input.sessionId) ??
118
+ (await this.options.projection.createMetadataProjection(input.sessionId));
119
+ if (current === undefined)
120
+ throw new Error('SESSION_NOT_FOUND');
121
+ const configuration = {
122
+ runner: current.runner,
123
+ model: input.model,
124
+ effort: input.effort,
125
+ access: input.access
126
+ };
127
+ const workspace = this.options.database().getWorkspace(current.workspaceId);
128
+ if (workspace === undefined ||
129
+ !this.options.runners.supportsConfiguration(configuration, workspace))
130
+ throw new Error('RUNNER_CONFIGURATION_UNSUPPORTED');
131
+ const session = this.options.runtime.updateAgentSessionConfiguration({
132
+ sessionId: current.id,
133
+ model: input.model,
134
+ effort: input.effort,
135
+ access: input.access
136
+ });
137
+ if (session.externalSessionId !== null)
138
+ this.options.database().saveNativeSessionMetadata({
139
+ runner: session.runner,
140
+ externalSessionId: session.externalSessionId,
141
+ workspaceId: session.workspaceId,
142
+ model: session.model,
143
+ effort: session.effort,
144
+ access: session.access
145
+ });
146
+ this.options.emitSession(session);
147
+ return { session: this.options.present(session) };
148
+ }
149
+ async start(input) {
150
+ if (typeof input.clientMessageId !== 'string' ||
151
+ input.clientMessageId.length === 0 ||
152
+ typeof input.content !== 'string' ||
153
+ input.content.length === 0)
154
+ throw new Error('MESSAGE_INVALID');
155
+ if (input.deferredCommands !== undefined &&
156
+ (!Array.isArray(input.deferredCommands) ||
157
+ input.deferredCommands.some((value) => !isPlainRecord(value) ||
158
+ typeof value.commandId !== 'string' ||
159
+ typeof value.input !== 'string')))
160
+ throw new Error('RUNNER_COMMAND_INVALID');
161
+ const startKey = `${input.workspaceId}\u0000${input.runner}\u0000${input.clientMessageId}`;
162
+ const existing = this.starts.get(startKey);
163
+ if (existing !== undefined)
164
+ return existing;
165
+ const start = this.startFresh(input);
166
+ this.starts.set(startKey, start);
167
+ try {
168
+ const result = await start;
169
+ while (this.starts.size > 512) {
170
+ const oldest = this.starts.keys().next().value;
171
+ if (oldest === undefined || oldest === startKey)
172
+ break;
173
+ this.starts.delete(oldest);
174
+ }
175
+ return result;
176
+ }
177
+ catch (error) {
178
+ this.starts.delete(startKey);
179
+ throw error;
180
+ }
181
+ }
182
+ async startFresh(input) {
183
+ const created = (await this.options.execute('session.create', {
184
+ ...input,
185
+ bootstrapOnly: true
186
+ }));
187
+ const bootstrapId = created.session.id;
188
+ const identity = this.options.identity.wait(bootstrapId);
189
+ try {
190
+ if (Array.isArray(input.deferredCommands))
191
+ for (const value of input.deferredCommands)
192
+ if (isPlainRecord(value))
193
+ await this.options.execute('session.command.execute', {
194
+ sessionId: bootstrapId,
195
+ commandId: value.commandId,
196
+ input: value.input,
197
+ ...(input.secretEnvironment === undefined
198
+ ? {}
199
+ : { secretEnvironment: input.secretEnvironment })
200
+ });
201
+ const interceptedId = this.options.interceptedSessionId(input.clientMessageId);
202
+ const messageSessionId = interceptedId ?? bootstrapId;
203
+ if (interceptedId !== undefined)
204
+ this.options.identity.promote(bootstrapId, interceptedId);
205
+ const message = (await this.options.execute('session.message', {
206
+ sessionId: messageSessionId,
207
+ clientMessageId: input.clientMessageId,
208
+ content: input.content,
209
+ ...(input.secretEnvironment === undefined
210
+ ? {}
211
+ : { secretEnvironment: input.secretEnvironment }),
212
+ ...(input.attachmentIds === undefined ? {} : { attachmentIds: input.attachmentIds })
213
+ }));
214
+ const nativeSessionId = await identity;
215
+ const session = this.options.runtime.getAgentSession(nativeSessionId);
216
+ const run = this.options.runtime.getRun(message.run.id);
217
+ if (session === undefined || run === undefined)
218
+ throw new Error('SESSION_START_FAILED');
219
+ return {
220
+ session: this.options.present(session),
221
+ run,
222
+ delivery: message.delivery,
223
+ idempotent: message.idempotent
224
+ };
225
+ }
226
+ catch (error) {
227
+ this.options.identity.reject(bootstrapId, 'SESSION_START_FAILED');
228
+ const promotedSessionId = await identity.catch(() => undefined);
229
+ if (promotedSessionId !== undefined)
230
+ try {
231
+ this.options.runtime.removeAgentSession(promotedSessionId);
232
+ }
233
+ catch {
234
+ // A started Run owns the promoted Session and finishes normally.
235
+ }
236
+ throw error;
237
+ }
238
+ }
239
+ }
240
+ function record(data) {
241
+ return data !== null && typeof data === 'object' ? data : {};
242
+ }
@@ -0,0 +1,33 @@
1
+ import type { NodeOperationHandler } from '../connector/node-operation-router.js';
2
+ import type { NodeAgentSession } from '../database.js';
3
+ import type { NodeRuntimeState } from '../runtime-state.js';
4
+ import type { CommandStateStore } from '../runner-command-engine.js';
5
+ import type { RunnerRegistry } from '../runner/runner-registry.js';
6
+ import type { WorkspaceUploadService } from './workspace-upload-service.js';
7
+ import type { RunnerImageAttachment, RunAttachmentService } from './run-attachment-service.js';
8
+ import type { RunnerInteractionService } from './runner-interaction-service.js';
9
+ import type { WorkspaceQueueWorkbenchService } from './workspace-queue-workbench-service.js';
10
+ interface SessionMessageServiceOptions {
11
+ readonly runtime: NodeRuntimeState;
12
+ readonly uploads: WorkspaceUploadService;
13
+ readonly attachments: RunAttachmentService;
14
+ readonly commands: CommandStateStore;
15
+ readonly queue: WorkspaceQueueWorkbenchService<RunnerImageAttachment>;
16
+ readonly interaction: RunnerInteractionService;
17
+ readonly runners: RunnerRegistry;
18
+ readonly resolve: (sessionId: string) => Promise<NodeAgentSession>;
19
+ readonly present: (session: NodeAgentSession) => unknown;
20
+ readonly emitSession: (session: NodeAgentSession) => void;
21
+ readonly emitQueue: (workspaceId: string, sessionId: string) => void;
22
+ readonly channelToken: (sessionId: string) => string | undefined;
23
+ }
24
+ export declare class SessionMessageService {
25
+ private readonly options;
26
+ constructor(options: SessionMessageServiceOptions);
27
+ operations(): Readonly<Record<string, NodeOperationHandler>>;
28
+ private message;
29
+ private persistAttachments;
30
+ private channelMessage;
31
+ private channelToken;
32
+ }
33
+ export {};
@@ -0,0 +1,157 @@
1
+ import { isImageAttachment, isRunnerImageAttachment, parseComposerAttachmentIds, parseMessageAttachments, sessionTitleFromFirstMessage, withNonImageAttachmentPrompt } from '../util/node-operation-parsers.js';
2
+ import { parseSecretEnvironment } from '../util/secret-environment.js';
3
+ export class SessionMessageService {
4
+ options;
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ operations() {
9
+ return {
10
+ 'session.message': (data) => this.message(record(data)),
11
+ 'session.channel.message': (data) => this.channelMessage(record(data)),
12
+ 'session.channel.token': (data) => this.channelToken(record(data))
13
+ };
14
+ }
15
+ async message(input) {
16
+ if (typeof input.sessionId !== 'string' ||
17
+ typeof input.clientMessageId !== 'string' ||
18
+ input.clientMessageId.length === 0 ||
19
+ typeof input.content !== 'string' ||
20
+ input.content.length === 0)
21
+ throw new Error('MESSAGE_INVALID');
22
+ const inlineAttachments = parseMessageAttachments(input.attachments);
23
+ const secretEnvironment = parseSecretEnvironment(input.secretEnvironment);
24
+ const attachmentIds = parseComposerAttachmentIds(input.attachmentIds);
25
+ if (inlineAttachments.length > 0 && attachmentIds.length > 0)
26
+ throw new Error('MESSAGE_ATTACHMENTS_INVALID');
27
+ const session = this.options.runtime.setTemporaryTitleIfMissing((await this.options.resolve(input.sessionId)).id, sessionTitleFromFirstMessage(input.content));
28
+ this.options.emitSession(session);
29
+ const intent = input.deliveryIntent;
30
+ if (intent !== undefined &&
31
+ intent !== 'SEND' &&
32
+ intent !== 'QUEUE' &&
33
+ intent !== 'REPLACE_CURRENT')
34
+ throw new Error('MESSAGE_INVALID');
35
+ const existing = this.options.runtime.findMessageRun(session.id, input.clientMessageId);
36
+ if (existing !== undefined)
37
+ return {
38
+ session: this.options.present(session),
39
+ run: existing,
40
+ delivery: this.options.runtime.isQueuedRun(existing.id) ? 'QUEUED' : 'STARTED',
41
+ idempotent: true
42
+ };
43
+ const uploaded = await this.options.uploads.resolve(session.workspaceId, attachmentIds);
44
+ const attachments = attachmentIds.length === 0
45
+ ? inlineAttachments
46
+ : uploaded.map((attachment) => attachment.attachment);
47
+ const imageAttachments = attachments.filter(isImageAttachment);
48
+ const runnerImageAttachments = attachments.filter(isRunnerImageAttachment);
49
+ const active = this.options.runtime
50
+ .listRunsForSession(session.id)
51
+ .find((run) => run.status === 'STARTING' || run.status === 'RUNNING');
52
+ const replaceCurrentRun = intent === 'REPLACE_CURRENT' ? active : undefined;
53
+ const queueWasPaused = this.options.runtime.sessionQueue(session.id).paused;
54
+ const queuedBeforeCreate = this.options.runtime.hasActiveSessionLease(session.id) || intent === 'QUEUE';
55
+ const created = this.options.runtime.createMessageRun({
56
+ sessionId: session.id,
57
+ clientMessageId: input.clientMessageId,
58
+ content: input.content,
59
+ ...(attachments.length === 0 ? {} : { attachments })
60
+ });
61
+ if (!created.created)
62
+ return {
63
+ session: this.options.present(session),
64
+ run: created.run,
65
+ delivery: this.options.runtime.isQueuedRun(created.run.id) ? 'QUEUED' : 'STARTED',
66
+ idempotent: true
67
+ };
68
+ this.options.attachments.cache(created.run.id, imageAttachments);
69
+ let attachmentPaths;
70
+ try {
71
+ attachmentPaths = await this.persistAttachments(session, created.run.id, attachmentIds, attachments, uploaded);
72
+ }
73
+ catch (error) {
74
+ this.options.runtime.deleteQueuedRun(created.run.id);
75
+ this.options.attachments.clear(created.run.id);
76
+ throw error;
77
+ }
78
+ const runnerInput = withNonImageAttachmentPrompt(input.content, attachmentPaths);
79
+ this.options.runtime.updateQueuedMessage(created.run.id, runnerInput);
80
+ const planActive = this.options.commands
81
+ .list(session.id)
82
+ .some((state) => state.commandId === 'plan');
83
+ this.options.queue.remember({
84
+ runId: created.run.id,
85
+ sessionId: created.run.sessionId,
86
+ workspaceId: created.run.workspaceId,
87
+ runner: created.run.runner,
88
+ input: this.options.runners
89
+ .require(created.run.runner)
90
+ .prepareMessageInput(runnerInput, planActive ? 'plan' : 'default'),
91
+ attachments: runnerImageAttachments,
92
+ attachmentPaths,
93
+ cwd: session.cwd,
94
+ externalSessionId: session.externalSessionId,
95
+ model: session.model,
96
+ effort: session.effort,
97
+ access: session.access,
98
+ collaborationMode: planActive ? 'plan' : 'default',
99
+ secretEnvironment,
100
+ ...(this.options.runners.require(created.run.runner).serviceTier() === 'fast'
101
+ ? { serviceTier: 'fast' }
102
+ : {})
103
+ });
104
+ if (queueWasPaused && active === undefined && intent !== 'QUEUE')
105
+ this.options.runtime.prioritizeQueuedRun(created.run.id);
106
+ if (!queueWasPaused || intent !== 'QUEUE')
107
+ this.options.runtime.resumeSessionQueue(session.id);
108
+ this.options.emitQueue(session.workspaceId, session.id);
109
+ if (replaceCurrentRun !== undefined)
110
+ this.options.queue.replace(created.run.id, replaceCurrentRun.id);
111
+ else if (intent !== 'QUEUE' || !queuedBeforeCreate)
112
+ this.options.queue.schedule(session.id);
113
+ return {
114
+ session: this.options.present(session),
115
+ run: created.run,
116
+ delivery: replaceCurrentRun !== undefined ? 'RESTARTING' : queuedBeforeCreate ? 'QUEUED' : 'STARTED',
117
+ idempotent: false
118
+ };
119
+ }
120
+ async persistAttachments(session, runId, attachmentIds, attachments, uploaded) {
121
+ return [
122
+ ...uploaded
123
+ .filter(({ attachment }) => !isRunnerImageAttachment(attachment))
124
+ .map(({ path }) => path),
125
+ ...(await this.options.attachments.persistFiles(session.cwd, runId, attachmentIds.length === 0
126
+ ? attachments.filter((attachment) => !isRunnerImageAttachment(attachment))
127
+ : []))
128
+ ];
129
+ }
130
+ channelMessage(input) {
131
+ if (typeof input.sessionId !== 'string' ||
132
+ typeof input.token !== 'string' ||
133
+ typeof input.clientMessageId !== 'string' ||
134
+ typeof input.content !== 'string' ||
135
+ input.content.length === 0)
136
+ throw new Error('MESSAGE_INVALID');
137
+ const session = this.options.runtime.getAgentSession(input.sessionId);
138
+ if (session === undefined || session.channelToken !== input.token)
139
+ throw new Error('SESSION_CHANNEL_UNAVAILABLE');
140
+ return {
141
+ delivery: this.options.interaction.deliverChannel(session.runner, input.sessionId, input.clientMessageId, 'admin', input.token, input.content)
142
+ };
143
+ }
144
+ channelToken(input) {
145
+ if (typeof input.sessionId !== 'string')
146
+ throw new Error('SESSION_INVALID');
147
+ const token = this.options.channelToken(input.sessionId);
148
+ if (token === undefined)
149
+ throw new Error('SESSION_CHANNEL_UNAVAILABLE');
150
+ return { token };
151
+ }
152
+ }
153
+ function record(value) {
154
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
155
+ return {};
156
+ return value;
157
+ }
@@ -0,0 +1,33 @@
1
+ import type { NodeCapabilities, SessionActivityState, RunnerCommandState } from '@myagentroam/protocol';
2
+ import type { CodexSessionControl } from '../codex-app-server.js';
3
+ import type { NodeAgentSession, NodeDatabase } from '../database.js';
4
+ import type { NodeRuntimeState } from '../runtime-state.js';
5
+ import type { CommandStateStore } from '../runner-command-engine.js';
6
+ import type { RunnerRegistry } from '../runner/runner-registry.js';
7
+ export type PresentedSession = NodeAgentSession & {
8
+ readonly control: CodexSessionControl;
9
+ readonly activity: SessionActivityState;
10
+ readonly runtime: {
11
+ readonly level: CodexSessionControl['level'];
12
+ readonly capabilities: readonly string[];
13
+ readonly reasonCode: string | null;
14
+ };
15
+ readonly commandStates: readonly RunnerCommandState[];
16
+ };
17
+ interface SessionPresentationServiceOptions {
18
+ readonly database: () => NodeDatabase | undefined;
19
+ readonly runtime: NodeRuntimeState;
20
+ readonly commands: CommandStateStore;
21
+ readonly runners: RunnerRegistry;
22
+ readonly capabilities: () => NodeCapabilities;
23
+ readonly hasManagedActiveRun: (session: NodeAgentSession) => boolean;
24
+ readonly externalActivity: (sessionId: string) => SessionActivityState | undefined;
25
+ readonly projectionForNative: (session: NodeAgentSession) => NodeAgentSession | undefined;
26
+ }
27
+ export declare class SessionPresentationService {
28
+ private readonly options;
29
+ constructor(options: SessionPresentationServiceOptions);
30
+ present(session: NodeAgentSession): PresentedSession;
31
+ presentDiscovered(sessions: readonly NodeAgentSession[]): readonly PresentedSession[];
32
+ }
33
+ export {};
@@ -0,0 +1,36 @@
1
+ export class SessionPresentationService {
2
+ options;
3
+ constructor(options) {
4
+ this.options = options;
5
+ }
6
+ present(session) {
7
+ const database = this.options.database();
8
+ if (session.externalSessionId !== null && database !== undefined) {
9
+ const metadata = database.getNativeSessionMetadata(session.runner, session.externalSessionId, session.workspaceId);
10
+ if (metadata !== undefined)
11
+ session = { ...session, pinnedAt: metadata.pinnedAt, pinOrder: metadata.pinOrder };
12
+ }
13
+ const presentation = this.options.runners
14
+ .require(session.runner)
15
+ .presentSession(session, this.options.capabilities(), {
16
+ managedActive: this.options.hasManagedActiveRun(session),
17
+ externalActivity: this.options.externalActivity(session.id),
18
+ commandStates: this.options.commands.list(session.id)
19
+ });
20
+ return {
21
+ ...session,
22
+ ...presentation
23
+ };
24
+ }
25
+ presentDiscovered(sessions) {
26
+ return sessions.map((session) => {
27
+ const projected = this.options.projectionForNative(session);
28
+ if (projected === undefined)
29
+ return this.present(session);
30
+ const synchronized = session.runnerTitle === null
31
+ ? projected
32
+ : this.options.runtime.setRunnerTitleFromNative(projected.id, session.runnerTitle);
33
+ return this.present({ ...synchronized, lastActivityAt: session.lastActivityAt });
34
+ });
35
+ }
36
+ }
@@ -0,0 +1,31 @@
1
+ import type { NodeOperationHandler } from '../connector/node-operation-router.js';
2
+ import type { NodeAgentSession } from '../database.js';
3
+ import type { NodeRuntimeState } from '../runtime-state.js';
4
+ import type { RunnerRegistry } from '../runner/runner-registry.js';
5
+ import type { SessionContextService } from './session-context-service.js';
6
+ import type { RunnerName } from '@myagentroam/protocol';
7
+ export interface SessionQueryServiceOptions {
8
+ readonly runtime: NodeRuntimeState;
9
+ readonly runners: RunnerRegistry;
10
+ readonly resolve: (sessionId: string) => Promise<NodeAgentSession | undefined>;
11
+ readonly present: (session: NodeAgentSession) => unknown;
12
+ readonly history: (session: NodeAgentSession, input: {
13
+ readonly cursor?: string;
14
+ readonly limit?: number;
15
+ }) => Promise<unknown>;
16
+ readonly context: SessionContextService;
17
+ readonly discover: (runner: RunnerName, workspaceId?: string) => Promise<readonly NodeAgentSession[]>;
18
+ readonly workspaceIdForCwd: (cwd: string) => string | undefined;
19
+ }
20
+ export declare class SessionQueryService {
21
+ private readonly options;
22
+ constructor(options: SessionQueryServiceOptions);
23
+ operations(): Readonly<Record<string, NodeOperationHandler>>;
24
+ private get;
25
+ private runs;
26
+ private turns;
27
+ private events;
28
+ private readContext;
29
+ private discover;
30
+ private requireSession;
31
+ }
@@ -0,0 +1,77 @@
1
+ import { isPlainRecord } from '../runner/codex/conversation-parser.js';
2
+ export class SessionQueryService {
3
+ options;
4
+ constructor(options) {
5
+ this.options = options;
6
+ }
7
+ operations() {
8
+ return {
9
+ 'session.get': (data) => this.get(record(data)),
10
+ 'session.runs': (data) => this.runs(record(data)),
11
+ 'session.turns.list': (data) => this.turns(record(data)),
12
+ 'session.events.read': (data) => this.events(record(data)),
13
+ 'session.context.read': (data) => this.readContext(record(data)),
14
+ 'session.discover': (data) => this.discover(record(data))
15
+ };
16
+ }
17
+ async get(input) {
18
+ return { session: this.options.present(await this.requireSession(input.sessionId)) };
19
+ }
20
+ async runs(input) {
21
+ const session = await this.requireSession(input.sessionId);
22
+ return { runs: this.options.runtime.listRunsForSession(session.id) };
23
+ }
24
+ async turns(input) {
25
+ const page = pageInput(input);
26
+ return this.options.history(await this.requireSession(input.sessionId), page);
27
+ }
28
+ events(input) {
29
+ const page = pageInput(input);
30
+ if (typeof input.sessionId !== 'string')
31
+ throw new Error('SESSION_NOT_FOUND');
32
+ const session = this.options.runtime.getAgentSession(input.sessionId);
33
+ if (session === undefined)
34
+ throw new Error('SESSION_NOT_FOUND');
35
+ return this.options.runtime.listConversationItems({ sessionId: session.id, ...page });
36
+ }
37
+ async readContext(input) {
38
+ return this.options.context.read(await this.requireSession(input.sessionId));
39
+ }
40
+ async discover(input) {
41
+ const runner = this.options.runners.product(input.runner);
42
+ if (runner === undefined)
43
+ throw new Error('SESSION_INVALID');
44
+ const workspaceId = typeof input.workspaceId === 'string'
45
+ ? input.workspaceId
46
+ : typeof input.cwd === 'string'
47
+ ? this.options.workspaceIdForCwd(input.cwd)
48
+ : undefined;
49
+ if (runner.discoveryScope === 'WORKSPACE_REQUIRED' && workspaceId === undefined)
50
+ throw new Error('SESSION_INVALID');
51
+ const sessions = await this.options.discover(runner.name, workspaceId);
52
+ return { sessions: sessions.map((session) => this.options.present(session)) };
53
+ }
54
+ async requireSession(sessionId) {
55
+ if (typeof sessionId !== 'string')
56
+ throw new Error('SESSION_INVALID');
57
+ const session = this.options.runtime.getAgentSession(sessionId) ?? (await this.options.resolve(sessionId));
58
+ if (session === undefined)
59
+ throw new Error('SESSION_NOT_FOUND');
60
+ return session;
61
+ }
62
+ }
63
+ function pageInput(input) {
64
+ if ((input.cursor !== undefined && typeof input.cursor !== 'string') ||
65
+ (input.limit !== undefined &&
66
+ (!Number.isInteger(input.limit) ||
67
+ input.limit < 1 ||
68
+ input.limit > 500)))
69
+ throw new Error('EVENT_CURSOR_INVALID');
70
+ return {
71
+ ...(typeof input.cursor === 'string' ? { cursor: input.cursor } : {}),
72
+ ...(typeof input.limit === 'number' ? { limit: input.limit } : {})
73
+ };
74
+ }
75
+ function record(value) {
76
+ return isPlainRecord(value) ? value : {};
77
+ }
@@ -0,0 +1,6 @@
1
+ export declare class TerminalRelayState {
2
+ readonly attachments: Map<string, {
3
+ readonly attachmentId: string;
4
+ readonly terminalId: string;
5
+ }>;
6
+ }
@@ -0,0 +1,3 @@
1
+ export class TerminalRelayState {
2
+ attachments = new Map();
3
+ }
@@ -0,0 +1,12 @@
1
+ import type { NodeDatabase } from '../database.js';
2
+ import type { NodeOperationHandler } from '../connector/node-operation-router.js';
3
+ import type { TerminalManager } from '../terminal.js';
4
+ export declare class TerminalService {
5
+ private readonly database;
6
+ private readonly terminalManager;
7
+ constructor(database: () => NodeDatabase, terminalManager: TerminalManager);
8
+ operations(): Readonly<Record<string, NodeOperationHandler>>;
9
+ private open;
10
+ private close;
11
+ private authorizeAttach;
12
+ }