@myagentroam/agent 0.10.60 → 0.10.61
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/agent-core/sdk/agent.js +1 -1
- package/dist/agent-core/sdk/extension-agents.js +5 -1
- package/dist/agent-core/sdk/session.d.ts +18 -12
- package/dist/agent-core/sdk/session.js +34 -39
- package/dist/agent-core/sdk/subagent-backend.js +20 -17
- package/dist/agent-core/session/store/directory-index.d.ts +2 -2
- package/dist/agent-core/session/store/directory-index.js +17 -11
- package/dist/agent-core/session/store/execution-state.d.ts +1 -0
- package/dist/agent-core/session/store/execution-state.js +9 -0
- package/dist/agent-core/session/store/jsonl-store.d.ts +2 -2
- package/dist/agent-core/session/store/jsonl-store.js +217 -218
- package/dist/agent-core/session/store/types.d.ts +1 -0
- package/dist/agent-core/subagent/scheduler.js +1 -1
- package/dist/agent-core/subagent/session-controller.d.ts +4 -1
- package/dist/agent-core/subagent/session-controller.js +99 -15
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ export class SubagentScheduler {
|
|
|
12
12
|
#disposed = false;
|
|
13
13
|
#createdInExecution = 0;
|
|
14
14
|
#runner;
|
|
15
|
-
constructor(runner, limits = { maxTasks:
|
|
15
|
+
constructor(runner, limits = { maxTasks: 16, maxConcurrent: 16 }) {
|
|
16
16
|
this.limits = limits;
|
|
17
17
|
this.#runner = runner;
|
|
18
18
|
}
|
|
@@ -78,7 +78,7 @@ export interface SubagentSessionControllerBackend {
|
|
|
78
78
|
}>;
|
|
79
79
|
deleteSession(sessionId: string): Promise<void>;
|
|
80
80
|
/** Returns children in durable creation order. */
|
|
81
|
-
listChildren(parentSessionId: string): Promise<readonly PersistedSubagentSession[]>;
|
|
81
|
+
listChildren(parentSessionId: string): Promise<readonly Omit<PersistedSubagentSession, 'records'>[]>;
|
|
82
82
|
loadChild(parentSessionId: string, sessionId: string): Promise<PersistedSubagentSession | undefined>;
|
|
83
83
|
}
|
|
84
84
|
export declare class SubagentSessionController implements SubagentController {
|
|
@@ -109,6 +109,9 @@ export declare class SubagentSessionController implements SubagentController {
|
|
|
109
109
|
withOperation<T>(operation: SubagentParentOperation, run: () => T): T;
|
|
110
110
|
inspect(agentId: string): Promise<SubagentTaskSnapshot>;
|
|
111
111
|
inspectAll(): readonly SubagentTaskSnapshot[];
|
|
112
|
+
/** Full listing is explicit; ordinary opens and executions never require it. */
|
|
113
|
+
prepareAll(signal?: AbortSignal): Promise<void>;
|
|
114
|
+
prepareSessionResources(signal?: AbortSignal): Promise<void>;
|
|
112
115
|
collectModelUpdates(): readonly {
|
|
113
116
|
snapshot: SubagentTaskSnapshot;
|
|
114
117
|
truncated: boolean;
|
|
@@ -4,12 +4,16 @@ import { randomUUID } from 'node:crypto';
|
|
|
4
4
|
import { MarAgentError, describeMarAgentError } from '../error.js';
|
|
5
5
|
import { TOOL_EXECUTION_LIMITS } from '../tools/execution-limits.js';
|
|
6
6
|
import { parseAgentWaitTargets, waitForAgentCondition } from './wait.js';
|
|
7
|
+
const RECENT_TERMINAL_RESOURCE_LIMIT = 16;
|
|
7
8
|
export class SubagentSessionController {
|
|
8
9
|
parentSessionId;
|
|
9
10
|
rootSessionId;
|
|
10
11
|
backend;
|
|
11
12
|
limits;
|
|
12
13
|
#agents = new Map();
|
|
14
|
+
// Discovery order is independent of the order in which history is requested.
|
|
15
|
+
#discovered = new Map();
|
|
16
|
+
#loading = new Map();
|
|
13
17
|
#queue = [];
|
|
14
18
|
#completionWaiters = new Set();
|
|
15
19
|
#activityWaiters = new Set();
|
|
@@ -35,7 +39,7 @@ export class SubagentSessionController {
|
|
|
35
39
|
#retainedExecution = new AsyncLocalStorage();
|
|
36
40
|
#execution;
|
|
37
41
|
#pendingDeliveries = new Set();
|
|
38
|
-
constructor(parentSessionId, rootSessionId, backend, limits = { maxTasks:
|
|
42
|
+
constructor(parentSessionId, rootSessionId, backend, limits = { maxTasks: 16, maxConcurrent: 16 }) {
|
|
39
43
|
this.parentSessionId = parentSessionId;
|
|
40
44
|
this.rootSessionId = rootSessionId;
|
|
41
45
|
this.backend = backend;
|
|
@@ -43,11 +47,12 @@ export class SubagentSessionController {
|
|
|
43
47
|
}
|
|
44
48
|
static async open(input) {
|
|
45
49
|
const controller = new SubagentSessionController(input.parentSessionId, input.rootSessionId, input.backend, input.limits);
|
|
46
|
-
const sessions =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
const sessions = await input.backend.listChildren(input.parentSessionId);
|
|
51
|
+
// Map insertion order is creation order; values independently rank activity.
|
|
52
|
+
for (const session of sessions)
|
|
53
|
+
controller.#discovered.set(session.id, 0);
|
|
54
|
+
for (const session of [...sessions].sort((a, b) => a.lastActivityAt.localeCompare(b.lastActivityAt))) {
|
|
55
|
+
controller.#discovered.set(session.id, ++controller.#resourceRevision);
|
|
51
56
|
}
|
|
52
57
|
return controller;
|
|
53
58
|
}
|
|
@@ -98,7 +103,49 @@ export class SubagentSessionController {
|
|
|
98
103
|
inspectAll() {
|
|
99
104
|
if (this.#disposed)
|
|
100
105
|
throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
|
|
101
|
-
return
|
|
106
|
+
return this.#orderedStates().map((state) => this.#snapshot(state, { full: true }));
|
|
107
|
+
}
|
|
108
|
+
/** Full listing is explicit; ordinary opens and executions never require it. */
|
|
109
|
+
async prepareAll(signal) {
|
|
110
|
+
for (const id of this.#discovered.keys()) {
|
|
111
|
+
signal?.throwIfAborted();
|
|
112
|
+
await this.#restoreDiscovered(id);
|
|
113
|
+
}
|
|
114
|
+
signal?.throwIfAborted();
|
|
115
|
+
}
|
|
116
|
+
async prepareSessionResources(signal) {
|
|
117
|
+
const candidates = [...this.#discovered]
|
|
118
|
+
.filter(([id]) => {
|
|
119
|
+
const state = this.#agents.get(id);
|
|
120
|
+
return !state || isTerminalState(state);
|
|
121
|
+
})
|
|
122
|
+
.sort(([a, ar], [b, br]) => (this.#agents.get(b)?.resourceRevision ?? br) -
|
|
123
|
+
(this.#agents.get(a)?.resourceRevision ?? ar));
|
|
124
|
+
let prepared = 0;
|
|
125
|
+
for (const [id] of candidates) {
|
|
126
|
+
signal?.throwIfAborted();
|
|
127
|
+
const state = await this.#restoreDiscovered(id);
|
|
128
|
+
if (state && isTerminalState(state) && ++prepared === RECENT_TERMINAL_RESOURCE_LIMIT)
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
signal?.throwIfAborted();
|
|
132
|
+
}
|
|
133
|
+
async #restoreDiscovered(agentId) {
|
|
134
|
+
try {
|
|
135
|
+
return await this.#find(agentId);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
if (!(error instanceof MarAgentError) || error.code !== 'MAR_AGENT_SUBAGENT_NOT_FOUND')
|
|
139
|
+
throw error;
|
|
140
|
+
this.#discovered.delete(agentId);
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
#orderedStates() {
|
|
145
|
+
return [...this.#discovered.keys()].flatMap((id) => {
|
|
146
|
+
const state = this.#agents.get(id);
|
|
147
|
+
return state ? [state] : [];
|
|
148
|
+
});
|
|
102
149
|
}
|
|
103
150
|
collectModelUpdates() {
|
|
104
151
|
return [...this.#agents.values()]
|
|
@@ -223,6 +270,7 @@ export class SubagentSessionController {
|
|
|
223
270
|
if (!native)
|
|
224
271
|
(this.#retainedExecution.getStore()?.budget ?? this.#creationBudget).count++;
|
|
225
272
|
this.#agents.set(state.agentId, state);
|
|
273
|
+
this.#discovered.set(state.agentId, state.resourceRevision);
|
|
226
274
|
const knownModel = execution.models.get(input.modelId ?? execution.modelId);
|
|
227
275
|
if (knownModel) {
|
|
228
276
|
state.modelId = knownModel.id;
|
|
@@ -335,7 +383,16 @@ export class SubagentSessionController {
|
|
|
335
383
|
if (options.untilSettled)
|
|
336
384
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'An unbounded wait requires an agent id.');
|
|
337
385
|
validateOutputOptions(options, this.#retainedExecution.getStore()?.native === true);
|
|
338
|
-
const
|
|
386
|
+
const hasLive = [...this.#agents.values()].some((state) => !isTerminalState(state));
|
|
387
|
+
if (!hasLive) {
|
|
388
|
+
for (const id of [...this.#discovered.keys()].reverse()) {
|
|
389
|
+
options.signal?.throwIfAborted();
|
|
390
|
+
if (await this.#restoreDiscovered(id))
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
options.signal?.throwIfAborted();
|
|
395
|
+
const states = this.#orderedStates();
|
|
339
396
|
if (states.length === 0)
|
|
340
397
|
throw new MarAgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Subagent was not found.');
|
|
341
398
|
const live = states.filter((state) => state.status === 'queued' || state.status === 'running');
|
|
@@ -625,7 +682,7 @@ export class SubagentSessionController {
|
|
|
625
682
|
const terminal = states
|
|
626
683
|
.filter((state) => state.status !== 'queued' && state.status !== 'running')
|
|
627
684
|
.sort((left, right) => right.resourceRevision - left.resourceRevision)
|
|
628
|
-
.slice(0,
|
|
685
|
+
.slice(0, RECENT_TERMINAL_RESOURCE_LIMIT);
|
|
629
686
|
return [...active, ...terminal].map((state) => ({
|
|
630
687
|
agentId: state.agentId,
|
|
631
688
|
status: state.status,
|
|
@@ -946,6 +1003,7 @@ export class SubagentSessionController {
|
|
|
946
1003
|
}
|
|
947
1004
|
async #deleteUnstartedSession(agentId, error) {
|
|
948
1005
|
this.#agents.delete(agentId);
|
|
1006
|
+
this.#discovered.delete(agentId);
|
|
949
1007
|
try {
|
|
950
1008
|
await this.backend.deleteSession(agentId);
|
|
951
1009
|
}
|
|
@@ -957,15 +1015,41 @@ export class SubagentSessionController {
|
|
|
957
1015
|
throw error;
|
|
958
1016
|
}
|
|
959
1017
|
async #find(agentId) {
|
|
1018
|
+
if (this.#disposed)
|
|
1019
|
+
throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
|
|
960
1020
|
const known = this.#agents.get(agentId);
|
|
961
1021
|
if (known)
|
|
962
1022
|
return known;
|
|
963
|
-
const
|
|
964
|
-
if (
|
|
965
|
-
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
1023
|
+
const pending = this.#loading.get(agentId);
|
|
1024
|
+
if (pending)
|
|
1025
|
+
return pending;
|
|
1026
|
+
const load = (async () => {
|
|
1027
|
+
const persisted = await this.backend.loadChild(this.parentSessionId, agentId);
|
|
1028
|
+
if (this.#disposed)
|
|
1029
|
+
throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
|
|
1030
|
+
if (!persisted)
|
|
1031
|
+
throw new MarAgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Subagent was not found.');
|
|
1032
|
+
const existing = this.#agents.get(agentId);
|
|
1033
|
+
if (existing)
|
|
1034
|
+
return existing;
|
|
1035
|
+
const restored = restoreAgentState(persisted);
|
|
1036
|
+
let revision = this.#discovered.get(agentId);
|
|
1037
|
+
if (revision === undefined) {
|
|
1038
|
+
revision = ++this.#resourceRevision;
|
|
1039
|
+
this.#discovered.set(agentId, revision);
|
|
1040
|
+
}
|
|
1041
|
+
restored.resourceRevision = revision;
|
|
1042
|
+
this.#agents.set(agentId, restored);
|
|
1043
|
+
return restored;
|
|
1044
|
+
})();
|
|
1045
|
+
this.#loading.set(agentId, load);
|
|
1046
|
+
try {
|
|
1047
|
+
return await load;
|
|
1048
|
+
}
|
|
1049
|
+
finally {
|
|
1050
|
+
if (this.#loading.get(agentId) === load)
|
|
1051
|
+
this.#loading.delete(agentId);
|
|
1052
|
+
}
|
|
969
1053
|
}
|
|
970
1054
|
#requireExecution() {
|
|
971
1055
|
if (this.#disposed)
|