@myagentroam/node 0.11.44 → 0.11.46
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.
- package/dist/connector.js +4 -4
- package/dist/database.js +7 -5
- package/dist/marctl-guidance.js +2 -2
- package/dist/migrations/v023.js +25 -0
- package/dist/runner/abstract-runner.js +1 -1
- package/dist/runner/claude/managed-run-controller.js +4 -11
- package/dist/runner/claude/native-transcript.js +1 -1
- package/dist/runner/claude/tool-normalizer.js +16 -11
- package/dist/runner/claude-code-runner.js +1 -1
- package/dist/runner/codex/conversation-parser.js +29 -38
- package/dist/runner/codex/managed-run-controller.js +5 -5
- package/dist/runner/codex/native-transcript.js +5 -3
- package/dist/runner/codex/tool-normalizer.js +13 -17
- package/dist/runner/codex-runner.js +10 -5
- package/dist/runner/execution-presentation.js +8 -51
- package/dist/runner/mar-agent/agent-network-extension.js +80 -24
- package/dist/runner/mar-agent/agent-operation-presentation.js +167 -0
- package/dist/runner/mar-agent/conversation-normalizer.js +118 -107
- package/dist/runner/mar-agent/history-projection.js +1 -1
- package/dist/runner/mar-agent/hosted-conversation-runtime.js +1 -1
- package/dist/runner/mar-agent/managed-run-controller.js +2 -2
- package/dist/runner/mar-agent-runner.js +5 -5
- package/dist/runner/normalization.js +5 -0
- package/dist/runner/opencode/managed-run-controller.js +2 -2
- package/dist/runner/opencode/tool-normalizer.js +10 -10
- package/dist/runner/opencode-runner.js +5 -5
- package/dist/runner-profiles.js +1 -1
- package/dist/runtime-state.js +7 -7
- package/dist/service/agent-network-bindings.js +10 -6
- package/dist/service/agent-network-service.js +2 -1
- package/dist/service/continuous-run-coordinator.js +1 -1
- package/dist/service/external-session-resume-service.js +1 -1
- package/dist/service/goal-continuation-coordinator.js +3 -3
- package/dist/service/native-session-watch-service.js +6 -3
- package/dist/service/planned-restart-handoff.js +24 -5
- package/dist/service/restart-session-restorer.js +2 -2
- package/dist/service/session-recent-activity.js +1 -1
- package/dist/session/native-session-projection-service.js +8 -8
- package/dist/session/runtime/conversation-projection.js +10 -3
- package/dist/session/session-catalog-service.js +2 -2
- package/dist/session/session-command-service.js +1 -0
- package/dist/session/session-context-service.js +1 -1
- package/dist/session/session-lifecycle-service.js +12 -10
- package/dist/session/session-message-service.js +4 -3
- package/dist/workspace/git.js +44 -1
- package/dist/workspace/workspace-change-service.js +39 -1
- package/dist/workspace/workspace-session-service.js +1 -1
- package/dist/workspace/workspace-workbench-service.js +8 -0
- package/package.json +4 -4
package/dist/runner-profiles.js
CHANGED
|
@@ -239,7 +239,7 @@ export function supportsRunnerConfiguration(input) {
|
|
|
239
239
|
const profile = input.runner === 'claude-code'
|
|
240
240
|
? claudeProfile(input.environment ?? process.env)
|
|
241
241
|
: definitions[input.runner];
|
|
242
|
-
const model = profile.models.find((candidate) => candidate.id === input.
|
|
242
|
+
const model = profile.models.find((candidate) => candidate.id === input.modelId);
|
|
243
243
|
return (model !== undefined &&
|
|
244
244
|
model.supportedEfforts.includes(input.effort) &&
|
|
245
245
|
profile.accessOptions.some((candidate) => candidate.id === input.access));
|
package/dist/runtime-state.js
CHANGED
|
@@ -103,7 +103,7 @@ export class NodeRuntimeState {
|
|
|
103
103
|
nativeControl: input.externalSessionId === undefined ? 'MAR_MANAGED' : (input.nativeControl ?? 'EXTERNAL'),
|
|
104
104
|
channelToken: null,
|
|
105
105
|
cwd: input.cwd,
|
|
106
|
-
|
|
106
|
+
modelId: input.modelId ?? null,
|
|
107
107
|
effort: input.effort ?? null,
|
|
108
108
|
access: input.access ?? null,
|
|
109
109
|
customTitle,
|
|
@@ -139,7 +139,7 @@ export class NodeRuntimeState {
|
|
|
139
139
|
nativeControl: input.externalSessionId === undefined ? 'MAR_MANAGED' : 'EXTERNAL',
|
|
140
140
|
channelToken: null,
|
|
141
141
|
cwd: input.cwd,
|
|
142
|
-
|
|
142
|
+
modelId: null,
|
|
143
143
|
effort: null,
|
|
144
144
|
access: null,
|
|
145
145
|
customTitle: null,
|
|
@@ -214,7 +214,7 @@ export class NodeRuntimeState {
|
|
|
214
214
|
}
|
|
215
215
|
updateAgentSessionConfiguration(input) {
|
|
216
216
|
return this.replaceSession(input.sessionId, {
|
|
217
|
-
...(input.
|
|
217
|
+
...(input.modelId === undefined ? {} : { modelId: input.modelId }),
|
|
218
218
|
...(input.effort === undefined ? {} : { effort: input.effort }),
|
|
219
219
|
...(input.access === undefined ? {} : { access: input.access })
|
|
220
220
|
});
|
|
@@ -456,7 +456,7 @@ export class NodeRuntimeState {
|
|
|
456
456
|
runId: run.id,
|
|
457
457
|
userItemId: user.id,
|
|
458
458
|
status: 'PREPARING',
|
|
459
|
-
model: session.
|
|
459
|
+
model: session.modelId,
|
|
460
460
|
effort: session.effort,
|
|
461
461
|
access: session.access,
|
|
462
462
|
items: [user],
|
|
@@ -1059,7 +1059,7 @@ export class NodeRuntimeState {
|
|
|
1059
1059
|
runId: null,
|
|
1060
1060
|
userItemId: null,
|
|
1061
1061
|
status: input.failed ? 'FAILED' : 'SUCCEEDED',
|
|
1062
|
-
model: session.
|
|
1062
|
+
model: session.modelId,
|
|
1063
1063
|
effort: session.effort,
|
|
1064
1064
|
access: session.access,
|
|
1065
1065
|
items: [],
|
|
@@ -1374,7 +1374,7 @@ export class NodeRuntimeState {
|
|
|
1374
1374
|
runId: run.id,
|
|
1375
1375
|
userItemId: null,
|
|
1376
1376
|
status: terminal(run.status) ? run.status : 'RUNNING',
|
|
1377
|
-
model: session.
|
|
1377
|
+
model: session.modelId,
|
|
1378
1378
|
effort: session.effort,
|
|
1379
1379
|
access: session.access,
|
|
1380
1380
|
items: [],
|
|
@@ -1416,7 +1416,7 @@ export class NodeRuntimeState {
|
|
|
1416
1416
|
runId: run.id,
|
|
1417
1417
|
userItemId: userId,
|
|
1418
1418
|
status: 'PREPARING',
|
|
1419
|
-
model: session.
|
|
1419
|
+
model: session.modelId,
|
|
1420
1420
|
effort: session.effort,
|
|
1421
1421
|
access: session.access,
|
|
1422
1422
|
items: [user],
|
|
@@ -167,9 +167,9 @@ export function createNodeAgentNetwork(options) {
|
|
|
167
167
|
const workspace = workspaceId === undefined ? undefined : database?.getWorkspace(workspaceId);
|
|
168
168
|
if (!runnerAdapter || !database || !workspace)
|
|
169
169
|
throw new Error('SESSION_INVALID');
|
|
170
|
-
const model = input.
|
|
171
|
-
database.getRunnerDefaults().find((entry) => entry.runner === runner)?.
|
|
172
|
-
runnerAdapter.defaultConfiguration(workspace, actor.secretEnvironment ?? {}, runnerAdapter.prepareExecutionContext(actor.runnerConfiguration ?? {})).
|
|
170
|
+
const model = input.modelId ??
|
|
171
|
+
database.getRunnerDefaults().find((entry) => entry.runner === runner)?.modelId ??
|
|
172
|
+
runnerAdapter.defaultConfiguration(workspace, actor.secretEnvironment ?? {}, runnerAdapter.prepareExecutionContext(actor.runnerConfiguration ?? {})).modelId;
|
|
173
173
|
const request = {
|
|
174
174
|
...executionInput(actor, runner, model, parent !== undefined),
|
|
175
175
|
workspaceId: workspace.id,
|
|
@@ -179,9 +179,10 @@ export function createNodeAgentNetwork(options) {
|
|
|
179
179
|
cwd: parent?.cwd ?? workspace.path,
|
|
180
180
|
content: input.content,
|
|
181
181
|
clientMessageId: input.clientMessageId,
|
|
182
|
+
__suppressNotifications: true,
|
|
182
183
|
...(!terminal && parent ? { __marctlOrigin: origin(parent) } : {}),
|
|
183
184
|
...(input.title === undefined ? {} : { title: input.title }),
|
|
184
|
-
...(model ? { model } : {}),
|
|
185
|
+
...(model ? { modelId: model } : {}),
|
|
185
186
|
...(input.effort === undefined ? {} : { effort: input.effort }),
|
|
186
187
|
...(input.attachments === undefined ? {} : { attachments: input.attachments }),
|
|
187
188
|
...(input.attachmentIds === undefined ? {} : { attachmentIds: input.attachmentIds })
|
|
@@ -194,6 +195,7 @@ export function createNodeAgentNetwork(options) {
|
|
|
194
195
|
objective: input.goal.kind === 'set' ? input.goal.objective : '',
|
|
195
196
|
secretEnvironment: childEnvironment(actor),
|
|
196
197
|
messageContent: input.content,
|
|
198
|
+
suppressNotifications: true,
|
|
197
199
|
...(!terminal && parent ? { messageOrigin: origin(parent) } : {}),
|
|
198
200
|
clientMessageId: input.clientMessageId,
|
|
199
201
|
...(input.attachments === undefined ? {} : { attachments: input.attachments }),
|
|
@@ -227,12 +229,13 @@ export function createNodeAgentNetwork(options) {
|
|
|
227
229
|
const current = await options.resume.resolveForMessage(session.id);
|
|
228
230
|
const executionContext = options.runners
|
|
229
231
|
.require(current.runner)
|
|
230
|
-
.prepareExecutionContext(executionInput(actor, current.runner, current.
|
|
232
|
+
.prepareExecutionContext(executionInput(actor, current.runner, current.modelId, current.parentSessionId != null));
|
|
231
233
|
signal?.throwIfAborted();
|
|
232
234
|
const result = await options.withGrant(actor, () => options.coordinator.start({
|
|
233
235
|
session: current,
|
|
234
236
|
objective: change.objective,
|
|
235
237
|
executionContext,
|
|
238
|
+
suppressNotifications: true,
|
|
236
239
|
secretEnvironment: childEnvironment(actor),
|
|
237
240
|
...(actor.requestUserId === undefined ? {} : { initiatedByUserId: actor.requestUserId }),
|
|
238
241
|
...(actor.personalInstructions === undefined
|
|
@@ -259,10 +262,11 @@ export function createNodeAgentNetwork(options) {
|
|
|
259
262
|
const parent = terminalCalls.has(actor) ? undefined : await actorSession(actor);
|
|
260
263
|
signal?.throwIfAborted();
|
|
261
264
|
const result = await options.withGrant(actor, () => options.messages.operations()['session.message']({
|
|
262
|
-
...executionInput(actor, session.runner, session.
|
|
265
|
+
...executionInput(actor, session.runner, session.modelId, session.parentSessionId != null),
|
|
263
266
|
...input,
|
|
264
267
|
...(signal ? { [SESSION_ADMISSION_SIGNAL]: signal } : {}),
|
|
265
268
|
sessionId: session.id,
|
|
269
|
+
__suppressNotifications: true,
|
|
266
270
|
...(parent ? { __marctlOrigin: origin(parent) } : {})
|
|
267
271
|
}));
|
|
268
272
|
return publicReceipt(result);
|
|
@@ -27,7 +27,8 @@ export class AgentNetworkService {
|
|
|
27
27
|
input.attachmentIds.some((id) => typeof id !== 'string' || !id))) ||
|
|
28
28
|
(!!input.attachments?.length && !!input.attachmentIds?.length) ||
|
|
29
29
|
!validOptionalString(input.runner) ||
|
|
30
|
-
|
|
30
|
+
'model' in input ||
|
|
31
|
+
!validOptionalString(input.modelId) ||
|
|
31
32
|
!validOptionalString(input.effort) ||
|
|
32
33
|
!validOptionalString(input.title) ||
|
|
33
34
|
!validOptionalString(input.parentSessionId) ||
|
|
@@ -717,7 +717,7 @@ export class ContinuousRunCoordinator {
|
|
|
717
717
|
...(pending.externalSessionId === null
|
|
718
718
|
? {}
|
|
719
719
|
: { externalSessionId: pending.externalSessionId }),
|
|
720
|
-
...(pending.
|
|
720
|
+
...(pending.modelId === null ? {} : { model: pending.modelId }),
|
|
721
721
|
...(pending.effort === null ? {} : { effort: pending.effort }),
|
|
722
722
|
...(pending.access === null ? {} : { access: pending.access }),
|
|
723
723
|
collaborationMode: pending.collaborationMode,
|
|
@@ -106,7 +106,7 @@ export class ExternalSessionResumeService {
|
|
|
106
106
|
cwd,
|
|
107
107
|
createdAt: session.createdAt,
|
|
108
108
|
lastActivityAt: session.lastActivityAt,
|
|
109
|
-
|
|
109
|
+
modelId: configuration.modelId,
|
|
110
110
|
effort: configuration.effort,
|
|
111
111
|
access: configuration.access,
|
|
112
112
|
...(session.runnerTitle === null ? {} : { title: session.runnerTitle }),
|
|
@@ -184,7 +184,7 @@ export class GoalContinuationCoordinator {
|
|
|
184
184
|
this.options
|
|
185
185
|
.runners()
|
|
186
186
|
.require(session.runner)
|
|
187
|
-
.assertChildModel(session.
|
|
187
|
+
.assertChildModel(session.modelId, executionContext);
|
|
188
188
|
this.invalidateGoalContinuation(session.id);
|
|
189
189
|
const prepared = this.prepareGoalSession(session);
|
|
190
190
|
const goalSession = prepared.session;
|
|
@@ -558,13 +558,13 @@ export class GoalContinuationCoordinator {
|
|
|
558
558
|
: session;
|
|
559
559
|
if (changed &&
|
|
560
560
|
prepared.externalSessionId !== null &&
|
|
561
|
-
prepared.
|
|
561
|
+
prepared.modelId !== null &&
|
|
562
562
|
prepared.effort !== null)
|
|
563
563
|
this.options.database()?.saveNativeSessionMetadata({
|
|
564
564
|
runner: prepared.runner,
|
|
565
565
|
externalSessionId: prepared.externalSessionId,
|
|
566
566
|
workspaceId: prepared.workspaceId,
|
|
567
|
-
|
|
567
|
+
modelId: prepared.modelId,
|
|
568
568
|
effort: prepared.effort,
|
|
569
569
|
access
|
|
570
570
|
});
|
|
@@ -5,6 +5,7 @@ import { SessionWatchSlotCoordinator } from './session-watch-slot-coordinator.js
|
|
|
5
5
|
const INTERVAL_MS = 2_000;
|
|
6
6
|
const TTL_MS = 45_000;
|
|
7
7
|
const MAX_FAILURES = 3;
|
|
8
|
+
const INITIAL_SEGMENT_LIMIT = 3;
|
|
8
9
|
export class NativeSessionWatchService {
|
|
9
10
|
options;
|
|
10
11
|
watches = new Map();
|
|
@@ -323,7 +324,9 @@ export class NativeSessionWatchService {
|
|
|
323
324
|
return;
|
|
324
325
|
stage = 'project_snapshot';
|
|
325
326
|
const page = update.page;
|
|
326
|
-
const payload = lease.unit === 'SEGMENT'
|
|
327
|
+
const payload = lease.unit === 'SEGMENT'
|
|
328
|
+
? projectInitialPage(session, page, 'SEGMENT', INITIAL_SEGMENT_LIMIT)
|
|
329
|
+
: page;
|
|
327
330
|
this.emitSnapshot(lease, session, payload);
|
|
328
331
|
this.releaseSnapshotPendingEvents(lease, page);
|
|
329
332
|
const hydration = watch.observation.hydrate();
|
|
@@ -405,7 +408,7 @@ export class NativeSessionWatchService {
|
|
|
405
408
|
if (!lease.ready)
|
|
406
409
|
continue;
|
|
407
410
|
const payload = lease.unit === 'SEGMENT'
|
|
408
|
-
? this.options.projectInitialPage?.(session, page, 'SEGMENT',
|
|
411
|
+
? this.options.projectInitialPage?.(session, page, 'SEGMENT', INITIAL_SEGMENT_LIMIT)
|
|
409
412
|
: page;
|
|
410
413
|
if (payload === undefined)
|
|
411
414
|
continue;
|
|
@@ -431,7 +434,7 @@ export class NativeSessionWatchService {
|
|
|
431
434
|
if (!lease.snapshotSent)
|
|
432
435
|
continue;
|
|
433
436
|
const payload = lease.unit === 'SEGMENT'
|
|
434
|
-
? this.options.projectInitialPage?.(session, page, 'SEGMENT',
|
|
437
|
+
? this.options.projectInitialPage?.(session, page, 'SEGMENT', INITIAL_SEGMENT_LIMIT)
|
|
435
438
|
: page;
|
|
436
439
|
if (payload === undefined)
|
|
437
440
|
continue;
|
|
@@ -51,7 +51,7 @@ const sessionSchema = z
|
|
|
51
51
|
id: identifier,
|
|
52
52
|
workspaceId: identifier,
|
|
53
53
|
cwd: z.string().min(1),
|
|
54
|
-
|
|
54
|
+
modelId: z.string().nullable(),
|
|
55
55
|
effort: z.string().nullable(),
|
|
56
56
|
access: z.string().nullable()
|
|
57
57
|
})
|
|
@@ -68,7 +68,7 @@ const sessionSchema = z
|
|
|
68
68
|
.strict();
|
|
69
69
|
export const plannedRestartSchema = z
|
|
70
70
|
.object({
|
|
71
|
-
schemaVersion: z.literal(
|
|
71
|
+
schemaVersion: z.literal(2),
|
|
72
72
|
id: identifier,
|
|
73
73
|
nodeId: identifier,
|
|
74
74
|
serverUrl: z.string().min(1),
|
|
@@ -76,6 +76,14 @@ export const plannedRestartSchema = z
|
|
|
76
76
|
sessions: z.array(sessionSchema)
|
|
77
77
|
})
|
|
78
78
|
.strict();
|
|
79
|
+
const legacyPlannedRestartSchema = plannedRestartSchema.extend({
|
|
80
|
+
schemaVersion: z.literal(1),
|
|
81
|
+
sessions: z.array(sessionSchema.extend({
|
|
82
|
+
session: sessionSchema.shape.session.omit({ modelId: true }).extend({
|
|
83
|
+
model: z.string().nullable()
|
|
84
|
+
})
|
|
85
|
+
}))
|
|
86
|
+
});
|
|
79
87
|
export const RESTART_NOTICE = 'The previous execution was interrupted by a system restart. Check the session history and current workspace, then continue unfinished work. Verify completed or uncertain actions before proceeding; do not blindly repeat actions with external side effects.';
|
|
80
88
|
/** A one-shot orderly-shutdown handoff, not a crash-recovery journal. */
|
|
81
89
|
export class PlannedRestartHandoff {
|
|
@@ -129,7 +137,18 @@ export class PlannedRestartHandoff {
|
|
|
129
137
|
return;
|
|
130
138
|
}
|
|
131
139
|
try {
|
|
132
|
-
const
|
|
140
|
+
const saved = JSON.parse(text);
|
|
141
|
+
const legacy = legacyPlannedRestartSchema.safeParse(saved);
|
|
142
|
+
const value = plannedRestartSchema.parse(legacy.success
|
|
143
|
+
? {
|
|
144
|
+
...legacy.data,
|
|
145
|
+
schemaVersion: 2,
|
|
146
|
+
sessions: legacy.data.sessions.map((entry) => {
|
|
147
|
+
const { model, ...session } = entry.session;
|
|
148
|
+
return { ...entry, session: { ...session, modelId: model } };
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
: saved);
|
|
133
152
|
const identity = this.options.identity();
|
|
134
153
|
if (value.nodeId !== identity.nodeId || value.serverUrl !== identity.serverUrl)
|
|
135
154
|
throw new Error('RESTART_HANDOFF_IDENTITY_MISMATCH');
|
|
@@ -190,7 +209,7 @@ export class PlannedRestartHandoff {
|
|
|
190
209
|
id: session.externalSessionId,
|
|
191
210
|
workspaceId: session.workspaceId,
|
|
192
211
|
cwd: session.cwd,
|
|
193
|
-
|
|
212
|
+
modelId: session.modelId,
|
|
194
213
|
effort: session.effort,
|
|
195
214
|
access: session.access
|
|
196
215
|
},
|
|
@@ -207,7 +226,7 @@ export class PlannedRestartHandoff {
|
|
|
207
226
|
});
|
|
208
227
|
}
|
|
209
228
|
return plannedRestartSchema.parse({
|
|
210
|
-
schemaVersion:
|
|
229
|
+
schemaVersion: 2,
|
|
211
230
|
id: randomUUID(),
|
|
212
231
|
...this.options.identity(),
|
|
213
232
|
createdAt: Date.now(),
|
|
@@ -18,7 +18,7 @@ export class RestartSessionRestorer {
|
|
|
18
18
|
workspaceId: entry.session.workspaceId,
|
|
19
19
|
ownerUserId,
|
|
20
20
|
runner: 'mar-agent',
|
|
21
|
-
|
|
21
|
+
modelId: entry.session.modelId
|
|
22
22
|
}));
|
|
23
23
|
return contexts.get(ownerUserId);
|
|
24
24
|
};
|
|
@@ -35,7 +35,7 @@ export class RestartSessionRestorer {
|
|
|
35
35
|
throw new Error('RESTART_SESSION_MISMATCH');
|
|
36
36
|
session = this.options.runtime.updateAgentSessionConfiguration({
|
|
37
37
|
sessionId: session.id,
|
|
38
|
-
|
|
38
|
+
modelId: entry.session.modelId ?? session.modelId,
|
|
39
39
|
effort: entry.session.effort ?? session.effort,
|
|
40
40
|
access: entry.session.access ?? session.access
|
|
41
41
|
});
|
|
@@ -89,7 +89,7 @@ export class NativeSessionProjectionService {
|
|
|
89
89
|
nativeControl: 'EXTERNAL',
|
|
90
90
|
channelToken: null,
|
|
91
91
|
cwd: workspace.path,
|
|
92
|
-
|
|
92
|
+
modelId: record.metadata.modelId,
|
|
93
93
|
effort: record.metadata.effort,
|
|
94
94
|
access: record.metadata.access,
|
|
95
95
|
customTitle: record.metadata.customTitle,
|
|
@@ -123,7 +123,7 @@ export class NativeSessionProjectionService {
|
|
|
123
123
|
cwd: direct.cwd,
|
|
124
124
|
createdAt: direct.createdAt,
|
|
125
125
|
lastActivityAt: direct.lastActivityAt,
|
|
126
|
-
...(direct.
|
|
126
|
+
...(direct.modelId === null ? {} : { modelId: direct.modelId }),
|
|
127
127
|
...(direct.effort === null ? {} : { effort: direct.effort }),
|
|
128
128
|
...(direct.access === null ? {} : { access: direct.access }),
|
|
129
129
|
...(direct.runnerTitle === null ? {} : { title: direct.runnerTitle }),
|
|
@@ -142,9 +142,9 @@ export class NativeSessionProjectionService {
|
|
|
142
142
|
const fallback = saved ?? runner.defaultConfiguration(workspace);
|
|
143
143
|
const configuration = {
|
|
144
144
|
runner: session.runner,
|
|
145
|
-
|
|
146
|
-
? session.
|
|
147
|
-
: fallback.
|
|
145
|
+
modelId: typeof session.modelId === 'string' && session.modelId.trim().length > 0
|
|
146
|
+
? session.modelId
|
|
147
|
+
: fallback.modelId,
|
|
148
148
|
effort: typeof session.effort === 'string' ? session.effort : fallback.effort,
|
|
149
149
|
access: runner.restoreStoredAccess(session.access, fallback.access)
|
|
150
150
|
};
|
|
@@ -152,13 +152,13 @@ export class NativeSessionProjectionService {
|
|
|
152
152
|
}
|
|
153
153
|
ensureResumedConfiguration(session) {
|
|
154
154
|
const configuration = this.resumedConfiguration(session);
|
|
155
|
-
if (session.
|
|
155
|
+
if (session.modelId === configuration.modelId &&
|
|
156
156
|
session.effort === configuration.effort &&
|
|
157
157
|
session.access === configuration.access)
|
|
158
158
|
return session;
|
|
159
159
|
return this.options.runtime.updateAgentSessionConfiguration({
|
|
160
160
|
sessionId: session.id,
|
|
161
|
-
|
|
161
|
+
modelId: configuration.modelId,
|
|
162
162
|
effort: configuration.effort,
|
|
163
163
|
access: configuration.access
|
|
164
164
|
});
|
|
@@ -214,7 +214,7 @@ export class NativeSessionProjectionService {
|
|
|
214
214
|
nativeControl: 'EXTERNAL',
|
|
215
215
|
channelToken: null,
|
|
216
216
|
cwd: input.cwd,
|
|
217
|
-
|
|
217
|
+
modelId: metadata?.modelId ?? null,
|
|
218
218
|
effort: metadata?.effort ?? null,
|
|
219
219
|
access: metadata?.access ?? null,
|
|
220
220
|
customTitle: metadata?.customTitle ?? null,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { appendToolOutput, retainExecutionInputTruncation, toolOutputSchema, permissionStopSchema } from '@myagentroam/protocol';
|
|
2
2
|
import { projectConversationWorkProcesses } from './work-process.js';
|
|
3
3
|
function isRecord(value) {
|
|
4
4
|
return typeof value === 'object' && value !== null;
|
|
@@ -292,9 +292,16 @@ export function mergeConversationItemPayload(existing, next) {
|
|
|
292
292
|
}
|
|
293
293
|
if (next.append === 'output' &&
|
|
294
294
|
isRecord(next.payload.output) &&
|
|
295
|
-
|
|
295
|
+
Array.isArray(next.payload.output.blocks)) {
|
|
296
|
+
const update = toolOutputSchema.parse(next.payload.output);
|
|
297
|
+
const prior = base.output === undefined ? undefined : toolOutputSchema.parse(base.output);
|
|
298
|
+
value.output = appendToolOutput(prior, update);
|
|
296
299
|
if (next.payload.toolPresentation !== undefined)
|
|
297
|
-
value.toolPresentation =
|
|
300
|
+
value.toolPresentation = retainExecutionInputTruncation(base.toolPresentation, next.payload.toolPresentation);
|
|
301
|
+
}
|
|
302
|
+
if (next.append === 'output' &&
|
|
303
|
+
isRecord(next.payload.output) &&
|
|
304
|
+
typeof next.payload.output.text === 'string') {
|
|
298
305
|
const prior = isRecord(base.output) && typeof base.output.text === 'string' ? base.output : undefined;
|
|
299
306
|
const text = `${prior?.text ?? ''}${next.payload.output.text}`;
|
|
300
307
|
value.output = {
|
|
@@ -187,7 +187,7 @@ export class SessionCatalogService {
|
|
|
187
187
|
nativeControl: 'EXTERNAL',
|
|
188
188
|
channelToken: null,
|
|
189
189
|
cwd: workspace.path,
|
|
190
|
-
|
|
190
|
+
modelId: persisted.metadata.modelId,
|
|
191
191
|
effort: persisted.metadata.effort,
|
|
192
192
|
access: persisted.metadata.access,
|
|
193
193
|
customTitle: persisted.metadata.customTitle,
|
|
@@ -283,7 +283,7 @@ export class SessionCatalogService {
|
|
|
283
283
|
cwd,
|
|
284
284
|
nativeControl: 'MAR_MANAGED',
|
|
285
285
|
...(nativeId === null ? {} : { externalSessionId: nativeId }),
|
|
286
|
-
...(source.
|
|
286
|
+
...(source.modelId === null ? {} : { modelId: source.modelId }),
|
|
287
287
|
...(source.effort === null ? {} : { effort: source.effort }),
|
|
288
288
|
...(source.access === null ? {} : { access: source.access }),
|
|
289
289
|
title
|
|
@@ -31,6 +31,7 @@ export class SessionCommandService {
|
|
|
31
31
|
}
|
|
32
32
|
async sessionCommand(input) {
|
|
33
33
|
if (typeof input.sessionId !== 'string' ||
|
|
34
|
+
Object.hasOwn(input, 'model') ||
|
|
34
35
|
typeof input.commandId !== 'string' ||
|
|
35
36
|
input.commandId.length === 0 ||
|
|
36
37
|
typeof input.input !== 'string' ||
|
|
@@ -20,7 +20,7 @@ export class SessionContextService {
|
|
|
20
20
|
return { context: null, pending: false };
|
|
21
21
|
}
|
|
22
22
|
const context = {
|
|
23
|
-
model: usage.model ?? session.
|
|
23
|
+
model: usage.model ?? session.modelId,
|
|
24
24
|
usedTokens: usage.usedTokens,
|
|
25
25
|
maxTokens: usage.maxTokens,
|
|
26
26
|
percentage: Math.round((usage.usedTokens / usage.maxTokens) * 1000) / 10
|
|
@@ -120,7 +120,8 @@ export class SessionLifecycleService {
|
|
|
120
120
|
(input.externalSessionId !== undefined && typeof input.externalSessionId !== 'string') ||
|
|
121
121
|
!validSessionTitle(input.title) ||
|
|
122
122
|
!validSessionTitle(input.customTitle) ||
|
|
123
|
-
|
|
123
|
+
Object.hasOwn(input, 'model') ||
|
|
124
|
+
!validSessionOption(input.modelId) ||
|
|
124
125
|
!validSessionEffort(input.effort) ||
|
|
125
126
|
!validSessionOption(input.access))
|
|
126
127
|
throw new Error('SESSION_INVALID');
|
|
@@ -172,7 +173,7 @@ export class SessionLifecycleService {
|
|
|
172
173
|
const fallback = runner.defaultConfiguration(workspace, environment, executionContext);
|
|
173
174
|
const configuration = {
|
|
174
175
|
runner: runner.name,
|
|
175
|
-
|
|
176
|
+
modelId: typeof input.modelId === 'string' ? input.modelId : (saved?.modelId ?? fallback.modelId),
|
|
176
177
|
effort: typeof input.effort === 'string' ? input.effort : (saved?.effort ?? fallback.effort),
|
|
177
178
|
access: typeof input.access === 'string'
|
|
178
179
|
? input.access
|
|
@@ -183,7 +184,7 @@ export class SessionLifecycleService {
|
|
|
183
184
|
if (runner.name === 'mar-agent' && !['auto', 'full-access'].includes(configuration.access))
|
|
184
185
|
throw new Error('SESSION_INVALID');
|
|
185
186
|
if (parentSessionId !== undefined)
|
|
186
|
-
runner.assertChildModel(configuration.
|
|
187
|
+
runner.assertChildModel(configuration.modelId, executionContext);
|
|
187
188
|
const session = this.options.runtime.createAgentSession({
|
|
188
189
|
...(parentSessionId === undefined ? {} : { parentSessionId }),
|
|
189
190
|
workspaceId: input.workspaceId,
|
|
@@ -192,7 +193,7 @@ export class SessionLifecycleService {
|
|
|
192
193
|
...(typeof input.externalSessionId === 'string'
|
|
193
194
|
? { externalSessionId: input.externalSessionId }
|
|
194
195
|
: {}),
|
|
195
|
-
|
|
196
|
+
modelId: configuration.modelId,
|
|
196
197
|
effort: configuration.effort,
|
|
197
198
|
access: configuration.access,
|
|
198
199
|
...(typeof input.title === 'string' ? { title: input.title } : {}),
|
|
@@ -208,8 +209,9 @@ export class SessionLifecycleService {
|
|
|
208
209
|
}
|
|
209
210
|
async updateConfiguration(input) {
|
|
210
211
|
if (typeof input.sessionId !== 'string' ||
|
|
211
|
-
(input.
|
|
212
|
-
|
|
212
|
+
(input.modelId === undefined && input.effort === undefined && input.access === undefined) ||
|
|
213
|
+
Object.hasOwn(input, 'model') ||
|
|
214
|
+
!validSessionOption(input.modelId) ||
|
|
213
215
|
!validSessionEffort(input.effort) ||
|
|
214
216
|
!validSessionOption(input.access))
|
|
215
217
|
throw new Error('SESSION_INVALID');
|
|
@@ -222,13 +224,13 @@ export class SessionLifecycleService {
|
|
|
222
224
|
input.access !== undefined &&
|
|
223
225
|
!['auto', 'full-access'].includes(input.access))
|
|
224
226
|
throw new Error('SESSION_INVALID');
|
|
225
|
-
if (current.parentSessionId != null && typeof input.
|
|
227
|
+
if (current.parentSessionId != null && typeof input.modelId === 'string') {
|
|
226
228
|
const runner = this.options.runners.require(current.runner);
|
|
227
|
-
runner.assertChildModel(input.
|
|
229
|
+
runner.assertChildModel(input.modelId, runner.prepareExecutionContext(input));
|
|
228
230
|
}
|
|
229
231
|
const session = this.options.runtime.updateAgentSessionConfiguration({
|
|
230
232
|
sessionId: current.id,
|
|
231
|
-
...(typeof input.
|
|
233
|
+
...(typeof input.modelId === 'string' ? { modelId: input.modelId } : {}),
|
|
232
234
|
...(typeof input.effort === 'string' ? { effort: input.effort } : {}),
|
|
233
235
|
...(typeof input.access === 'string' ? { access: input.access } : {})
|
|
234
236
|
});
|
|
@@ -237,7 +239,7 @@ export class SessionLifecycleService {
|
|
|
237
239
|
runner: session.runner,
|
|
238
240
|
externalSessionId: session.externalSessionId,
|
|
239
241
|
workspaceId: session.workspaceId,
|
|
240
|
-
|
|
242
|
+
modelId: session.modelId,
|
|
241
243
|
effort: session.effort,
|
|
242
244
|
access: session.access
|
|
243
245
|
});
|
|
@@ -91,7 +91,8 @@ export class SessionMessageService {
|
|
|
91
91
|
}
|
|
92
92
|
async prepareMessage(input) {
|
|
93
93
|
checkSessionAdmission(input);
|
|
94
|
-
if (
|
|
94
|
+
if (Object.hasOwn(input, 'model') ||
|
|
95
|
+
typeof input.sessionId !== 'string' ||
|
|
95
96
|
typeof input.clientMessageId !== 'string' ||
|
|
96
97
|
input.clientMessageId.length === 0 ||
|
|
97
98
|
typeof input.content !== 'string' ||
|
|
@@ -148,7 +149,7 @@ export class SessionMessageService {
|
|
|
148
149
|
if (session.parentSessionId != null)
|
|
149
150
|
this.options.runners
|
|
150
151
|
.require(session.runner)
|
|
151
|
-
.assertChildModel(session.
|
|
152
|
+
.assertChildModel(session.modelId, executionContext);
|
|
152
153
|
const sourceContent = withSessionMessageOrigin(content, input.__marctlOrigin, session.parentSessionId);
|
|
153
154
|
const goalMessage = internalGoal || internalSchedule || internalRestore
|
|
154
155
|
? undefined
|
|
@@ -296,7 +297,7 @@ export class SessionMessageService {
|
|
|
296
297
|
cwd,
|
|
297
298
|
workspacePath,
|
|
298
299
|
externalSessionId: currentSession.externalSessionId,
|
|
299
|
-
|
|
300
|
+
modelId: currentSession.modelId,
|
|
300
301
|
effort: currentSession.effort,
|
|
301
302
|
access: currentSession.access,
|
|
302
303
|
collaborationMode: planActive ? 'plan' : 'default',
|
package/dist/workspace/git.js
CHANGED
|
@@ -107,6 +107,23 @@ export async function readRestrictedDiff(workspacePath, fromRef, toRef) {
|
|
|
107
107
|
}
|
|
108
108
|
return { binary: false, text: result.text, truncated: result.truncated };
|
|
109
109
|
}
|
|
110
|
+
/** Fetches refs only; failures never expose remote URLs or authentication diagnostics. */
|
|
111
|
+
export async function fetchGitRepository(workspacePath, timeoutMs) {
|
|
112
|
+
if (timeoutMs <= 0)
|
|
113
|
+
return false;
|
|
114
|
+
try {
|
|
115
|
+
const result = await runGitAllowFailure(workspacePath, ['fetch', '--all', '--quiet', '--no-recurse-submodules', '--no-write-fetch-head'], {
|
|
116
|
+
timeoutMs,
|
|
117
|
+
processGroup: true,
|
|
118
|
+
includeStderrInTimeout: false,
|
|
119
|
+
environment: { GIT_TERMINAL_PROMPT: '0', GCM_INTERACTIVE: 'never' }
|
|
120
|
+
});
|
|
121
|
+
return result.code === 0;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
110
127
|
function currentBranchFromStatusHeader(header) {
|
|
111
128
|
const value = header.slice(3);
|
|
112
129
|
if (value.startsWith('HEAD '))
|
|
@@ -898,10 +915,12 @@ async function runGitAllowFailure(workspacePath, args, options = {}) {
|
|
|
898
915
|
cwd: workspacePath,
|
|
899
916
|
shell: false,
|
|
900
917
|
windowsHide: true,
|
|
918
|
+
detached: options.processGroup === true && process.platform !== 'win32',
|
|
901
919
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
902
920
|
env: {
|
|
903
921
|
...process.env,
|
|
904
922
|
...gitOperationEnvironment.getStore(),
|
|
923
|
+
...options.environment,
|
|
905
924
|
...(options.readOnly === true ? { GIT_OPTIONAL_LOCKS: '0' } : {})
|
|
906
925
|
}
|
|
907
926
|
});
|
|
@@ -911,6 +930,7 @@ async function runGitAllowFailure(workspacePath, args, options = {}) {
|
|
|
911
930
|
let errorSize = 0;
|
|
912
931
|
let truncated = false;
|
|
913
932
|
let timedOut = false;
|
|
933
|
+
let forcedTerminationTimer;
|
|
914
934
|
const timer = setTimeout(() => {
|
|
915
935
|
timedOut = true;
|
|
916
936
|
if (process.platform === 'win32') {
|
|
@@ -932,6 +952,26 @@ async function runGitAllowFailure(workspacePath, args, options = {}) {
|
|
|
932
952
|
});
|
|
933
953
|
return;
|
|
934
954
|
}
|
|
955
|
+
if (options.processGroup === true && child.pid !== undefined) {
|
|
956
|
+
const pid = child.pid;
|
|
957
|
+
try {
|
|
958
|
+
// Give Git a chance to release transaction locks before bounded escalation.
|
|
959
|
+
process.kill(-pid, 'SIGTERM');
|
|
960
|
+
forcedTerminationTimer = setTimeout(() => {
|
|
961
|
+
try {
|
|
962
|
+
process.kill(-pid, 'SIGKILL');
|
|
963
|
+
}
|
|
964
|
+
catch {
|
|
965
|
+
// The process group may have completed its cleanup already.
|
|
966
|
+
}
|
|
967
|
+
}, 250);
|
|
968
|
+
forcedTerminationTimer.unref();
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
catch {
|
|
972
|
+
// The group may have exited before the timeout fired.
|
|
973
|
+
}
|
|
974
|
+
}
|
|
935
975
|
child.kill('SIGKILL');
|
|
936
976
|
}, options.timeoutMs ?? GIT_TIMEOUT_MS);
|
|
937
977
|
child.stdout.on('data', (chunk) => {
|
|
@@ -953,6 +993,7 @@ async function runGitAllowFailure(workspacePath, args, options = {}) {
|
|
|
953
993
|
});
|
|
954
994
|
child.once('error', (error) => {
|
|
955
995
|
clearTimeout(timer);
|
|
996
|
+
clearTimeout(forcedTerminationTimer);
|
|
956
997
|
logNodeCommandFailure(`git ${args[0] ?? 'unknown'}`, {
|
|
957
998
|
reason: error.code ?? 'START_FAILED'
|
|
958
999
|
});
|
|
@@ -960,9 +1001,11 @@ async function runGitAllowFailure(workspacePath, args, options = {}) {
|
|
|
960
1001
|
});
|
|
961
1002
|
child.once('close', (code) => {
|
|
962
1003
|
clearTimeout(timer);
|
|
1004
|
+
clearTimeout(forcedTerminationTimer);
|
|
963
1005
|
const stderr = Buffer.concat(errors).toString('utf8');
|
|
964
1006
|
if (timedOut) {
|
|
965
|
-
|
|
1007
|
+
const details = options.includeStderrInTimeout === false ? '' : stderr;
|
|
1008
|
+
reject(gitCommandFailure(code === null || code === 0 ? 1 : code, `Git command timed out after ${options.timeoutMs ?? GIT_TIMEOUT_MS} ms.${details ? `\n${details}` : ''}`, args[0] ?? 'unknown', Math.round(performance.now() - startedAt), 'TIMEOUT'));
|
|
966
1009
|
return;
|
|
967
1010
|
}
|
|
968
1011
|
const output = Buffer.concat(chunks);
|