@myagentroam/agent 0.9.159 → 0.9.160
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/extensions/agents.d.ts +69 -0
- package/dist/agent-core/extensions/agents.js +160 -0
- package/dist/agent-core/extensions/contracts.d.ts +260 -0
- package/dist/agent-core/extensions/contracts.js +1 -0
- package/dist/agent-core/extensions/data.d.ts +84 -0
- package/dist/agent-core/extensions/data.js +83 -0
- package/dist/agent-core/extensions/environment.d.ts +27 -0
- package/dist/agent-core/extensions/environment.js +52 -0
- package/dist/agent-core/extensions/files.d.ts +64 -0
- package/dist/agent-core/extensions/files.js +26 -0
- package/dist/agent-core/extensions/hooks.d.ts +32 -0
- package/dist/agent-core/extensions/hooks.js +135 -0
- package/dist/agent-core/extensions/manager.d.ts +39 -0
- package/dist/agent-core/extensions/manager.js +458 -0
- package/dist/agent-core/extensions/mcp.d.ts +99 -0
- package/dist/agent-core/extensions/mcp.js +433 -0
- package/dist/agent-core/extensions/model-request.d.ts +60 -0
- package/dist/agent-core/extensions/model-request.js +167 -0
- package/dist/agent-core/extensions/model-stream.d.ts +9 -0
- package/dist/agent-core/extensions/model-stream.js +101 -0
- package/dist/agent-core/extensions/models.d.ts +43 -0
- package/dist/agent-core/extensions/models.js +1 -0
- package/dist/agent-core/extensions/network.d.ts +32 -0
- package/dist/agent-core/extensions/network.js +97 -0
- package/dist/agent-core/extensions/operation.d.ts +29 -0
- package/dist/agent-core/extensions/operation.js +98 -0
- package/dist/agent-core/extensions/processes.d.ts +52 -0
- package/dist/agent-core/extensions/processes.js +283 -0
- package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
- package/dist/agent-core/extensions/prompt-composition.js +106 -0
- package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
- package/dist/agent-core/extensions/prompt-input.js +46 -0
- package/dist/agent-core/extensions/resources.d.ts +27 -0
- package/dist/agent-core/extensions/resources.js +221 -0
- package/dist/agent-core/extensions/services.d.ts +31 -0
- package/dist/agent-core/extensions/services.js +61 -0
- package/dist/agent-core/extensions/session-input.d.ts +151 -0
- package/dist/agent-core/extensions/session-input.js +351 -0
- package/dist/agent-core/extensions/session-messages.d.ts +41 -0
- package/dist/agent-core/extensions/session-messages.js +76 -0
- package/dist/agent-core/extensions/session.d.ts +68 -0
- package/dist/agent-core/extensions/session.js +593 -0
- package/dist/agent-core/extensions/settings.d.ts +59 -0
- package/dist/agent-core/extensions/settings.js +115 -0
- package/dist/agent-core/extensions/store.d.ts +10 -0
- package/dist/agent-core/extensions/store.js +131 -0
- package/dist/agent-core/extensions/tools.d.ts +61 -0
- package/dist/agent-core/extensions/tools.js +266 -0
- package/dist/agent-core/host/contracts.d.ts +25 -0
- package/dist/agent-core/host/local-agent-host.d.ts +10 -1
- package/dist/agent-core/host/local-agent-host.js +132 -20
- package/dist/agent-core/host/local-file-access.d.ts +30 -0
- package/dist/agent-core/host/local-file-access.js +107 -0
- package/dist/agent-core/host/local-network-access.d.ts +6 -0
- package/dist/agent-core/host/local-network-access.js +97 -0
- package/dist/agent-core/host/local-process-executor.d.ts +5 -1
- package/dist/agent-core/host/local-session-environment.d.ts +18 -0
- package/dist/agent-core/host/local-session-environment.js +102 -0
- package/dist/agent-core/model/anthropic-messages.js +73 -23
- package/dist/agent-core/model/contracts.d.ts +12 -0
- package/dist/agent-core/model/openai-responses.js +73 -12
- package/dist/agent-core/model/text-blocks.d.ts +7 -0
- package/dist/agent-core/model/text-blocks.js +40 -0
- package/dist/agent-core/prompts/index.d.ts +6 -2
- package/dist/agent-core/prompts/index.js +38 -24
- package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
- package/dist/agent-core/runtime/context/normalize.js +21 -1
- package/dist/agent-core/runtime/context/projection.js +35 -1
- package/dist/agent-core/runtime/context/request-budget.js +5 -2
- package/dist/agent-core/runtime/context/restore.d.ts +5 -0
- package/dist/agent-core/runtime/context/restore.js +47 -4
- package/dist/agent-core/runtime/context/usage.js +2 -3
- package/dist/agent-core/runtime/contracts.d.ts +2 -0
- package/dist/agent-core/runtime/execution.js +5 -2
- package/dist/agent-core/sdk/agent.js +177 -40
- package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
- package/dist/agent-core/sdk/extension-agents.js +51 -0
- package/dist/agent-core/sdk/extension-models.d.ts +13 -0
- package/dist/agent-core/sdk/extension-models.js +128 -0
- package/dist/agent-core/sdk/extension-session.d.ts +30 -0
- package/dist/agent-core/sdk/extension-session.js +103 -0
- package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
- package/dist/agent-core/sdk/extension-tools.js +126 -0
- package/dist/agent-core/sdk/session.d.ts +26 -2
- package/dist/agent-core/sdk/session.js +1215 -506
- package/dist/agent-core/sdk/types.d.ts +3 -0
- package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
- package/dist/agent-core/session/jsonl-store.d.ts +28 -8
- package/dist/agent-core/session/jsonl-store.js +124 -84
- package/dist/agent-core/session/types.d.ts +4 -1
- package/dist/agent-core/storage/file-lock.d.ts +4 -0
- package/dist/agent-core/storage/file-lock.js +83 -0
- package/dist/agent-core/subagent/contracts.d.ts +11 -1
- package/dist/agent-core/subagent/scheduler.d.ts +8 -0
- package/dist/agent-core/subagent/session-controller.d.ts +22 -1
- package/dist/agent-core/subagent/session-controller.js +355 -98
- package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
- package/dist/agent-core/tools/code-mode/cells.js +5 -15
- package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
- package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
- package/dist/agent-core/tools/code-mode/index.js +1 -2
- package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
- package/dist/agent-core/tools/code-mode/observations.js +16 -5
- package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
- package/dist/agent-core/tools/execution.d.ts +11 -2
- package/dist/agent-core/tools/execution.js +58 -21
- package/dist/agent-core/tools/input.d.ts +2 -0
- package/dist/agent-core/tools/input.js +36 -0
- package/dist/agent-core/tools/local/apply-patch.js +21 -17
- package/dist/agent-core/tools/local/exec.d.ts +4 -4
- package/dist/agent-core/tools/local/exec.js +234 -62
- package/dist/agent-core/tools/local/registry.d.ts +6 -0
- package/dist/agent-core/tools/local/registry.js +60 -9
- package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
- package/dist/agent-core/tools/mcp/catalog.js +4 -4
- package/dist/agent-core/tools/mcp/history.js +2 -2
- package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
- package/dist/agent-core/tools/mcp/tool-search.js +2 -2
- package/dist/agent-core/tools/output-projection.d.ts +3 -1
- package/dist/agent-core/tools/output-projection.js +11 -2
- package/dist/agent-core/tools/runtime-context.d.ts +1 -0
- package/dist/agent-core/tools/runtime.d.ts +3 -1
- package/dist/agent-core/tools/runtime.js +22 -7
- package/dist/agent-core/tools/scheduler.d.ts +2 -0
- package/dist/agent-core/tools/scheduler.js +43 -6
- package/dist/index.d.ts +21 -0
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MarAgentHost } from '../host/contracts.js';
|
|
2
|
+
import type { CoreExtensionSource, CoreRuntimeExtension } from '../extensions/contracts.js';
|
|
2
3
|
import type { MarAgentModelConfiguration } from '../model/configuration.js';
|
|
3
4
|
import type { ExecutionResult, RolloutBudgetOptions, RunInput } from '../runtime/contracts.js';
|
|
4
5
|
import type { MarAgentEvent } from '../runtime/events.js';
|
|
@@ -29,6 +30,7 @@ export interface MarAgentSession {
|
|
|
29
30
|
cancel(executionId: string): Promise<void>;
|
|
30
31
|
}
|
|
31
32
|
export interface MarAgent {
|
|
33
|
+
extensionDiagnostics(): readonly import('../extensions/contracts.js').CoreExtensionDiagnostic[];
|
|
32
34
|
createSession(input: CreateSessionInput): Promise<MarAgentSession>;
|
|
33
35
|
openSession(sessionId: string): Promise<MarAgentSession>;
|
|
34
36
|
listSessions(input?: SessionListInput): Promise<MarAgentSessionPage>;
|
|
@@ -59,5 +61,6 @@ export interface CreateMarAgentOptions {
|
|
|
59
61
|
readonly physicalPath: string;
|
|
60
62
|
};
|
|
61
63
|
rolloutBudget?: false | RolloutBudgetOptions;
|
|
64
|
+
runtimeExtensions?: readonly CoreRuntimeExtension[] | CoreExtensionSource;
|
|
62
65
|
}
|
|
63
66
|
export type { MarAgentSessionSummary, SessionListInput, SessionSource };
|
|
@@ -659,7 +659,7 @@ function modelContextCallId(payload) {
|
|
|
659
659
|
return typeof value === 'string' ? value : undefined;
|
|
660
660
|
}
|
|
661
661
|
function modelContextToolName(payload) {
|
|
662
|
-
if (payload.role === 'tool_call' && typeof payload.name === 'string')
|
|
662
|
+
if ((payload.role === 'tool_call' || payload.role === 'tool') && typeof payload.name === 'string')
|
|
663
663
|
return payload.name;
|
|
664
664
|
if (payload.role !== 'provider' || !isRecord(payload.item))
|
|
665
665
|
return undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CoreCheckpoint, type CoreJsonValue } from '../extensions/data.js';
|
|
2
|
+
import type { ModelMessage, ModelUsage } from '../model/contracts.js';
|
|
2
3
|
import type { EnvironmentContextState } from '../runtime/context/contracts.js';
|
|
3
4
|
import { type CodeModeStateEntry } from '../tools/code-mode/state.js';
|
|
4
5
|
import { type TodoSnapshot } from '../tools/agent/todo.js';
|
|
@@ -16,6 +17,13 @@ export interface SessionRecord {
|
|
|
16
17
|
type: string;
|
|
17
18
|
payload: unknown;
|
|
18
19
|
}
|
|
20
|
+
interface SessionRecordInput {
|
|
21
|
+
type: string;
|
|
22
|
+
payload: unknown;
|
|
23
|
+
executionId?: string;
|
|
24
|
+
turnId?: string;
|
|
25
|
+
timestamp?: string;
|
|
26
|
+
}
|
|
19
27
|
export interface SessionExecutionState {
|
|
20
28
|
automaticTitlePending: boolean;
|
|
21
29
|
todo: TodoSnapshot;
|
|
@@ -139,15 +147,19 @@ export declare class JsonlSessionStore {
|
|
|
139
147
|
source: SessionSource;
|
|
140
148
|
automaticTitlePending: boolean;
|
|
141
149
|
}>;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
executionId?: string;
|
|
146
|
-
turnId?: string;
|
|
147
|
-
timestamp?: string;
|
|
150
|
+
/** Serialize physical writes and honor cancellation before entering the writer. */
|
|
151
|
+
append(sessionId: string, input: SessionRecordInput, options?: {
|
|
152
|
+
readonly signal?: AbortSignal;
|
|
148
153
|
}): Promise<SessionRecord>;
|
|
149
154
|
read(sessionId: string): Promise<SessionRecord[]>;
|
|
150
155
|
readCodeModeState(sessionId: string): Promise<CodeModeStateEntry[]>;
|
|
156
|
+
readExtensionCheckpoint(sessionId: string, extensionId: string, key: string): Promise<CoreCheckpoint>;
|
|
157
|
+
writeExtensionCheckpoint(sessionId: string, extensionId: string, key: string, value: CoreJsonValue | undefined, expectedRevision: number | undefined, input: {
|
|
158
|
+
owner: object;
|
|
159
|
+
signal: AbortSignal;
|
|
160
|
+
executionId?: string;
|
|
161
|
+
turnId?: string;
|
|
162
|
+
}): Promise<CoreCheckpoint>;
|
|
151
163
|
commitCodeModeState(sessionId: string, mutations: unknown, input: {
|
|
152
164
|
readonly executionId: string;
|
|
153
165
|
readonly turnId: string;
|
|
@@ -168,6 +180,7 @@ export declare class JsonlSessionStore {
|
|
|
168
180
|
compact(sessionId: string, input: {
|
|
169
181
|
summary: string;
|
|
170
182
|
replacementHistory?: readonly CompactHistoryMessage[];
|
|
183
|
+
retainedModelContext?: readonly ModelMessage[];
|
|
171
184
|
environment?: EnvironmentContextState;
|
|
172
185
|
automatic?: boolean;
|
|
173
186
|
modelId?: string;
|
|
@@ -175,10 +188,14 @@ export declare class JsonlSessionStore {
|
|
|
175
188
|
usage?: ModelUsage;
|
|
176
189
|
executionId?: string;
|
|
177
190
|
turnId?: string;
|
|
191
|
+
}, options?: {
|
|
192
|
+
readonly signal?: AbortSignal;
|
|
178
193
|
}): Promise<SessionRecord>;
|
|
179
194
|
/** A warm cell retains physical writer ownership between Executions of the same Agent. */
|
|
180
195
|
acquireSessionLease(sessionId: string, owner?: object): Promise<() => Promise<void>>;
|
|
181
196
|
hasRetainedSessionLease(sessionId: string, owner: object): boolean;
|
|
197
|
+
/** Native writes join their Agent's sole writer, without admitting another Execution. */
|
|
198
|
+
acquireSessionWriteLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
|
|
182
199
|
retainSessionLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
|
|
183
200
|
list(input?: SessionListInput): Promise<{
|
|
184
201
|
nextCursor?: string;
|
|
@@ -208,7 +225,10 @@ export declare class JsonlSessionStore {
|
|
|
208
225
|
readonly type: "subagent";
|
|
209
226
|
readonly parentSessionId: string;
|
|
210
227
|
readonly rootSessionId: string;
|
|
211
|
-
readonly parentExecutionId
|
|
228
|
+
readonly parentExecutionId?: string;
|
|
229
|
+
readonly parentOperationId?: string;
|
|
230
|
+
readonly ownerInstanceId?: string;
|
|
231
|
+
readonly extensionId?: string;
|
|
212
232
|
readonly role: "general-purpose" | "explore";
|
|
213
233
|
readonly description: string;
|
|
214
234
|
};
|
|
@@ -2,9 +2,10 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { constants } from 'node:fs';
|
|
3
3
|
import { copyFile, lstat, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { basename, join } from 'node:path';
|
|
5
|
-
import { setTimeout as delay } from 'node:timers/promises';
|
|
6
5
|
import { isDeepStrictEqual } from 'node:util';
|
|
7
6
|
import { MarAgentError } from '../error.js';
|
|
7
|
+
import { acquireFileLock } from '../storage/file-lock.js';
|
|
8
|
+
import { cloneCoreJson } from '../extensions/data.js';
|
|
8
9
|
import { applyCodeModeStateMutations, parseCodeModeStateMutations, restoreCodeModeState } from '../tools/code-mode/state.js';
|
|
9
10
|
import { todoItemsFromSnapshot } from '../tools/agent/todo.js';
|
|
10
11
|
import { contextGcBaseFromRecords, contextGcBaseSequence, contextGcMinimumUsageGrowth, contextGcSourceProjectionHash, latestValidContextGcCheckpoint, normalizeContextGcReplacements, parseContextGcCheckpoint, sameContextGcBase, validateContextGcCheckpoint } from './context-gc-checkpoint.js';
|
|
@@ -153,6 +154,7 @@ export class JsonlSessionStore {
|
|
|
153
154
|
return {
|
|
154
155
|
root,
|
|
155
156
|
index,
|
|
157
|
+
indexOperations: Promise.resolve(),
|
|
156
158
|
pendingSessions: new Map(),
|
|
157
159
|
references: 1,
|
|
158
160
|
closed: false,
|
|
@@ -170,6 +172,14 @@ export class JsonlSessionStore {
|
|
|
170
172
|
}
|
|
171
173
|
/** Merge dirty metadata with disk membership; only createdIds publish locally new entries. */
|
|
172
174
|
async #saveIndex(deletedIds, createdIds = []) {
|
|
175
|
+
return this.#serializeIndexOperation(() => this.#commitIndex(deletedIds, createdIds));
|
|
176
|
+
}
|
|
177
|
+
async #serializeIndexOperation(operation) {
|
|
178
|
+
const next = this.#shared.indexOperations.then(operation, operation);
|
|
179
|
+
this.#shared.indexOperations = next.then(() => undefined, () => undefined);
|
|
180
|
+
return next;
|
|
181
|
+
}
|
|
182
|
+
async #commitIndex(deletedIds, createdIds = []) {
|
|
173
183
|
const release = await acquireFileLock(join(this.#shared.root, 'sessions', 'index.lock'));
|
|
174
184
|
const path = join(this.#shared.root, 'sessions', 'index.json');
|
|
175
185
|
try {
|
|
@@ -229,6 +239,9 @@ export class JsonlSessionStore {
|
|
|
229
239
|
}
|
|
230
240
|
}
|
|
231
241
|
async #refreshIndex() {
|
|
242
|
+
return this.#serializeIndexOperation(() => this.#readCurrentIndex());
|
|
243
|
+
}
|
|
244
|
+
async #readCurrentIndex() {
|
|
232
245
|
const disk = await readSessionIndex(join(this.#shared.root, 'sessions', 'index.json'));
|
|
233
246
|
if (!disk)
|
|
234
247
|
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session index is missing.');
|
|
@@ -874,7 +887,7 @@ export class JsonlSessionStore {
|
|
|
874
887
|
installedPaths.push(item.path);
|
|
875
888
|
}
|
|
876
889
|
meta.lastActivityAt = clearedAt;
|
|
877
|
-
meta.nextSequence =
|
|
890
|
+
meta.nextSequence = retained.length + 1;
|
|
878
891
|
meta.storageTransactionId = transactionId;
|
|
879
892
|
meta.resume = { version: 1, todoRevision: 0, firstUserMessageIndexed: true };
|
|
880
893
|
meta.segments = staged.segments;
|
|
@@ -945,8 +958,14 @@ export class JsonlSessionStore {
|
|
|
945
958
|
automaticTitlePending: meta.automaticTitlePending === true
|
|
946
959
|
};
|
|
947
960
|
}
|
|
948
|
-
|
|
949
|
-
|
|
961
|
+
/** Serialize physical writes and honor cancellation before entering the writer. */
|
|
962
|
+
async append(sessionId, input, options = {}) {
|
|
963
|
+
this.#assertOpen();
|
|
964
|
+
options.signal?.throwIfAborted();
|
|
965
|
+
return this.#serializeRecordOperation(sessionId, () => {
|
|
966
|
+
options.signal?.throwIfAborted();
|
|
967
|
+
return this.#appendRecord(sessionId, input);
|
|
968
|
+
});
|
|
950
969
|
}
|
|
951
970
|
async #serializeRecordOperation(sessionId, action) {
|
|
952
971
|
const operation = (this.#shared.recordWrites.get(sessionId) ?? Promise.resolve())
|
|
@@ -1058,6 +1077,42 @@ export class JsonlSessionStore {
|
|
|
1058
1077
|
async readCodeModeState(sessionId) {
|
|
1059
1078
|
return restoreCodeModeState(await this.read(sessionId));
|
|
1060
1079
|
}
|
|
1080
|
+
async readExtensionCheckpoint(sessionId, extensionId, key) {
|
|
1081
|
+
return restoreExtensionCheckpoint(await this.read(sessionId), extensionId, key);
|
|
1082
|
+
}
|
|
1083
|
+
async writeExtensionCheckpoint(sessionId, extensionId, key, value, expectedRevision, input) {
|
|
1084
|
+
if (typeof key !== 'string' ||
|
|
1085
|
+
(expectedRevision !== undefined &&
|
|
1086
|
+
(!Number.isSafeInteger(expectedRevision) || expectedRevision < 0)))
|
|
1087
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_DATA_INVALID', 'Checkpoint key or expected revision is invalid.');
|
|
1088
|
+
const copied = value === undefined ? undefined : cloneCoreJson(value);
|
|
1089
|
+
input.signal.throwIfAborted();
|
|
1090
|
+
const release = await this.acquireSessionWriteLease(sessionId, input.owner);
|
|
1091
|
+
try {
|
|
1092
|
+
return await this.#serializeRecordOperation(sessionId, async () => {
|
|
1093
|
+
const current = restoreExtensionCheckpoint(await this.#readRecords(sessionId), extensionId, key);
|
|
1094
|
+
if (expectedRevision !== undefined && expectedRevision !== current.revision)
|
|
1095
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_DATA_CONFLICT', 'Checkpoint revision changed before commit.');
|
|
1096
|
+
input.signal.throwIfAborted();
|
|
1097
|
+
const revision = current.revision + 1;
|
|
1098
|
+
await this.#appendRecord(sessionId, {
|
|
1099
|
+
type: 'extension.checkpoint.updated',
|
|
1100
|
+
payload: {
|
|
1101
|
+
extensionId,
|
|
1102
|
+
key,
|
|
1103
|
+
revision,
|
|
1104
|
+
...(copied === undefined ? { deleted: true } : { value: copied })
|
|
1105
|
+
},
|
|
1106
|
+
...(input.executionId ? { executionId: input.executionId } : {}),
|
|
1107
|
+
...(input.turnId ? { turnId: input.turnId } : {})
|
|
1108
|
+
});
|
|
1109
|
+
return { revision, ...(copied === undefined ? {} : { value: cloneCoreJson(copied) }) };
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
finally {
|
|
1113
|
+
await release();
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1061
1116
|
async commitCodeModeState(sessionId, mutations, input) {
|
|
1062
1117
|
const operation = (this.#shared.codeStateWrites.get(sessionId) ?? Promise.resolve())
|
|
1063
1118
|
.catch(() => undefined)
|
|
@@ -1212,7 +1267,7 @@ export class JsonlSessionStore {
|
|
|
1212
1267
|
const previousBeforeSequence = hasEarlier ? page[0]?.sequence : undefined;
|
|
1213
1268
|
return { records: page, ...(previousBeforeSequence ? { previousBeforeSequence } : {}) };
|
|
1214
1269
|
}
|
|
1215
|
-
async compact(sessionId, input) {
|
|
1270
|
+
async compact(sessionId, input, options = {}) {
|
|
1216
1271
|
if (!input.summary.trim())
|
|
1217
1272
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Compact summary is required.');
|
|
1218
1273
|
const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
|
|
@@ -1225,6 +1280,9 @@ export class JsonlSessionStore {
|
|
|
1225
1280
|
...(input.replacementHistory === undefined
|
|
1226
1281
|
? {}
|
|
1227
1282
|
: { replacementHistory: input.replacementHistory }),
|
|
1283
|
+
...(input.retainedModelContext?.length
|
|
1284
|
+
? { retainedModelContext: input.retainedModelContext }
|
|
1285
|
+
: {}),
|
|
1228
1286
|
...(input.environment === undefined ? {} : { environment: input.environment }),
|
|
1229
1287
|
boundarySequence: meta.nextSequence - 1,
|
|
1230
1288
|
automatic: input.automatic === true,
|
|
@@ -1236,7 +1294,7 @@ export class JsonlSessionStore {
|
|
|
1236
1294
|
},
|
|
1237
1295
|
...(input.executionId ? { executionId: input.executionId } : {}),
|
|
1238
1296
|
...(input.turnId ? { turnId: input.turnId } : {})
|
|
1239
|
-
});
|
|
1297
|
+
}, options);
|
|
1240
1298
|
}
|
|
1241
1299
|
/** A warm cell retains physical writer ownership between Executions of the same Agent. */
|
|
1242
1300
|
async acquireSessionLease(sessionId, owner) {
|
|
@@ -1268,6 +1326,30 @@ export class JsonlSessionStore {
|
|
|
1268
1326
|
const lease = this.#shared.writerLeases.get(sessionId);
|
|
1269
1327
|
return lease?.owner === owner && lease.retained > 0;
|
|
1270
1328
|
}
|
|
1329
|
+
/** Native writes join their Agent's sole writer, without admitting another Execution. */
|
|
1330
|
+
async acquireSessionWriteLease(sessionId, owner) {
|
|
1331
|
+
return this.#serializeRecordOperation(sessionId, async () => {
|
|
1332
|
+
this.#assertOpen();
|
|
1333
|
+
let lease = this.#shared.writerLeases.get(sessionId);
|
|
1334
|
+
if (lease && lease.owner !== owner)
|
|
1335
|
+
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
|
|
1336
|
+
if (!lease) {
|
|
1337
|
+
const release = await this.#acquireSessionLease(sessionId);
|
|
1338
|
+
lease = { owner, executing: false, retained: 0, release };
|
|
1339
|
+
this.#shared.writerLeases.set(sessionId, lease);
|
|
1340
|
+
}
|
|
1341
|
+
lease.retained++;
|
|
1342
|
+
const current = lease;
|
|
1343
|
+
let released = false;
|
|
1344
|
+
return () => this.#serializeRecordOperation(sessionId, async () => {
|
|
1345
|
+
if (released)
|
|
1346
|
+
return;
|
|
1347
|
+
released = true;
|
|
1348
|
+
current.retained--;
|
|
1349
|
+
await this.#releaseUnusedWriter(sessionId, current);
|
|
1350
|
+
});
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1271
1353
|
async retainSessionLease(sessionId, owner) {
|
|
1272
1354
|
return this.#serializeRecordOperation(sessionId, async () => {
|
|
1273
1355
|
const lease = this.#shared.writerLeases.get(sessionId);
|
|
@@ -2167,9 +2249,20 @@ function normalizeSessionSource(value) {
|
|
|
2167
2249
|
if (value === undefined || value.type === 'user')
|
|
2168
2250
|
return { type: 'user' };
|
|
2169
2251
|
if (value.type !== 'subagent' ||
|
|
2170
|
-
|
|
2171
|
-
!value.
|
|
2172
|
-
|
|
2252
|
+
typeof value.parentSessionId !== 'string' ||
|
|
2253
|
+
!value.parentSessionId.trim() ||
|
|
2254
|
+
typeof value.rootSessionId !== 'string' ||
|
|
2255
|
+
!value.rootSessionId.trim() ||
|
|
2256
|
+
(!value.parentExecutionId && !value.parentOperationId) ||
|
|
2257
|
+
[
|
|
2258
|
+
value.parentExecutionId,
|
|
2259
|
+
value.parentOperationId,
|
|
2260
|
+
value.ownerInstanceId,
|
|
2261
|
+
value.extensionId
|
|
2262
|
+
].some((identity) => identity !== undefined && (typeof identity !== 'string' || !identity.trim())) ||
|
|
2263
|
+
((value.ownerInstanceId !== undefined || value.extensionId !== undefined) &&
|
|
2264
|
+
value.parentOperationId === undefined) ||
|
|
2265
|
+
typeof value.description !== 'string' ||
|
|
2173
2266
|
!value.description.trim() ||
|
|
2174
2267
|
(value.role !== 'general-purpose' && value.role !== 'explore'))
|
|
2175
2268
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Session source is invalid.');
|
|
@@ -2177,7 +2270,14 @@ function normalizeSessionSource(value) {
|
|
|
2177
2270
|
type: 'subagent',
|
|
2178
2271
|
parentSessionId: value.parentSessionId,
|
|
2179
2272
|
rootSessionId: value.rootSessionId,
|
|
2180
|
-
|
|
2273
|
+
...(value.parentExecutionId === undefined
|
|
2274
|
+
? {}
|
|
2275
|
+
: { parentExecutionId: value.parentExecutionId }),
|
|
2276
|
+
...(value.parentOperationId === undefined
|
|
2277
|
+
? {}
|
|
2278
|
+
: { parentOperationId: value.parentOperationId }),
|
|
2279
|
+
...(value.ownerInstanceId === undefined ? {} : { ownerInstanceId: value.ownerInstanceId }),
|
|
2280
|
+
...(value.extensionId === undefined ? {} : { extensionId: value.extensionId }),
|
|
2181
2281
|
role: value.role,
|
|
2182
2282
|
description: value.description.trim()
|
|
2183
2283
|
};
|
|
@@ -2630,81 +2730,21 @@ function segmentNumberFromPath(path) {
|
|
|
2630
2730
|
const match = /events-(\d{6})\.jsonl$/.exec(path);
|
|
2631
2731
|
return match === null ? undefined : Number(match[1]);
|
|
2632
2732
|
}
|
|
2633
|
-
|
|
2634
|
-
const
|
|
2635
|
-
|
|
2636
|
-
let handle;
|
|
2637
|
-
try {
|
|
2638
|
-
handle = await open(path, 'wx', 0o600);
|
|
2639
|
-
}
|
|
2640
|
-
catch (cause) {
|
|
2641
|
-
if (cause.code !== 'EEXIST')
|
|
2642
|
-
throw cause;
|
|
2643
|
-
if (await removeDeadOwner(path))
|
|
2644
|
-
continue;
|
|
2645
|
-
if (Date.now() >= deadline)
|
|
2646
|
-
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', message, { cause });
|
|
2647
|
-
await delay(25 + Math.floor(Math.random() * 25));
|
|
2733
|
+
function restoreExtensionCheckpoint(records, extensionId, key) {
|
|
2734
|
+
for (const record of [...records].reverse()) {
|
|
2735
|
+
if (record.type !== 'extension.checkpoint.updated')
|
|
2648
2736
|
continue;
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
}
|
|
2660
|
-
throw error;
|
|
2661
|
-
}
|
|
2662
|
-
let released = false;
|
|
2663
|
-
return async () => {
|
|
2664
|
-
if (released)
|
|
2665
|
-
return;
|
|
2666
|
-
released = true;
|
|
2667
|
-
await handle.close();
|
|
2668
|
-
await rm(path, { force: true });
|
|
2737
|
+
const payload = record.payload;
|
|
2738
|
+
if (!payload || payload.extensionId !== extensionId || payload.key !== key)
|
|
2739
|
+
continue;
|
|
2740
|
+
if (typeof payload.revision !== 'number' ||
|
|
2741
|
+
!Number.isSafeInteger(payload.revision) ||
|
|
2742
|
+
payload.revision < 1)
|
|
2743
|
+
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Extension checkpoint revision is corrupt.');
|
|
2744
|
+
return {
|
|
2745
|
+
revision: payload.revision,
|
|
2746
|
+
...(payload.deleted === true ? {} : { value: cloneCoreJson(payload.value) })
|
|
2669
2747
|
};
|
|
2670
2748
|
}
|
|
2671
|
-
}
|
|
2672
|
-
async function removeDeadOwner(path) {
|
|
2673
|
-
const reclaimerPath = `${path}.reclaim`;
|
|
2674
|
-
let reclaimer;
|
|
2675
|
-
try {
|
|
2676
|
-
// Only one process may unlink a dead owner's path; all other contenders wait.
|
|
2677
|
-
reclaimer = await open(reclaimerPath, 'wx', 0o600);
|
|
2678
|
-
}
|
|
2679
|
-
catch (error) {
|
|
2680
|
-
if (error.code === 'EEXIST')
|
|
2681
|
-
return false;
|
|
2682
|
-
throw error;
|
|
2683
|
-
}
|
|
2684
|
-
try {
|
|
2685
|
-
const owner = JSON.parse(await readFile(path, 'utf8'));
|
|
2686
|
-
if (typeof owner.pid !== 'number' || !Number.isSafeInteger(owner.pid))
|
|
2687
|
-
return false;
|
|
2688
|
-
try {
|
|
2689
|
-
process.kill(owner.pid, 0);
|
|
2690
|
-
return false;
|
|
2691
|
-
}
|
|
2692
|
-
catch (error) {
|
|
2693
|
-
if (error.code !== 'ESRCH')
|
|
2694
|
-
return false;
|
|
2695
|
-
await rm(path, { force: true });
|
|
2696
|
-
return true;
|
|
2697
|
-
}
|
|
2698
|
-
}
|
|
2699
|
-
catch {
|
|
2700
|
-
return false;
|
|
2701
|
-
}
|
|
2702
|
-
finally {
|
|
2703
|
-
try {
|
|
2704
|
-
await reclaimer.close();
|
|
2705
|
-
}
|
|
2706
|
-
finally {
|
|
2707
|
-
await rm(reclaimerPath, { force: true });
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2749
|
+
return { revision: 0 };
|
|
2710
2750
|
}
|
|
@@ -5,7 +5,10 @@ export type SessionSource = {
|
|
|
5
5
|
readonly type: 'subagent';
|
|
6
6
|
readonly parentSessionId: string;
|
|
7
7
|
readonly rootSessionId: string;
|
|
8
|
-
readonly parentExecutionId
|
|
8
|
+
readonly parentExecutionId?: string;
|
|
9
|
+
readonly parentOperationId?: string;
|
|
10
|
+
readonly ownerInstanceId?: string;
|
|
11
|
+
readonly extensionId?: string;
|
|
9
12
|
readonly role: 'general-purpose' | 'explore';
|
|
10
13
|
readonly description: string;
|
|
11
14
|
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { open, readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import { MarAgentError } from '../error.js';
|
|
4
|
+
export async function acquireFileLock(path, message = 'Session index is busy.', waitMs = 5000, options = {}) {
|
|
5
|
+
const deadline = Date.now() + waitMs;
|
|
6
|
+
for (;;) {
|
|
7
|
+
options.signal?.throwIfAborted();
|
|
8
|
+
let handle;
|
|
9
|
+
try {
|
|
10
|
+
handle = await open(path, 'wx', 0o600);
|
|
11
|
+
}
|
|
12
|
+
catch (cause) {
|
|
13
|
+
if (cause.code !== 'EEXIST')
|
|
14
|
+
throw cause;
|
|
15
|
+
if (await removeDeadOwner(path))
|
|
16
|
+
continue;
|
|
17
|
+
if (Date.now() >= deadline)
|
|
18
|
+
throw new MarAgentError(options.code ?? 'MAR_AGENT_SESSION_LOCKED', message, { cause });
|
|
19
|
+
await delay(25 + Math.floor(Math.random() * 25), undefined, options.signal ? { signal: options.signal } : {});
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }));
|
|
24
|
+
options.signal?.throwIfAborted();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
try {
|
|
28
|
+
await handle.close();
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await rm(path, { force: true });
|
|
32
|
+
}
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
let released = false;
|
|
36
|
+
return async () => {
|
|
37
|
+
if (released)
|
|
38
|
+
return;
|
|
39
|
+
released = true;
|
|
40
|
+
await handle.close();
|
|
41
|
+
await rm(path, { force: true });
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function removeDeadOwner(path) {
|
|
46
|
+
const reclaimerPath = `${path}.reclaim`;
|
|
47
|
+
let reclaimer;
|
|
48
|
+
try {
|
|
49
|
+
// Only one process may unlink a dead owner's path; all other contenders wait.
|
|
50
|
+
reclaimer = await open(reclaimerPath, 'wx', 0o600);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error.code === 'EEXIST')
|
|
54
|
+
return false;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const owner = JSON.parse(await readFile(path, 'utf8'));
|
|
59
|
+
if (typeof owner.pid !== 'number' || !Number.isSafeInteger(owner.pid))
|
|
60
|
+
return false;
|
|
61
|
+
try {
|
|
62
|
+
process.kill(owner.pid, 0);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error.code !== 'ESRCH')
|
|
67
|
+
return false;
|
|
68
|
+
await rm(path, { force: true });
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
try {
|
|
77
|
+
await reclaimer.close();
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await rm(reclaimerPath, { force: true });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -39,6 +39,8 @@ export type SubagentLatestActivity = {
|
|
|
39
39
|
export interface SubagentTaskSnapshot extends Record<string, unknown> {
|
|
40
40
|
agentId: string;
|
|
41
41
|
description: string;
|
|
42
|
+
role?: SubagentTaskInput['role'];
|
|
43
|
+
extensionId?: string;
|
|
42
44
|
status: 'queued' | 'running' | 'completed' | 'failed' | 'cancelled' | 'interrupted';
|
|
43
45
|
modelId?: string;
|
|
44
46
|
modelName?: string;
|
|
@@ -67,17 +69,25 @@ export interface SubagentMessageOptions {
|
|
|
67
69
|
signal?: AbortSignal;
|
|
68
70
|
}
|
|
69
71
|
export interface SubagentRunOptions {
|
|
72
|
+
turnSignal?: AbortSignal;
|
|
73
|
+
waitFor?: 'start' | 'completion';
|
|
70
74
|
signal?: AbortSignal;
|
|
71
75
|
onCreated?: (task: SubagentTaskSnapshot) => Promise<void> | void;
|
|
72
76
|
}
|
|
73
77
|
export interface SubagentOutputOptions {
|
|
78
|
+
untilSettled?: true;
|
|
74
79
|
waitMs?: number;
|
|
75
80
|
waitFor?: SubagentWaitFor;
|
|
76
81
|
signal?: AbortSignal;
|
|
77
82
|
}
|
|
83
|
+
export interface SubagentHandledResult {
|
|
84
|
+
readonly outcome: 'handled';
|
|
85
|
+
readonly value?: unknown;
|
|
86
|
+
}
|
|
87
|
+
export type SubagentRunResult = SubagentTaskSnapshot | SubagentHandledResult;
|
|
78
88
|
export interface SubagentController {
|
|
79
89
|
beginExecution?(runner: (task: SubagentTaskInput, signal: AbortSignal) => Promise<SubagentTaskResult>): void;
|
|
80
|
-
run(input: SubagentTaskInput, options?: SubagentRunOptions): Promise<
|
|
90
|
+
run(input: SubagentTaskInput, options?: SubagentRunOptions): Promise<SubagentRunResult>;
|
|
81
91
|
output(agentId: string, options?: SubagentOutputOptions): Promise<SubagentTaskSnapshot>;
|
|
82
92
|
outputAny(options?: SubagentOutputOptions): Promise<SubagentTaskSnapshot>;
|
|
83
93
|
message(agentId: string, message: string, options?: SubagentMessageOptions): Promise<SubagentTaskSnapshot>;
|
|
@@ -16,6 +16,8 @@ export declare class SubagentScheduler implements SubagentController {
|
|
|
16
16
|
waitOutcome: "snapshot";
|
|
17
17
|
agentId: string;
|
|
18
18
|
description: string;
|
|
19
|
+
role?: SubagentTaskInput["role"];
|
|
20
|
+
extensionId?: string;
|
|
19
21
|
status: "queued" | "running" | "completed" | "failed" | "cancelled" | "interrupted";
|
|
20
22
|
modelId?: string;
|
|
21
23
|
modelName?: string;
|
|
@@ -39,6 +41,8 @@ export declare class SubagentScheduler implements SubagentController {
|
|
|
39
41
|
waitOutcome: "completion" | "timeout";
|
|
40
42
|
agentId: string;
|
|
41
43
|
description: string;
|
|
44
|
+
role?: SubagentTaskInput["role"];
|
|
45
|
+
extensionId?: string;
|
|
42
46
|
status: "queued" | "running" | "completed" | "failed" | "cancelled" | "interrupted";
|
|
43
47
|
modelId?: string;
|
|
44
48
|
modelName?: string;
|
|
@@ -63,6 +67,8 @@ export declare class SubagentScheduler implements SubagentController {
|
|
|
63
67
|
waitOutcome: "snapshot";
|
|
64
68
|
agentId: string;
|
|
65
69
|
description: string;
|
|
70
|
+
role?: SubagentTaskInput["role"];
|
|
71
|
+
extensionId?: string;
|
|
66
72
|
status: "queued" | "running" | "completed" | "failed" | "cancelled" | "interrupted";
|
|
67
73
|
modelId?: string;
|
|
68
74
|
modelName?: string;
|
|
@@ -86,6 +92,8 @@ export declare class SubagentScheduler implements SubagentController {
|
|
|
86
92
|
waitOutcome: "completion" | "timeout";
|
|
87
93
|
agentId: string;
|
|
88
94
|
description: string;
|
|
95
|
+
role?: SubagentTaskInput["role"];
|
|
96
|
+
extensionId?: string;
|
|
89
97
|
status: "queued" | "running" | "completed" | "failed" | "cancelled" | "interrupted";
|
|
90
98
|
modelId?: string;
|
|
91
99
|
modelName?: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CoreOperationContext } from '../extensions/operation.js';
|
|
1
2
|
import type { MarAgentReasoningEffort } from '../model/configuration.js';
|
|
2
3
|
import type { ExecutionResult } from '../runtime/contracts.js';
|
|
3
4
|
import type { MarAgentEvent } from '../runtime/events.js';
|
|
@@ -18,7 +19,10 @@ export interface SubagentTurnHandle extends AsyncIterable<MarAgentEvent> {
|
|
|
18
19
|
}
|
|
19
20
|
export interface SubagentMessageDelivery {
|
|
20
21
|
readonly messageId: string;
|
|
22
|
+
readonly origin?: CoreOperationContext;
|
|
23
|
+
readonly signal?: AbortSignal;
|
|
21
24
|
applied(): void;
|
|
25
|
+
settled?(status: 'cancelled' | 'failed'): void;
|
|
22
26
|
}
|
|
23
27
|
export interface SubagentMailboxMessage {
|
|
24
28
|
readonly text: string;
|
|
@@ -39,8 +43,18 @@ export interface SubagentParentExecution {
|
|
|
39
43
|
readonly initialMailbox: readonly SubagentMailboxMessage[];
|
|
40
44
|
readonly turnId: string;
|
|
41
45
|
readonly delivery?: SubagentMessageDelivery;
|
|
46
|
+
readonly origin?: CoreOperationContext;
|
|
42
47
|
}): Promise<SubagentTurnHandle>;
|
|
43
48
|
}
|
|
49
|
+
/** A native operation has its own provenance even when no parent Execution is active. */
|
|
50
|
+
export interface SubagentParentOperation extends Omit<SubagentParentExecution, 'executionId'> {
|
|
51
|
+
readonly operationId: string;
|
|
52
|
+
readonly executionId?: string;
|
|
53
|
+
readonly ownerInstanceId?: string;
|
|
54
|
+
readonly extensionId?: string;
|
|
55
|
+
readonly signal: AbortSignal;
|
|
56
|
+
readonly origin?: CoreOperationContext;
|
|
57
|
+
}
|
|
44
58
|
export interface PersistedSubagentSession {
|
|
45
59
|
readonly id: string;
|
|
46
60
|
readonly createdAt: string;
|
|
@@ -85,8 +99,15 @@ export declare class SubagentSessionController implements SubagentController {
|
|
|
85
99
|
}): Promise<SubagentSessionController>;
|
|
86
100
|
beginParentExecution(execution: SubagentParentExecution): Promise<void>;
|
|
87
101
|
/** A retained cell must not borrow a later model Execution's identity or task budget. */
|
|
88
|
-
captureExecution(): SubagentController;
|
|
102
|
+
captureExecution(executionId?: string): SubagentController;
|
|
103
|
+
/** SDK physical tool ingress shares the same captured parent as its model controls. */
|
|
104
|
+
captureParentFrame(executionId?: string): <Value>(run: () => Value) => Value;
|
|
105
|
+
withOperation<T>(operation: SubagentParentOperation, run: () => T): T;
|
|
106
|
+
inspect(agentId: string): Promise<SubagentTaskSnapshot>;
|
|
107
|
+
inspectAll(): readonly SubagentTaskSnapshot[];
|
|
108
|
+
cancelOwned(ownerInstanceId: string): Promise<void>;
|
|
89
109
|
endParentExecution(executionId: string): void;
|
|
110
|
+
parentExecution(executionId: string): SubagentParentExecution | undefined;
|
|
90
111
|
run(input: SubagentTaskInput, options?: SubagentRunOptions): Promise<SubagentTaskSnapshot>;
|
|
91
112
|
output(agentId: string, options?: SubagentOutputOptions): Promise<SubagentTaskSnapshot>;
|
|
92
113
|
outputAny(options?: SubagentOutputOptions): Promise<SubagentTaskSnapshot>;
|