@myagentroam/agent 0.11.39 → 0.11.41

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 (166) 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/permissions.d.ts +1 -1
  55. package/dist/prompts/permissions.js +1 -0
  56. package/dist/prompts/resources.d.ts +1 -5
  57. package/dist/prompts/resources.js +3 -12
  58. package/dist/prompts/subagent.d.ts +1 -1
  59. package/dist/prompts/subagent.js +1 -1
  60. package/dist/runtime/context/gc-candidates-web.d.ts +6 -0
  61. package/dist/runtime/context/gc-candidates-web.js +53 -1
  62. package/dist/runtime/context/gc-candidates.js +1 -9
  63. package/dist/runtime/context/gc.js +19 -11
  64. package/dist/runtime/context/projection.js +1 -0
  65. package/dist/runtime/events.d.ts +4 -3
  66. package/dist/runtime/execution-handle.d.ts +2 -0
  67. package/dist/runtime/wait.js +12 -2
  68. package/dist/sdk/agent.js +94 -5
  69. package/dist/sdk/execution-context-coordinator.d.ts +2 -3
  70. package/dist/sdk/execution-context-coordinator.js +3 -3
  71. package/dist/sdk/execution-input.d.ts +3 -4
  72. package/dist/sdk/execution-input.js +31 -25
  73. package/dist/sdk/execution-round-preparation.d.ts +1 -1
  74. package/dist/sdk/execution-round-preparation.js +4 -4
  75. package/dist/sdk/execution-tool-catalog.js +2 -10
  76. package/dist/sdk/execution-tool-scope.d.ts +1 -3
  77. package/dist/sdk/execution-tool-scope.js +10 -12
  78. package/dist/sdk/extension-models.js +3 -0
  79. package/dist/sdk/extension-session-launch.d.ts +3 -2
  80. package/dist/sdk/extension-session-launch.js +2 -3
  81. package/dist/sdk/extension-tools.js +0 -2
  82. package/dist/sdk/native-steer.d.ts +2 -2
  83. package/dist/sdk/native-steer.js +2 -2
  84. package/dist/sdk/session-composition.d.ts +2 -17
  85. package/dist/sdk/session-execution-factory.d.ts +2 -3
  86. package/dist/sdk/session-execution-factory.js +20 -114
  87. package/dist/sdk/session-execution.d.ts +13 -7
  88. package/dist/sdk/session-execution.js +11 -3
  89. package/dist/sdk/session-extension-messages.d.ts +1 -3
  90. package/dist/sdk/session-extension-messages.js +14 -9
  91. package/dist/sdk/session-extension-prompt.js +1 -3
  92. package/dist/sdk/session-maintenance.d.ts +1 -0
  93. package/dist/sdk/session-maintenance.js +16 -7
  94. package/dist/sdk/session-open.d.ts +2 -3
  95. package/dist/sdk/session-open.js +5 -24
  96. package/dist/sdk/session-runtime.d.ts +19 -8
  97. package/dist/sdk/session-runtime.js +36 -30
  98. package/dist/sdk/session.d.ts +3 -2
  99. package/dist/sdk/session.js +22 -89
  100. package/dist/sdk/types.d.ts +36 -0
  101. package/dist/session/catalog.d.ts +22 -1
  102. package/dist/session/catalog.js +102 -0
  103. package/dist/session/context-gc-checkpoint.js +30 -1
  104. package/dist/session/context-gc-contract.d.ts +7 -0
  105. package/dist/session/context-gc-replacements.d.ts +3 -0
  106. package/dist/session/context-gc-replacements.js +26 -0
  107. package/dist/session/store/contracts.d.ts +2 -2
  108. package/dist/session/store/jsonl/directory-index.d.ts +1 -1
  109. package/dist/session/store/jsonl/directory-index.js +18 -17
  110. package/dist/session/store/jsonl/history.js +1 -1
  111. package/dist/session/store/jsonl/index-transaction.js +2 -8
  112. package/dist/session/store/jsonl/jsonl-store.d.ts +1 -1
  113. package/dist/session/store/jsonl/jsonl-store.js +24 -9
  114. package/dist/session/store/jsonl/resources.js +5 -1
  115. package/dist/session/types.d.ts +1 -0
  116. package/dist/tools/agent/sleep.js +1 -1
  117. package/dist/tools/callers.js +1 -12
  118. package/dist/tools/code-mode/dispatch.js +5 -2
  119. package/dist/tools/code-mode/index.d.ts +1 -1
  120. package/dist/tools/code-mode/index.js +4 -2
  121. package/dist/tools/contracts.d.ts +20 -0
  122. package/dist/tools/execution-limits.d.ts +5 -20
  123. package/dist/tools/execution-limits.js +0 -2
  124. package/dist/tools/execution.js +16 -4
  125. package/dist/tools/local/exec.js +8 -2
  126. package/dist/tools/output-projection.d.ts +5 -2
  127. package/dist/tools/output-projection.js +133 -92
  128. package/dist/tools/permissions.d.ts +0 -1
  129. package/dist/tools/permissions.js +1 -9
  130. package/dist/tools/registry.js +1 -13
  131. package/dist/tools/runtime-context.d.ts +1 -21
  132. package/package.json +2 -2
  133. package/dist/extensions/agents.d.ts +0 -100
  134. package/dist/extensions/agents.js +0 -121
  135. package/dist/runtime/context/gc-candidates-subagent.d.ts +0 -6
  136. package/dist/runtime/context/gc-candidates-subagent.js +0 -100
  137. package/dist/sdk/extension-agent-controller.d.ts +0 -3
  138. package/dist/sdk/extension-agent-controller.js +0 -69
  139. package/dist/sdk/extension-agents.d.ts +0 -9
  140. package/dist/sdk/extension-agents.js +0 -58
  141. package/dist/sdk/subagent-backend.d.ts +0 -3
  142. package/dist/sdk/subagent-backend.js +0 -49
  143. package/dist/subagent/contracts.d.ts +0 -107
  144. package/dist/subagent/contracts.js +0 -1
  145. package/dist/subagent/limits.d.ts +0 -17
  146. package/dist/subagent/limits.js +0 -17
  147. package/dist/subagent/model-update.d.ts +0 -2
  148. package/dist/subagent/model-update.js +0 -81
  149. package/dist/subagent/session-controller.d.ts +0 -87
  150. package/dist/subagent/session-controller.js +0 -1343
  151. package/dist/subagent/task.d.ts +0 -134
  152. package/dist/subagent/task.js +0 -436
  153. package/dist/subagent/turn-contracts.d.ts +0 -64
  154. package/dist/subagent/turn-contracts.js +0 -1
  155. package/dist/subagent/wait-targets.d.ts +0 -10
  156. package/dist/subagent/wait-targets.js +0 -26
  157. package/dist/subagent/wait.d.ts +0 -26
  158. package/dist/subagent/wait.js +0 -73
  159. package/dist/tools/agent/agent-cancel.d.ts +0 -7
  160. package/dist/tools/agent/agent-cancel.js +0 -29
  161. package/dist/tools/agent/agent-message.d.ts +0 -7
  162. package/dist/tools/agent/agent-message.js +0 -57
  163. package/dist/tools/agent/agent-start.d.ts +0 -12
  164. package/dist/tools/agent/agent-start.js +0 -106
  165. package/dist/tools/agent/agent-wait.d.ts +0 -7
  166. package/dist/tools/agent/agent-wait.js +0 -91
@@ -2,21 +2,15 @@ import { createExtensionSessionLaunch, extensionInputSource } from './extension-
2
2
  import { createSessionExecutionFactory } from './session-execution-factory.js';
3
3
  import { createSessionExtensionMessages } from './session-extension-messages.js';
4
4
  import { createSessionExtensionPrompt } from './session-extension-prompt.js';
5
- import { createSubagentBackend } from './subagent-backend.js';
6
5
  import { AgentError } from '../error.js';
7
6
  import { SessionInputCoordinator, recordSessionInputCommit } from '../extensions/session-input.js';
8
7
  import { buildSystemPromptSections } from '../prompts/index.js';
9
8
  import { childSubagentInstruction } from '../prompts/subagent.js';
10
9
  import { DEFAULT_COMPACTION_FOCUS } from '../runtime/compaction/history.js';
11
10
  import { restoreEnvironmentState } from '../runtime/context/environment.js';
12
- import { SubagentSessionController } from '../subagent/session-controller.js';
13
11
  import { allowsDirectTool } from '../tools/callers.js';
14
- import { createExtensionAgentsBackend } from './extension-agents.js';
15
12
  import { createExtensionSessionBackend } from './extension-session.js';
16
13
  import { createSessionOpen } from './session-open.js';
17
- function restoreSubagentOrigin(origin) {
18
- return { ...origin.correlation, signal: origin.signal };
19
- }
20
14
  /** SDK Session orchestration owns warm resources; Runtime owns only the model/tool loop. */
21
15
  export function createSessionOpener(services) {
22
16
  const { host, description, settings, store, createAdapter, activeAdaptersExcept } = services;
@@ -27,11 +21,7 @@ export function createSessionOpener(services) {
27
21
  generation: () => services.modelState().generation,
28
22
  createAdapter,
29
23
  hasCustomAdapters: services.hasCustomAdapters,
30
- openSubagents: (id, workspace) => SubagentSessionController.open({
31
- parentSessionId: id,
32
- rootSessionId: id,
33
- backend: createSubagentBackend(store, workspace)
34
- }),
24
+ onExecutionRegistered: services.agentResources.onExecutionRegistered,
35
25
  assertAdmission: services.assertSessionAdmission,
36
26
  epoch: services.openEpoch,
37
27
  canPublish: services.canPublishSession,
@@ -149,8 +139,7 @@ export function createSessionOpener(services) {
149
139
  refreshSessionAdapters,
150
140
  sessionInputs,
151
141
  withRecordProjection,
152
- appendExtensionMessage,
153
- startSubagentTurn: async (id, input) => (await openSession(id)).startSubagentTurn(input)
142
+ appendExtensionMessage
154
143
  });
155
144
  sessionRuntime.ensureSessionBackend(() => createExtensionSessionBackend({
156
145
  describe: async (call) => {
@@ -200,70 +189,32 @@ export function createSessionOpener(services) {
200
189
  messages: createSessionExtensionMessages({
201
190
  sessionId,
202
191
  store,
203
- agents: () => sessionRuntime.agentsBackend,
204
192
  runtime: (id) => sessionRuntimes.get(id),
205
193
  models: () => services.modelState()
206
194
  }),
207
- launch: createExtensionSessionLaunch(sessionSource.type, execution),
195
+ launch: createExtensionSessionLaunch(execution),
208
196
  diagnose: (code, operationId) => services.extensions.session(sessionId)?.diagnose({ code, operationId, sessionId })
209
197
  }));
210
- if (sessionSource.type === 'user')
211
- sessionRuntime.ensureAgentsBackend(() => createExtensionAgentsBackend({
212
- existingController: () => sessionRuntime.subagents,
213
- controller: async () => {
214
- // Access to managed children is not a new Execution admission for their parent.
215
- services.assertCanStart();
216
- if (sessionRuntime.subagents === undefined)
217
- await openSession(sessionId);
218
- if (!sessionRuntime.subagents)
219
- throw new AgentError('MAR_AGENT_SUBAGENT_FAILED', 'Session task controller is unavailable.');
220
- return sessionRuntime.subagents;
221
- },
222
- parent: (controller, call) => {
223
- const current = call.executionId === undefined
224
- ? undefined
225
- : controller.parentExecution(call.executionId);
226
- if (call.executionId !== undefined && !current)
227
- throw new AgentError('MAR_AGENT_SUBAGENT_FAILED', 'Parent Execution source is unavailable.');
228
- const state = services.modelState();
229
- const selected = state.models.get(state.defaultModelId);
230
- return {
231
- ...(current ?? {
232
- modelId: selected.id,
233
- reasoningEffort: selected.defaultReasoningEffort,
234
- models: state.models,
235
- emitTerminal: async () => { },
236
- startTurn: async (turn) => {
237
- const child = await openSession(turn.sessionId);
238
- return child.startSubagentTurn({
239
- prompt: turn.prompt,
240
- modelId: turn.modelId,
241
- reasoningEffort: turn.reasoningEffort,
242
- role: turn.role,
243
- initialMailbox: turn.initialMailbox,
244
- turnId: turn.turnId,
245
- ...(turn.origin === undefined
246
- ? {}
247
- : { origin: restoreSubagentOrigin(turn.origin) }),
248
- ...(turn.delivery === undefined ? {} : { delivery: turn.delivery })
249
- });
250
- }
251
- }),
252
- operationId: call.operationId,
253
- origin: { signal: call.signal, correlation: call },
254
- signal: call.signal,
255
- ...(call.ownerInstanceId === undefined
256
- ? {}
257
- : { ownerInstanceId: call.ownerInstanceId }),
258
- ...(call.extensionId === undefined ? {} : { extensionId: call.extensionId })
259
- };
260
- }
261
- }));
198
+ const inputSource = () => {
199
+ const current = services.extensions.operations.current();
200
+ return current && current.sessionId !== sessionId && current.extensionId
201
+ ? extensionInputSource(current)
202
+ : undefined;
203
+ };
204
+ const inputOptions = () => {
205
+ const source = inputSource();
206
+ return source ? { extensionSource: source } : {};
207
+ };
262
208
  return {
263
209
  id: sessionId,
264
- run: (input) => execution('run', input),
265
- plan: (input) => execution('plan', input),
266
- steer: (input) => sessionRuntime.execution?.steer?.(input) ?? Promise.resolve({ status: 'notAccepted' }),
210
+ subscribe: (listener) => {
211
+ services.assertSessionAdmission(sessionId);
212
+ return sessionRuntime.subscribe(listener);
213
+ },
214
+ run: (input) => execution('run', input, input.clientMessageId ? inputOptions() : undefined),
215
+ plan: (input) => execution('plan', input, input.clientMessageId ? inputOptions() : undefined),
216
+ steer: (input) => sessionRuntime.execution?.steer?.(input, inputSource()) ??
217
+ Promise.resolve({ status: 'notAccepted' }),
267
218
  compact: (input = {}) => {
268
219
  const requests = sessionRuntime.execution?.compactRequests;
269
220
  if (requests) {
@@ -276,25 +227,7 @@ export function createSessionOpener(services) {
276
227
  });
277
228
  },
278
229
  cancel: async (executionId) => sessionRuntime.cancelExecution(executionId),
279
- startSubagentTurn: (turn) => execution(turn.role === 'explore' ? 'plan' : 'run', {
280
- prompt: turn.prompt,
281
- modelId: turn.modelId,
282
- reasoningEffort: turn.reasoningEffort,
283
- ...(turn.permissionMode === undefined ? {} : { permissionMode: turn.permissionMode })
284
- }, {
285
- publishHostEvents: false,
286
- ...(turn.origin === undefined ? {} : { signal: turn.origin.signal }),
287
- inputSource: turn.origin === undefined ? 'parent_agent' : 'extension',
288
- ...(turn.origin === undefined
289
- ? {}
290
- : { extensionSource: extensionInputSource(turn.origin) }),
291
- initialMailbox: turn.initialMailbox,
292
- turnId: turn.turnId,
293
- ...(turn.mcpExecutionScopeId === undefined
294
- ? {}
295
- : { mcpExecutionScopeId: turn.mcpExecutionScopeId }),
296
- ...(turn.delivery ? { delivery: turn.delivery } : {})
297
- })
230
+ joinExecution: (executionId) => sessionRuntime.joinExecution(executionId)
298
231
  };
299
232
  };
300
233
  return openSession;
@@ -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();