@myagentroam/node 0.9.3 → 0.9.5

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 (66) hide show
  1. package/dist/claude-agent-sdk.d.ts +1 -0
  2. package/dist/claude-agent-sdk.js +9 -0
  3. package/dist/codex-app-server.d.ts +25 -0
  4. package/dist/codex-app-server.js +104 -7
  5. package/dist/connector.d.ts +1 -0
  6. package/dist/connector.js +21 -12
  7. package/dist/database.d.ts +2 -0
  8. package/dist/native-session-history.d.ts +3 -2
  9. package/dist/native-session-history.js +199 -30
  10. package/dist/opencode-server.d.ts +1 -0
  11. package/dist/opencode-server.js +1 -0
  12. package/dist/runner/abstract-runner.d.ts +11 -2
  13. package/dist/runner/abstract-runner.js +4 -0
  14. package/dist/runner/claude/managed-run-controller.js +3 -0
  15. package/dist/runner/claude-code-runner.d.ts +2 -1
  16. package/dist/runner/claude-code-runner.js +4 -0
  17. package/dist/runner/codex/managed-run-controller.d.ts +9 -0
  18. package/dist/runner/codex/managed-run-controller.js +63 -17
  19. package/dist/runner/codex-runner.d.ts +3 -1
  20. package/dist/runner/codex-runner.js +23 -2
  21. package/dist/runner/opencode/managed-run-controller.js +4 -0
  22. package/dist/runner/opencode-runner.d.ts +10 -3
  23. package/dist/runner/opencode-runner.js +87 -14
  24. package/dist/runner/runner-registry.js +16 -1
  25. package/dist/runtime-command-detector.js +1 -6
  26. package/dist/runtime-state.d.ts +2 -0
  27. package/dist/runtime-state.js +12 -7
  28. package/dist/service/conversation-history-service.d.ts +10 -1
  29. package/dist/service/conversation-history-service.js +272 -42
  30. package/dist/service/conversation-segment-service.js +7 -1
  31. package/dist/service/external-session-resume-service.d.ts +2 -0
  32. package/dist/service/external-session-resume-service.js +45 -34
  33. package/dist/service/native-session-projection-service.d.ts +1 -0
  34. package/dist/service/native-session-projection-service.js +4 -0
  35. package/dist/service/native-session-watch-service.d.ts +26 -2
  36. package/dist/service/native-session-watch-service.js +258 -42
  37. package/dist/service/node-request-service.js +2 -1
  38. package/dist/service/run-event-service.js +16 -5
  39. package/dist/service/run-workbench-service.d.ts +2 -4
  40. package/dist/service/run-workbench-service.js +2 -7
  41. package/dist/service/session-command-service.js +1 -1
  42. package/dist/service/session-identity-service.d.ts +1 -1
  43. package/dist/service/session-identity-service.js +1 -1
  44. package/dist/service/session-lifecycle-service.js +7 -12
  45. package/dist/service/session-message-service.d.ts +1 -1
  46. package/dist/service/session-message-service.js +104 -83
  47. package/dist/service/skill-directory-service.d.ts +1 -0
  48. package/dist/service/skill-directory-service.js +40 -6
  49. package/dist/service/workspace-change-service.js +1 -1
  50. package/dist/service/workspace-domain-state.d.ts +3 -0
  51. package/dist/service/workspace-domain-state.js +1 -0
  52. package/dist/service/workspace-file-service.d.ts +1 -0
  53. package/dist/service/workspace-file-service.js +14 -1
  54. package/dist/service/workspace-queue-workbench-service.d.ts +29 -2
  55. package/dist/service/workspace-queue-workbench-service.js +73 -8
  56. package/dist/service/workspace-watch-service.d.ts +7 -2
  57. package/dist/service/workspace-watch-service.js +32 -6
  58. package/dist/service/workspace-workbench-service.d.ts +14 -0
  59. package/dist/service/workspace-workbench-service.js +215 -16
  60. package/dist/util/personal-instructions.d.ts +2 -0
  61. package/dist/util/personal-instructions.js +31 -0
  62. package/dist/util/runner-native-session-parsers.d.ts +3 -1
  63. package/dist/util/runner-native-session-parsers.js +35 -17
  64. package/dist/workspace.d.ts +14 -8
  65. package/dist/workspace.js +98 -50
  66. package/package.json +2 -2
@@ -3,15 +3,19 @@ const INTERVAL_MS = 2_000;
3
3
  const TTL_MS = 45_000;
4
4
  const MAX_FAILURES = 3;
5
5
  const INITIAL_TURN_LIMIT = 30;
6
+ const EMITTED_TURN_LIMIT = 10;
6
7
  export class NativeSessionWatchService {
7
8
  options;
8
9
  watches = new Map();
10
+ leases = new Map();
11
+ snapshotSequence = 0;
9
12
  constructor(options) {
10
13
  this.options = options;
11
14
  }
12
15
  operations() {
13
16
  return {
14
- 'session.watch': (data) => this.executeWatch(isPlainRecord(data) ? data : {})
17
+ 'session.watch': (data) => this.executeWatch(isPlainRecord(data) ? data : {}),
18
+ 'session.unwatch': (data) => this.executeUnwatch(isPlainRecord(data) ? data : {})
15
19
  };
16
20
  }
17
21
  async executeWatch(input) {
@@ -19,39 +23,75 @@ export class NativeSessionWatchService {
19
23
  if (input.unit === 'SEGMENT' && projectInitialPage === undefined)
20
24
  throw new Error('SESSION_INVALID');
21
25
  if (typeof input.sessionId !== 'string' ||
26
+ (input.workspaceId !== undefined && typeof input.workspaceId !== 'string') ||
27
+ typeof input.watchId !== 'string' ||
28
+ typeof input.workbenchConnectionId !== 'string' ||
22
29
  (input.mode !== 'initial' && input.mode !== 'renew') ||
23
30
  (input.unit !== undefined && input.unit !== 'SEGMENT'))
24
31
  throw new Error('SESSION_INVALID');
25
- const session = await this.options.resolve(input.sessionId);
26
- if (session === undefined || session.externalSessionId === null)
27
- throw new Error('SESSION_NOT_FOUND');
32
+ const ownerKey = this.ownerKey(input);
28
33
  if (input.mode === 'renew') {
29
- const watch = this.renew(session);
30
- if (watch === undefined)
34
+ const lease = this.leases.get(ownerKey);
35
+ if (lease === undefined ||
36
+ lease.watchId !== input.watchId ||
37
+ lease.sessionId !== input.sessionId ||
38
+ (input.workspaceId !== undefined && lease.workspaceId !== input.workspaceId))
39
+ throw new Error('SESSION_WATCH_NOT_FOUND');
40
+ if (lease.expiresAt <= Date.now()) {
41
+ this.removeLease(ownerKey);
31
42
  throw new Error('SESSION_WATCH_NOT_FOUND');
32
- return { renewed: true };
33
- }
34
- const watch = this.watch(session);
35
- const turnSnapshot = watch === undefined ? undefined : await this.refresh(session.id, INITIAL_TURN_LIMIT);
36
- if (watch !== undefined && turnSnapshot === undefined) {
37
- this.stop(session.id);
38
- throw new Error('NATIVE_TRANSCRIPT_UNAVAILABLE');
39
- }
40
- let snapshot = turnSnapshot;
41
- if (turnSnapshot !== undefined && input.unit === 'SEGMENT') {
42
- try {
43
- snapshot = projectInitialPage(session, turnSnapshot, 'SEGMENT', 10);
44
- }
45
- catch (error) {
46
- this.stop(session.id);
47
- throw error;
48
43
  }
44
+ lease.expiresAt = Date.now() + TTL_MS;
45
+ const observer = this.watches.get(lease.sessionId);
46
+ if (observer !== undefined)
47
+ observer.expiresAt = this.latestLeaseExpiry(lease.sessionId);
48
+ clearTimeout(lease.timer);
49
+ lease.timer = this.leaseTimer(ownerKey);
50
+ return this.accepted(lease);
49
51
  }
50
- return {
51
- session: this.options.present(session),
52
- watch: watch === undefined ? null : { expiresAt: watch.expiresAt },
53
- ...(snapshot === undefined ? {} : { snapshot })
52
+ const existing = this.leases.get(ownerKey);
53
+ const unit = input.unit === 'SEGMENT' ? 'SEGMENT' : 'TURN';
54
+ if (existing?.watchId === input.watchId &&
55
+ existing.sessionId === input.sessionId &&
56
+ (input.workspaceId === undefined || existing.workspaceId === input.workspaceId) &&
57
+ existing.unit === unit &&
58
+ existing.expiresAt > Date.now()) {
59
+ existing.expiresAt = Date.now() + TTL_MS;
60
+ clearTimeout(existing.timer);
61
+ existing.timer = this.leaseTimer(ownerKey);
62
+ return this.accepted(existing);
63
+ }
64
+ this.removeLease(ownerKey);
65
+ const lease = {
66
+ ownerKey,
67
+ sessionHash: this.sessionHash(input),
68
+ workbenchConnectionId: input.workbenchConnectionId,
69
+ watchId: input.watchId,
70
+ sessionId: input.sessionId,
71
+ unit,
72
+ nodeGeneration: this.options.nodeGeneration?.() ?? 'local-test-generation',
73
+ ...(typeof input.workspaceId === 'string' ? { workspaceId: input.workspaceId } : {}),
74
+ expiresAt: Date.now() + TTL_MS,
75
+ timer: undefined
54
76
  };
77
+ lease.timer = this.leaseTimer(ownerKey);
78
+ this.leases.set(ownerKey, lease);
79
+ const observer = this.watches.get(lease.sessionId);
80
+ if (observer !== undefined)
81
+ observer.expiresAt = this.latestLeaseExpiry(lease.sessionId);
82
+ void this.initializeLease(lease, projectInitialPage);
83
+ return this.accepted(lease);
84
+ }
85
+ executeUnwatch(input) {
86
+ if (typeof input.sessionId !== 'string' ||
87
+ typeof input.watchId !== 'string' ||
88
+ typeof input.workbenchConnectionId !== 'string')
89
+ throw new Error('SESSION_INVALID');
90
+ const ownerKey = this.ownerKey(input);
91
+ const lease = this.leases.get(ownerKey);
92
+ if (lease?.watchId === input.watchId && lease.sessionId === input.sessionId)
93
+ this.removeLease(ownerKey);
94
+ return { stopped: true };
55
95
  }
56
96
  watch(session) {
57
97
  if (this.options.managedActive(session)) {
@@ -68,11 +108,13 @@ export class NativeSessionWatchService {
68
108
  refresh: undefined,
69
109
  sessionSignature: undefined,
70
110
  signature: undefined,
111
+ page: undefined,
112
+ observedPage: undefined,
113
+ authority: undefined,
71
114
  failures: 0,
72
115
  timer: undefined
73
116
  };
74
- watch.timer = setInterval(() => void this.refresh(session.id), INTERVAL_MS);
75
- watch.timer.unref();
117
+ watch.timer = this.refreshTimer(session.id);
76
118
  this.watches.set(session.id, watch);
77
119
  return { expiresAt: watch.expiresAt };
78
120
  }
@@ -93,13 +135,16 @@ export class NativeSessionWatchService {
93
135
  const watch = this.watches.get(sessionId);
94
136
  if (watch === undefined)
95
137
  return;
96
- clearInterval(watch.timer);
138
+ clearTimeout(watch.timer);
97
139
  this.watches.delete(sessionId);
98
140
  }
99
141
  clear() {
100
142
  for (const watch of this.watches.values())
101
- clearInterval(watch.timer);
143
+ clearTimeout(watch.timer);
102
144
  this.watches.clear();
145
+ for (const lease of this.leases.values())
146
+ clearTimeout(lease.timer);
147
+ this.leases.clear();
103
148
  }
104
149
  has(sessionId) {
105
150
  return this.watches.has(sessionId);
@@ -110,7 +155,7 @@ export class NativeSessionWatchService {
110
155
  get size() {
111
156
  return this.watches.size;
112
157
  }
113
- async refresh(sessionId, limit = 10) {
158
+ async refresh(sessionId, limit = 10, emit = true) {
114
159
  const watch = this.watches.get(sessionId);
115
160
  if (watch === undefined)
116
161
  return undefined;
@@ -120,26 +165,33 @@ export class NativeSessionWatchService {
120
165
  this.stop(sessionId);
121
166
  return undefined;
122
167
  }
123
- const refresh = this.performRefresh(sessionId, watch, limit);
168
+ const refresh = this.performRefresh(sessionId, watch, limit, emit);
124
169
  watch.refresh = refresh;
125
170
  try {
126
171
  return await refresh;
127
172
  }
128
173
  finally {
129
- if (this.watches.get(sessionId) === watch)
174
+ if (this.watches.get(sessionId) === watch) {
130
175
  watch.refresh = undefined;
176
+ clearTimeout(watch.timer);
177
+ watch.timer = this.refreshTimer(sessionId);
178
+ }
131
179
  }
132
180
  }
133
- async performRefresh(sessionId, watch, limit) {
181
+ async performRefresh(sessionId, watch, limit, emit) {
134
182
  try {
183
+ if (this.options.suspended?.(sessionId) === true)
184
+ return watch.page;
135
185
  const session = await this.options.resolve(sessionId);
136
186
  if (session === undefined ||
137
187
  session.externalSessionId === null ||
138
188
  this.options.managedActive(session)) {
139
- this.stop(sessionId);
189
+ this.releaseSessionLeases(sessionId);
140
190
  return undefined;
141
191
  }
142
192
  await this.options.refreshActivity(session);
193
+ if (this.options.suspended?.(sessionId) === true)
194
+ return watch.page;
143
195
  const presented = this.options.present(session);
144
196
  const sessionSignature = JSON.stringify(presented);
145
197
  if (sessionSignature !== watch.sessionSignature) {
@@ -148,20 +200,184 @@ export class NativeSessionWatchService {
148
200
  }
149
201
  const page = await this.options.readPage(session, limit);
150
202
  watch.failures = 0;
151
- const turns = page.turns.length <= 10 ? page.turns : page.turns.slice(-10);
152
- const signature = JSON.stringify(turns);
203
+ const turns = page.turns.length <= EMITTED_TURN_LIMIT
204
+ ? page.turns
205
+ : page.turns.slice(-EMITTED_TURN_LIMIT);
206
+ const observed = { ...page, turns };
207
+ const convergence = this.options.convergePage(watch.page, watch.observedPage, observed, watch.authority);
208
+ const converged = convergence.page;
209
+ const accepted = converged.turns.length <= EMITTED_TURN_LIMIT + 1
210
+ ? converged
211
+ : {
212
+ ...converged,
213
+ turns: converged.turns.slice(-(EMITTED_TURN_LIMIT + 1))
214
+ };
215
+ watch.observedPage = observed;
216
+ watch.authority = convergence.authority;
217
+ const signature = JSON.stringify({ source: accepted.source, turns: accepted.turns });
153
218
  if (signature === watch.signature)
154
- return page;
219
+ return accepted;
155
220
  watch.signature = signature;
156
- for (const turn of turns)
157
- this.options.emitTurn(turn);
158
- return page;
221
+ watch.page = accepted;
222
+ if (emit)
223
+ this.emitObservedPage(session, accepted);
224
+ return emit ? accepted : page;
159
225
  }
160
226
  catch {
161
227
  watch.failures += 1;
162
228
  if (watch.failures >= MAX_FAILURES)
163
- this.stop(sessionId);
229
+ this.failSession(sessionId);
164
230
  return undefined;
165
231
  }
166
232
  }
233
+ sessionHash(input) {
234
+ return typeof input.__workspaceWatchSessionHash === 'string'
235
+ ? input.__workspaceWatchSessionHash
236
+ : 'local-test';
237
+ }
238
+ ownerKey(input) {
239
+ return `${this.sessionHash(input)}:${input.workbenchConnectionId}`;
240
+ }
241
+ accepted(lease) {
242
+ return {
243
+ accepted: true,
244
+ watchId: lease.watchId,
245
+ nodeGeneration: lease.nodeGeneration,
246
+ expiresAt: lease.expiresAt
247
+ };
248
+ }
249
+ async initializeLease(lease, projectInitialPage) {
250
+ try {
251
+ const session = await this.options.resolve(lease.sessionId);
252
+ if (session === undefined || session.externalSessionId === null)
253
+ throw new Error('SESSION_NOT_FOUND');
254
+ if (lease.workspaceId !== undefined && session.workspaceId !== lease.workspaceId)
255
+ throw new Error('SESSION_WORKSPACE_MISMATCH');
256
+ if (this.leases.get(lease.ownerKey) !== lease)
257
+ return;
258
+ lease.workspaceId = session.workspaceId;
259
+ const watch = this.watch(session);
260
+ const page = watch === undefined ? undefined : await this.refresh(session.id, INITIAL_TURN_LIMIT, false);
261
+ if (page === undefined)
262
+ throw new Error('NATIVE_TRANSCRIPT_UNAVAILABLE');
263
+ const payload = lease.unit === 'SEGMENT' ? projectInitialPage(session, page, 'SEGMENT', 10) : page;
264
+ if (this.leases.get(lease.ownerKey) !== lease)
265
+ return;
266
+ this.options.emitWatchEvent?.({
267
+ type: 'watch.session.snapshot',
268
+ workbenchConnectionId: lease.workbenchConnectionId,
269
+ watchId: lease.watchId,
270
+ nodeGeneration: lease.nodeGeneration,
271
+ nodeId: this.options.nodeId?.() ?? 'local-test-node',
272
+ workspaceId: session.workspaceId,
273
+ sessionId: session.id,
274
+ revision: 1,
275
+ snapshotSequence: ++this.snapshotSequence,
276
+ expiresAt: lease.expiresAt,
277
+ payload
278
+ });
279
+ }
280
+ catch (error) {
281
+ if (this.leases.get(lease.ownerKey) !== lease)
282
+ return;
283
+ this.options.emitWatchEvent?.({
284
+ type: 'watch.failed',
285
+ workbenchConnectionId: lease.workbenchConnectionId,
286
+ watchType: 'session',
287
+ watchId: lease.watchId,
288
+ nodeGeneration: lease.nodeGeneration,
289
+ nodeId: this.options.nodeId?.() ?? 'local-test-node',
290
+ workspaceId: lease.workspaceId ?? 'unknown',
291
+ sessionId: lease.sessionId,
292
+ code: error instanceof Error && /^[A-Z][A-Z0-9_]+$/.test(error.message)
293
+ ? error.message
294
+ : 'SESSION_WATCH_SNAPSHOT_FAILED',
295
+ message: 'Session watch snapshot failed.'
296
+ });
297
+ this.removeLease(lease.ownerKey);
298
+ }
299
+ }
300
+ removeLease(ownerKey) {
301
+ const lease = this.leases.get(ownerKey);
302
+ if (lease === undefined)
303
+ return;
304
+ clearTimeout(lease.timer);
305
+ this.leases.delete(ownerKey);
306
+ const observer = this.watches.get(lease.sessionId);
307
+ const nextExpiry = this.latestLeaseExpiry(lease.sessionId);
308
+ if (nextExpiry === 0)
309
+ this.stop(lease.sessionId);
310
+ else if (observer !== undefined)
311
+ observer.expiresAt = nextExpiry;
312
+ }
313
+ emitObservedPage(session, page) {
314
+ const leases = [...this.leases.values()].filter((lease) => lease.sessionId === session.id);
315
+ if (leases.length === 0) {
316
+ this.options.emitPage(session, page);
317
+ return;
318
+ }
319
+ for (const lease of leases) {
320
+ const payload = lease.unit === 'SEGMENT'
321
+ ? this.options.projectInitialPage?.(session, page, 'SEGMENT', 10)
322
+ : page;
323
+ if (payload === undefined)
324
+ continue;
325
+ this.options.emitWatchEvent?.({
326
+ type: 'watch.session.snapshot',
327
+ workbenchConnectionId: lease.workbenchConnectionId,
328
+ watchId: lease.watchId,
329
+ nodeGeneration: lease.nodeGeneration,
330
+ nodeId: this.options.nodeId?.() ?? 'local-test-node',
331
+ workspaceId: session.workspaceId,
332
+ sessionId: session.id,
333
+ revision: 1,
334
+ snapshotSequence: ++this.snapshotSequence,
335
+ expiresAt: lease.expiresAt,
336
+ payload
337
+ });
338
+ }
339
+ }
340
+ failSession(sessionId) {
341
+ for (const lease of [...this.leases.values()]) {
342
+ if (lease.sessionId !== sessionId)
343
+ continue;
344
+ this.options.emitWatchEvent?.({
345
+ type: 'watch.failed',
346
+ workbenchConnectionId: lease.workbenchConnectionId,
347
+ watchType: 'session',
348
+ watchId: lease.watchId,
349
+ nodeGeneration: lease.nodeGeneration,
350
+ nodeId: this.options.nodeId?.() ?? 'local-test-node',
351
+ workspaceId: lease.workspaceId ?? 'unknown',
352
+ sessionId,
353
+ code: 'NATIVE_TRANSCRIPT_UNAVAILABLE',
354
+ message: 'Session watch snapshot failed.'
355
+ });
356
+ this.removeLease(lease.ownerKey);
357
+ }
358
+ this.stop(sessionId);
359
+ }
360
+ releaseSessionLeases(sessionId) {
361
+ for (const lease of [...this.leases.values()])
362
+ if (lease.sessionId === sessionId)
363
+ this.removeLease(lease.ownerKey);
364
+ this.stop(sessionId);
365
+ }
366
+ leaseTimer(ownerKey) {
367
+ const timer = setTimeout(() => this.removeLease(ownerKey), TTL_MS);
368
+ timer.unref();
369
+ return timer;
370
+ }
371
+ latestLeaseExpiry(sessionId) {
372
+ let latest = 0;
373
+ for (const lease of this.leases.values())
374
+ if (lease.sessionId === sessionId)
375
+ latest = Math.max(latest, lease.expiresAt);
376
+ return latest;
377
+ }
378
+ refreshTimer(sessionId) {
379
+ const timer = setTimeout(() => void this.refresh(sessionId), INTERVAL_MS);
380
+ timer.unref();
381
+ return timer;
382
+ }
167
383
  }
@@ -23,7 +23,8 @@ export class NodeRequestService {
23
23
  ? {
24
24
  ...payload.data,
25
25
  __workspaceWatchSessionHash: payload.userAuthorization
26
- .sessionHash
26
+ .sessionHash,
27
+ __requestUserId: payload.userAuthorization.userId
27
28
  }
28
29
  : payload.data;
29
30
  const result = await this.options.execute(payload.operation, data);
@@ -95,11 +95,19 @@ export class RunEventService {
95
95
  this.options.emitWorkbench('conversation', {
96
96
  event: { ...event, createdAt: persisted?.createdAt ?? Date.now() }
97
97
  });
98
- if (notificationPayload !== undefined)
99
- this.emitNow(runId, 'notification', {
100
- ...notificationPayload,
101
- occurredAt: persisted?.createdAt ?? Date.now()
98
+ if (notificationPayload !== undefined) {
99
+ const occurredAt = persisted?.createdAt ?? Date.now();
100
+ nodeLog('run.event', { runId, eventType: 'notification' });
101
+ this.options.emitWorkbench('conversation', {
102
+ event: {
103
+ runId,
104
+ sequence: event.sequence,
105
+ eventType: 'notification',
106
+ payload: { ...notificationPayload, occurredAt },
107
+ createdAt: occurredAt
108
+ }
102
109
  });
110
+ }
103
111
  if (status === undefined || !isTerminalRunStatus(status))
104
112
  return;
105
113
  const turn = this.options.runtime.conversationTurnForRun(runId);
@@ -126,11 +134,14 @@ export class RunEventService {
126
134
  if (session === undefined)
127
135
  return undefined;
128
136
  const base = {
137
+ userId: run.initiatedByUserId,
129
138
  workspaceId: run.workspaceId,
130
139
  sessionId: run.sessionId,
131
140
  sessionTitle: session.customTitle ?? session.runnerTitle ?? session.id,
132
141
  runId
133
142
  };
143
+ if (!Number.isSafeInteger(base.userId))
144
+ return undefined;
134
145
  if (status !== undefined && isTerminalRunStatus(status)) {
135
146
  if (this.options.state.interruptRequested.has(runId))
136
147
  return undefined;
@@ -159,7 +170,7 @@ export class RunEventService {
159
170
  if (payload.kind === 'plan' && typeof payload.itemId === 'string')
160
171
  return {
161
172
  ...base,
162
- notificationId: `plan:${run.workspaceId}:${run.sessionId}`,
173
+ notificationId: `plan:${run.workspaceId}:${runId}:${payload.itemId}`,
163
174
  kind: 'PLAN_READY'
164
175
  };
165
176
  if (payload.kind === 'user_input_request' &&
@@ -4,10 +4,8 @@ export interface RunWorkbenchServiceOptions {
4
4
  readonly runtime: NodeRuntimeState;
5
5
  readonly readImage: (runId: string, imageIndex: number) => Promise<unknown | undefined>;
6
6
  readonly respondUserInput: (runId: string, requestId: string, answers: Record<string, unknown>) => void;
7
- readonly deleteQueuedStart: (runId: string) => void;
8
- readonly clearImages: (runId: string) => void;
9
- readonly cleanupAttachments: (runId: string) => void;
10
- readonly emitQueue: (workspaceId: string, sessionId: string) => void;
7
+ readonly cancelQueued: (runId: string) => void;
8
+ readonly pauseQueue: (sessionId: string) => void;
11
9
  readonly emitRun: (runId: string, eventType: string, payload: unknown, status?: 'CANCELLED') => void;
12
10
  readonly control: (operation: string, payload: Record<string, unknown>) => void;
13
11
  readonly markInterrupted: (runId: string) => void;
@@ -55,11 +55,7 @@ export class RunWorkbenchService {
55
55
  if (operation === 'run.cancel' &&
56
56
  run.status === 'QUEUED' &&
57
57
  this.options.runtime.isQueuedRun(run.id)) {
58
- this.options.runtime.deleteQueuedRun(run.id);
59
- this.options.deleteQueuedStart(run.id);
60
- this.options.clearImages(run.id);
61
- this.options.cleanupAttachments(run.id);
62
- this.options.emitQueue(run.workspaceId, run.sessionId);
58
+ this.options.cancelQueued(run.id);
63
59
  return { cancelled: true };
64
60
  }
65
61
  const next = this.options.runtime.transitionRun(run.id, operation === 'run.cancel' && run.status === 'QUEUED' ? 'CANCELLED' : 'CANCELLING');
@@ -68,8 +64,7 @@ export class RunWorkbenchService {
68
64
  return { run: next };
69
65
  }
70
66
  if (operation === 'run.interrupt') {
71
- this.options.runtime.pauseSessionQueue(run.sessionId);
72
- this.options.emitQueue(run.workspaceId, run.sessionId);
67
+ this.options.pauseQueue(run.sessionId);
73
68
  this.options.markInterrupted(run.id);
74
69
  }
75
70
  this.options.control(operation, { runId: run.id });
@@ -36,7 +36,7 @@ export class SessionCommandService {
36
36
  input.input.length > 20_000)
37
37
  throw new Error('RUNNER_COMMAND_INVALID');
38
38
  const resolved = this.options.runtime.getAgentSession(input.sessionId) ??
39
- (await this.options.projection.resolve(input.sessionId));
39
+ (await this.options.projection.resolveCurrent(input.sessionId));
40
40
  if (resolved === undefined)
41
41
  throw new Error('SESSION_NOT_FOUND');
42
42
  const session = resolved.nativeControl === 'EXTERNAL' ? await this.options.resume.resume(resolved) : resolved;
@@ -8,7 +8,7 @@ export interface SessionIdentityServiceOptions {
8
8
  readonly migrateRunnerState: (previousId: string, nextId: string) => void;
9
9
  readonly emitSession: (session: NodeAgentSession) => void;
10
10
  readonly emitRun: (run: unknown) => void;
11
- readonly emitQueue: (workspaceId: string, sessionId: string) => void;
11
+ readonly emitQueue: (sessionId: string) => void;
12
12
  readonly emitTurn: (turn: unknown) => void;
13
13
  }
14
14
  export declare class SessionIdentityService {
@@ -29,7 +29,7 @@ export class SessionIdentityService {
29
29
  this.options.emitSession(session);
30
30
  for (const run of this.options.runtime.listRunsForSession(nativeSessionId))
31
31
  this.options.emitRun(run);
32
- this.options.emitQueue(session.workspaceId, nativeSessionId);
32
+ this.options.emitQueue(nativeSessionId);
33
33
  for (const turn of this.options.runtime.listConversationTurns({ sessionId: nativeSessionId })
34
34
  .turns)
35
35
  if (turn.status !== 'QUEUED')
@@ -112,25 +112,16 @@ export class SessionLifecycleService {
112
112
  async updateConfiguration(input) {
113
113
  if (typeof input.sessionId !== 'string' ||
114
114
  typeof input.model !== 'string' ||
115
+ !validSessionOption(input.model) ||
115
116
  typeof input.effort !== 'string' ||
116
117
  !validSessionEffort(input.effort) ||
117
- typeof input.access !== 'string')
118
+ typeof input.access !== 'string' ||
119
+ !validSessionOption(input.access))
118
120
  throw new Error('SESSION_INVALID');
119
121
  const current = this.options.runtime.getAgentSession(input.sessionId) ??
120
122
  (await this.options.projection.createMetadataProjection(input.sessionId));
121
123
  if (current === undefined)
122
124
  throw new Error('SESSION_NOT_FOUND');
123
- const configuration = {
124
- runner: current.runner,
125
- model: input.model,
126
- effort: input.effort,
127
- access: input.access
128
- };
129
- const workspace = this.options.database().getWorkspace(current.workspaceId);
130
- const environment = parseSecretEnvironment(input.secretEnvironment);
131
- if (workspace === undefined ||
132
- !this.options.runners.supportsConfiguration(configuration, workspace, environment))
133
- throw new Error('RUNNER_CONFIGURATION_UNSUPPORTED');
134
125
  const session = this.options.runtime.updateAgentSessionConfiguration({
135
126
  sessionId: current.id,
136
127
  model: input.model,
@@ -209,9 +200,13 @@ export class SessionLifecycleService {
209
200
  sessionId: messageSessionId,
210
201
  clientMessageId: input.clientMessageId,
211
202
  content: input.content,
203
+ ...(input.__requestUserId === undefined ? {} : { __requestUserId: input.__requestUserId }),
212
204
  ...(input.secretEnvironment === undefined
213
205
  ? {}
214
206
  : { secretEnvironment: input.secretEnvironment }),
207
+ ...(input.personalInstructions === undefined
208
+ ? {}
209
+ : { personalInstructions: input.personalInstructions }),
215
210
  ...(input.attachmentIds === undefined ? {} : { attachmentIds: input.attachmentIds })
216
211
  }));
217
212
  const nativeSessionId = await identity;
@@ -18,11 +18,11 @@ interface SessionMessageServiceOptions {
18
18
  readonly resolve: (sessionId: string) => Promise<NodeAgentSession>;
19
19
  readonly present: (session: NodeAgentSession) => unknown;
20
20
  readonly emitSession: (session: NodeAgentSession) => void;
21
- readonly emitQueue: (workspaceId: string, sessionId: string) => void;
22
21
  readonly channelToken: (sessionId: string) => string | undefined;
23
22
  }
24
23
  export declare class SessionMessageService {
25
24
  private readonly options;
25
+ private readonly pendingMessages;
26
26
  constructor(options: SessionMessageServiceOptions);
27
27
  operations(): Readonly<Record<string, NodeOperationHandler>>;
28
28
  private message;