@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
@@ -1,5 +1,5 @@
1
1
  import { contextGcTargetKey } from '../../session/context-gc-checkpoint.js';
2
- import { contextGcWebOutputReplacement } from '../../session/context-gc-replacements.js';
2
+ import { contextGcWebOutputReplacement, contextGcToolOutputReplacement, isGenericContextGcTool } from '../../session/context-gc-replacements.js';
3
3
  import { CONTEXT_GC_POLICY } from './gc-economics.js';
4
4
  import { successfulRolloutsAfter, targetFor, isRecord } from './gc-candidates.js';
5
5
  export function collectWebCandidates(input, groups, existingTargets, candidateSavings) {
@@ -53,6 +53,58 @@ export function collectWebCandidates(input, groups, existingTargets, candidateSa
53
53
  }
54
54
  return { eligible, protectedTokens, retryAfterRollouts };
55
55
  }
56
+ /** Generic successful tool results share output economics, without inferring resource state. */
57
+ export function collectToolOutputCandidates(input, groups, existingTargets, candidateSavings) {
58
+ const eligible = [];
59
+ let protectedTokens = 0;
60
+ let retryAfterRollouts = Infinity;
61
+ for (const group of groups.values()) {
62
+ if (!isGenericContextGcTool(group.toolName) ||
63
+ group.evidenceOnly ||
64
+ !group.call ||
65
+ !group.result ||
66
+ group.failed ||
67
+ !group.completed ||
68
+ group.call.message.role !== 'tool_call' ||
69
+ group.result.message.role !== 'tool' ||
70
+ typeof group.result.message.content !== 'string' ||
71
+ !isRecord(group.completed.payload) ||
72
+ group.completed.payload.type !== 'tool.completed' ||
73
+ group.completed.payload.callId !== group.callId ||
74
+ group.completed.payload.toolName !== group.toolName ||
75
+ typeof group.completed.payload.truncated !== 'boolean')
76
+ continue;
77
+ const target = targetFor(group.result.record, group.result.message);
78
+ if (!target || existingTargets.has(contextGcTargetKey(target)))
79
+ continue;
80
+ const evidenceSequence = group.result.record.sequence;
81
+ const observedRollouts = successfulRolloutsAfter(input.records, evidenceSequence, input.modelId, input.contextWindowTokens);
82
+ const candidate = {
83
+ replacements: [
84
+ {
85
+ kind: 'TOOL_OUTPUT',
86
+ toolName: group.toolName,
87
+ callId: group.callId,
88
+ target,
89
+ replacementContent: contextGcToolOutputReplacement(group.toolName, group.result.message.content, group.completed.payload.truncated),
90
+ evidenceSequence
91
+ }
92
+ ],
93
+ observedRollouts,
94
+ evidenceSequence
95
+ };
96
+ const reclaimedTokens = candidateSavings(candidate);
97
+ if (reclaimedTokens <= 0)
98
+ continue;
99
+ if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts)
100
+ eligible.push(candidate);
101
+ else {
102
+ protectedTokens += reclaimedTokens;
103
+ retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - observedRollouts);
104
+ }
105
+ }
106
+ return { eligible, protectedTokens, retryAfterRollouts };
107
+ }
56
108
  function isContextGcWebToolName(value) {
57
109
  return value === 'web_search' || value === 'web_fetch';
58
110
  }
@@ -44,15 +44,7 @@ export function buildToolGroups(records, messages, sources) {
44
44
  continue;
45
45
  const record = sources[index];
46
46
  for (const entry of message.calls ?? []) {
47
- if (![
48
- 'apply_patch',
49
- 'exec',
50
- 'exec_wait',
51
- 'agent_start',
52
- 'agent_wait',
53
- 'agent_message',
54
- 'agent_cancel'
55
- ].includes(entry.toolName) ||
47
+ if (!['apply_patch', 'exec', 'exec_wait'].includes(entry.toolName) ||
56
48
  (entry.status === 'completed' &&
57
49
  message.observations.some((value) => value.type === 'tool_result' && value.source.callId === entry.callId)))
58
50
  continue;
@@ -5,9 +5,8 @@ import { CONTEXT_GC_POLICY, COMPACT_CREDIT_ACTIVATION_RATIO, COMPACT_THRESHOLD_R
5
5
  import { buildToolGroups, countSuccessfulNormalRollouts, isRecord } from './gc-candidates.js';
6
6
  import { collectCodeModeCandidates } from './gc-candidates-code.js';
7
7
  import { collectStageACandidates } from './gc-candidates-read-patch.js';
8
- import { collectSubagentCandidates } from './gc-candidates-subagent.js';
9
8
  import { collectExecCandidates } from './gc-candidates-exec.js';
10
- import { collectWebCandidates } from './gc-candidates-web.js';
9
+ import { collectToolOutputCandidates, collectWebCandidates } from './gc-candidates-web.js';
11
10
  export { CONTEXT_GC_POLICY, evaluateContextGcEconomics, contextGcMinimumGrossReductionRate, contextGcMinimumReclaimedTokens, contextGcEligibleUsage, contextGcMarginalCacheInvestmentPaysBack, contextGcImminentCompactAvoidanceCredit, contextGcAppliedCompactCreditCostUnits } from './gc-economics.js';
12
11
  export function planContextGc(input) {
13
12
  return inspectContextGc(input).plan;
@@ -110,9 +109,8 @@ function inspectTextGc(input) {
110
109
  : [])
111
110
  ]));
112
111
  const stageA = collectStageACandidates(input, groups, existing, existingTargets, candidateSavings);
113
- const subagent = collectSubagentCandidates(input, groups, existingTargets, candidateSavings);
114
- const deterministicCandidates = [...stageA.eligible, ...subagent.eligible];
115
- const deterministicProtectedTokens = stageA.protectedTokens + subagent.protectedTokens;
112
+ const deterministicCandidates = stageA.eligible;
113
+ const deterministicProtectedTokens = stageA.protectedTokens;
116
114
  const stageAPlan = evaluateCandidateSet(input, existing, deterministicCandidates, {
117
115
  stage: 'STAGE_A',
118
116
  contextUsageTokens,
@@ -125,10 +123,12 @@ function inspectTextGc(input) {
125
123
  const exec = collectExecCandidates(input, groups, existingTargets, candidateSavings);
126
124
  const codeMode = collectCodeModeCandidates(input, groups, existingTargets, candidateSavings);
127
125
  const web = collectWebCandidates(input, groups, existingTargets, candidateSavings);
126
+ const tools = collectToolOutputCandidates(input, groups, existingTargets, candidateSavings);
128
127
  const protectedCandidateTokens = deterministicProtectedTokens +
129
128
  exec.protectedTokens +
130
129
  codeMode.protectedTokens +
131
- web.protectedTokens;
130
+ web.protectedTokens +
131
+ tools.protectedTokens;
132
132
  const stageAPlanWithProtectedCandidates = evaluateCandidateSet(input, existing, deterministicCandidates, {
133
133
  stage: 'STAGE_A',
134
134
  contextUsageTokens,
@@ -137,7 +137,7 @@ function inspectTextGc(input) {
137
137
  normalRolloutOrdinal,
138
138
  protectedCandidateTokens
139
139
  }) ?? stageAPlan;
140
- const marginal = mergeMarginalLargeOutputCandidates(input, existing, deterministicCandidates, [...exec.eligible, ...codeMode.eligible, ...web.eligible], stageAPlanWithProtectedCandidates, {
140
+ const marginal = mergeMarginalLargeOutputCandidates(input, existing, deterministicCandidates, [...exec.eligible, ...codeMode.eligible, ...web.eligible, ...tools.eligible], stageAPlanWithProtectedCandidates, {
141
141
  contextUsageTokens,
142
142
  usageRatio,
143
143
  usageRecord,
@@ -147,7 +147,7 @@ function inspectTextGc(input) {
147
147
  const plan = checkpointEligible
148
148
  ? finalizePlan(input, marginal ?? stageAPlanWithProtectedCandidates)
149
149
  : undefined;
150
- const retryAfterRollouts = Math.min(exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts);
150
+ const retryAfterRollouts = Math.min(exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts, tools.retryAfterRollouts);
151
151
  if (!plan && checkpointEligible)
152
152
  return {};
153
153
  return {
@@ -160,11 +160,19 @@ function inspectTextGc(input) {
160
160
  const exec = collectExecCandidates(input, groups, existingTargets, candidateSavings);
161
161
  const codeMode = collectCodeModeCandidates(input, groups, existingTargets, candidateSavings);
162
162
  const web = collectWebCandidates(input, groups, existingTargets, candidateSavings);
163
+ const tools = collectToolOutputCandidates(input, groups, existingTargets, candidateSavings);
163
164
  const protectedCandidateTokens = deterministicProtectedTokens +
164
165
  exec.protectedTokens +
165
166
  codeMode.protectedTokens +
166
- web.protectedTokens;
167
- const combined = evaluateCandidateSet(input, existing, [...deterministicCandidates, ...exec.eligible, ...codeMode.eligible, ...web.eligible], {
167
+ web.protectedTokens +
168
+ tools.protectedTokens;
169
+ const combined = evaluateCandidateSet(input, existing, [
170
+ ...deterministicCandidates,
171
+ ...exec.eligible,
172
+ ...codeMode.eligible,
173
+ ...web.eligible,
174
+ ...tools.eligible
175
+ ], {
168
176
  stage: 'STAGE_A_AND_EXEC',
169
177
  contextUsageTokens,
170
178
  usageRatio,
@@ -176,7 +184,7 @@ function inspectTextGc(input) {
176
184
  const plan = finalizePlan(input, combined);
177
185
  return plan ? { plan } : {};
178
186
  }
179
- const retryAfterRollouts = Math.min(stageA.retryAfterRollouts, subagent.retryAfterRollouts, exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts);
187
+ const retryAfterRollouts = Math.min(stageA.retryAfterRollouts, exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts, tools.retryAfterRollouts);
180
188
  // A worthwhile mixed set retains every positive protected candidate's retry
181
189
  // hint even when the checkpoint interval prevents a durable draft.
182
190
  return protectedCandidateTokens > 0 &&
@@ -101,6 +101,7 @@ export function applyContextGcReplacementsToMessages(messages, messageSources, r
101
101
  next = { ...next, arguments: structuredClone(replacement.replacementArguments) };
102
102
  else if ((replacement.kind === 'EXEC_OUTPUT' ||
103
103
  replacement.kind === 'CODE_MODE_OUTPUT' ||
104
+ replacement.kind === 'TOOL_OUTPUT' ||
104
105
  replacement.kind === 'SUBAGENT_OUTPUT' ||
105
106
  replacement.kind === 'WEB_OUTPUT') &&
106
107
  next.role === 'tool')
@@ -104,7 +104,7 @@ export type AgentEvent = AgentEventBase & ({
104
104
  summary: string;
105
105
  truncated?: boolean;
106
106
  artifacts?: readonly AgentToolArtifact[];
107
- data?: CodeCellEventData | import('../tools/contracts.js').ExecResult | Omit<import('../tools/contracts.js').ExecResult, 'stdout' | 'stderr'> | import('../tools/contracts.js').ApplyPatchResult | import('../tools/contracts.js').ReadEventData | import('../tools/contracts.js').ViewImageResult | import('../subagent/contracts.js').SubagentTaskSnapshot | import('../subagent/contracts.js').SubagentManyOutput | import('../tools/contracts.js').SleepResult | import('./public-tool-metadata.js').PublicToolMetadata;
107
+ data?: CodeCellEventData | import('../tools/contracts.js').ExecResult | Omit<import('../tools/contracts.js').ExecResult, 'stdout' | 'stderr'> | import('../tools/contracts.js').ApplyPatchResult | import('../tools/contracts.js').ReadEventData | import('../tools/contracts.js').ViewImageResult | import('../tools/contracts.js').SleepResult | Readonly<Record<string, unknown>> | import('./public-tool-metadata.js').PublicToolMetadata;
108
108
  } | {
109
109
  type: 'tool.failed';
110
110
  resultExtensions?: readonly import('../tools/result-extension-contract.js').ToolResultExtension[];
@@ -156,8 +156,9 @@ export type AgentEvent = AgentEventBase & ({
156
156
  modelId?: string;
157
157
  modelName?: string;
158
158
  reasoningEffort?: AgentReasoningEffort;
159
- latestMessage?: import('../subagent/contracts.js').SubagentLatestMessage;
160
- latestActivity?: import('../subagent/contracts.js').SubagentLatestActivity;
159
+ /** Historical event fields retained for reading existing session records. */
160
+ latestMessage?: unknown;
161
+ latestActivity?: unknown;
161
162
  errorCode?: string;
162
163
  errorMessage?: string;
163
164
  } | {
@@ -3,6 +3,8 @@ import type { AgentEvent } from './events.js';
3
3
  /** A live execution exposes streamed events separately from its terminal result. */
4
4
  export interface ExecutionHandle extends AsyncIterable<AgentEvent> {
5
5
  readonly executionId: string;
6
+ /** Identity of the admitted Turn when the operation creates one. */
7
+ readonly turnId?: string;
6
8
  result(): Promise<ExecutionResult>;
7
9
  /** Signals abort; await result() to observe the terminal status and cleanup. */
8
10
  cancel(): void;
@@ -49,8 +49,18 @@ export function waitForToolCondition(input) {
49
49
  };
50
50
  const abort = () => finish('interrupted', input.signal?.reason ??
51
51
  new AgentError('MAR_AGENT_EXECUTION_CANCELLED', 'Execution cancelled.'));
52
- unsubscribe = input.subscribe(check);
53
- unsubscribeInterrupt = input.interrupt?.subscribe(check) ?? (() => { });
52
+ const releaseSubscription = input.subscribe(check);
53
+ if (settled) {
54
+ releaseSubscription();
55
+ return;
56
+ }
57
+ unsubscribe = releaseSubscription;
58
+ const releaseInterrupt = input.interrupt?.subscribe(check) ?? (() => { });
59
+ if (settled) {
60
+ releaseInterrupt();
61
+ return;
62
+ }
63
+ unsubscribeInterrupt = releaseInterrupt;
54
64
  cancelTimer = waitDeadline(input.waitMs, () => {
55
65
  check();
56
66
  finish('timeout');
package/dist/sdk/agent.js CHANGED
@@ -13,6 +13,7 @@ import { toolCallers } from '../tools/callers.js';
13
13
  import { AnthropicMessagesAdapter } from '../model/anthropic-messages.js';
14
14
  import { OpenAiResponsesAdapter } from '../model/openai-responses.js';
15
15
  import { JsonlSessionStore } from '../session/store/jsonl/jsonl-store.js';
16
+ import { createSessionCatalog } from '../session/catalog.js';
16
17
  /** Validate models before Host negotiation and expose only built-in protocol adapters. */
17
18
  export async function createAgent(options) {
18
19
  if (Object.hasOwn(options, 'adapterFactory'))
@@ -33,6 +34,7 @@ export async function createAgentWithAdapter(options, adapterFactory) {
33
34
  throw new AgentError('MAR_AGENT_SESSION_STORE_INVALID', 'Session store must not be null.');
34
35
  const ownsStore = options.sessionStore === undefined;
35
36
  const store = options.sessionStore ?? (await JsonlSessionStore.open(description.homeDirectory));
37
+ const historyCatalog = createSessionCatalog(store, true);
36
38
  // The same adapter can be borrowed concurrently by Execution and Extension operations.
37
39
  const activeExecutionAdapters = new Map();
38
40
  const activeAdapterReferences = {
@@ -50,6 +52,23 @@ export async function createAgentWithAdapter(options, adapterFactory) {
50
52
  }
51
53
  };
52
54
  const sessionRuntimes = new SessionRegistry();
55
+ const executionListeners = new Set();
56
+ const liveSession = (runtime) => ({
57
+ id: runtime.storedSession.id,
58
+ workspace: runtime.storedSession.workspace,
59
+ source: structuredClone(runtime.storedSession.source),
60
+ subscribe: (listener) => runtime.subscribe(listener)
61
+ });
62
+ const onExecutionRegistered = (runtime) => {
63
+ for (const listener of [...executionListeners]) {
64
+ try {
65
+ listener(liveSession(runtime));
66
+ }
67
+ catch {
68
+ /* Observers cannot affect execution admission. */
69
+ }
70
+ }
71
+ };
53
72
  const sessionWriterOwner = {};
54
73
  let disposed = false;
55
74
  let disposal;
@@ -86,7 +105,6 @@ export async function createAgentWithAdapter(options, adapterFactory) {
86
105
  'data.checkpoint',
87
106
  'data.store',
88
107
  'session',
89
- 'agents',
90
108
  'settings',
91
109
  ...(options.host.extensionPorts?.environment ? ['environment'] : []),
92
110
  ...(options.host.extensionPorts?.files ? ['files'] : []),
@@ -109,7 +127,6 @@ export async function createAgentWithAdapter(options, adapterFactory) {
109
127
  agentRole: sessionRuntimes.get(call.sessionId)?.sourceType === 'subagent' ? 'subagent' : 'root'
110
128
  })),
111
129
  sessionBackend: (id) => sessionRuntimes.get(id)?.sessionBackend,
112
- agentsBackend: (id) => sessionRuntimes.get(id)?.agentsBackend,
113
130
  scheduleTool: (id) => (definition, run) => {
114
131
  const schedule = sessionRuntimes.get(id)?.scheduleTool;
115
132
  if (!schedule)
@@ -180,7 +197,8 @@ export async function createAgentWithAdapter(options, adapterFactory) {
180
197
  agentResources: {
181
198
  activeExecutionAdapters: activeAdapterReferences,
182
199
  sessionRuntimes,
183
- sessionWriterOwner
200
+ sessionWriterOwner,
201
+ onExecutionRegistered
184
202
  },
185
203
  hasCustomAdapters: adapterFactory !== undefined,
186
204
  modelState: () => ({ models, defaultModelId, generation: modelGeneration }),
@@ -197,9 +215,39 @@ export async function createAgentWithAdapter(options, adapterFactory) {
197
215
  });
198
216
  return {
199
217
  extensionDiagnostics: () => extensions.diagnostics(),
218
+ observeSessionExecutions: (listener) => {
219
+ assertSessionAdmission();
220
+ executionListeners.add(listener);
221
+ const sessions = [...sessionRuntimes.values()]
222
+ .filter((runtime) => runtime.activeExecutionId !== undefined)
223
+ .map(liveSession);
224
+ return {
225
+ sessions,
226
+ unsubscribe: () => {
227
+ executionListeners.delete(listener);
228
+ }
229
+ };
230
+ },
200
231
  createSession: async (input) => {
201
232
  assertSessionAdmission();
202
- return sessionRuntimes.trackOpen(undefined, async () => openSession((await store.createSession(input)).id, input.workspace));
233
+ if (input.source !== undefined &&
234
+ input.parentSessionId !== undefined &&
235
+ input.source.parentSessionId !== input.parentSessionId)
236
+ throw new AgentError('MAR_AGENT_SESSION_SOURCE_INVALID', 'Session source conflicts with its parent.');
237
+ return sessionRuntimes.trackOpen(undefined, async () => {
238
+ const created = await store.createSession({
239
+ workspace: input.workspace,
240
+ ...(input.title === undefined ? {} : { title: input.title }),
241
+ ...(input.source !== undefined
242
+ ? { source: input.source }
243
+ : input.parentSessionId === undefined
244
+ ? {}
245
+ : {
246
+ source: { type: 'user', parentSessionId: input.parentSessionId }
247
+ })
248
+ });
249
+ return openSession(created.id, input.workspace);
250
+ });
203
251
  },
204
252
  openSession: (id) => openSession(id),
205
253
  listSessions: async (input) => {
@@ -210,6 +258,46 @@ export async function createAgentWithAdapter(options, adapterFactory) {
210
258
  assertSessionAdmission();
211
259
  return store.getSession(id);
212
260
  },
261
+ sessionHistory: async (id, input) => {
262
+ assertSessionAdmission(id);
263
+ return historyCatalog.history(id, input);
264
+ },
265
+ sessionInputReceipt: async (id, input) => {
266
+ assertSessionAdmission(id);
267
+ return historyCatalog.inputReceipt(id, input);
268
+ },
269
+ sessionExecutionWasInterrupted: async (id, executionId, input) => {
270
+ assertSessionAdmission(id);
271
+ input?.signal?.throwIfAborted();
272
+ // Maintenance admission excludes every writer without adopting pending resources.
273
+ const maintenance = await store.acquireSessionMaintenance(id, 'clear', sessionWriterOwner);
274
+ try {
275
+ input?.signal?.throwIfAborted();
276
+ if (sessionRuntimes.get(id)?.activeExecutionId !== undefined)
277
+ return false;
278
+ let cursor;
279
+ do {
280
+ const page = await historyCatalog.history(id, {
281
+ limit: 100,
282
+ ...(cursor === undefined ? {} : { cursor })
283
+ });
284
+ input?.signal?.throwIfAborted();
285
+ const latest = page.entries
286
+ .flatMap((entry) => (entry.kind === 'event' ? [entry.event] : []))
287
+ .findLast((event) => event.type === 'execution.started' ||
288
+ event.type === 'execution.completed' ||
289
+ event.type === 'execution.failed' ||
290
+ event.type === 'execution.cancelled');
291
+ if (latest)
292
+ return latest.type === 'execution.started' && latest.executionId === executionId;
293
+ cursor = page.nextCursor;
294
+ } while (cursor);
295
+ return false;
296
+ }
297
+ finally {
298
+ await maintenance.release();
299
+ }
300
+ },
213
301
  setSessionTitle: async (id, title, input) => {
214
302
  assertSessionAdmission();
215
303
  return store.updateTitle(id, title, { ...input, automatic: input?.automatic ?? false }, sessionWriterOwner);
@@ -293,10 +381,12 @@ export async function createAgentWithAdapter(options, adapterFactory) {
293
381
  }
294
382
  },
295
383
  deleteSession: (id) => maintenanceCoordinator.delete(id),
384
+ deleteSessions: (ids) => maintenanceCoordinator.deleteMembers(ids),
296
385
  dispose: () => {
297
386
  if (disposal)
298
387
  return disposal;
299
388
  disposed = true;
389
+ executionListeners.clear();
300
390
  for (const runtime of sessionRuntimes.values())
301
391
  runtime.cancelExecutions();
302
392
  // Explicit end observers finish in a separate lifecycle frame before Session retirement.
@@ -307,7 +397,6 @@ export async function createAgentWithAdapter(options, adapterFactory) {
307
397
  () => maintenanceCoordinator.join(),
308
398
  () => extensions.dispose(),
309
399
  ...runtimes.map((runtime) => () => runtime.retireInputs()),
310
- ...runtimes.map((runtime) => () => runtime.disposeSubagents()),
311
400
  () => Promise.all(runtimes.map((runtime) => runtime.joinExecutions())),
312
401
  () => pendingReconfiguration,
313
402
  () => SessionRuntime.closeModelAdapters(runtimes),
@@ -25,7 +25,6 @@ export declare class ExecutionContextCoordinator {
25
25
  readonly window: number;
26
26
  readonly gc: Pick<ContextGcRuntime, 'run'>;
27
27
  readonly gcBase: Omit<ContextGcRuntimeInput, 'system' | 'tools' | 'usage' | 'inlineMedia' | 'mediaView' | 'getActiveProcessIds'>;
28
- readonly child: boolean;
29
28
  start(input?: {
30
29
  focus?: string;
31
30
  async?: boolean;
@@ -41,9 +40,9 @@ export declare class ExecutionContextCoordinator {
41
40
  advanceBoundary(): void;
42
41
  drainRequests(): boolean;
43
42
  prepareInputs(ports: {
44
- includeSubagentUpdates: boolean;
43
+ includeSessionContext: boolean;
45
44
  appendMailbox(): Promise<unknown>;
46
- appendSubagentUpdates(): Promise<void>;
45
+ appendSessionContext(): Promise<void>;
47
46
  refreshTools(): void;
48
47
  updateEnvironment(): void;
49
48
  }): Promise<void>;
@@ -26,8 +26,8 @@ export class ExecutionContextCoordinator {
26
26
  }
27
27
  async prepareInputs(ports) {
28
28
  await ports.appendMailbox();
29
- if (ports.includeSubagentUpdates)
30
- await ports.appendSubagentUpdates();
29
+ if (ports.includeSessionContext)
30
+ await ports.appendSessionContext();
31
31
  ports.refreshTools();
32
32
  await this.ports.context.syncMcpWorldState(this.ports.messages.length);
33
33
  this.advanceBoundary();
@@ -80,7 +80,7 @@ export class ExecutionContextCoordinator {
80
80
  this.ports.signal.throwIfAborted();
81
81
  this.ports.execution.assertContextValid();
82
82
  });
83
- if (!this.ports.compact.pending && !this.ports.child)
83
+ if (!this.ports.compact.pending)
84
84
  await this.gc(round, usage, await this.ports.resources.gcCandidate(), true);
85
85
  }
86
86
  async gc(round, usage, media, includeProcesses = false) {
@@ -4,6 +4,7 @@ import type { SessionModelMessage as ModelMessage } from '../session/model-reque
4
4
  import { SessionInputCoordinator, type SessionInputEntry, type ExtensionSessionRequest, type ExtensionSessionActionResult } from '../extensions/session-input.js';
5
5
  import type { ExtensionPromptInput, ExtensionPromptResult } from '../extensions/prompt-input.js';
6
6
  import type { ExtensionOperationContext } from '../extensions/operation.js';
7
+ import type { ExtensionSessionContextProvider } from '../extensions/session-context.js';
7
8
  import type { ResolvedModelConfiguration } from './model-runtime.js';
8
9
  import type { SmartCompactionInput } from '../runtime/compaction/smart.js';
9
10
  import type { CompactFlight } from '../runtime/compaction/coordinator.js';
@@ -11,7 +12,6 @@ import { type ResourcePublisher } from '../runtime/resource-publication.js';
11
12
  import type { createExecutionModelContext } from './execution-model-context.js';
12
13
  import type { InternalExecutionOptions } from './session-execution.js';
13
14
  import type { RunInput } from './types.js';
14
- import type { SubagentSessionController } from '../subagent/session-controller.js';
15
15
  /** Owns committed input, mailbox admission and live projection for one Execution. */
16
16
  export declare function createExecutionInput(input: {
17
17
  host: ResourcePublisher;
@@ -20,7 +20,6 @@ export declare function createExecutionInput(input: {
20
20
  executionId: string;
21
21
  turnId: string;
22
22
  mode: 'run' | 'plan' | 'compact';
23
- source: 'user' | 'subagent';
24
23
  controller: AbortController;
25
24
  inputs: Pick<SessionInputCoordinator, 'drain' | 'boundarySize'>;
26
25
  messages: ModelMessage[];
@@ -52,7 +51,7 @@ export declare function createExecutionInput(input: {
52
51
  enqueue(content: ExtensionPromptInput, call: ExtensionOperationContext): Promise<unknown>;
53
52
  project(actual: ExtensionPromptInput, metadata: NonNullable<ModelMessage["metadata"]>): void;
54
53
  }) => Promise<boolean>;
55
- receiveSteer: (content: ExtensionPromptInput, clientMessageId: string, call: ExtensionOperationContext) => Promise<SessionRecord>;
56
- appendSubagentUpdates: (subagents: Pick<SubagentSessionController, "collectModelUpdates"> | undefined) => Promise<void>;
54
+ receiveSteer: (content: ExtensionPromptInput, clientMessageId: string, call: ExtensionOperationContext, source?: import("../extensions/session-input.js").ExtensionInputSource) => Promise<SessionRecord>;
55
+ appendExtensionContext: (provider: ExtensionSessionContextProvider | undefined) => Promise<void>;
57
56
  appendMailboxMessages: (restoring?: boolean) => Promise<boolean>;
58
57
  };
@@ -4,11 +4,9 @@ import { inputMetadataFromRecord } from '../session/record-projection.js';
4
4
  import { AGENT_EXECUTION_POLICY } from '../runtime/execution-policy.js';
5
5
  import { publishModelResources } from '../runtime/resource-publication.js';
6
6
  import { extensionInputSource } from './extension-session-launch.js';
7
- import { renderSubagentUpdate } from '../subagent/model-update.js';
8
7
  /** Owns committed input, mailbox admission and live projection for one Execution. */
9
8
  export function createExecutionInput(input) {
10
9
  const { host, store, sessionId, executionId, turnId, mode, controller, inputs, messages, pendingInputs, messageSources, modelContext, model, tokens, setSmartInput, withRecordProjection, appendExtensionMessage, startCompact, compactContext } = input;
11
- const sessionSource = { type: input.source };
12
10
  const writeInputRecord = async (content, type, metadata, call, commitEvidence, projectCommitted) => {
13
11
  call.signal.throwIfAborted();
14
12
  if (content.images?.length && !model().inputCapabilities.includes('IMAGE'))
@@ -17,7 +15,6 @@ export function createExecutionInput(input) {
17
15
  call.signal.throwIfAborted();
18
16
  const smartPercent = tokens() === undefined ? undefined : (tokens() * 100) / model().contextWindowTokens;
19
17
  const smartCompaction = mode !== 'compact' &&
20
- sessionSource.type === 'user' &&
21
18
  type === 'turn.user' &&
22
19
  metadata.inputSource === undefined &&
23
20
  smartPercent !== undefined &&
@@ -64,6 +61,13 @@ export function createExecutionInput(input) {
64
61
  projectCommitted(actualContent, record.recordId, inputMetadata);
65
62
  for (const text of content.modelContext ?? content.context ?? [])
66
63
  messages.push({ role: 'user', content: text, contextKind: 'contextual' });
64
+ if (metadata.clientMessageId)
65
+ await store.append(sessionId, {
66
+ type: 'input.applied',
67
+ payload: { inputRef: record.recordId },
68
+ executionId,
69
+ turnId
70
+ });
67
71
  });
68
72
  };
69
73
  const appendMailboxMessages = async (restoring = false) => inputs.drain(async (entry, request, call) => {
@@ -91,8 +95,7 @@ export function createExecutionInput(input) {
91
95
  if (images.length && !model().inputCapabilities.includes('IMAGE'))
92
96
  throw new AgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Selected model does not support image input.');
93
97
  const percent = tokens() === undefined ? undefined : (tokens() * 100) / model().contextWindowTokens;
94
- const eligible = sessionSource.type === 'user' &&
95
- percent !== undefined &&
98
+ const eligible = percent !== undefined &&
96
99
  percent >= AGENT_EXECUTION_POLICY.smartCompactionPercent &&
97
100
  percent < AGENT_EXECUTION_POLICY.automaticCompactionPercent;
98
101
  const actual = {
@@ -135,7 +138,6 @@ export function createExecutionInput(input) {
135
138
  return outcome;
136
139
  });
137
140
  }
138
- const parent = entry.parentMessage;
139
141
  let outcome;
140
142
  const content = {
141
143
  text: request.input.text,
@@ -146,15 +148,9 @@ export function createExecutionInput(input) {
146
148
  : { modelContext: request.input.modelContext }),
147
149
  ...(request.input.images === undefined ? {} : { images: request.input.images })
148
150
  };
149
- await writeInputRecord(content, parent && !parent.delivery?.origin ? 'turn.user' : 'extension.input', parent && !parent.delivery?.origin
150
- ? {
151
- inputSource: 'parent_agent',
152
- ...(parent.delivery ? { messageId: parent.delivery.messageId } : {})
153
- }
154
- : {
155
- source: extensionInputSource(call, request.input.provenance, request.input.modelAttribution),
156
- ...(parent?.delivery ? { messageId: parent.delivery.messageId } : {})
157
- }, call, (recordId) => {
151
+ await writeInputRecord(content, 'extension.input', {
152
+ source: extensionInputSource(call, request.input.provenance, request.input.modelAttribution)
153
+ }, call, (recordId) => {
158
154
  outcome = { changed: true, executionId, recordId };
159
155
  recordSessionInputCommit(entry, outcome);
160
156
  }, (actual, recordId, metadata) => {
@@ -168,7 +164,6 @@ export function createExecutionInput(input) {
168
164
  }
169
165
  : {})
170
166
  });
171
- parent?.delivery?.applied();
172
167
  });
173
168
  return outcome;
174
169
  }, {
@@ -195,7 +190,6 @@ export function createExecutionInput(input) {
195
190
  const { internal, input: request } = ports;
196
191
  await writeInputRecord(content, internal.inputSource === 'extension' ? 'extension.input' : 'turn.user', {
197
192
  ...(internal.extensionSource ? { source: internal.extensionSource } : {}),
198
- ...(internal.delivery ? { messageId: internal.delivery.messageId } : {}),
199
193
  ...(internal.inputSource === undefined ? {} : { inputSource: internal.inputSource }),
200
194
  ...(request.clientMessageId ? { clientMessageId: request.clientMessageId } : {})
201
195
  }, call, (recordId) => {
@@ -203,7 +197,6 @@ export function createExecutionInput(input) {
203
197
  internal.inputCommitted?.(recordId);
204
198
  }, (actual, _recordId, metadata) => {
205
199
  ports.project(actual, metadata);
206
- internal.delivery?.applied();
207
200
  });
208
201
  return {};
209
202
  };
@@ -218,7 +211,7 @@ export function createExecutionInput(input) {
218
211
  await ports.operation((call) => internal.promptAdmitted ? write(content, call) : ports.admit(content, call, write));
219
212
  return entered;
220
213
  };
221
- const receiveSteer = (content, clientMessageId, call) => withRecordProjection(async () => {
214
+ const receiveSteer = (content, clientMessageId, call, source) => withRecordProjection(async () => {
222
215
  call.signal.throwIfAborted();
223
216
  if (content.images?.length && !model().inputCapabilities.includes('IMAGE'))
224
217
  throw new AgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Selected model does not support image input.');
@@ -234,6 +227,7 @@ export function createExecutionInput(input) {
234
227
  ...(content.modelText === undefined ? {} : { modelText: content.modelText }),
235
228
  ...(content.context === undefined ? {} : { context: content.context }),
236
229
  ...(content.modelContext === undefined ? {} : { modelContext: content.modelContext }),
230
+ ...(source === undefined ? {} : { source }),
237
231
  clientMessageId,
238
232
  ...(images.length ? { images } : {})
239
233
  },
@@ -243,24 +237,36 @@ export function createExecutionInput(input) {
243
237
  pendingInputs.set(record.recordId, record);
244
238
  return record;
245
239
  });
246
- const appendSubagentUpdates = async (subagents) => {
247
- if (!subagents)
240
+ const appendExtensionContext = async (provider) => {
241
+ if (!provider)
248
242
  return;
249
243
  await withRecordProjection(async () => {
250
244
  controller.signal.throwIfAborted();
251
- for (const update of subagents.collectModelUpdates()) {
245
+ const proposed = await provider.collect(controller.signal);
246
+ controller.signal.throwIfAborted();
247
+ if (!Array.isArray(proposed))
248
+ throw new AgentError('MAR_AGENT_EXTENSION_CONTEXT_INVALID', 'Invalid context batch.');
249
+ const batch = proposed.slice(0, 8);
250
+ for (const item of batch) {
251
+ if (typeof item?.content !== 'string' ||
252
+ item.content.length === 0 ||
253
+ Buffer.byteLength(item.content, 'utf8') > 32_768 ||
254
+ typeof item.acknowledge !== 'function')
255
+ throw new AgentError('MAR_AGENT_EXTENSION_CONTEXT_INVALID', 'Invalid context item.');
256
+ }
257
+ for (const item of batch) {
252
258
  controller.signal.throwIfAborted();
253
259
  const message = {
254
260
  role: 'user',
255
- content: renderSubagentUpdate(update.snapshot, update.truncated),
261
+ content: item.content,
256
262
  contextKind: 'contextual'
257
263
  };
258
264
  const stored = await store.append(sessionId, { type: 'model.context', payload: message, executionId, turnId }, { signal: controller.signal });
259
265
  messages.push(message);
260
266
  messageSources.set(message, stored);
261
- update.acknowledge();
267
+ item.acknowledge();
262
268
  }
263
269
  });
264
270
  };
265
- return { startInitialInput, receiveSteer, appendSubagentUpdates, appendMailboxMessages };
271
+ return { startInitialInput, receiveSteer, appendExtensionContext, appendMailboxMessages };
266
272
  }
@@ -1,6 +1,6 @@
1
1
  /** Owns round retries when late input or resource collection invalidates a candidate. */
2
2
  export declare function createExecutionRoundPreparer<Continuation, Candidate, Round>(ports: {
3
- prepareInputs(includeSubagentUpdates: boolean): Promise<void>;
3
+ prepareInputs(includeSessionContext: boolean): Promise<void>;
4
4
  buildCandidate(continuation: Continuation | undefined): Promise<Candidate>;
5
5
  adoptLateInput(): Promise<boolean>;
6
6
  prepareResources(candidate: Candidate): Promise<{