@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
@@ -484,22 +484,23 @@ export class NodeRuntimeState {
484
484
  // Queued turns have not received `run.started` yet. Their user item still
485
485
  // has the acceptance timestamp, so use it rather than sorting every
486
486
  // queued turn as time zero. The insertion index makes same-millisecond
487
- // sends deterministic; callers receive newest first and render reverse.
487
+ // sends deterministic; callers receive every page from oldest to newest.
488
488
  const all = [...(this.turns.get(input.sessionId) ?? [])]
489
489
  .map((turn, index) => ({ turn, index }))
490
490
  .sort((a, b) => {
491
491
  const timestamp = (value) => value.startedAt ??
492
492
  value.items.find((item) => item.id === value.userItemId)?.startedAt ??
493
493
  0;
494
- return timestamp(b.turn) - timestamp(a.turn) || b.index - a.index;
494
+ return timestamp(a.turn) - timestamp(b.turn) || a.index - b.index;
495
495
  })
496
496
  .map(({ turn }) => turn);
497
- const start = decodeCursor(input.cursor, input.sessionId);
497
+ const end = input.cursor === undefined ? all.length : decodeCursor(input.cursor, input.sessionId);
498
498
  const limit = Math.min(Math.max(input.limit ?? 30, 1), 500);
499
- const slice = all.slice(start, start + limit);
499
+ const start = Math.max(0, end - limit);
500
+ const slice = all.slice(start, end);
500
501
  return {
501
502
  turns: slice,
502
- nextCursor: start + limit < all.length ? encodeCursor(input.sessionId, start + limit) : null
503
+ nextCursor: start > 0 ? encodeCursor(input.sessionId, start) : null
503
504
  };
504
505
  }
505
506
  conversationTurnForRun(runId) {
@@ -753,15 +754,18 @@ export class NodeRuntimeState {
753
754
  if (event.eventType === 'text.delta' &&
754
755
  isRecord(event.payload) &&
755
756
  typeof event.payload.text === 'string') {
756
- const existing = turn.items.find((item) => item.id === `${event.runId}:assistant`);
757
+ const lastItem = turn.items.at(-1);
758
+ const existing = lastItem?.kind === 'assistant_message' && activeConversationItem(lastItem.status)
759
+ ? lastItem
760
+ : undefined;
757
761
  const text = `${existing === undefined ? '' : readableText(existing.payload)}${event.payload.text}`;
758
762
  const item = {
759
- id: `${event.runId}:assistant`,
763
+ id: existing?.id ?? `${event.runId}:assistant:${event.sequence}`,
760
764
  sessionId: run.sessionId,
761
765
  turnId: turn.id,
762
766
  runId: event.runId,
763
767
  parentItemId: null,
764
- sourceSequence: event.sequence,
768
+ sourceSequence: existing?.sourceSequence ?? event.sequence,
765
769
  revision: event.sequence,
766
770
  kind: 'assistant_message',
767
771
  status: 'IN_PROGRESS',
@@ -773,12 +777,20 @@ export class NodeRuntimeState {
773
777
  ...turn,
774
778
  status: 'RUNNING',
775
779
  startedAt: turn.startedAt ?? event.createdAt,
776
- items: [...turn.items.filter((value) => value.id !== item.id), item]
780
+ items: existing === undefined
781
+ ? [...turn.items, item]
782
+ : turn.items.map((value) => (value.id === item.id ? item : value))
777
783
  };
778
784
  }
779
785
  const projected = conversationItemFromEvent(event);
780
786
  if (projected !== undefined) {
781
- const existing = turn.items.find((item) => item.id === `${event.runId}:${projected.itemId}`);
787
+ const trailingItem = turn.items.at(-1);
788
+ const orderedItems = trailingItem?.kind === 'assistant_message' && activeConversationItem(trailingItem.status)
789
+ ? turn.items.map((item) => item.id === trailingItem.id
790
+ ? { ...item, status: 'COMPLETED', completedAt: event.createdAt }
791
+ : item)
792
+ : turn.items;
793
+ const existing = orderedItems.find((item) => item.id === `${event.runId}:${projected.itemId}`);
782
794
  const status = projected.status;
783
795
  const item = {
784
796
  id: `${event.runId}:${projected.itemId}`,
@@ -786,7 +798,7 @@ export class NodeRuntimeState {
786
798
  turnId: turn.id,
787
799
  runId: event.runId,
788
800
  parentItemId: null,
789
- sourceSequence: event.sequence,
801
+ sourceSequence: existing?.sourceSequence ?? event.sequence,
790
802
  revision: (existing?.revision ?? -1) + 1,
791
803
  kind: projected.kind,
792
804
  status,
@@ -799,8 +811,8 @@ export class NodeRuntimeState {
799
811
  status: terminal(run.status) ? run.status : 'RUNNING',
800
812
  startedAt: turn.startedAt ?? event.createdAt,
801
813
  items: existing === undefined
802
- ? [...turn.items, item]
803
- : turn.items.map((value) => (value.id === item.id ? item : value))
814
+ ? [...orderedItems, item]
815
+ : orderedItems.map((value) => (value.id === item.id ? item : value))
804
816
  };
805
817
  }
806
818
  if (event.eventType === 'run.started' || event.eventType === 'run.running')
@@ -873,6 +885,7 @@ function projectableConversationKind(value) {
873
885
  value === 'plan' ||
874
886
  value === 'file_change' ||
875
887
  value === 'context_compaction' ||
888
+ value === 'unknown' ||
876
889
  value === 'warning' ||
877
890
  value === 'error');
878
891
  }
@@ -954,4 +967,3 @@ function decodeCursor(cursor, scope) {
954
967
  return 0;
955
968
  }
956
969
  }
957
- //# sourceMappingURL=runtime-state.js.map
@@ -0,0 +1,5 @@
1
+ export declare class AttachmentDomainState<TCachedRunImages, TNativeImageSource> {
2
+ readonly runDirectories: Map<string, string>;
3
+ readonly runImages: Map<string, TCachedRunImages>;
4
+ readonly nativeHistoryImages: Map<string, TNativeImageSource[]>;
5
+ }
@@ -0,0 +1,5 @@
1
+ export class AttachmentDomainState {
2
+ runDirectories = new Map();
3
+ runImages = new Map();
4
+ nativeHistoryImages = new Map();
5
+ }
@@ -0,0 +1,34 @@
1
+ import type { NodeCapabilities, SessionActivityState } from '@myagentroam/protocol';
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 ConversationHistoryPage } from '../util/runner-native-session-parsers.js';
6
+ import type { RunAttachmentService } from './run-attachment-service.js';
7
+ export declare function hasManagedActiveRun(runtime: NodeRuntimeState, session: NodeAgentSession): boolean;
8
+ interface ConversationHistoryServiceOptions {
9
+ readonly runtime: NodeRuntimeState;
10
+ readonly runners: RunnerRegistry;
11
+ readonly capabilities: () => NodeCapabilities;
12
+ readonly attachments: RunAttachmentService;
13
+ readonly snapshotSequence: () => number;
14
+ readonly hasManagedActiveRun: (session: NodeAgentSession) => boolean;
15
+ readonly setExternalActivity: (sessionId: string, activity: SessionActivityState) => void;
16
+ }
17
+ export declare class ConversationHistoryService {
18
+ private readonly options;
19
+ constructor(options: ConversationHistoryServiceOptions);
20
+ read(session: NodeAgentSession, input: {
21
+ readonly cursor?: string;
22
+ readonly limit?: number;
23
+ }): Promise<ConversationHistoryPage>;
24
+ refreshExternalActivity(session: NodeAgentSession): Promise<void>;
25
+ readNative(session: NodeAgentSession): Promise<import("../native-session-history.js").NativeSessionHistory | undefined>;
26
+ private attachNativeImages;
27
+ readOfficial(session: NodeAgentSession, input: {
28
+ readonly cursor?: string;
29
+ readonly limit?: number;
30
+ }): Promise<import("../runner/abstract-runner.js").RunnerOfficialConversationPage | undefined>;
31
+ private managedRunnerTurn;
32
+ private page;
33
+ }
34
+ export {};
@@ -0,0 +1,158 @@
1
+ import { discoverNativeSessions, readNativeSession } from '../native-session-history.js';
2
+ import { coalesceImageGenerationItems } from '../runner/codex/conversation-parser.js';
3
+ import { nativeConversationPage } from '../util/runner-native-session-parsers.js';
4
+ import { isPlainRecord } from '../util/node-operation-parsers.js';
5
+ export function hasManagedActiveRun(runtime, session) {
6
+ return (session.nativeControl === 'MAR_MANAGED' &&
7
+ runtime
8
+ .listRunsForSession(session.id)
9
+ .some((run) => run.status === 'STARTING' || run.status === 'RUNNING' || run.status === 'CANCELLING'));
10
+ }
11
+ export class ConversationHistoryService {
12
+ options;
13
+ constructor(options) {
14
+ this.options = options;
15
+ }
16
+ async read(session, input) {
17
+ const snapshotSequence = this.options.snapshotSequence();
18
+ const readAt = Date.now();
19
+ const official = await this.readOfficial(session, input);
20
+ if (official !== undefined) {
21
+ const officialTurns = await this.attachNativeImages(session, official.turns);
22
+ const officialRunIds = new Set(officialTurns.flatMap((turn) => (turn.runId === null ? [] : [turn.runId])));
23
+ const localTurns = this.options.runtime.getAgentSession(session.id) === undefined
24
+ ? []
25
+ : this.options.runtime
26
+ .listConversationTurns({ sessionId: session.id, limit: 500 })
27
+ .turns.flatMap((turn) => {
28
+ if (turn.runId === null || !officialRunIds.has(turn.runId))
29
+ return [turn];
30
+ const pendingItems = turn.items.filter((item) => item.kind === 'user_input_request' && item.status === 'PENDING');
31
+ return pendingItems.length === 0 ? [] : [{ ...turn, items: pendingItems }];
32
+ });
33
+ const localRequestIds = new Set(localTurns.flatMap((turn) => turn.items.flatMap((item) => {
34
+ const requestId = item.kind === 'user_input_request' && isPlainRecord(item.payload)
35
+ ? item.payload.requestId
36
+ : undefined;
37
+ return typeof requestId === 'string' ? [requestId] : [];
38
+ })));
39
+ const turns = [
40
+ ...officialTurns
41
+ .map((turn) => ({
42
+ ...turn,
43
+ items: turn.items.filter((item) => item.kind !== 'user_input_request' ||
44
+ !isPlainRecord(item.payload) ||
45
+ typeof item.payload.requestId !== 'string' ||
46
+ !localRequestIds.has(item.payload.requestId))
47
+ }))
48
+ .filter((turn) => turn.items.length > 0),
49
+ ...localTurns
50
+ ].sort((left, right) => (left.startedAt ?? 0) - (right.startedAt ?? 0));
51
+ return this.page(official.nextCursor, turns, {
52
+ snapshotSequence,
53
+ source: 'official',
54
+ readAt
55
+ });
56
+ }
57
+ if (session.externalSessionId !== null && !this.options.hasManagedActiveRun(session)) {
58
+ const history = await this.readNative(session);
59
+ if (history !== undefined) {
60
+ if (history.truncated !== true ||
61
+ this.options.runners.require(session.runner).acceptsTruncatedNativeHistory()) {
62
+ const runtimeTurns = this.options.runtime.getAgentSession(session.id) === undefined
63
+ ? []
64
+ : this.options.runtime.listConversationTurns({ sessionId: session.id, limit: 500 })
65
+ .turns;
66
+ const page = nativeConversationPage(session, history, input, runtimeTurns, (images) => this.options.attachments.registerNative(session, images));
67
+ return this.page(page.nextCursor, page.turns, {
68
+ snapshotSequence,
69
+ source: 'native',
70
+ readAt
71
+ });
72
+ }
73
+ const runtimePage = this.options.runtime.listConversationTurns({
74
+ sessionId: session.id,
75
+ ...input
76
+ });
77
+ if (runtimePage.turns.length === 0)
78
+ throw new Error('NATIVE_TRANSCRIPT_INCOMPLETE');
79
+ return this.page(runtimePage.nextCursor, runtimePage.turns, {
80
+ snapshotSequence,
81
+ source: 'runtime',
82
+ readAt
83
+ });
84
+ }
85
+ if (session.nativeControl === 'EXTERNAL')
86
+ throw new Error('NATIVE_TRANSCRIPT_UNAVAILABLE');
87
+ }
88
+ const page = this.options.runtime.listConversationTurns({ sessionId: session.id, ...input });
89
+ return this.page(page.nextCursor, page.turns, { snapshotSequence, source: 'runtime', readAt });
90
+ }
91
+ async refreshExternalActivity(session) {
92
+ const runner = this.options.runners.require(session.runner);
93
+ if (!runner.available(this.options.capabilities())) {
94
+ this.options.setExternalActivity(session.id, 'UNAVAILABLE');
95
+ return;
96
+ }
97
+ const activity = await runner.readExternalActivity(session);
98
+ if (activity !== undefined)
99
+ this.options.setExternalActivity(session.id, activity);
100
+ }
101
+ async readNative(session) {
102
+ if (session.externalSessionId === null)
103
+ return undefined;
104
+ const current = await readNativeSession(session.runner, session.cwd, session.externalSessionId);
105
+ if (current !== undefined)
106
+ return current;
107
+ await discoverNativeSessions(session.runner, session.cwd);
108
+ return readNativeSession(session.runner, session.cwd, session.externalSessionId);
109
+ }
110
+ async attachNativeImages(session, turns) {
111
+ const history = await this.readNative(session);
112
+ if (history === undefined)
113
+ return turns;
114
+ const attachments = new Map();
115
+ for (const item of history.items) {
116
+ if (item.kind !== 'tool_call' || item.imageAttachments === undefined)
117
+ continue;
118
+ const registered = this.options.attachments.registerNative(session, item.imageAttachments);
119
+ if (registered.length > 0)
120
+ attachments.set(item.toolUseId, registered);
121
+ }
122
+ if (attachments.size === 0)
123
+ return turns;
124
+ return turns.map((turn) => ({
125
+ ...turn,
126
+ items: coalesceImageGenerationItems(turn.items.map((item) => {
127
+ const registered = attachments.get(item.id.slice(item.id.lastIndexOf(':') + 1));
128
+ if (registered === undefined || !isPlainRecord(item.payload))
129
+ return item;
130
+ return { ...item, payload: { ...item.payload, attachments: registered } };
131
+ }))
132
+ }));
133
+ }
134
+ async readOfficial(session, input) {
135
+ const runner = this.options.runners.require(session.runner);
136
+ if (session.externalSessionId === null || !runner.available(this.options.capabilities()))
137
+ return undefined;
138
+ return runner.readOfficialConversation(session, input, (nativeTurnId) => this.managedRunnerTurn(session, nativeTurnId));
139
+ }
140
+ managedRunnerTurn(session, nativeTurnId) {
141
+ const runId = this.options.runners
142
+ .require(session.runner)
143
+ .managedRunForNativeTurn(nativeTurnId);
144
+ if (runId === undefined)
145
+ return undefined;
146
+ return this.options.runtime
147
+ .listConversationTurns({ sessionId: session.id, limit: 500 })
148
+ .turns.find((turn) => turn.runId === runId);
149
+ }
150
+ page(nextCursor, turns, metadata) {
151
+ return {
152
+ ...metadata,
153
+ turns,
154
+ nextCursor,
155
+ latestVisibleAt: Math.max(0, ...turns.map((turn) => Math.max(turn.startedAt ?? 0, turn.completedAt ?? 0)))
156
+ };
157
+ }
158
+ }
@@ -0,0 +1,32 @@
1
+ import type { RunnerDefaultConfiguration } from '@myagentroam/protocol';
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 { SessionDomainState } from './session-domain-state.js';
6
+ interface ExternalSessionResumeServiceOptions {
7
+ readonly runtime: NodeRuntimeState;
8
+ readonly runners: RunnerRegistry;
9
+ readonly state: SessionDomainState<NodeAgentSession, unknown>;
10
+ readonly available: (runner: NodeAgentSession['runner']) => boolean;
11
+ readonly projectionFor: (session: NodeAgentSession) => NodeAgentSession | undefined;
12
+ readonly managedAliasFor: (session: NodeAgentSession) => NodeAgentSession | undefined;
13
+ readonly ensureConfiguration: (session: NodeAgentSession) => NodeAgentSession;
14
+ readonly configuration: (session: NodeAgentSession) => RunnerDefaultConfiguration;
15
+ readonly workspacePath: (workspaceId: string) => string | undefined;
16
+ readonly rememberNative: (nativeSessionId: string, sessionId: string) => void;
17
+ readonly resolve: (sessionId: string) => Promise<NodeAgentSession | undefined>;
18
+ }
19
+ export declare class ExternalSessionResumeService {
20
+ private readonly options;
21
+ constructor(options: ExternalSessionResumeServiceOptions);
22
+ resume(external: NodeAgentSession, force?: boolean): Promise<NodeAgentSession | undefined>;
23
+ failure(sessionId: string): {
24
+ readonly code: string;
25
+ readonly detail?: string;
26
+ };
27
+ resolveForMessage(sessionId: string): Promise<NodeAgentSession>;
28
+ delivery(session: NodeAgentSession): 'RESUMABLE' | 'REJECTED';
29
+ private clearFailure;
30
+ private setFailure;
31
+ }
32
+ export {};
@@ -0,0 +1,98 @@
1
+ export class ExternalSessionResumeService {
2
+ options;
3
+ constructor(options) {
4
+ this.options = options;
5
+ }
6
+ async resume(external, force = false) {
7
+ this.clearFailure(external.id);
8
+ const priorId = this.options.state.resumedByDirectId.get(external.id);
9
+ const prior = priorId === undefined ? undefined : this.options.runtime.getAgentSession(priorId);
10
+ if (prior !== undefined)
11
+ return this.options.ensureConfiguration(prior);
12
+ const runner = this.options.runners.product(external.runner);
13
+ if (runner === undefined) {
14
+ this.setFailure(external.id, 'RUNNER_UNAVAILABLE');
15
+ return undefined;
16
+ }
17
+ const result = await runner.resumeExternal(external, {
18
+ available: this.options.available(external.runner),
19
+ projectionFor: this.options.projectionFor,
20
+ managedAliasFor: this.options.managedAliasFor,
21
+ currentResumed: (sessionId) => {
22
+ const resumedId = this.options.state.resumedByDirectId.get(sessionId);
23
+ return resumedId === undefined
24
+ ? undefined
25
+ : this.options.runtime.getAgentSession(resumedId);
26
+ },
27
+ promote: (sessionId) => {
28
+ this.options.runtime.setNativeControl(sessionId, 'MAR_MANAGED');
29
+ return this.options.runtime.getAgentSession(sessionId);
30
+ },
31
+ ensureConfiguration: this.options.ensureConfiguration,
32
+ create: (session) => {
33
+ const configuration = this.options.configuration(session);
34
+ return this.options.runtime.createAgentSession({
35
+ workspaceId: session.workspaceId,
36
+ runner: session.runner,
37
+ ...(session.externalSessionId === null
38
+ ? {}
39
+ : { externalSessionId: session.externalSessionId }),
40
+ nativeControl: 'MAR_MANAGED',
41
+ cwd: session.cwd,
42
+ model: configuration.model,
43
+ effort: configuration.effort,
44
+ access: configuration.access,
45
+ ...(session.runnerTitle === null ? {} : { title: session.runnerTitle })
46
+ });
47
+ },
48
+ workspacePath: this.options.workspacePath,
49
+ rememberManagedNative: this.options.rememberNative
50
+ }, force);
51
+ if (result.activity !== undefined)
52
+ this.options.state.externalActivity.set(external.id, result.activity);
53
+ if (result.session !== undefined) {
54
+ this.options.state.resumedByDirectId.set(external.id, result.session.id);
55
+ return result.session;
56
+ }
57
+ this.setFailure(external.id, result.failureCode ?? 'SESSION_RESUME_REJECTED', result.failureDetail);
58
+ this.options.state.messageResumeFailures.set(external.id, result.messageFailureCode ?? result.failureCode ?? 'SESSION_RESUME_REJECTED');
59
+ return undefined;
60
+ }
61
+ failure(sessionId) {
62
+ const code = this.options.state.resumeFailures.get(sessionId) ?? 'SESSION_RESUME_REJECTED';
63
+ const detail = this.options.state.resumeFailureDetails.get(sessionId);
64
+ return detail === undefined ? { code } : { code, detail };
65
+ }
66
+ async resolveForMessage(sessionId) {
67
+ const resumedId = this.options.state.resumedByDirectId.get(sessionId);
68
+ const current = this.options.runtime.getAgentSession(resumedId ?? sessionId) ??
69
+ (await this.options.resolve(sessionId));
70
+ if (current === undefined)
71
+ throw new Error('SESSION_NOT_FOUND');
72
+ if (current.nativeControl !== 'EXTERNAL')
73
+ return current;
74
+ const resumed = await this.resume(current);
75
+ if (resumed !== undefined)
76
+ return resumed;
77
+ if (this.options.state.externalActivity.get(current.id) === 'EXTERNAL_ACTIVE')
78
+ throw new Error('EXTERNAL_SESSION_ACTIVE');
79
+ throw new Error(this.options.state.messageResumeFailures.get(current.id) ?? this.failure(current.id).code);
80
+ }
81
+ delivery(session) {
82
+ return session.nativeControl === 'EXTERNAL' && session.externalSessionId !== null
83
+ ? 'RESUMABLE'
84
+ : 'REJECTED';
85
+ }
86
+ clearFailure(sessionId) {
87
+ this.options.state.resumeFailures.delete(sessionId);
88
+ this.options.state.resumeFailureDetails.delete(sessionId);
89
+ this.options.state.messageResumeFailures.delete(sessionId);
90
+ }
91
+ setFailure(sessionId, code, detail) {
92
+ this.options.state.resumeFailures.set(sessionId, code);
93
+ if (detail === undefined)
94
+ this.options.state.resumeFailureDetails.delete(sessionId);
95
+ else
96
+ this.options.state.resumeFailureDetails.set(sessionId, detail);
97
+ }
98
+ }
@@ -0,0 +1,29 @@
1
+ import type { NodeEnvelope } from '@myagentroam/protocol';
2
+ import type { FakeRunnerStatus, FakeRunnerStep } from '../fake-runner.js';
3
+ import type { NodeRuntimeState } from '../runtime-state.js';
4
+ import type { FakeTestRunner } from '../runner/fake-test-runner.js';
5
+ import type { RunnerRegistry } from '../runner/runner-registry.js';
6
+ import type { RunDomainState } from './run-domain-state.js';
7
+ interface FakeManagedRunServiceOptions {
8
+ readonly runner: FakeTestRunner;
9
+ readonly script: (input: unknown, runId: string) => readonly FakeRunnerStep[];
10
+ readonly runners: RunnerRegistry;
11
+ readonly runtime: NodeRuntimeState;
12
+ readonly state: RunDomainState;
13
+ readonly emitRun: (runId: string, type: string, payload: unknown, status?: FakeRunnerStatus) => void;
14
+ readonly publishApproval: (input: {
15
+ readonly runId: string;
16
+ readonly approvalId: string;
17
+ readonly payload: unknown;
18
+ readonly expiresAt: number;
19
+ }) => void;
20
+ readonly captureSnapshot: (runId: string, cwd: string) => Promise<void>;
21
+ readonly disconnect: () => void;
22
+ }
23
+ /** Test-only managed Run adapter; production Runner paths never depend on it. */
24
+ export declare class FakeManagedRunService {
25
+ private readonly options;
26
+ constructor(options: FakeManagedRunServiceOptions);
27
+ start(envelope: NodeEnvelope): void;
28
+ }
29
+ export {};
@@ -0,0 +1,62 @@
1
+ import { isTerminalRunStatus } from '../runner/codex/conversation-parser.js';
2
+ /** Test-only managed Run adapter; production Runner paths never depend on it. */
3
+ export class FakeManagedRunService {
4
+ options;
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ start(envelope) {
9
+ const payload = envelope.payload;
10
+ if (typeof payload.runId !== 'string' || this.options.state.active.has(payload.runId))
11
+ return;
12
+ const runId = payload.runId;
13
+ const cwd = typeof payload.cwd === 'string' ? payload.cwd : undefined;
14
+ if (typeof payload.sessionId === 'string' &&
15
+ typeof payload.runner === 'string' &&
16
+ cwd !== undefined) {
17
+ const runner = this.options.runners.product(payload.runner);
18
+ if (runner === undefined)
19
+ return;
20
+ this.options.runtime.adoptRun({
21
+ runId,
22
+ sessionId: payload.sessionId,
23
+ ...(typeof payload.workspaceId === 'string' ? { workspaceId: payload.workspaceId } : {}),
24
+ runner: runner.name,
25
+ cwd
26
+ });
27
+ }
28
+ this.options.state.active.add(runId);
29
+ void this.options.runner
30
+ .start(runId, this.options.script(payload.input, runId), {
31
+ event: (eventType, eventPayload, status) => {
32
+ this.options.emitRun(runId, eventType, eventPayload, status);
33
+ if (status !== undefined && isTerminalRunStatus(status)) {
34
+ this.options.state.active.delete(runId);
35
+ if (cwd !== undefined)
36
+ void this.options.captureSnapshot(runId, cwd);
37
+ }
38
+ },
39
+ approval: (approvalId, approvalPayload, expiresAt) => {
40
+ try {
41
+ this.options.runtime.createApproval({
42
+ runId,
43
+ approvalId,
44
+ payload: approvalPayload,
45
+ expiresAt
46
+ });
47
+ }
48
+ catch {
49
+ return;
50
+ }
51
+ this.options.publishApproval({
52
+ runId,
53
+ approvalId,
54
+ payload: approvalPayload,
55
+ expiresAt
56
+ });
57
+ },
58
+ disconnect: this.options.disconnect
59
+ })
60
+ .finally(() => this.options.state.active.delete(runId));
61
+ }
62
+ }
@@ -0,0 +1,12 @@
1
+ import type { NodeRuntimeState } from '../runtime-state.js';
2
+ import type { RunnerRegistry } from '../runner/runner-registry.js';
3
+ import type { NodeAgentSession } from '../database.js';
4
+ /** Owns managed native-session identity and best-effort native title refresh. */
5
+ export declare class ManagedRunnerSessionService {
6
+ private readonly runtime;
7
+ private readonly runners;
8
+ private readonly emitSession;
9
+ constructor(runtime: NodeRuntimeState, runners: RunnerRegistry, emitSession: (session: NodeAgentSession) => void);
10
+ remember(nativeSessionId: string, sessionId: string): void;
11
+ refreshTitle(sessionId: string, externalSessionId: string, cwd: string): Promise<void>;
12
+ }
@@ -0,0 +1,35 @@
1
+ /** Owns managed native-session identity and best-effort native title refresh. */
2
+ export class ManagedRunnerSessionService {
3
+ runtime;
4
+ runners;
5
+ emitSession;
6
+ constructor(runtime, runners, emitSession) {
7
+ this.runtime = runtime;
8
+ this.runners = runners;
9
+ this.emitSession = emitSession;
10
+ }
11
+ remember(nativeSessionId, sessionId) {
12
+ const session = this.runtime.getAgentSession(sessionId);
13
+ if (session === undefined || session.nativeControl !== 'MAR_MANAGED')
14
+ return;
15
+ this.runners.require(session.runner).rememberManagedNative(session, nativeSessionId);
16
+ }
17
+ async refreshTitle(sessionId, externalSessionId, cwd) {
18
+ const session = this.runtime.getAgentSession(sessionId);
19
+ if (session === undefined || session.nativeControl !== 'MAR_MANAGED')
20
+ return;
21
+ try {
22
+ const title = await this.runners
23
+ .require(session.runner)
24
+ .readManagedRunnerTitle({ ...session, cwd }, externalSessionId);
25
+ if (title === undefined)
26
+ return;
27
+ const updated = this.runtime.setRunnerTitleFromNative(session.id, title);
28
+ if (updated.runnerTitle !== session.runnerTitle)
29
+ this.emitSession(updated);
30
+ }
31
+ catch {
32
+ // Native history is a best-effort source and cannot alter the completed Run result.
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,51 @@
1
+ import type { NodeCapabilities, RunnerDefaultConfiguration } 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 { RunnerRegistry } from '../runner/runner-registry.js';
6
+ import type { RunnerDiscoveryContext } from '../runner/abstract-runner.js';
7
+ import type { CommandStateStore } from '../runner-command-engine.js';
8
+ import type { SessionDomainState } from './session-domain-state.js';
9
+ interface NativeSessionProjectionServiceOptions {
10
+ readonly database: () => NodeDatabase | undefined;
11
+ readonly nodeId: () => string;
12
+ readonly runtime: NodeRuntimeState;
13
+ readonly runners: RunnerRegistry;
14
+ readonly capabilities: () => NodeCapabilities;
15
+ readonly state: SessionDomainState<NodeAgentSession, unknown>;
16
+ readonly commands: CommandStateStore;
17
+ readonly present: (session: NodeAgentSession) => NodeAgentSession & {
18
+ readonly control: CodexSessionControl;
19
+ };
20
+ }
21
+ export declare class NativeSessionProjectionService {
22
+ private readonly options;
23
+ constructor(options: NativeSessionProjectionServiceOptions);
24
+ discover(runnerName: NodeAgentSession['runner'], workspaceId?: string): Promise<readonly NodeAgentSession[]>;
25
+ discoverCodex(workspaceId?: string): Promise<{
26
+ readonly sessions: readonly (NodeAgentSession & {
27
+ readonly control: CodexSessionControl;
28
+ })[];
29
+ }>;
30
+ list(): Promise<readonly (NodeAgentSession & {
31
+ readonly control: CodexSessionControl;
32
+ })[]>;
33
+ discoverWorkspace(workspaceId: string): Promise<readonly NodeAgentSession[]>;
34
+ resolve(sessionId: string): Promise<NodeAgentSession | undefined>;
35
+ createMetadataProjection(sessionId: string): Promise<NodeAgentSession | undefined>;
36
+ resumedConfiguration(session: NodeAgentSession): RunnerDefaultConfiguration;
37
+ ensureResumedConfiguration(session: NodeAgentSession): NodeAgentSession;
38
+ rename(session: NodeAgentSession, input: {
39
+ readonly title?: string | null;
40
+ readonly pinned?: boolean;
41
+ }): Promise<void>;
42
+ project(input: Parameters<RunnerDiscoveryContext['createProjection']>[0]): NodeAgentSession;
43
+ remember(sessions: readonly NodeAgentSession[]): void;
44
+ projectionForNative(native: NodeAgentSession): NodeAgentSession | undefined;
45
+ managedForNative(native: NodeAgentSession): NodeAgentSession | undefined;
46
+ nativeIds(session: NodeAgentSession): ReadonlySet<string>;
47
+ forget(session: NodeAgentSession, knownNativeIds: ReadonlySet<string>): void;
48
+ private isManagedProjection;
49
+ private requireDatabase;
50
+ }
51
+ export {};