@myagentroam/agent 0.11.38 → 0.11.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/cli/catalog-file.d.ts +2 -0
  2. package/dist/cli/main.js +11 -4
  3. package/dist/cli/subagent/control.d.ts +56 -0
  4. package/dist/cli/subagent/control.js +219 -0
  5. package/dist/cli/subagent/history.d.ts +17 -0
  6. package/dist/cli/subagent/history.js +85 -0
  7. package/dist/cli/subagent/index.d.ts +10 -0
  8. package/dist/cli/subagent/index.js +94 -0
  9. package/dist/cli/subagent/native.d.ts +91 -0
  10. package/dist/cli/subagent/native.js +359 -0
  11. package/dist/cli/subagent/observations.d.ts +57 -0
  12. package/dist/cli/subagent/observations.js +351 -0
  13. package/dist/cli/subagent/query.d.ts +4 -0
  14. package/dist/cli/subagent/query.js +114 -0
  15. package/dist/cli/subagent/resources.d.ts +3 -0
  16. package/dist/cli/subagent/resources.js +112 -0
  17. package/dist/cli/subagent/snapshot.d.ts +77 -0
  18. package/dist/cli/subagent/snapshot.js +127 -0
  19. package/dist/cli/subagent/start.d.ts +24 -0
  20. package/dist/cli/subagent/start.js +201 -0
  21. package/dist/cli/subagent/wait-output.d.ts +19 -0
  22. package/dist/cli/subagent/wait-output.js +56 -0
  23. package/dist/cli/subagent/wait.d.ts +175 -0
  24. package/dist/cli/subagent/wait.js +453 -0
  25. package/dist/extensions/contracts.d.ts +2 -5
  26. package/dist/extensions/manager.d.ts +0 -2
  27. package/dist/extensions/manager.js +1 -2
  28. package/dist/extensions/models.d.ts +3 -0
  29. package/dist/extensions/operation.d.ts +8 -0
  30. package/dist/extensions/operation.js +3 -0
  31. package/dist/extensions/services.d.ts +9 -1
  32. package/dist/extensions/services.js +26 -3
  33. package/dist/extensions/session-context.d.ts +18 -0
  34. package/dist/extensions/session-context.js +4 -0
  35. package/dist/extensions/session-input.d.ts +2 -2
  36. package/dist/extensions/session-input.js +3 -4
  37. package/dist/extensions/session-messages.d.ts +1 -1
  38. package/dist/extensions/session-tools.js +20 -3
  39. package/dist/extensions/session.d.ts +1 -4
  40. package/dist/extensions/session.js +2 -16
  41. package/dist/extensions/tools.js +21 -3
  42. package/dist/extensions.d.ts +5 -2
  43. package/dist/extensions.js +2 -1
  44. package/dist/host/local-agent-host.d.ts +2 -2
  45. package/dist/host/local-exec-tool.d.ts +1 -1
  46. package/dist/host/local-exec-tool.js +28 -3
  47. package/dist/host/local-tool-registry.d.ts +2 -2
  48. package/dist/index.d.ts +2 -2
  49. package/dist/model/configuration.d.ts +3 -1
  50. package/dist/model/configuration.js +1 -0
  51. package/dist/model/openai-responses.js +5 -0
  52. package/dist/model/responses-request.js +9 -0
  53. package/dist/prompts/index.js +1 -3
  54. package/dist/prompts/resources.d.ts +1 -5
  55. package/dist/prompts/resources.js +3 -12
  56. package/dist/prompts/subagent.d.ts +1 -1
  57. package/dist/prompts/subagent.js +1 -1
  58. package/dist/runtime/context/gc-candidates-web.d.ts +6 -0
  59. package/dist/runtime/context/gc-candidates-web.js +53 -1
  60. package/dist/runtime/context/gc-candidates.js +1 -9
  61. package/dist/runtime/context/gc.js +19 -11
  62. package/dist/runtime/context/projection.js +1 -0
  63. package/dist/runtime/events.d.ts +4 -3
  64. package/dist/runtime/execution-handle.d.ts +2 -0
  65. package/dist/runtime/wait.js +12 -2
  66. package/dist/sdk/agent.js +94 -5
  67. package/dist/sdk/execution-context-coordinator.d.ts +2 -3
  68. package/dist/sdk/execution-context-coordinator.js +3 -3
  69. package/dist/sdk/execution-input.d.ts +3 -4
  70. package/dist/sdk/execution-input.js +31 -25
  71. package/dist/sdk/execution-round-preparation.d.ts +1 -1
  72. package/dist/sdk/execution-round-preparation.js +4 -4
  73. package/dist/sdk/execution-tool-catalog.js +2 -10
  74. package/dist/sdk/execution-tool-scope.d.ts +1 -3
  75. package/dist/sdk/execution-tool-scope.js +10 -12
  76. package/dist/sdk/extension-models.js +3 -0
  77. package/dist/sdk/extension-session-launch.d.ts +3 -2
  78. package/dist/sdk/extension-session-launch.js +2 -3
  79. package/dist/sdk/extension-tools.js +0 -2
  80. package/dist/sdk/native-steer.d.ts +2 -2
  81. package/dist/sdk/native-steer.js +2 -2
  82. package/dist/sdk/session-composition.d.ts +2 -17
  83. package/dist/sdk/session-execution-factory.d.ts +2 -3
  84. package/dist/sdk/session-execution-factory.js +20 -114
  85. package/dist/sdk/session-execution.d.ts +13 -7
  86. package/dist/sdk/session-execution.js +11 -3
  87. package/dist/sdk/session-extension-messages.d.ts +1 -3
  88. package/dist/sdk/session-extension-messages.js +14 -9
  89. package/dist/sdk/session-extension-prompt.js +1 -3
  90. package/dist/sdk/session-maintenance.d.ts +1 -0
  91. package/dist/sdk/session-maintenance.js +16 -7
  92. package/dist/sdk/session-open.d.ts +2 -3
  93. package/dist/sdk/session-open.js +5 -24
  94. package/dist/sdk/session-runtime.d.ts +19 -8
  95. package/dist/sdk/session-runtime.js +36 -30
  96. package/dist/sdk/session.d.ts +3 -2
  97. package/dist/sdk/session.js +22 -89
  98. package/dist/sdk/types.d.ts +36 -0
  99. package/dist/session/catalog.d.ts +22 -1
  100. package/dist/session/catalog.js +102 -0
  101. package/dist/session/context-gc-checkpoint.js +30 -1
  102. package/dist/session/context-gc-contract.d.ts +7 -0
  103. package/dist/session/context-gc-replacements.d.ts +3 -0
  104. package/dist/session/context-gc-replacements.js +26 -0
  105. package/dist/session/store/contracts.d.ts +2 -2
  106. package/dist/session/store/jsonl/directory-index.d.ts +1 -1
  107. package/dist/session/store/jsonl/directory-index.js +18 -17
  108. package/dist/session/store/jsonl/history.js +1 -1
  109. package/dist/session/store/jsonl/index-transaction.js +2 -8
  110. package/dist/session/store/jsonl/jsonl-store.d.ts +1 -1
  111. package/dist/session/store/jsonl/jsonl-store.js +24 -9
  112. package/dist/session/types.d.ts +1 -0
  113. package/dist/tools/agent/sleep.js +1 -1
  114. package/dist/tools/callers.js +1 -12
  115. package/dist/tools/code-mode/dispatch.js +5 -2
  116. package/dist/tools/code-mode/index.d.ts +1 -1
  117. package/dist/tools/code-mode/index.js +2 -0
  118. package/dist/tools/contracts.d.ts +20 -0
  119. package/dist/tools/execution-limits.d.ts +5 -20
  120. package/dist/tools/execution-limits.js +0 -2
  121. package/dist/tools/execution.js +16 -4
  122. package/dist/tools/local/exec.js +8 -2
  123. package/dist/tools/output-projection.d.ts +5 -2
  124. package/dist/tools/output-projection.js +133 -92
  125. package/dist/tools/registry.js +1 -13
  126. package/dist/tools/runtime-context.d.ts +1 -21
  127. package/package.json +2 -2
  128. package/dist/extensions/agents.d.ts +0 -100
  129. package/dist/extensions/agents.js +0 -121
  130. package/dist/runtime/context/gc-candidates-subagent.d.ts +0 -6
  131. package/dist/runtime/context/gc-candidates-subagent.js +0 -100
  132. package/dist/sdk/extension-agent-controller.d.ts +0 -3
  133. package/dist/sdk/extension-agent-controller.js +0 -69
  134. package/dist/sdk/extension-agents.d.ts +0 -9
  135. package/dist/sdk/extension-agents.js +0 -58
  136. package/dist/sdk/subagent-backend.d.ts +0 -3
  137. package/dist/sdk/subagent-backend.js +0 -49
  138. package/dist/subagent/contracts.d.ts +0 -107
  139. package/dist/subagent/contracts.js +0 -1
  140. package/dist/subagent/limits.d.ts +0 -17
  141. package/dist/subagent/limits.js +0 -17
  142. package/dist/subagent/model-update.d.ts +0 -2
  143. package/dist/subagent/model-update.js +0 -81
  144. package/dist/subagent/session-controller.d.ts +0 -87
  145. package/dist/subagent/session-controller.js +0 -1343
  146. package/dist/subagent/task.d.ts +0 -134
  147. package/dist/subagent/task.js +0 -436
  148. package/dist/subagent/turn-contracts.d.ts +0 -64
  149. package/dist/subagent/turn-contracts.js +0 -1
  150. package/dist/subagent/wait-targets.d.ts +0 -10
  151. package/dist/subagent/wait-targets.js +0 -26
  152. package/dist/subagent/wait.d.ts +0 -26
  153. package/dist/subagent/wait.js +0 -73
  154. package/dist/tools/agent/agent-cancel.d.ts +0 -7
  155. package/dist/tools/agent/agent-cancel.js +0 -29
  156. package/dist/tools/agent/agent-message.d.ts +0 -7
  157. package/dist/tools/agent/agent-message.js +0 -57
  158. package/dist/tools/agent/agent-start.d.ts +0 -12
  159. package/dist/tools/agent/agent-start.js +0 -106
  160. package/dist/tools/agent/agent-wait.d.ts +0 -7
  161. package/dist/tools/agent/agent-wait.js +0 -91
@@ -8,6 +8,10 @@ import type { SessionListInput, SessionSource, SessionTitleUpdateInput } from '.
8
8
  export interface CreateSessionInput {
9
9
  workspace: string;
10
10
  title?: string;
11
+ /** Optional creation relationship; authorization and depth are owned by the Host. */
12
+ parentSessionId?: string;
13
+ /** Optional provenance snapshot; it cannot contradict the creation relationship. */
14
+ source?: SessionSource;
11
15
  }
12
16
  export type { SessionTitleUpdateInput } from '../session/types.js';
13
17
  export type { HostExecutionExtensions, HostSessionResourceSnapshot, HostSkill, AgentHost } from '../host/contracts.js';
@@ -35,20 +39,50 @@ export type SteerResult = {
35
39
  /** A persisted Session permits one active main execution at a time. */
36
40
  export interface AgentSession {
37
41
  readonly id: string;
42
+ /** Subscribe to future events and atomically capture the currently active Execution, if any. */
43
+ subscribe(listener: (event: import('../runtime/events.js').AgentEvent) => void): {
44
+ readonly activeExecutionId?: string;
45
+ /** Terminal fact already published by the still-admitted Execution, if any. */
46
+ readonly terminalEvent?: Extract<import('../runtime/events.js').AgentEvent, {
47
+ type: 'execution.completed' | 'execution.failed' | 'execution.cancelled';
48
+ }>;
49
+ unsubscribe(): void;
50
+ };
38
51
  run(input: RunInput): ExecutionHandle;
39
52
  plan(input: RunInput): ExecutionHandle;
40
53
  steer(input: SteerInput): Promise<SteerResult>;
41
54
  compact(input?: CompactInput): ExecutionHandle;
42
55
  cancel(executionId: string): Promise<void>;
56
+ /** Join a currently owned Execution after its cleanup, without cancelling it. */
57
+ joinExecution(executionId: string): Promise<import('../runtime/contracts.js').ExecutionResult> | undefined;
58
+ }
59
+ /** A Session already owned by this Agent, with no history opening or replay. */
60
+ export interface AgentLiveSession {
61
+ readonly id: string;
62
+ readonly workspace: string;
63
+ readonly source: SessionSource;
64
+ readonly subscribe: AgentSession['subscribe'];
43
65
  }
44
66
  export interface Agent {
45
67
  extensionDiagnostics(): readonly import('../extensions/contracts.js').ExtensionDiagnostic[];
68
+ /** Atomically subscribe to future admitted Executions and snapshot currently admitted Sessions. */
69
+ observeSessionExecutions(listener: (session: AgentLiveSession) => void): {
70
+ readonly sessions: readonly AgentLiveSession[];
71
+ unsubscribe(): void;
72
+ };
46
73
  createSession(input: CreateSessionInput): Promise<AgentSession>;
47
74
  openSession(sessionId: string): Promise<AgentSession>;
48
75
  listSessions(input?: SessionListInput): Promise<AgentSessionPage>;
49
76
  getSession(sessionId: string): Promise<AgentSessionSummary & {
50
77
  automaticTitlePending: boolean;
51
78
  }>;
79
+ /** Read persisted Session history without opening or starting an Execution. */
80
+ sessionHistory: import('../session/catalog.js').AgentSessionCatalog['history'];
81
+ sessionInputReceipt: import('../session/catalog.js').AgentSessionCatalog['inputReceipt'];
82
+ /** Check an unfinished persisted Execution under the Session's exclusive writer admission. */
83
+ sessionExecutionWasInterrupted(sessionId: string, executionId: string, input?: {
84
+ readonly signal?: AbortSignal;
85
+ }): Promise<boolean>;
52
86
  setSessionTitle(sessionId: string, title: string, input?: SessionTitleUpdateInput): Promise<boolean>;
53
87
  clearSession(sessionId: string): Promise<void>;
54
88
  /** Updates future execution snapshots only; undefined clears the Host instruction extension. */
@@ -59,6 +93,8 @@ export interface Agent {
59
93
  readonly defaultModelId?: string;
60
94
  }): Promise<void>;
61
95
  deleteSession(sessionId: string): Promise<void>;
96
+ /** Deletes the explicit members under one maintenance admission; never infers descendants. */
97
+ deleteSessions(sessionIds: readonly string[]): Promise<void>;
62
98
  /** Joins executions/reconfiguration, closes owned resources, and releases borrowed Store leases. */
63
99
  dispose(): Promise<void>;
64
100
  }
@@ -15,6 +15,14 @@ export interface AgentSessionHistoryPage {
15
15
  entries: AgentSessionHistoryEntry[];
16
16
  nextCursor?: string;
17
17
  }
18
+ export interface AgentSessionInputReceipt {
19
+ readonly clientMessageId: string;
20
+ readonly executionId: string;
21
+ readonly turnId: string;
22
+ readonly status: 'accepted' | 'applied';
23
+ readonly appliedExecutionId?: string;
24
+ readonly appliedTurnId?: string;
25
+ }
18
26
  export interface AgentSessionContextUsage {
19
27
  inputTokens: number;
20
28
  }
@@ -40,6 +48,17 @@ export interface AgentSessionCatalog {
40
48
  limit?: number;
41
49
  cursor?: string;
42
50
  }): Promise<AgentSessionHistoryPage>;
51
+ /** Read the specified receipt, or the latest receipt associated with the fixed Execution. */
52
+ inputReceipt(sessionId: string, input: {
53
+ clientMessageId?: string;
54
+ executionId: string;
55
+ signal?: AbortSignal;
56
+ source?: {
57
+ sessionId: string;
58
+ extensionId: string;
59
+ actors?: readonly string[];
60
+ };
61
+ }): Promise<AgentSessionInputReceipt | undefined>;
43
62
  readImage(sessionId: string, input: {
44
63
  readonly sequence: number;
45
64
  readonly imageIndex: number;
@@ -57,6 +76,8 @@ export interface AgentSessionCatalog {
57
76
  rollback(sessionId: string, input: AgentSessionRollbackInput): Promise<AgentSessionSummary>;
58
77
  clear(sessionId: string): Promise<AgentSessionSummary>;
59
78
  delete(sessionId: string): Promise<AgentSessionDeleteResult>;
79
+ /** Delete exactly these members after their owners have released their resources. */
80
+ deleteMembers(sessionIds: readonly string[]): Promise<AgentSessionDeleteResult>;
60
81
  close(): Promise<void>;
61
82
  }
62
83
  export type OpenAgentSessionCatalogInput = {
@@ -66,5 +87,5 @@ export type OpenAgentSessionCatalogInput = {
66
87
  sessionStore: SessionStore;
67
88
  homeDirectory?: never;
68
89
  };
69
- export declare function createSessionCatalog(store: Pick<SessionStore, 'list' | 'firstUserMessagePreview' | 'executionConfiguration' | 'readPage' | 'readInputImage' | 'readResource' | 'readToolResultResource' | 'forkSession' | 'rollbackSession' | 'clearSession' | 'delete' | 'close'>, borrowed: boolean): AgentSessionCatalog;
90
+ export declare function createSessionCatalog(store: Pick<SessionStore, 'list' | 'firstUserMessagePreview' | 'executionConfiguration' | 'readPage' | 'readInputImage' | 'readResource' | 'readToolResultResource' | 'forkSession' | 'rollbackSession' | 'clearSession' | 'delete' | 'acquireSessionMaintenance' | 'close'>, borrowed: boolean): AgentSessionCatalog;
70
91
  export declare function imageMetadata(value: unknown): AgentSessionHistoryImageMetadata[];
@@ -1,5 +1,6 @@
1
1
  import { AgentError } from '../error.js';
2
2
  import { publicHostedToolEvents } from '../runtime/event-projection.js';
3
+ import { appliedInputRecord } from './deferred-input.js';
3
4
  const MAX_INPUT_IMAGE_BYTES = 5 * 1024 * 1024;
4
5
  const INPUT_IMAGE_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp']);
5
6
  export function createSessionCatalog(store, borrowed) {
@@ -21,6 +22,80 @@ export function createSessionCatalog(store, borrowed) {
21
22
  assertOpen();
22
23
  return store.executionConfiguration(sessionId);
23
24
  },
25
+ inputReceipt: async (sessionId, input) => {
26
+ assertOpen();
27
+ if ((input.clientMessageId !== undefined && !input.clientMessageId.trim()) ||
28
+ !input.executionId.trim())
29
+ throw new AgentError('MAR_AGENT_SESSION_INPUT_INVALID', 'Input receipt identity is invalid.');
30
+ const applied = new Map();
31
+ let beforeSequence;
32
+ do {
33
+ input.signal?.throwIfAborted();
34
+ const page = await store.readPage(sessionId, {
35
+ limit: 1000,
36
+ ...(beforeSequence === undefined ? {} : { beforeSequence })
37
+ });
38
+ input.signal?.throwIfAborted();
39
+ for (const record of [...page.records].reverse()) {
40
+ if (!isRecord(record.payload))
41
+ continue;
42
+ if (record.type === 'input.applied' &&
43
+ typeof record.payload.inputRef === 'string' &&
44
+ !applied.has(record.payload.inputRef))
45
+ applied.set(record.payload.inputRef, {
46
+ ...(record.executionId === undefined ? {} : { executionId: record.executionId }),
47
+ ...(record.turnId === undefined ? {} : { turnId: record.turnId })
48
+ });
49
+ if (record.type === 'model.context' &&
50
+ isRecord(record.payload.input) &&
51
+ (input.clientMessageId === undefined ||
52
+ record.payload.input.clientMessageId === input.clientMessageId)) {
53
+ const projected = appliedInputRecord(record);
54
+ if (projected && !applied.has(projected.recordId))
55
+ applied.set(projected.recordId, {
56
+ ...(record.executionId === undefined ? {} : { executionId: record.executionId }),
57
+ ...(record.turnId === undefined ? {} : { turnId: record.turnId })
58
+ });
59
+ }
60
+ if (record.type !== 'turn.user' ||
61
+ !record.executionId ||
62
+ !record.turnId ||
63
+ typeof record.payload.clientMessageId !== 'string' ||
64
+ !record.payload.clientMessageId ||
65
+ (input.clientMessageId !== undefined &&
66
+ record.payload.clientMessageId !== input.clientMessageId))
67
+ continue;
68
+ if (input.source) {
69
+ const source = record.payload.source;
70
+ if (!isRecord(source) ||
71
+ source.sessionId !== input.source.sessionId ||
72
+ source.extensionId !== input.source.extensionId ||
73
+ (input.source.actors && !input.source.actors.includes(String(source.actor))))
74
+ continue;
75
+ }
76
+ const application = applied.get(record.recordId);
77
+ if (input.clientMessageId === undefined
78
+ ? record.executionId !== input.executionId &&
79
+ application?.executionId !== input.executionId
80
+ : record.executionId !== input.executionId)
81
+ continue;
82
+ return {
83
+ clientMessageId: record.payload.clientMessageId,
84
+ executionId: record.executionId,
85
+ turnId: record.turnId,
86
+ status: application ? 'applied' : 'accepted',
87
+ ...{
88
+ ...(application?.executionId === undefined
89
+ ? {}
90
+ : { appliedExecutionId: application.executionId }),
91
+ ...(application?.turnId === undefined ? {} : { appliedTurnId: application.turnId })
92
+ }
93
+ };
94
+ }
95
+ beforeSequence = page.previousBeforeSequence;
96
+ } while (beforeSequence !== undefined);
97
+ return undefined;
98
+ },
24
99
  history: async (sessionId, options = {}) => {
25
100
  assertOpen();
26
101
  const beforeSequence = parseCursor(options.cursor);
@@ -139,6 +214,33 @@ export function createSessionCatalog(store, borrowed) {
139
214
  assertOpen();
140
215
  return store.delete(sessionId);
141
216
  },
217
+ deleteMembers: async (sessionIds) => {
218
+ assertOpen();
219
+ const ids = [...sessionIds];
220
+ if (ids.length === 0 ||
221
+ ids.some((id) => typeof id !== 'string' || id.length === 0) ||
222
+ new Set(ids).size !== ids.length)
223
+ throw new AgentError('MAR_AGENT_SESSION_INVALID', 'Invalid Session maintenance members.');
224
+ ids.sort();
225
+ const maintenance = await store.acquireSessionMaintenance(ids[0], 'delete', undefined, ids);
226
+ let failed = false;
227
+ try {
228
+ return await store.delete(ids[0], maintenance);
229
+ }
230
+ catch (error) {
231
+ failed = true;
232
+ throw error;
233
+ }
234
+ finally {
235
+ try {
236
+ await maintenance.release();
237
+ }
238
+ catch (error) {
239
+ if (!failed)
240
+ throw error;
241
+ }
242
+ }
243
+ },
142
244
  close: () => {
143
245
  closure ??= Promise.resolve().then(() => (borrowed ? undefined : store.close()));
144
246
  return closure;
@@ -3,7 +3,7 @@ import { isDeepStrictEqual } from 'node:util';
3
3
  import { inlineMediaGcAnchored, parseInlineMediaGc } from './inline-media.js';
4
4
  import { codeCallEvidence, codePartialPatchCallEvidence, callsFromRecord, codeObservationEvidence, observationsFromRecord, retainedCodeInputs } from './context-gc-observations.js';
5
5
  import { parseReadContextOutput } from '../prompts/read-context.js';
6
- import { codeCellCompletionSequence, contextGcApplyPatchInputReplacement, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcPatchResultFromFailedPayload, contextGcStaleReadReplacement, contextGcWebOutputReplacement, normalizeContextGcCodeModeInput, normalizeContextGcExecResult } from './context-gc-replacements.js';
6
+ import { codeCellCompletionSequence, contextGcApplyPatchInputReplacement, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcPatchResultFromFailedPayload, contextGcStaleReadReplacement, contextGcToolOutputReplacement, contextGcWebOutputReplacement, isGenericContextGcTool, normalizeContextGcCodeModeInput, normalizeContextGcExecResult } from './context-gc-replacements.js';
7
7
  import { contextGcSubagentNeedsRetainedPriorFinal, contextGcSubagentOutputReplacement, contextGcSubagentSnapshotIsPreciselyCovered, contextGcSubagentSnapshotIsSuperseded, normalizeContextGcSubagentSnapshot } from './context-gc-subagent.js';
8
8
  export const CONTEXT_GC_CHECKPOINT_VERSION = 1;
9
9
  export const CONTEXT_GC_MAX_TARGETS = 4_096;
@@ -350,6 +350,20 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
350
350
  value.truncated === true) === true
351
351
  : evidence.completed.payload.truncated));
352
352
  }
353
+ if (replacement.kind === 'TOOL_OUTPUT') {
354
+ const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, replacement.toolName, replacement.callId);
355
+ return (isGenericContextGcTool(replacement.toolName) &&
356
+ evidence !== undefined &&
357
+ evidence.result.sequence === replacement.target.sequence &&
358
+ !hasFailedToolEvidence(records, evidence.call, evidence.result, replacement.toolName) &&
359
+ isRecord(evidence.completed.payload) &&
360
+ typeof evidence.completed.payload.truncated === 'boolean' &&
361
+ typeof source.payload.content === 'string' &&
362
+ isRecord(evidence.result.payload) &&
363
+ evidence.result.payload.content === source.payload.content &&
364
+ replacement.replacementContent ===
365
+ contextGcToolOutputReplacement(replacement.toolName, source.payload.content, evidence.completed.payload.truncated));
366
+ }
353
367
  if (replacement.kind === 'SUBAGENT_OUTPUT') {
354
368
  const retainedPriorFinal = replacement.retainedPriorFinal;
355
369
  const sourceEvidence = successfulToolEvidence(records, recordsBySequence, source.sequence, replacement.toolName);
@@ -808,6 +822,21 @@ function isContextGcReplacement(value) {
808
822
  (value.target.toolName === undefined || value.target.toolName === value.toolName) &&
809
823
  isNonemptyString(value.replacementContent) &&
810
824
  isPositiveInteger(value.evidenceSequence));
825
+ if (value.kind === 'TOOL_OUTPUT')
826
+ return (hasOnlyKeys(value, [
827
+ 'kind',
828
+ 'toolName',
829
+ 'callId',
830
+ 'target',
831
+ 'replacementContent',
832
+ 'evidenceSequence'
833
+ ]) &&
834
+ value.target.role === 'tool' &&
835
+ isNonemptyString(value.toolName) &&
836
+ isGenericContextGcTool(value.toolName) &&
837
+ (value.target.toolName === undefined || value.target.toolName === value.toolName) &&
838
+ isNonemptyString(value.replacementContent) &&
839
+ isPositiveInteger(value.evidenceSequence));
811
840
  if (value.kind === 'CODE_MODE_OUTPUT')
812
841
  return (hasOnlyKeys(value, ['kind', 'callId', 'target', 'replacementContent', 'evidenceSequence']) &&
813
842
  value.target.role === 'tool' &&
@@ -68,6 +68,13 @@ export type ContextGcReplacement = {
68
68
  readonly target: ContextGcTarget;
69
69
  readonly replacementContent: string;
70
70
  readonly evidenceSequence: number;
71
+ } | {
72
+ readonly kind: 'TOOL_OUTPUT';
73
+ readonly toolName: string;
74
+ readonly callId: string;
75
+ readonly target: ContextGcTarget;
76
+ readonly replacementContent: string;
77
+ readonly evidenceSequence: number;
71
78
  } | {
72
79
  readonly kind: 'SUBAGENT_OUTPUT';
73
80
  readonly toolName: 'agent_wait' | 'agent_message' | 'agent_cancel';
@@ -55,6 +55,9 @@ export declare function contextGcStaleReadReplacement(input: {
55
55
  readonly operation: Exclude<ContextGcPatchFile['operation'], 'added'>;
56
56
  }): string;
57
57
  export declare function contextGcWebOutputReplacement(toolName: ContextGcWebToolName, originalContent: string, truncated: boolean): string;
58
+ /** Tools with their own evidence and lifecycle policies are never handled as generic output. */
59
+ export declare function isGenericContextGcTool(toolName: string): boolean;
60
+ export declare function contextGcToolOutputReplacement(toolName: string, originalContent: string, truncated: boolean): string;
58
61
  export declare function contextGcExecInputReplacement(arguments_: unknown, toolName?: string): unknown;
59
62
  export declare function contextGcExecOutputReplacement(result: ContextGcExecResult): string;
60
63
  export declare function normalizeContextGcExecResult(modelContent: unknown, completedPayload: unknown): ContextGcExecResult | undefined;
@@ -82,6 +82,32 @@ export function contextGcWebOutputReplacement(toolName, originalContent, truncat
82
82
  contextGc: 'Detailed web tool output was reclaimed after six successful rollouts. An incomplete preview is not the complete search result or page, and GC did not access the network.'
83
83
  });
84
84
  }
85
+ /** Tools with their own evidence and lifecycle policies are never handled as generic output. */
86
+ export function isGenericContextGcTool(toolName) {
87
+ return (toolName.length > 0 &&
88
+ ![
89
+ 'read',
90
+ 'apply_patch',
91
+ 'exec',
92
+ 'exec_wait',
93
+ 'code_exec',
94
+ 'code_exec_wait',
95
+ 'web_search',
96
+ 'web_fetch'
97
+ ].includes(toolName));
98
+ }
99
+ export function contextGcToolOutputReplacement(toolName, originalContent, truncated) {
100
+ const preview = unicodeHeadTail(originalContent, EXEC_OUTPUT_PREVIEW_CHARACTERS);
101
+ return JSON.stringify({
102
+ toolName,
103
+ originalBytes: Buffer.byteLength(originalContent),
104
+ lines: lineCount(originalContent),
105
+ truncated,
106
+ preview,
107
+ previewComplete: !truncated && preview === originalContent,
108
+ contextGc: 'Detailed tool output was reclaimed after six successful rollouts. An incomplete preview is not the complete result; the tool was not replayed.'
109
+ });
110
+ }
85
111
  export function contextGcExecInputReplacement(arguments_, toolName = 'exec') {
86
112
  if (!isRecord(arguments_))
87
113
  return contextGcMarkup('terminal_exec_input', {
@@ -144,8 +144,8 @@ export interface SessionStore {
144
144
  /** Retention requires the originating owner to hold an active Execution lease. */
145
145
  retainSessionLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
146
146
  hasRetainedSessionLease(sessionId: string, owner: object): boolean;
147
- /** Conflicts fail before cleanup; delete includes the Store's current Session tree. */
148
- acquireSessionMaintenance(sessionId: string, kind: 'clear' | 'delete', owner?: object): Promise<SessionStoreMaintenance>;
147
+ /** Conflicts fail before cleanup; delete admits only the explicit members, or the given Session. */
148
+ acquireSessionMaintenance(sessionId: string, kind: 'clear' | 'delete', owner?: object, members?: readonly string[]): Promise<SessionStoreMaintenance>;
149
149
  forkSession(sessionId: string, input: {
150
150
  readonly boundaryTurnId: string | null;
151
151
  readonly title: string;
@@ -14,7 +14,7 @@ export declare function persistSessionIndex(path: string, index: SessionIndex):
14
14
  export declare function persistFormatSelection(path: string, index: SessionIndex): Promise<void>;
15
15
  export declare function normalizeSessionSource(value: SessionSource | undefined): SessionSource;
16
16
  interface SessionListFilter {
17
- readonly source: SessionSource['type'];
17
+ readonly source?: SessionSource['type'];
18
18
  readonly parentSessionId?: string;
19
19
  readonly rootSessionId?: string;
20
20
  }
@@ -184,8 +184,17 @@ export async function persistFormatSelection(path, index) {
184
184
  }
185
185
  }
186
186
  export function normalizeSessionSource(value) {
187
- if (value === undefined || value.type === 'user')
187
+ if (value === undefined)
188
188
  return { type: 'user' };
189
+ if (value.type === 'user') {
190
+ if (value.parentSessionId !== undefined &&
191
+ (typeof value.parentSessionId !== 'string' || !value.parentSessionId.trim()))
192
+ throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Session source is invalid.');
193
+ return {
194
+ type: 'user',
195
+ ...(value.parentSessionId === undefined ? {} : { parentSessionId: value.parentSessionId })
196
+ };
197
+ }
189
198
  if (value.type !== 'subagent' ||
190
199
  typeof value.parentSessionId !== 'string' ||
191
200
  !value.parentSessionId.trim() ||
@@ -221,27 +230,21 @@ export function normalizeSessionSource(value) {
221
230
  };
222
231
  }
223
232
  export function sessionListFilter(input) {
224
- const source = input.source ??
225
- (input.parentSessionId !== undefined || input.rootSessionId !== undefined
226
- ? 'subagent'
227
- : 'user');
228
- if ((source !== 'user' && source !== 'subagent') ||
229
- (input.parentSessionId !== undefined &&
230
- (source !== 'subagent' || input.parentSessionId.length === 0)) ||
231
- (input.rootSessionId !== undefined &&
232
- (source !== 'subagent' || input.rootSessionId.length === 0)))
233
+ const source = input.source;
234
+ if ((source !== undefined && source !== 'user' && source !== 'subagent') ||
235
+ (input.parentSessionId !== undefined && input.parentSessionId.length === 0) ||
236
+ (input.rootSessionId !== undefined && (source === 'user' || input.rootSessionId.length === 0)))
233
237
  throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Session list filter is invalid.');
234
238
  return {
235
- source,
239
+ ...(source === undefined ? {} : { source }),
236
240
  ...(input.parentSessionId === undefined ? {} : { parentSessionId: input.parentSessionId }),
237
241
  ...(input.rootSessionId === undefined ? {} : { rootSessionId: input.rootSessionId })
238
242
  };
239
243
  }
240
244
  export function sessionMatchesFilter(meta, filter) {
241
245
  const source = normalizeSessionSource(meta.source);
242
- return (source.type === filter.source &&
243
- (filter.parentSessionId === undefined ||
244
- (source.type === 'subagent' && source.parentSessionId === filter.parentSessionId)) &&
246
+ return ((filter.source === undefined || source.type === filter.source) &&
247
+ (filter.parentSessionId === undefined || source.parentSessionId === filter.parentSessionId) &&
245
248
  (filter.rootSessionId === undefined ||
246
249
  (source.type === 'subagent' && source.rootSessionId === filter.rootSessionId)));
247
250
  }
@@ -273,9 +276,7 @@ export function sessionTreeIds(sessions, sessionId) {
273
276
  const parentId = ids[index];
274
277
  for (const session of sessions) {
275
278
  const source = normalizeSessionSource(session.source);
276
- if (source.type === 'subagent' &&
277
- source.parentSessionId === parentId &&
278
- !ids.includes(session.id))
279
+ if (source.parentSessionId === parentId && !ids.includes(session.id))
279
280
  ids.push(session.id);
280
281
  }
281
282
  }
@@ -155,7 +155,7 @@ export function forkedPayload(type, value, sourceSessionId, targetSessionId, tit
155
155
  : {})
156
156
  };
157
157
  };
158
- if (type === 'model.context')
158
+ if (type === 'model.context' || type === 'input.applied')
159
159
  return application(payload);
160
160
  if (type === 'compact.completed') {
161
161
  if (payload.inlineMediaState)
@@ -21,7 +21,7 @@ export async function commitSessionIndex(root, snapshot, committed, deletedIds,
21
21
  if (!created)
22
22
  throw new AgentError('MAR_AGENT_SESSION_CORRUPT', 'Session publication is missing.');
23
23
  const source = normalizeSessionSource(created.source);
24
- if (source.type !== 'subagent')
24
+ if (source.parentSessionId === undefined)
25
25
  continue;
26
26
  if (!disk.sessions.some((entry) => entry.id === source.parentSessionId))
27
27
  throw new ParentPublicationRejected('MAR_AGENT_SESSION_NOT_FOUND', 'Parent Session was not found.');
@@ -35,7 +35,6 @@ export async function commitSessionIndex(root, snapshot, committed, deletedIds,
35
35
  }
36
36
  }
37
37
  if (deleteExpected) {
38
- const ids = new Set(deleteExpected.map((entry) => entry.id));
39
38
  for (const expected of deleteExpected) {
40
39
  const current = disk.sessions.find((entry) => entry.id === expected.id);
41
40
  if (!current ||
@@ -45,12 +44,7 @@ export async function commitSessionIndex(root, snapshot, committed, deletedIds,
45
44
  !isDeepStrictEqual(current.storage, expected.storage) ||
46
45
  current.storageTransactionId !== expected.storageTransactionId ||
47
46
  current.nextSequence !== expected.nextSequence)
48
- throw new AgentError('MAR_AGENT_SESSION_CORRUPT', 'Session tree changed during deletion.');
49
- }
50
- for (const entry of disk.sessions) {
51
- const source = normalizeSessionSource(entry.source);
52
- if (source.type === 'subagent' && ids.has(source.parentSessionId) && !ids.has(entry.id))
53
- throw new AgentError('MAR_AGENT_SESSION_CORRUPT', 'Session tree changed during deletion.');
47
+ throw new AgentError('MAR_AGENT_SESSION_CORRUPT', 'Session changed during deletion.');
54
48
  }
55
49
  }
56
50
  const local = snapshot();
@@ -115,7 +115,7 @@ export declare class JsonlSessionStore implements SessionStore {
115
115
  /** A warm cell retains physical writer ownership between Executions of the same Agent. */
116
116
  acquireSessionLease(sessionId: string, owner?: object): Promise<() => Promise<void>>;
117
117
  hasRetainedSessionLease(sessionId: string, owner: object): boolean;
118
- acquireSessionMaintenance(sessionId: string, kind: 'clear' | 'delete', owner?: object): Promise<SessionStoreMaintenance>;
118
+ acquireSessionMaintenance(sessionId: string, kind: 'clear' | 'delete', owner?: object, members?: readonly string[]): Promise<SessionStoreMaintenance>;
119
119
  /** Native writes join their Agent's sole writer, without admitting another Execution. */
120
120
  acquireSessionWriteLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
121
121
  retainSessionLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
@@ -219,20 +219,21 @@ export class JsonlSessionStore {
219
219
  async createSession(input) {
220
220
  this.#assertOpen();
221
221
  const source = normalizeSessionSource(input.source);
222
- if (source.type !== 'subagent')
222
+ if (source.parentSessionId === undefined)
223
223
  return this.#createSession(input, source);
224
+ const parentSessionId = source.parentSessionId;
224
225
  // Publish under the parent's existing serial domain so tree admission cannot miss a child.
225
- return this.#serializeRecordOperation(source.parentSessionId, async () => {
226
+ return this.#serializeRecordOperation(parentSessionId, async () => {
226
227
  this.#assertOpen();
227
- if (this.#shared.writerLeases.get(source.parentSessionId)?.maintenance)
228
+ if (this.#shared.writerLeases.get(parentSessionId)?.maintenance)
228
229
  throw new AgentError('MAR_AGENT_SESSION_LOCKED', 'Session is under maintenance.');
229
- const release = await this.#acquirePublicationGate(source.parentSessionId, 5000);
230
+ const release = await this.#acquirePublicationGate(parentSessionId, 5000);
230
231
  let failure;
231
232
  try {
232
- await this.#recoverPendingTreeDelete(source.parentSessionId);
233
- if (this.#shared.writerLeases.get(source.parentSessionId)?.maintenance)
233
+ await this.#recoverPendingTreeDelete(parentSessionId);
234
+ if (this.#shared.writerLeases.get(parentSessionId)?.maintenance)
234
235
  throw new AgentError('MAR_AGENT_SESSION_LOCKED', 'Session is under maintenance.');
235
- await this.getSession(source.parentSessionId);
236
+ await this.getSession(parentSessionId);
236
237
  return await this.#createSession(input, source);
237
238
  }
238
239
  catch (error) {
@@ -1408,13 +1409,27 @@ export class JsonlSessionStore {
1408
1409
  const lease = this.#shared.writerLeases.get(sessionId);
1409
1410
  return lease?.owner === owner && lease.retained > 0;
1410
1411
  }
1411
- async acquireSessionMaintenance(sessionId, kind, owner) {
1412
+ async acquireSessionMaintenance(sessionId, kind, owner, members) {
1412
1413
  this.#assertOpen();
1414
+ const ids = members === undefined ? [sessionId] : [...members];
1415
+ if (ids.length === 0 ||
1416
+ !ids.includes(sessionId) ||
1417
+ new Set(ids).size !== ids.length ||
1418
+ (kind === 'clear' && (ids.length !== 1 || ids[0] !== sessionId)))
1419
+ throw new AgentError('MAR_AGENT_SESSION_INVALID', 'Invalid Session maintenance members.');
1420
+ ids.sort();
1421
+ for (const id of ids)
1422
+ this.#sessionRoot(id);
1413
1423
  await this.#recoverPendingTreeDelete(sessionId);
1414
1424
  return acquireTreeMaintenance({
1415
1425
  sessionId,
1416
1426
  kind,
1417
- tree: () => this.sessionTree(sessionId),
1427
+ tree: async () => {
1428
+ await this.#refreshIndex();
1429
+ if (ids.some((id) => !this.#shared.index.sessions.some((session) => session.id === id)))
1430
+ throw new AgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
1431
+ return ids;
1432
+ },
1418
1433
  acquire: (id, holding, registerGate) => this.#serializeRecordOperation(id, async () => {
1419
1434
  this.#assertOpen();
1420
1435
  registerGate(await this.#acquirePublicationGate(id));
@@ -1,6 +1,7 @@
1
1
  /** Public session provenance; storage may persist additional private metadata. */
2
2
  export type SessionSource = {
3
3
  readonly type: 'user';
4
+ readonly parentSessionId?: string;
4
5
  } | {
5
6
  readonly type: 'subagent';
6
7
  readonly parentSessionId: string;
@@ -36,7 +36,7 @@ export async function executeSleep(arguments_, context, callId) {
36
36
  durationMs,
37
37
  startedAt: new Date().toISOString()
38
38
  });
39
- const messages = context.subagentMessages;
39
+ const messages = context.contextUpdates;
40
40
  const outcome = await waitForToolCondition({
41
41
  ready: () => messages?.pending() ?? false,
42
42
  subscribe: (notify) => messages?.subscribe(notify) ?? (() => { }),
@@ -15,18 +15,7 @@ export function toolCallers(tool) {
15
15
  export function allowsToolCaller(tool, caller) {
16
16
  // Control tools cannot be widened by a Host override or historical definition.
17
17
  if (caller === 'programmatic' &&
18
- [
19
- 'code_exec',
20
- 'code_exec_wait',
21
- 'tool_search',
22
- 'question',
23
- 'skill',
24
- 'sleep',
25
- 'agent_start',
26
- 'agent_wait',
27
- 'agent_message',
28
- 'agent_cancel'
29
- ].includes(tool.name))
18
+ ['code_exec', 'code_exec_wait', 'tool_search', 'question', 'skill', 'sleep'].includes(tool.name))
30
19
  return false;
31
20
  return toolCallers(tool).includes(caller);
32
21
  }
@@ -32,7 +32,9 @@ export function createCodeToolDispatch(input) {
32
32
  ...(expected ? { expectedMcpTool: expected } : {})
33
33
  }, 'programmatic');
34
34
  const bounded = boundedModelToolOutput(call.name, result.content, result.data);
35
- const data = structuredToolEventData(call.name, result.data);
35
+ const data = structuredToolEventData(call.name, call.name === 'exec' || call.name === 'exec_wait'
36
+ ? (result.eventData ?? result.data)
37
+ : result.data);
36
38
  const preview = toolEventSummary(result.content);
37
39
  input.assertLive();
38
40
  await input.emit({
@@ -44,8 +46,9 @@ export function createCodeToolDispatch(input) {
44
46
  truncated: result.truncated === true || bounded.truncated || preview.truncated,
45
47
  ...(result.artifacts ? { artifacts: result.artifacts } : {})
46
48
  });
49
+ const { eventData: _eventData, ...guestResult } = result;
47
50
  return {
48
- ...result,
51
+ ...guestResult,
49
52
  content: bounded.content,
50
53
  truncated: result.truncated === true || bounded.truncated
51
54
  };
@@ -22,7 +22,7 @@ export declare class CodeModeTool {
22
22
  wait(value: unknown, context: ToolExecutionContext): Promise<ToolExecutionResult>;
23
23
  listSessionResources(sessionId: string): {
24
24
  cellId: string;
25
- status: "completed" | "failed" | "cancelled" | "running";
25
+ status: "running" | "failed" | "cancelled" | "completed";
26
26
  executionId: string;
27
27
  }[];
28
28
  constructor(paths: WorkspacePathResolver, environment: NodeJS.ProcessEnv | undefined, executor: LocalProcessExecutor, signalProcessTree: (child: import('node:child_process').ChildProcessWithoutNullStreams, signal: NodeJS.Signals) => Promise<void>, engineFactory?: CodeModeEngineFactory);
@@ -399,6 +399,8 @@ export class CodeModeTool {
399
399
  };
400
400
  const indices = original.result.images?.map(addImage);
401
401
  const data = [
402
+ 'exec',
403
+ 'exec_wait',
402
404
  'apply_patch',
403
405
  'todo_read',
404
406
  'todo_write',