@myagentroam/node 0.11.45 → 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 +1 -1
- package/dist/runner/claude-code-runner.js +1 -1
- package/dist/runner/codex/managed-run-controller.js +1 -1
- package/dist/runner/codex-runner.js +10 -5
- package/dist/runner/mar-agent/agent-network-extension.js +40 -14
- package/dist/runner/mar-agent/conversation-normalizer.js +1 -1
- 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/opencode/managed-run-controller.js +2 -2
- 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 +6 -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/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/workspace-session-service.js +1 -1
- package/package.json +5 -5
package/dist/connector.js
CHANGED
|
@@ -443,7 +443,7 @@ export class NodeConnector {
|
|
|
443
443
|
emitSession: (session) => this.emitSessionWorkbenchEvent({ session }),
|
|
444
444
|
emitPage: (session, page) => this.emitWorkbenchEvent('conversation', {
|
|
445
445
|
sessionId: session.id,
|
|
446
|
-
snapshot: this.conversationSegmentService.projectInitial(session, page,
|
|
446
|
+
snapshot: this.conversationSegmentService.projectInitial(session, page, 3)
|
|
447
447
|
}),
|
|
448
448
|
nodeId: () => this.config?.nodeId ?? 'runtime-node',
|
|
449
449
|
nodeGeneration: () => this.nodeGeneration,
|
|
@@ -854,7 +854,7 @@ export class NodeConnector {
|
|
|
854
854
|
prompt: item.content,
|
|
855
855
|
modelText: prepared.input,
|
|
856
856
|
clientMessageId: item.clientMessageId,
|
|
857
|
-
|
|
857
|
+
modelId: prepared.modelId,
|
|
858
858
|
effort: prepared.effort,
|
|
859
859
|
access: prepared.access,
|
|
860
860
|
collaborationMode: prepared.collaborationMode,
|
|
@@ -968,7 +968,7 @@ export class NodeConnector {
|
|
|
968
968
|
runner: session.runner,
|
|
969
969
|
externalSessionId: nativeSessionId,
|
|
970
970
|
workspaceId: session.workspaceId,
|
|
971
|
-
...(session.
|
|
971
|
+
...(session.modelId === null ? {} : { modelId: session.modelId }),
|
|
972
972
|
...(session.effort === null ? {} : { effort: session.effort }),
|
|
973
973
|
...(session.access === null ? {} : { access: session.access }),
|
|
974
974
|
...(session.customTitle === null ? {} : { customTitle: session.customTitle }),
|
|
@@ -1508,7 +1508,7 @@ export class NodeConnector {
|
|
|
1508
1508
|
workspaceId: task.workspaceId,
|
|
1509
1509
|
ownerUserId: task.ownerUserId,
|
|
1510
1510
|
runner: task.runner,
|
|
1511
|
-
|
|
1511
|
+
modelId: configuredSession.modelId
|
|
1512
1512
|
});
|
|
1513
1513
|
return {
|
|
1514
1514
|
secretEnvironment: context.secretEnvironment,
|
package/dist/database.js
CHANGED
|
@@ -4,6 +4,7 @@ import { NODE_SCHEMA_MIGRATION_V19 } from './migrations/v019.js';
|
|
|
4
4
|
import { NODE_SCHEMA_MIGRATION_V20 } from './migrations/v020.js';
|
|
5
5
|
import { NODE_SCHEMA_MIGRATION_V21 } from './migrations/v021.js';
|
|
6
6
|
import { NODE_SCHEMA_MIGRATION_V22 } from './migrations/v022.js';
|
|
7
|
+
import { NODE_SCHEMA_MIGRATION_V23 } from './migrations/v023.js';
|
|
7
8
|
import { normalizeSessionPrompt } from './runner/personal-instructions.js';
|
|
8
9
|
import { randomUUID } from 'node:crypto';
|
|
9
10
|
import { closeSync, existsSync, mkdirSync, openSync, rmSync } from 'node:fs';
|
|
@@ -27,7 +28,8 @@ export const NODE_SCHEMA_MIGRATIONS = [
|
|
|
27
28
|
NODE_SCHEMA_MIGRATION_V19,
|
|
28
29
|
NODE_SCHEMA_MIGRATION_V20,
|
|
29
30
|
NODE_SCHEMA_MIGRATION_V21,
|
|
30
|
-
NODE_SCHEMA_MIGRATION_V22
|
|
31
|
+
NODE_SCHEMA_MIGRATION_V22,
|
|
32
|
+
NODE_SCHEMA_MIGRATION_V23
|
|
31
33
|
];
|
|
32
34
|
const LEGACY_NODE_SCHEMA_V12 = NODE_SCHEMA_VERSION_V13 - 1;
|
|
33
35
|
const OBSOLETE_NODE_SCHEMA_V16 = 16;
|
|
@@ -314,7 +316,7 @@ export class NodeDatabase {
|
|
|
314
316
|
pinOrder = 0;
|
|
315
317
|
else if (input.pinned === false)
|
|
316
318
|
pinOrder = null;
|
|
317
|
-
const model = input.
|
|
319
|
+
const model = input.modelId === undefined ? (current?.modelId ?? null) : input.modelId;
|
|
318
320
|
const effort = input.effort === undefined ? (current?.effort ?? null) : input.effort;
|
|
319
321
|
const access = input.access === undefined ? (current?.access ?? null) : input.access;
|
|
320
322
|
const parentSessionId = current === undefined || input.initializeParent === true
|
|
@@ -326,7 +328,7 @@ export class NodeDatabase {
|
|
|
326
328
|
sessionPrompt,
|
|
327
329
|
runnerTitle,
|
|
328
330
|
runnerTitlePending,
|
|
329
|
-
model,
|
|
331
|
+
modelId: model,
|
|
330
332
|
effort,
|
|
331
333
|
access
|
|
332
334
|
});
|
|
@@ -367,7 +369,7 @@ export class NodeDatabase {
|
|
|
367
369
|
runnerTitlePending,
|
|
368
370
|
pinnedAt: pinOrder === null ? null : 1,
|
|
369
371
|
pinOrder,
|
|
370
|
-
model,
|
|
372
|
+
modelId: model,
|
|
371
373
|
effort,
|
|
372
374
|
access
|
|
373
375
|
};
|
|
@@ -666,7 +668,7 @@ function sessionMetadata(row) {
|
|
|
666
668
|
sessionPrompt: value('sessionPrompt'),
|
|
667
669
|
runnerTitle: value('runnerTitle'),
|
|
668
670
|
runnerTitlePending: raw['runnerTitlePending'] === true,
|
|
669
|
-
|
|
671
|
+
modelId: value('modelId'),
|
|
670
672
|
effort: value('effort'),
|
|
671
673
|
access: value('access')
|
|
672
674
|
};
|
package/dist/marctl-guidance.js
CHANGED
|
@@ -10,7 +10,7 @@ const marctlSafety = 'Acceptance is not completion. If a request result is unkno
|
|
|
10
10
|
export const marctlManagementInstructions = [
|
|
11
11
|
'## MAR Workspace management',
|
|
12
12
|
'Use native agent_* tools for Agent discovery, creation, communication, observation, waiting and stopping. Do not switch to marctl when a native operation fails.',
|
|
13
|
-
'Use marctl agents config <id> --model <id> --effort <effort> to change future model settings; agents rename <id> --title <title> changes a title.',
|
|
13
|
+
'Use marctl agents config <id> --model-id <id> --effort <effort> to change future model settings; agents rename <id> --title <title> changes a title.',
|
|
14
14
|
'Use agents prompt get/set/clear for persistent instructions; set accepts -m or --message-file. Use agents schedule list/get/create/update for scheduled tasks; update requires --expected-revision.',
|
|
15
15
|
'marctl nodes lists authorized Nodes; marctl ssh executes remote commands and marctl scp transfers files. Use marctl help agents, nodes, ssh or scp for options.',
|
|
16
16
|
marctlSafety
|
|
@@ -19,7 +19,7 @@ export const marctlDescription = 'Use marctl agents to create, send to, wait for
|
|
|
19
19
|
export const marctlInstructions = [
|
|
20
20
|
'## MAR Workspace CLI',
|
|
21
21
|
'Run marctl from the shell. Commands use the authenticated Node and Workspace; marctl help agents, nodes, ssh or scp shows options.',
|
|
22
|
-
'Use agents start --title <title> -m <text> [--runner <runner>] [--model <id>] [--effort <effort>] [--goal <objective>]. An Agent must supply a nonblank title; a manual terminal may omit it. The default Runner is the current Session Runner; a terminal without a current Session must specify --runner or an explicit --parent. Start returns without waiting.',
|
|
22
|
+
'Use agents start --title <title> -m <text> [--runner <runner>] [--model-id <id>] [--effort <effort>] [--goal <objective>]. An Agent must supply a nonblank title; a manual terminal may omit it. The default Runner is the current Session Runner; a terminal without a current Session must specify --runner or an explicit --parent. Start returns without waiting.',
|
|
23
23
|
'Use agents send <id> -m <text> [--steer|--queue], agents wait [<id>] [--wait-for message|completion] [--timeout <duration>], agents stop <id> --execution <execution-id>, agents list [--scope children|descendants|workspace|profile], and agents view <id> [--history]. Wait defaults to direct-child completion for 30 seconds; --timeout 0ms takes a snapshot. Stop clears the target Session Goal.',
|
|
24
24
|
'Prefer agents send <id> --steer -m <text> for timely communication or course correction. Use --queue only for deliberately deferred input: it is consumed after current work and earlier queued inputs finish, and with an active Goal it is not consumed until that Goal exits. Acceptance is not a guarantee of immediate processing.',
|
|
25
25
|
'Start and send accept repeated --image <local-path> for PNG, JPEG, GIF or WebP; limits are five images, 5 MiB each and 10 MiB total. Send can update a Goal alone with --goal <objective> or --clear-goal; omitted Goal leaves it unchanged. Decide a direct child approval with send <id> --approval <approval-id> --decision approve|reject.',
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { NODE_SCHEMA_MIGRATION_V22 } from './v022.js';
|
|
2
|
+
const defaultPaths = ['codex', 'claude-code', 'opencode', 'mar-agent'].map((runner) => `$.runnerDefaults."${runner}"`);
|
|
3
|
+
const rename = (column, path) => `json_remove(CASE WHEN json_type(${column}, '${path}.modelId') IS NULL
|
|
4
|
+
THEN json_set(${column}, '${path}.modelId', json_extract(${column}, '${path}.model'))
|
|
5
|
+
ELSE ${column} END, '${path}.model')`;
|
|
6
|
+
export const NODE_SCHEMA_MIGRATION_V23 = {
|
|
7
|
+
version: 23,
|
|
8
|
+
sql: [
|
|
9
|
+
`UPDATE agent_sessions SET metadata = ${rename('metadata', '$')}
|
|
10
|
+
WHERE json_type(metadata, '$.model') IS NOT NULL;`,
|
|
11
|
+
...defaultPaths.map((path) => `UPDATE node_settings SET settings = ${rename('settings', path)}
|
|
12
|
+
WHERE json_type(settings, '${path}.model') IS NOT NULL;`)
|
|
13
|
+
].join('\n'),
|
|
14
|
+
preflight: (database) => NODE_SCHEMA_MIGRATION_V22.assert(database),
|
|
15
|
+
assert(database) {
|
|
16
|
+
NODE_SCHEMA_MIGRATION_V22.assert(database);
|
|
17
|
+
if (database
|
|
18
|
+
.prepare("SELECT 1 FROM agent_sessions WHERE json_type(metadata, '$.model') IS NOT NULL")
|
|
19
|
+
.get() !== undefined ||
|
|
20
|
+
defaultPaths.some((path) => database
|
|
21
|
+
.prepare(`SELECT 1 FROM node_settings WHERE json_type(settings, '${path}.model') IS NOT NULL`)
|
|
22
|
+
.get() !== undefined))
|
|
23
|
+
throw new Error('NODE_DATABASE_SCHEMA_INVALID:modelId');
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -30,7 +30,7 @@ export class AbstractRunner {
|
|
|
30
30
|
supportsConfiguration(configuration, workspace, environment) {
|
|
31
31
|
void [workspace, environment];
|
|
32
32
|
const profile = this.profileForValidation();
|
|
33
|
-
const model = profile.models.find((candidate) => candidate.id === configuration.
|
|
33
|
+
const model = profile.models.find((candidate) => candidate.id === configuration.modelId);
|
|
34
34
|
return (configuration.runner === this.name &&
|
|
35
35
|
model !== undefined &&
|
|
36
36
|
model.supportedEfforts.includes(configuration.effort) &&
|
|
@@ -49,7 +49,7 @@ export class ClaudeCodeRunner extends AbstractRunner {
|
|
|
49
49
|
defaultConfiguration() {
|
|
50
50
|
return {
|
|
51
51
|
runner: this.name,
|
|
52
|
-
|
|
52
|
+
modelId: claudeDefaultModel(this.environment),
|
|
53
53
|
effort: 'medium',
|
|
54
54
|
access: 'default'
|
|
55
55
|
};
|
|
@@ -400,7 +400,7 @@ export class CodexManagedRunController {
|
|
|
400
400
|
usage = new CodexUsageNormalizer(runId, (value) => this.host.emit(runId, 'run.usage', {
|
|
401
401
|
...value,
|
|
402
402
|
sessionId: this.runner.execution.runs.get(runId)?.threadId
|
|
403
|
-
}), active ? (this.host.session(active.sessionId)?.
|
|
403
|
+
}), active ? (this.host.session(active.sessionId)?.modelId ?? undefined) : undefined);
|
|
404
404
|
this.usage.set(runId, usage);
|
|
405
405
|
}
|
|
406
406
|
return usage;
|
|
@@ -202,7 +202,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
202
202
|
? emptyCodexConfiguration()
|
|
203
203
|
: {
|
|
204
204
|
runner: this.name,
|
|
205
|
-
|
|
205
|
+
modelId: selected.model,
|
|
206
206
|
effort: selected.defaultEffort,
|
|
207
207
|
access: 'on-request'
|
|
208
208
|
}
|
|
@@ -361,7 +361,8 @@ export class CodexRunner extends AbstractRunner {
|
|
|
361
361
|
return false;
|
|
362
362
|
const profile = this.profileCacheForEnvironment(environment)?.profile;
|
|
363
363
|
return (profile !== undefined &&
|
|
364
|
-
profile.models.some((model) => model.id === configuration.
|
|
364
|
+
profile.models.some((model) => model.id === configuration.modelId &&
|
|
365
|
+
model.supportedEfforts.includes(configuration.effort)) &&
|
|
365
366
|
profile.accessOptions.some((option) => option.id === configuration.access));
|
|
366
367
|
}
|
|
367
368
|
profileCacheForEnvironment(_environment) {
|
|
@@ -695,7 +696,11 @@ export class CodexRunner extends AbstractRunner {
|
|
|
695
696
|
const threadId = session.externalSessionId;
|
|
696
697
|
if (threadId === null)
|
|
697
698
|
throw new Error('SESSION_REWIND_UNAVAILABLE');
|
|
698
|
-
const configuration = {
|
|
699
|
+
const configuration = {
|
|
700
|
+
model: session.modelId,
|
|
701
|
+
effort: session.effort,
|
|
702
|
+
access: session.access
|
|
703
|
+
};
|
|
699
704
|
return this.withTemporaryClient(async (client) => {
|
|
700
705
|
const result = boundary === null
|
|
701
706
|
? await client.startThread(session.cwd, configuration)
|
|
@@ -719,7 +724,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
719
724
|
const threadId = session.externalSessionId;
|
|
720
725
|
return this.withTemporaryClient(async (client) => {
|
|
721
726
|
const resumed = await client.resumeThread(threadId, session.cwd, {
|
|
722
|
-
model: session.
|
|
727
|
+
model: session.modelId,
|
|
723
728
|
effort: session.effort,
|
|
724
729
|
access: session.access
|
|
725
730
|
});
|
|
@@ -753,7 +758,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
753
758
|
}
|
|
754
759
|
}
|
|
755
760
|
function emptyCodexConfiguration() {
|
|
756
|
-
return { runner: 'codex',
|
|
761
|
+
return { runner: 'codex', modelId: '', effort: '', access: 'on-request' };
|
|
757
762
|
}
|
|
758
763
|
function codexModelPage(value) {
|
|
759
764
|
if (!isPlainRecord(value) || !Array.isArray(value.data))
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { agentNetworkWaitModelOutput } from './agent-network-wait-output.js';
|
|
2
2
|
import { agentOperationEvent } from './agent-operation-presentation.js';
|
|
3
3
|
const text = { type: 'string', minLength: 1 };
|
|
4
|
+
const runner = {
|
|
5
|
+
...text,
|
|
6
|
+
description: 'Runner type (for example mar-agent), not a model name; defaults to the current Runner.'
|
|
7
|
+
};
|
|
4
8
|
const attachmentIds = {
|
|
5
9
|
type: 'array',
|
|
6
10
|
items: text,
|
|
@@ -42,8 +46,11 @@ const tools = [
|
|
|
42
46
|
content: { type: 'string' },
|
|
43
47
|
attachments,
|
|
44
48
|
attachmentIds,
|
|
45
|
-
runner
|
|
46
|
-
|
|
49
|
+
runner,
|
|
50
|
+
modelId: {
|
|
51
|
+
...text,
|
|
52
|
+
description: 'Model ID from the target Runner Profile models[].id, not a display name. For MAR Agent this is an internal configuration ID, not an upstream model ID; omit to use the Runner default.'
|
|
53
|
+
},
|
|
47
54
|
effort: text,
|
|
48
55
|
title: text,
|
|
49
56
|
goal,
|
|
@@ -53,7 +60,7 @@ const tools = [
|
|
|
53
60
|
},
|
|
54
61
|
{
|
|
55
62
|
action: 'send',
|
|
56
|
-
description: 'Send input, decide an approval or update a Goal; goal:null leaves it unchanged, goal:{clear:true} clears it.
|
|
63
|
+
description: 'Send input, decide an approval or update a Goal; goal:null leaves it unchanged, goal:{clear:true} clears it. Messages default to REPLACE_CURRENT (steer); use QUEUE only when deferred delivery is explicitly requested. QUEUE is consumed only after current work and earlier queued inputs finish; an active Goal must exit first.',
|
|
57
64
|
properties: {
|
|
58
65
|
agentId: text,
|
|
59
66
|
content: { type: 'string' },
|
|
@@ -66,14 +73,18 @@ const tools = [
|
|
|
66
73
|
required: ['approvalId', 'decision'],
|
|
67
74
|
properties: { approvalId: text, decision: { type: 'string', enum: ['approve', 'reject'] } }
|
|
68
75
|
},
|
|
69
|
-
deliveryIntent: {
|
|
76
|
+
deliveryIntent: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
enum: ['SEND', 'QUEUE', 'REPLACE_CURRENT'],
|
|
79
|
+
description: 'Message delivery defaults to REPLACE_CURRENT (steer); explicit SEND remains supported.'
|
|
80
|
+
},
|
|
70
81
|
clientMessageId: text
|
|
71
82
|
},
|
|
72
83
|
required: ['agentId']
|
|
73
84
|
},
|
|
74
85
|
{
|
|
75
86
|
action: 'wait',
|
|
76
|
-
description: 'Wait for completion or messages; defaults to direct children, completion and 30 seconds. Use waitMs 0 for a snapshot.
|
|
87
|
+
description: 'Wait for completion or messages; defaults to direct children, completion and 30 seconds. Use waitMs 0 for a snapshot.',
|
|
77
88
|
properties: {
|
|
78
89
|
agentId: text,
|
|
79
90
|
agentIds: { type: 'array', items: text, minItems: 1, maxItems: 8 },
|
|
@@ -90,13 +101,18 @@ const tools = [
|
|
|
90
101
|
},
|
|
91
102
|
{
|
|
92
103
|
action: 'list',
|
|
93
|
-
description: 'List Sessions, descendants or a Runner profile.',
|
|
104
|
+
description: 'List direct children by default, discover Workspace Sessions, inspect descendants or query a Runner profile.',
|
|
94
105
|
properties: {
|
|
95
106
|
scope: { type: 'string', enum: ['workspace', 'children', 'descendants', 'profile'] },
|
|
96
|
-
agentId:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
agentId: {
|
|
108
|
+
...text,
|
|
109
|
+
description: 'Root for children or descendants; defaults to the current Session.'
|
|
110
|
+
},
|
|
111
|
+
runner: {
|
|
112
|
+
...runner,
|
|
113
|
+
description: 'Runner for profile scope; defaults to the current Runner, not a model name.'
|
|
114
|
+
},
|
|
115
|
+
query: { type: 'string', description: 'Search Sessions; not a model selector.' },
|
|
100
116
|
limit: { type: 'integer', minimum: 1, maximum: 100 },
|
|
101
117
|
cursor: text
|
|
102
118
|
}
|
|
@@ -117,12 +133,14 @@ const delegationPrompt = [
|
|
|
117
133
|
'# Delegation and coordination',
|
|
118
134
|
'Delegate bounded, self-contained tasks when independent progress or context savings materially helps. Keep simple or tightly coupled work local.',
|
|
119
135
|
'Give each child an objective, necessary context, expected output, and a read-only boundary or non-overlapping write scope.',
|
|
136
|
+
'Follow up through the direct delegate by default; inspect deeper Sessions only when a concrete issue needs independent verification.',
|
|
120
137
|
'While children run, continue meaningful non-overlapping work. Do not redo delegated work. Wait only when the next step needs the result.',
|
|
121
138
|
'Verify consequential findings and changes. Join or stop task-required child work before final delivery, and retain responsibility for the final result.'
|
|
122
139
|
].join('\n\n');
|
|
123
140
|
const collaborationPrompt = [
|
|
124
141
|
'# Workspace collaboration',
|
|
125
|
-
"Use native agent_* tools to collaborate within the Agent Network when another Session's context or ongoing work materially helps. Keep requests focused and avoid unnecessary cross-Session coordination. Peer Sessions retain their own work and lifecycle; do not treat them as task-owned children or stop them merely for task cleanup.
|
|
142
|
+
"Use native agent_* tools to collaborate within the Agent Network when another Session's context or ongoing work materially helps. Keep requests focused and avoid unnecessary cross-Session coordination. Peer Sessions retain their own work and lifecycle; do not treat them as task-owned children or stop them merely for task cleanup. Explicitly wait when you need progress or completion.",
|
|
143
|
+
"Reuse known Session IDs rather than rediscovering them. Send updates for meaningful progress, blockers, or changes that affect the recipient's work; combine related updates and avoid acknowledgment-only exchanges."
|
|
126
144
|
].join('\n\n');
|
|
127
145
|
const managementPrompt = [
|
|
128
146
|
'# Workspace management',
|
|
@@ -168,7 +186,7 @@ export function agentNetworkExtension(invoke, currentExecution) {
|
|
|
168
186
|
'title',
|
|
169
187
|
'executionId',
|
|
170
188
|
'runner',
|
|
171
|
-
'
|
|
189
|
+
'modelId',
|
|
172
190
|
'effort',
|
|
173
191
|
'waitFor',
|
|
174
192
|
'waitMs',
|
|
@@ -184,12 +202,20 @@ export function agentNetworkExtension(invoke, currentExecution) {
|
|
|
184
202
|
const executionId = currentExecution();
|
|
185
203
|
if (!executionId)
|
|
186
204
|
throw new Error('AGENT_ACTOR_NOT_ACTIVE');
|
|
187
|
-
const
|
|
205
|
+
const args = input;
|
|
206
|
+
const operationInput = tool.action === 'send' &&
|
|
207
|
+
args.deliveryIntent === undefined &&
|
|
208
|
+
((typeof args.content === 'string' && !!args.content.trim()) ||
|
|
209
|
+
(Array.isArray(args.attachments) && args.attachments.length > 0) ||
|
|
210
|
+
(Array.isArray(args.attachmentIds) && args.attachmentIds.length > 0))
|
|
211
|
+
? { ...args, deliveryIntent: 'REPLACE_CURRENT' }
|
|
212
|
+
: args;
|
|
213
|
+
const result = await invoke(executionId, tool.action, operationInput, call);
|
|
188
214
|
const modelResult = tool.action === 'wait' ? agentNetworkWaitModelOutput(result) : result;
|
|
189
215
|
return {
|
|
190
216
|
data: result,
|
|
191
217
|
content: JSON.stringify(modelResult),
|
|
192
|
-
...agentOperationEvent(tool.action,
|
|
218
|
+
...agentOperationEvent(tool.action, operationInput, result)
|
|
193
219
|
};
|
|
194
220
|
}
|
|
195
221
|
});
|
|
@@ -704,7 +704,7 @@ export class MarAgentConversationNormalizer {
|
|
|
704
704
|
return [
|
|
705
705
|
genericToolItem(state?.itemId ?? itemId('tool', event.callId), event.toolName, event.code === 'MAR_AGENT_CANCELLED' || event.code === 'MAR_AGENT_EXECUTION_CANCELLED'
|
|
706
706
|
? 'CANCELLED'
|
|
707
|
-
: 'FAILED', state?.input, event.message ?? event.code, event.code, event.truncated === true, state?.presentationData, state?.subagentTitle, state?.subagentDetails, state?.sleepStartedAt, event.outputTruncation, state?.inputTruncated)
|
|
707
|
+
: 'FAILED', state?.input, event.message ?? event.code, event.code, event.truncated === true, event.data === undefined ? state?.presentationData : event.data, state?.subagentTitle, state?.subagentDetails, state?.sleepStartedAt, event.outputTruncation, state?.inputTruncated)
|
|
708
708
|
];
|
|
709
709
|
}
|
|
710
710
|
#codeModeToolResults(event) {
|
|
@@ -15,7 +15,7 @@ export function projectMarAgentHistory(session, entries, options = {}) {
|
|
|
15
15
|
runId: null,
|
|
16
16
|
userItemId: null,
|
|
17
17
|
status: 'INTERRUPTED',
|
|
18
|
-
model: session.
|
|
18
|
+
model: session.modelId,
|
|
19
19
|
effort: session.effort,
|
|
20
20
|
access: session.access,
|
|
21
21
|
rewind: { before: previous?.id ?? null, after: turnId },
|
|
@@ -118,7 +118,7 @@ export class MarAgentManagedRunController {
|
|
|
118
118
|
const execution = context.executionContext;
|
|
119
119
|
const resolved = resolveMarAgentExecutionInput(execution?.runner === 'mar-agent'
|
|
120
120
|
? execution.runnerInput
|
|
121
|
-
: undefined, session.
|
|
121
|
+
: undefined, session.modelId ?? '');
|
|
122
122
|
if (resolved === undefined)
|
|
123
123
|
throw new Error('RUNNER_COMMAND_UNAVAILABLE');
|
|
124
124
|
const runId = randomUUID();
|
|
@@ -409,7 +409,7 @@ export class MarAgentManagedRunController {
|
|
|
409
409
|
if (input.mode !== 'compact')
|
|
410
410
|
this.#steerTargets.set(input.runId, async (request) => {
|
|
411
411
|
if (controller.signal.aborted ||
|
|
412
|
-
(request.
|
|
412
|
+
(request.modelId && request.modelId !== input.model.id) ||
|
|
413
413
|
(request.effort &&
|
|
414
414
|
request.effort !==
|
|
415
415
|
(reasoningEffort ??
|
|
@@ -232,7 +232,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
232
232
|
if (configuration.runner !== this.name)
|
|
233
233
|
return false;
|
|
234
234
|
const profile = this.profileForValidation();
|
|
235
|
-
const model = profile.models.find((candidate) => candidate.id === configuration.
|
|
235
|
+
const model = profile.models.find((candidate) => candidate.id === configuration.modelId);
|
|
236
236
|
return (model !== undefined &&
|
|
237
237
|
(model.supportedEfforts.length === 0
|
|
238
238
|
? configuration.effort === model.defaultEffort
|
|
@@ -246,7 +246,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
246
246
|
const model = profile.models[0];
|
|
247
247
|
return {
|
|
248
248
|
runner: 'mar-agent',
|
|
249
|
-
|
|
249
|
+
modelId: model?.id ?? '',
|
|
250
250
|
effort: model?.defaultEffort ?? '',
|
|
251
251
|
access: 'auto'
|
|
252
252
|
};
|
|
@@ -357,7 +357,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
357
357
|
}),
|
|
358
358
|
...(executionConfiguration?.modelId === undefined
|
|
359
359
|
? {}
|
|
360
|
-
: {
|
|
360
|
+
: { modelId: executionConfiguration.modelId }),
|
|
361
361
|
...(executionConfiguration?.reasoningEffort === undefined
|
|
362
362
|
? {}
|
|
363
363
|
: { effort: executionConfiguration.reasoningEffort })
|
|
@@ -577,9 +577,9 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
577
577
|
return [resolve(session.cwd), ...(generatedImages === undefined ? [] : [generatedImages])];
|
|
578
578
|
}
|
|
579
579
|
async readContextUsage(session) {
|
|
580
|
-
if (session.externalSessionId === null || session.
|
|
580
|
+
if (session.externalSessionId === null || session.modelId === null)
|
|
581
581
|
return undefined;
|
|
582
|
-
const model = this.#models.find((candidate) => candidate.id === session.
|
|
582
|
+
const model = this.#models.find((candidate) => candidate.id === session.modelId);
|
|
583
583
|
if (model === undefined)
|
|
584
584
|
return undefined;
|
|
585
585
|
let catalog;
|
|
@@ -51,7 +51,7 @@ export class OpenCodeManagedRunController {
|
|
|
51
51
|
}
|
|
52
52
|
compact(session, environment, initiatedByUserId) {
|
|
53
53
|
if (session.externalSessionId === null ||
|
|
54
|
-
session.
|
|
54
|
+
session.modelId === null ||
|
|
55
55
|
session.nativeControl !== 'MAR_MANAGED' ||
|
|
56
56
|
this.host.hasActiveSessionLease(session.id))
|
|
57
57
|
throw new Error('RUNNER_COMMAND_UNAVAILABLE');
|
|
@@ -78,7 +78,7 @@ export class OpenCodeManagedRunController {
|
|
|
78
78
|
});
|
|
79
79
|
this.host.emit(runId, 'run.started', { command: 'compact' }, 'STARTING');
|
|
80
80
|
this.host.emitItem(runId, openCodeCompactionItem('opencode-compact', runId, false));
|
|
81
|
-
void this.runCompact(runId, session.
|
|
81
|
+
void this.runCompact(runId, session.modelId);
|
|
82
82
|
return {
|
|
83
83
|
command: 'compact',
|
|
84
84
|
run: this.host.run(runId),
|
|
@@ -147,7 +147,7 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
147
147
|
if (configuration.runner !== this.name)
|
|
148
148
|
return false;
|
|
149
149
|
const models = this.cachedModels(environment);
|
|
150
|
-
const model = models.find((candidate) => candidate.id === configuration.
|
|
150
|
+
const model = models.find((candidate) => candidate.id === configuration.modelId);
|
|
151
151
|
return (model !== undefined &&
|
|
152
152
|
(model.supportedEfforts.length === 0 ||
|
|
153
153
|
model.supportedEfforts.includes(configuration.effort)) &&
|
|
@@ -157,7 +157,7 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
157
157
|
const model = this.cachedModels(environment)[0];
|
|
158
158
|
return {
|
|
159
159
|
runner: this.name,
|
|
160
|
-
|
|
160
|
+
modelId: model?.id ?? '',
|
|
161
161
|
effort: model?.supportedEfforts[0] ?? '',
|
|
162
162
|
access: 'default'
|
|
163
163
|
};
|
|
@@ -377,11 +377,11 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
377
377
|
return value.id;
|
|
378
378
|
}
|
|
379
379
|
async executeCompact(session, environment) {
|
|
380
|
-
if (session.externalSessionId === null || session.
|
|
380
|
+
if (session.externalSessionId === null || session.modelId === null)
|
|
381
381
|
throw new Error('RUNNER_COMMAND_UNAVAILABLE');
|
|
382
382
|
const client = this.managedClient(environment);
|
|
383
383
|
try {
|
|
384
|
-
return await client.summarize(session.cwd, session.externalSessionId, session.
|
|
384
|
+
return await client.summarize(session.cwd, session.externalSessionId, session.modelId);
|
|
385
385
|
}
|
|
386
386
|
finally {
|
|
387
387
|
this.releaseManagedClient(client);
|
|
@@ -402,7 +402,7 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
402
402
|
const tokens = object(latest?.tokens);
|
|
403
403
|
const cache = object(tokens?.cache);
|
|
404
404
|
const usedTokens = positiveNumber(tokens?.input) + positiveNumber(cache?.read);
|
|
405
|
-
const modelId = provider !== undefined && model !== undefined ? `${provider}/${model}` : session.
|
|
405
|
+
const modelId = provider !== undefined && model !== undefined ? `${provider}/${model}` : session.modelId;
|
|
406
406
|
if (modelId === null || usedTokens <= 0)
|
|
407
407
|
return undefined;
|
|
408
408
|
const maxTokens = Date.now() >= this.contextLimitsExpiresAt ? undefined : this.contextLimits.get(modelId);
|
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,
|
|
@@ -182,7 +182,7 @@ export function createNodeAgentNetwork(options) {
|
|
|
182
182
|
__suppressNotifications: true,
|
|
183
183
|
...(!terminal && parent ? { __marctlOrigin: origin(parent) } : {}),
|
|
184
184
|
...(input.title === undefined ? {} : { title: input.title }),
|
|
185
|
-
...(model ? { model } : {}),
|
|
185
|
+
...(model ? { modelId: model } : {}),
|
|
186
186
|
...(input.effort === undefined ? {} : { effort: input.effort }),
|
|
187
187
|
...(input.attachments === undefined ? {} : { attachments: input.attachments }),
|
|
188
188
|
...(input.attachmentIds === undefined ? {} : { attachmentIds: input.attachmentIds })
|
|
@@ -229,7 +229,7 @@ export function createNodeAgentNetwork(options) {
|
|
|
229
229
|
const current = await options.resume.resolveForMessage(session.id);
|
|
230
230
|
const executionContext = options.runners
|
|
231
231
|
.require(current.runner)
|
|
232
|
-
.prepareExecutionContext(executionInput(actor, current.runner, current.
|
|
232
|
+
.prepareExecutionContext(executionInput(actor, current.runner, current.modelId, current.parentSessionId != null));
|
|
233
233
|
signal?.throwIfAborted();
|
|
234
234
|
const result = await options.withGrant(actor, () => options.coordinator.start({
|
|
235
235
|
session: current,
|
|
@@ -262,7 +262,7 @@ export function createNodeAgentNetwork(options) {
|
|
|
262
262
|
const parent = terminalCalls.has(actor) ? undefined : await actorSession(actor);
|
|
263
263
|
signal?.throwIfAborted();
|
|
264
264
|
const result = await options.withGrant(actor, () => options.messages.operations()['session.message']({
|
|
265
|
-
...executionInput(actor, session.runner, session.
|
|
265
|
+
...executionInput(actor, session.runner, session.modelId, session.parentSessionId != null),
|
|
266
266
|
...input,
|
|
267
267
|
...(signal ? { [SESSION_ADMISSION_SIGNAL]: signal } : {}),
|
|
268
268
|
sessionId: session.id,
|
|
@@ -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,
|
|
@@ -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.46",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"sharp": "0.35.5",
|
|
39
39
|
"ws": "^8.21.3",
|
|
40
40
|
"zod": "4.4.3",
|
|
41
|
-
"@myagentroam/agent": "0.11.
|
|
42
|
-
"@myagentroam/
|
|
43
|
-
"@myagentroam/
|
|
44
|
-
"@myagentroam/
|
|
41
|
+
"@myagentroam/agent": "0.11.46",
|
|
42
|
+
"@myagentroam/marctl": "0.11.7",
|
|
43
|
+
"@myagentroam/protocol": "0.11.46",
|
|
44
|
+
"@myagentroam/code-navigation": "0.11.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/ws": "^8.18.1"
|