@myagentroam/node 0.11.36 → 0.11.38

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 (145) hide show
  1. package/dist/application/execution-lifecycle.js +54 -0
  2. package/dist/application/node-lifecycle.js +104 -0
  3. package/dist/application/node-operation-admission.js +51 -0
  4. package/dist/application/node-operation-contracts.js +1 -0
  5. package/dist/application/runner-composition.js +22 -0
  6. package/dist/application/session-projection-composition.js +26 -0
  7. package/dist/application/workspace-application.js +61 -0
  8. package/dist/application/workspace-composition.js +21 -0
  9. package/dist/capabilities.js +2 -3
  10. package/dist/codex-app-server.js +1 -471
  11. package/dist/connection/authorization.js +26 -0
  12. package/dist/{service → connection}/direct-transfer-service.js +1 -1
  13. package/dist/connection/endpoints.js +32 -0
  14. package/dist/connector.js +145 -253
  15. package/dist/control-outbound-scheduler.js +3 -0
  16. package/dist/database.js +34 -14
  17. package/dist/git-askpass.js +1 -1
  18. package/dist/git-execution-helper.js +1 -1
  19. package/dist/hosting/hosted-conversation-file-identity.js +27 -0
  20. package/dist/hosting/hosted-conversation-file-operations.js +44 -0
  21. package/dist/hosting/hosted-conversation-node-service.js +974 -0
  22. package/dist/hosting/hosted-conversation-runs.js +599 -0
  23. package/dist/hosting/hosted-conversation-templates.js +280 -0
  24. package/dist/hosting/hosted-conversation-uploads.js +344 -0
  25. package/dist/hosting/hosted-conversation-watch.js +194 -0
  26. package/dist/hosting/runtime-contracts.js +1 -0
  27. package/dist/runner/abstract-runner.js +4 -13
  28. package/dist/{claude-agent-sdk.js → runner/claude/claude-agent-sdk.js} +2 -2
  29. package/dist/runner/claude/conversation-parser.js +2 -1
  30. package/dist/runner/claude/managed-run-controller.js +6 -7
  31. package/dist/runner/claude/message-parsers.js +2 -1
  32. package/dist/runner/claude/native-transcript.js +2 -1
  33. package/dist/runner/claude/tool-normalizer.js +1 -1
  34. package/dist/runner/claude-code-runner.js +2 -2
  35. package/dist/runner/codex/codex-app-server.js +471 -0
  36. package/dist/runner/codex/conversation-parser.js +4 -2
  37. package/dist/runner/codex/managed-run-controller.js +7 -9
  38. package/dist/runner/codex/native-transcript.js +1 -1
  39. package/dist/runner/codex/tool-normalizer.js +1 -1
  40. package/dist/runner/codex-runner.js +9 -27
  41. package/dist/runner/contracts.js +1 -0
  42. package/dist/runner/mar-agent/conversation-normalizer.js +2 -2
  43. package/dist/runner/mar-agent/history-projection.js +1 -1
  44. package/dist/runner/mar-agent/hosted-conversation-runtime.js +6 -10
  45. package/dist/runner/mar-agent/managed-run-controller.js +85 -61
  46. package/dist/runner/mar-agent/runtime-model-factory.js +0 -3
  47. package/dist/runner/mar-agent-runner.js +1 -1
  48. package/dist/runner/normalization.js +42 -0
  49. package/dist/runner/opencode/conversation-parser.js +1 -1
  50. package/dist/runner/opencode/managed-run-controller.js +6 -7
  51. package/dist/{opencode-server.js → runner/opencode/opencode-server.js} +2 -2
  52. package/dist/runner/opencode/tool-normalizer.js +1 -1
  53. package/dist/runner/opencode-runner.js +4 -5
  54. package/dist/runner/runner-registry.js +4 -1
  55. package/dist/runtime-state.js +48 -372
  56. package/dist/service/computer-use-enablement-service.js +1 -1
  57. package/dist/service/continuous-run-coordinator.js +52 -7
  58. package/dist/service/conversation-history-service.js +4 -4
  59. package/dist/service/conversation-hosting-capability-service.js +1 -1
  60. package/dist/service/conversation-hosting-enablement-service.js +1 -1
  61. package/dist/service/conversation-hosting-lifecycle-service.js +3 -3
  62. package/dist/service/conversation-hosting-maintenance-service.js +1 -1
  63. package/dist/service/conversation-observation.js +1 -1
  64. package/dist/service/conversation-segment-service.js +1 -1
  65. package/dist/service/external-session-resume-service.js +47 -27
  66. package/dist/service/fake-managed-run-service.js +3 -7
  67. package/dist/service/git-operation-service.js +1 -1
  68. package/dist/service/goal-continuation-coordinator.js +14 -7
  69. package/dist/service/mar-agent-model-transport-test-service.js +1 -1
  70. package/dist/service/native-session-watch-service.js +1 -1
  71. package/dist/service/node-connection-lifecycle-service.js +6 -3
  72. package/dist/service/node-control-channel.js +48 -11
  73. package/dist/service/node-request-service.js +1 -1
  74. package/dist/service/node-terminal-channel.js +2 -1
  75. package/dist/service/node-workspace-coordinator.js +3 -2
  76. package/dist/service/notification-mcp-bridge.js +6 -2
  77. package/dist/service/recent-run-projection-service.js +1 -1
  78. package/dist/service/restart-session-restorer.js +1 -1
  79. package/dist/service/run-event-service.js +49 -42
  80. package/dist/service/run-workbench-service.js +3 -3
  81. package/dist/service/runner-interaction-service.js +6 -5
  82. package/dist/service/runner-service.js +1 -1
  83. package/dist/service/session-recent-activity.js +2 -1
  84. package/dist/service/skill-directory-service.js +1 -1
  85. package/dist/service/skill-install-service.js +2 -2
  86. package/dist/service/terminal-service.js +1 -1
  87. package/dist/service/workbench-event-projection.js +3 -2
  88. package/dist/service/workbench-event-service.js +1 -1
  89. package/dist/service/workspace-service.js +3 -2
  90. package/dist/session/attachments.js +110 -0
  91. package/dist/session/contracts.js +1 -0
  92. package/dist/{util → session}/conversation-identity.js +1 -1
  93. package/dist/session/input-validation.js +15 -0
  94. package/dist/{service → session}/native-session-projection-service.js +40 -11
  95. package/dist/session/pagination.js +77 -0
  96. package/dist/{util → session}/runner-native-session-parsers.js +2 -1
  97. package/dist/session/runtime/approvals.js +90 -0
  98. package/dist/session/runtime/conversation-projection.js +310 -0
  99. package/dist/session/runtime/type-guards.js +19 -0
  100. package/dist/{service → session}/session-catalog-service.js +5 -4
  101. package/dist/{service → session}/session-command-service.js +3 -3
  102. package/dist/{service → session}/session-identity-service.js +1 -1
  103. package/dist/{service → session}/session-lifecycle-service.js +18 -7
  104. package/dist/{service → session}/session-message-service.js +6 -5
  105. package/dist/{service → session}/session-presentation-service.js +1 -1
  106. package/dist/{service → session}/session-query-service.js +2 -2
  107. package/dist/{service → session}/session-schedule-service.js +2 -2
  108. package/dist/util/records.js +3 -0
  109. package/dist/workspace/contracts.js +1 -0
  110. package/dist/workspace/file-index.js +534 -0
  111. package/dist/workspace/files.js +466 -0
  112. package/dist/workspace/git.js +991 -0
  113. package/dist/workspace/path-access.js +184 -0
  114. package/dist/workspace/utf8.js +12 -0
  115. package/dist/{service → workspace}/workspace-change-service.js +45 -21
  116. package/dist/{service → workspace}/workspace-code-navigation-service.js +6 -6
  117. package/dist/{service → workspace}/workspace-file-manager-service.js +1 -1
  118. package/dist/{service → workspace}/workspace-file-service.js +16 -16
  119. package/dist/{service → workspace}/workspace-git-exclude-service.js +1 -1
  120. package/dist/{service → workspace}/workspace-queue-workbench-service.js +3 -2
  121. package/dist/{service → workspace}/workspace-session-service.js +45 -40
  122. package/dist/{service → workspace}/workspace-upload-service.js +28 -28
  123. package/dist/{service → workspace}/workspace-watch-service.js +29 -28
  124. package/dist/{service → workspace}/workspace-workbench-service.js +4 -3
  125. package/package.json +4 -4
  126. package/dist/service/hosted-conversation-node-service.js +0 -2252
  127. package/dist/service/run-domain-state.js +0 -4
  128. package/dist/service/session-domain-state.js +0 -9
  129. package/dist/service/workspace-domain-state.js +0 -41
  130. package/dist/util/node-operation-parsers.js +0 -320
  131. package/dist/workspace.js +0 -2168
  132. /package/dist/{connector → application}/node-connector-options.js +0 -0
  133. /package/dist/{util → connection}/node-operation-data.js +0 -0
  134. /package/dist/{connector → connection}/node-operation-router.js +0 -0
  135. /package/dist/{service → hosting}/hosted-conversation-sandbox-profile.js +0 -0
  136. /package/dist/{service → hosting}/hosted-conversation-store.js +0 -0
  137. /package/dist/{util → mcp}/secret-environment.js +0 -0
  138. /package/dist/{claude-channel.js → runner/claude/claude-channel.js} +0 -0
  139. /package/dist/{util → runner}/personal-instructions.js +0 -0
  140. /package/dist/{util → runner}/runner-error.js +0 -0
  141. /package/dist/{service/conversation-work-process.js → session/runtime/work-process.js} +0 -0
  142. /package/dist/{service → session}/session-context-service.js +0 -0
  143. /package/dist/{util → workspace}/git-credential-client.js +0 -0
  144. /package/dist/{service → workspace}/workspace-content-search-service.js +0 -0
  145. /package/dist/{service → workspace}/workspace-watch-snapshot-service.js +0 -0
@@ -0,0 +1,310 @@
1
+ import { permissionStopSchema } from '@myagentroam/protocol';
2
+ import { projectConversationWorkProcesses } from './work-process.js';
3
+ function isRecord(value) {
4
+ return typeof value === 'object' && value !== null;
5
+ }
6
+ export function runConversationItemId(runId, itemId, kind) {
7
+ return kind === 'user_message' ? itemId : `${runId}:${itemId}`;
8
+ }
9
+ export function projectConversationEvent(turn, run, event, runTerminal) {
10
+ let needsProcessProjection = false;
11
+ const update = (turn) => {
12
+ if (event.eventType === 'text.delta' &&
13
+ isRecord(event.payload) &&
14
+ typeof event.payload.text === 'string') {
15
+ const nativeItemId = typeof event.payload.itemId === 'string' && event.payload.itemId.length <= 88
16
+ ? event.payload.itemId
17
+ : undefined;
18
+ const stableItemId = nativeItemId === undefined ? undefined : `${event.runId}:${nativeItemId}`;
19
+ const lastItem = turn.items.at(-1);
20
+ const existing = stableItemId === undefined
21
+ ? lastItem?.kind === 'assistant_message' && activeConversationItem(lastItem.status)
22
+ ? lastItem
23
+ : undefined
24
+ : turn.items.find((item) => item.id === stableItemId && item.kind === 'assistant_message');
25
+ const text = `${existing === undefined ? '' : readableText(existing.payload)}${event.payload.text}`;
26
+ const sourceItemId = conversationSourceItemId(event.payload) ?? existing?.sourceItemId;
27
+ needsProcessProjection =
28
+ existing === undefined ||
29
+ existing.sourceItemId !== sourceItemId ||
30
+ (!readableText(existing.payload).trim() && !!text.trim());
31
+ const item = {
32
+ id: existing?.id ?? stableItemId ?? `${event.runId}:assistant:${event.sequence}`,
33
+ ...(sourceItemId === undefined ? {} : { sourceItemId }),
34
+ sessionId: run.sessionId,
35
+ turnId: turn.id,
36
+ runId: event.runId,
37
+ parentItemId: null,
38
+ sourceSequence: existing?.sourceSequence ?? event.sequence,
39
+ revision: event.sequence,
40
+ kind: 'assistant_message',
41
+ ...(existing?.processGroup === undefined ? {} : { processGroup: existing.processGroup }),
42
+ updatedAt: event.createdAt,
43
+ status: 'IN_PROGRESS',
44
+ payload: { text, format: 'markdown', model: null },
45
+ startedAt: existing?.startedAt ?? event.createdAt,
46
+ completedAt: null
47
+ };
48
+ return {
49
+ ...turn,
50
+ status: 'RUNNING',
51
+ startedAt: turn.startedAt ?? event.createdAt,
52
+ items: existing === undefined
53
+ ? [...turn.items, item]
54
+ : turn.items.map((value) => (value.id === item.id ? item : value))
55
+ };
56
+ }
57
+ const projected = conversationItemFromEvent(event);
58
+ if (projected !== undefined) {
59
+ const itemId = runConversationItemId(event.runId, projected.itemId, projected.kind);
60
+ const trailingItem = turn.items.at(-1);
61
+ const orderedItems = projected.kind !== 'user_message' &&
62
+ trailingItem?.kind === 'assistant_message' &&
63
+ activeConversationItem(trailingItem.status)
64
+ ? turn.items.map((item) => item.id === trailingItem.id
65
+ ? {
66
+ ...item,
67
+ status: 'COMPLETED',
68
+ completedAt: event.createdAt,
69
+ updatedAt: event.createdAt
70
+ }
71
+ : item)
72
+ : turn.items;
73
+ const existing = orderedItems.find((item) => item.id === itemId) ??
74
+ (projected.kind === 'assistant_message' &&
75
+ projected.itemId.startsWith('codex:') &&
76
+ trailingItem?.kind === 'assistant_message' &&
77
+ trailingItem.id.startsWith(`${event.runId}:assistant:`) &&
78
+ activeConversationItem(trailingItem.status)
79
+ ? trailingItem
80
+ : undefined);
81
+ const status = projected.status;
82
+ const sourceItemId = projected.sourceItemId ?? existing?.sourceItemId;
83
+ const parentItemId = projected.parentItemId === undefined
84
+ ? (existing?.parentItemId ?? null)
85
+ : runConversationItemId(event.runId, projected.parentItemId);
86
+ needsProcessProjection =
87
+ existing === undefined ||
88
+ existing.kind !== projected.kind ||
89
+ projected.kind === 'context_compaction' ||
90
+ existing.parentItemId !== parentItemId ||
91
+ existing.sourceItemId !== sourceItemId ||
92
+ (projected.kind === 'assistant_message' &&
93
+ !!readableText(existing.payload).trim() !== !!readableText(projected.payload).trim()) ||
94
+ (projected.kind === 'reasoning' && existing.processGroup === undefined);
95
+ const item = {
96
+ id: itemId,
97
+ sessionId: run.sessionId,
98
+ turnId: turn.id,
99
+ runId: event.runId,
100
+ parentItemId,
101
+ ...(sourceItemId === undefined ? {} : { sourceItemId }),
102
+ sourceSequence: existing?.sourceSequence ?? event.sequence,
103
+ revision: (existing?.revision ?? -1) + 1,
104
+ kind: projected.kind,
105
+ ...(existing?.processGroup === undefined ? {} : { processGroup: existing.processGroup }),
106
+ updatedAt: event.createdAt,
107
+ status,
108
+ payload: mergeConversationItemPayload(existing?.payload, projected),
109
+ ...(projected.permissionStop === undefined
110
+ ? {}
111
+ : { permissionStop: projected.permissionStop }),
112
+ startedAt: existing?.startedAt ?? event.createdAt,
113
+ completedAt: completedConversationItem(status) ? event.createdAt : null
114
+ };
115
+ return {
116
+ ...turn,
117
+ status: runTerminal ? run.status : 'RUNNING',
118
+ startedAt: turn.startedAt ?? event.createdAt,
119
+ items: existing === undefined
120
+ ? [...orderedItems, item]
121
+ : orderedItems.map((value) => (value.id === existing.id ? item : value))
122
+ };
123
+ }
124
+ if (event.eventType === 'run.started' || event.eventType === 'run.running')
125
+ return { ...turn, status: 'RUNNING', startedAt: turn.startedAt ?? event.createdAt };
126
+ if (event.eventType === 'run.completed' ||
127
+ event.eventType === 'run.failed' ||
128
+ event.eventType === 'run.rejected') {
129
+ const status = event.status ?? run.status;
130
+ return {
131
+ ...turn,
132
+ status,
133
+ completedAt: event.createdAt,
134
+ items: turn.items.map((item) => activeConversationItem(item.status)
135
+ ? {
136
+ ...item,
137
+ status: completedStatusForRun(status),
138
+ completedAt: event.createdAt
139
+ }
140
+ : item)
141
+ };
142
+ }
143
+ return turn;
144
+ };
145
+ const next = update(turn);
146
+ // Ordinary token/output updates retain membership; only structural input needs a scan.
147
+ return needsProcessProjection ? projectConversationWorkProcesses(next) : next;
148
+ }
149
+ function readableText(value) {
150
+ return isRecord(value) && typeof value.text === 'string' ? value.text : '';
151
+ }
152
+ export function conversationItemFromEvent(event) {
153
+ if (isRecord(event.payload)) {
154
+ const approvalId = event.payload.approvalId;
155
+ if (typeof approvalId === 'string' && approvalId.length > 0) {
156
+ if (event.eventType === 'approval.request')
157
+ return {
158
+ itemId: `approval:${approvalId}`,
159
+ kind: 'approval_request',
160
+ status: 'PENDING',
161
+ payload: {
162
+ approvalId,
163
+ approvalStatus: 'PENDING',
164
+ approval: event.payload.payload,
165
+ expiresAt: event.payload.expiresAt
166
+ },
167
+ merge: false
168
+ };
169
+ if (event.eventType === 'approval.decided') {
170
+ const decision = event.payload.decision;
171
+ if (decision === 'APPROVED' || decision === 'REJECTED')
172
+ return {
173
+ itemId: `approval:${approvalId}`,
174
+ kind: 'approval_request',
175
+ status: decision === 'APPROVED' ? 'COMPLETED' : 'DECLINED',
176
+ payload: { approvalId, approvalStatus: decision },
177
+ merge: true
178
+ };
179
+ }
180
+ if (event.eventType === 'approval.expired')
181
+ return {
182
+ itemId: `approval:${approvalId}`,
183
+ kind: 'approval_request',
184
+ status: 'CANCELLED',
185
+ payload: { approvalId, approvalStatus: 'EXPIRED' },
186
+ merge: true
187
+ };
188
+ }
189
+ }
190
+ if (event.eventType !== 'conversation.item' || !isRecord(event.payload))
191
+ return undefined;
192
+ const itemId = event.payload.itemId;
193
+ const kind = event.payload.kind;
194
+ const status = event.payload.status;
195
+ const payload = event.payload.payload;
196
+ if (typeof itemId !== 'string' ||
197
+ itemId.length === 0 ||
198
+ itemId.length > 88 ||
199
+ typeof kind !== 'string' ||
200
+ !projectableConversationKind(kind) ||
201
+ typeof status !== 'string' ||
202
+ !projectableConversationStatus(status) ||
203
+ !isRecord(payload))
204
+ return undefined;
205
+ const append = event.payload.append;
206
+ const sourceItemId = conversationSourceItemId(event.payload);
207
+ const permissionStop = permissionStopSchema.safeParse(event.payload.permissionStop);
208
+ return {
209
+ itemId,
210
+ kind,
211
+ status,
212
+ payload,
213
+ ...(sourceItemId === undefined ? {} : { sourceItemId }),
214
+ ...(typeof event.payload.parentItemId === 'string' &&
215
+ event.payload.parentItemId.length > 0 &&
216
+ event.payload.parentItemId.length <= 88
217
+ ? { parentItemId: event.payload.parentItemId }
218
+ : {}),
219
+ ...(permissionStop.success ? { permissionStop: permissionStop.data } : {}),
220
+ merge: event.payload.merge === true,
221
+ ...(append === 'sections' || append === 'outputPreview' || append === 'outputSummary'
222
+ ? { append }
223
+ : {})
224
+ };
225
+ }
226
+ function conversationSourceItemId(payload) {
227
+ return typeof payload.sourceItemId === 'string' &&
228
+ payload.sourceItemId.length > 0 &&
229
+ payload.sourceItemId.length <= 1024
230
+ ? payload.sourceItemId
231
+ : undefined;
232
+ }
233
+ function projectableConversationKind(value) {
234
+ return (value === 'user_message' ||
235
+ value === 'assistant_message' ||
236
+ value === 'reasoning' ||
237
+ value === 'tool_call' ||
238
+ value === 'command_execution' ||
239
+ value === 'approval_request' ||
240
+ value === 'user_input_request' ||
241
+ value === 'plan' ||
242
+ value === 'todo_list' ||
243
+ value === 'file_change' ||
244
+ value === 'context_compaction' ||
245
+ value === 'unknown' ||
246
+ value === 'warning' ||
247
+ value === 'error');
248
+ }
249
+ function projectableConversationStatus(value) {
250
+ return [
251
+ 'PENDING',
252
+ 'IN_PROGRESS',
253
+ 'COMPLETED',
254
+ 'FAILED',
255
+ 'DECLINED',
256
+ 'CANCELLED',
257
+ 'INTERRUPTED'
258
+ ].includes(value);
259
+ }
260
+ function mergeConversationItemPayload(existing, next) {
261
+ const base = next.merge && isRecord(existing) ? existing : {};
262
+ const value = { ...base, ...next.payload };
263
+ if (next.append === 'sections') {
264
+ const prior = Array.isArray(base.sections) ? base.sections : [];
265
+ const incoming = Array.isArray(next.payload.sections) ? next.payload.sections : [];
266
+ const sections = new Map();
267
+ for (const section of [...prior, ...incoming]) {
268
+ if (!isRecord(section) ||
269
+ typeof section.index !== 'number' ||
270
+ !Number.isInteger(section.index) ||
271
+ section.index < 0 ||
272
+ typeof section.text !== 'string')
273
+ continue;
274
+ sections.set(section.index, appendProjectionText(sections.get(section.index) ?? '', section.text));
275
+ }
276
+ value.sections = [...sections]
277
+ .sort(([left], [right]) => left - right)
278
+ .map(([index, text]) => ({ index, text }));
279
+ }
280
+ if (next.append === 'outputPreview') {
281
+ const prior = typeof base.outputPreview === 'string' ? base.outputPreview : '';
282
+ const incoming = typeof next.payload.outputPreview === 'string' ? next.payload.outputPreview : '';
283
+ value.outputPreview = appendProjectionText(prior, incoming);
284
+ }
285
+ if (next.append === 'outputSummary') {
286
+ const prior = typeof base.outputSummary === 'string' ? base.outputSummary : '';
287
+ const incoming = typeof next.payload.outputSummary === 'string' ? next.payload.outputSummary : '';
288
+ value.outputSummary = appendProjectionText(prior, incoming);
289
+ }
290
+ return value;
291
+ }
292
+ function appendProjectionText(previous, next) {
293
+ const text = `${previous}${next}`;
294
+ return text.length <= 10_000 ? text : `${text.slice(0, 9_999)}…`;
295
+ }
296
+ export function activeConversationItem(status) {
297
+ return status === 'PENDING' || status === 'IN_PROGRESS';
298
+ }
299
+ function completedConversationItem(status) {
300
+ return !activeConversationItem(status);
301
+ }
302
+ export function completedStatusForRun(status) {
303
+ if (status === 'INTERRUPTED')
304
+ return 'INTERRUPTED';
305
+ if (status === 'CANCELLED')
306
+ return 'CANCELLED';
307
+ if (status === 'FAILED')
308
+ return 'FAILED';
309
+ return 'COMPLETED';
310
+ }
@@ -0,0 +1,19 @@
1
+ import { isPlainRecord } from '../../util/records.js';
2
+ export function isTerminalRunStatus(status) {
3
+ return (status === 'SUCCEEDED' ||
4
+ status === 'FAILED' ||
5
+ status === 'CANCELLED' ||
6
+ status === 'INTERRUPTED');
7
+ }
8
+ export function isNodeAgentRun(value) {
9
+ return (isPlainRecord(value) &&
10
+ typeof value.id === 'string' &&
11
+ typeof value.sessionId === 'string' &&
12
+ typeof value.status === 'string');
13
+ }
14
+ export function isNodeConversationTurn(value) {
15
+ return (isPlainRecord(value) &&
16
+ typeof value.id === 'string' &&
17
+ typeof value.sessionId === 'string' &&
18
+ Array.isArray(value.items));
19
+ }
@@ -1,11 +1,12 @@
1
- import { resolveSessionRollbackTarget } from './session-rollback-target.js';
1
+ import { resolveSessionRollbackTarget } from '../service/session-rollback-target.js';
2
2
  import { sessionGroup } from './session-presentation-service.js';
3
3
  import { removeNativeSession } from '../native-session-history.js';
4
4
  import { nodeLog } from '../operational.js';
5
- import { isTerminalRunStatus, sessionPage } from '../util/node-operation-parsers.js';
5
+ import { isTerminalRunStatus } from './runtime/type-guards.js';
6
+ import { sessionPage } from './pagination.js';
6
7
  import { latestSessionActivityAt } from './session-presentation-service.js';
7
- import { marctlSessionSummary } from './session-recent-activity.js';
8
- import { WorkspaceSessionCleanupBudget, WorkspaceSessionCleanupDeadline } from './workspace-session-cleanup-budget.js';
8
+ import { marctlSessionSummary } from '../service/session-recent-activity.js';
9
+ import { WorkspaceSessionCleanupBudget, WorkspaceSessionCleanupDeadline } from '../service/workspace-session-cleanup-budget.js';
9
10
  export class SessionCatalogService {
10
11
  options;
11
12
  rewindQueue = Promise.resolve();
@@ -1,8 +1,8 @@
1
1
  import { modelUsageGrantSchema } from '@myagentroam/protocol';
2
2
  import { parseCommandInvocation } from '../runner-command-engine.js';
3
- import { isPlainRecord } from '../util/node-operation-parsers.js';
4
- import { parseSecretEnvironment } from '../util/secret-environment.js';
5
- import { personalInstructionsForRunner } from '../util/personal-instructions.js';
3
+ import { isPlainRecord } from '../util/records.js';
4
+ import { parseSecretEnvironment } from '../mcp/secret-environment.js';
5
+ import { personalInstructionsForRunner } from '../runner/personal-instructions.js';
6
6
  export class SessionCommandService {
7
7
  options;
8
8
  constructor(options) {
@@ -1,5 +1,5 @@
1
1
  import { nodeLog } from '../operational.js';
2
- import { isTerminalRunStatus } from '../util/node-operation-parsers.js';
2
+ import { isTerminalRunStatus } from './runtime/type-guards.js';
3
3
  import { safeErrorCode } from '../util/safe-error.js';
4
4
  export class SessionIdentityService {
5
5
  options;
@@ -1,10 +1,11 @@
1
1
  import { RUNNER_EXECUTION_CONTEXT } from '../runner/execution-context.js';
2
- import { parseMessageAttachments, validSessionEffort, validSessionOption, validSessionTitle } from '../util/node-operation-parsers.js';
3
- import { isPlainRecord } from '../util/node-operation-parsers.js';
4
- import { parseSecretEnvironment } from '../util/secret-environment.js';
5
- import { ScheduledSessionStore } from './scheduled-session-store.js';
6
- import { normalizeSessionPrompt } from '../util/personal-instructions.js';
7
- import { resolveWorkspaceExecutionDirectory } from '../workspace.js';
2
+ import { validSessionEffort, validSessionOption, validSessionTitle } from './input-validation.js';
3
+ import { parseMessageAttachments } from './attachments.js';
4
+ import { isPlainRecord } from '../util/records.js';
5
+ import { parseSecretEnvironment } from '../mcp/secret-environment.js';
6
+ import { ScheduledSessionStore } from '../service/scheduled-session-store.js';
7
+ import { normalizeSessionPrompt } from '../runner/personal-instructions.js';
8
+ import { resolveWorkspaceExecutionDirectory } from '../workspace/path-access.js';
8
9
  export class SessionLifecycleService {
9
10
  options;
10
11
  starts = new Map();
@@ -109,7 +110,17 @@ export class SessionLifecycleService {
109
110
  if (typeof input.sessionId !== 'string' ||
110
111
  (input.direction !== 'up' && input.direction !== 'down'))
111
112
  throw new Error('SESSION_PIN_MOVE_INVALID');
112
- const workspaceId = this.options.database().movePinnedSession(input.sessionId, input.direction);
113
+ let target;
114
+ if (input.targetSessionId !== undefined || input.position !== undefined) {
115
+ if (typeof input.targetSessionId !== 'string' ||
116
+ input.targetSessionId.length === 0 ||
117
+ (input.position !== 'before' && input.position !== 'after'))
118
+ throw new Error('SESSION_PIN_MOVE_INVALID');
119
+ target = { sessionId: input.targetSessionId, position: input.position };
120
+ }
121
+ const workspaceId = this.options
122
+ .database()
123
+ .movePinnedSession(input.sessionId, input.direction, target);
113
124
  this.options.invalidateWorkspaceSessions(workspaceId);
114
125
  return { moved: true };
115
126
  }
@@ -1,14 +1,15 @@
1
1
  import { RUNNER_EXECUTION_CONTEXT } from '../runner/execution-context.js';
2
2
  import { modelUsageGrantSchema } from '@myagentroam/protocol';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { isImageAttachment, isRunnerImageAttachment, parseComposerAttachmentIds, parseMessageAttachments, sessionTitleFromFirstMessage, withNonImageAttachmentPrompt } from '../util/node-operation-parsers.js';
5
- import { parseSecretEnvironment } from '../util/secret-environment.js';
6
- import { personalInstructionsForRunner } from '../util/personal-instructions.js';
4
+ import { sessionTitleFromFirstMessage } from './input-validation.js';
5
+ import { isImageAttachment, isRunnerImageAttachment, parseComposerAttachmentIds, parseMessageAttachments, withNonImageAttachmentPrompt } from './attachments.js';
6
+ import { parseSecretEnvironment } from '../mcp/secret-environment.js';
7
+ import { personalInstructionsForRunner } from '../runner/personal-instructions.js';
7
8
  import { safeErrorCode } from '../util/safe-error.js';
8
- import { publicGoalStartText } from './goal-runtime.js';
9
+ import { publicGoalStartText } from '../service/goal-runtime.js';
9
10
  import { serializeComposerTag, composerVisibleText, steerComposerMessage } from '@myagentroam/protocol';
10
11
  import { relative } from 'node:path';
11
- import { prepareComposerResourceInput, composerResourceInputTags, withSessionMessageOrigin, validateComposerSkills } from './composer-message-input.js';
12
+ import { prepareComposerResourceInput, composerResourceInputTags, withSessionMessageOrigin, validateComposerSkills } from '../service/composer-message-input.js';
12
13
  const INTERNAL_GOAL = Symbol('internalGoal');
13
14
  const INTERNAL_SCHEDULE = Symbol('internalSchedule');
14
15
  const INTERNAL_RESTORE = Symbol('internalRestore');
@@ -1,4 +1,4 @@
1
- import { ScheduledSessionStore } from './scheduled-session-store.js';
1
+ import { ScheduledSessionStore } from '../service/scheduled-session-store.js';
2
2
  export class SessionPresentationService {
3
3
  options;
4
4
  constructor(options) {
@@ -1,5 +1,5 @@
1
- import { marctlSessionDetail } from './session-recent-activity.js';
2
- import { isPlainRecord } from '../util/node-operation-parsers.js';
1
+ import { marctlSessionDetail } from '../service/session-recent-activity.js';
2
+ import { isPlainRecord } from '../util/records.js';
3
3
  import { toolResultResourceInputSchema, toolResultResourcePageSchema } from '@myagentroam/protocol';
4
4
  export class SessionQueryService {
5
5
  options;
@@ -1,5 +1,5 @@
1
- import { assertScheduleTimezone, parseScheduledCron } from './scheduled-session-recurrence.js';
2
- import { ScheduledSessionStore } from './scheduled-session-store.js';
1
+ import { assertScheduleTimezone, parseScheduledCron } from '../service/scheduled-session-recurrence.js';
2
+ import { ScheduledSessionStore } from '../service/scheduled-session-store.js';
3
3
  export class SessionScheduleService {
4
4
  options;
5
5
  constructor(options) {
@@ -0,0 +1,3 @@
1
+ export function isPlainRecord(value) {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3
+ }
@@ -0,0 +1 @@
1
+ export {};