@myagentroam/agent 0.11.39 → 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
@@ -12,6 +12,7 @@ export declare function writeModelCatalog(home: string, catalog: CliModelCatalog
12
12
  export declare function serializeModel(model: CliModelConfiguration): {
13
13
  apiKey: string;
14
14
  conversationModel?: boolean;
15
+ subagentModel?: boolean;
15
16
  useResourceUrl?: boolean;
16
17
  responsesConfigurationUpdates?: boolean;
17
18
  responsesTransport?: import("../index.js").ResponsesTransport;
@@ -46,6 +47,7 @@ export declare function serializeModel(model: CliModelConfiguration): {
46
47
  type: "CODEX_AUTH_FILE";
47
48
  };
48
49
  conversationModel?: boolean;
50
+ subagentModel?: boolean;
49
51
  useResourceUrl?: boolean;
50
52
  responsesConfigurationUpdates?: boolean;
51
53
  responsesTransport?: import("../index.js").ResponsesTransport;
package/dist/cli/main.js CHANGED
@@ -16,7 +16,7 @@ Usage:
16
16
  maragent run <prompt> [--permission-mode auto|full-access] [--reasoning-effort EFFORT] [--format text|json|stream-json] [--codex-auth-file PATH]
17
17
  maragent resume [sessionId] [--reasoning-effort EFFORT] [--codex-auth-file PATH]
18
18
  maragent sessions list|delete <sessionId>
19
- maragent models list|add|edit|remove|test [id] [--default-reasoning-effort EFFORT] [--code-mode|--no-code-mode] [--fast-mode|--no-fast-mode] [--codex-auth-file PATH]
19
+ maragent models list|add|edit|remove|test [id] [--default-reasoning-effort EFFORT] [--conversation-model|--no-conversation-model] [--subagent-model|--no-subagent-model] [--code-mode|--no-code-mode] [--fast-mode|--no-fast-mode] [--codex-auth-file PATH]
20
20
  `;
21
21
  function takeOption(arguments_, name) {
22
22
  const index = arguments_.indexOf(name);
@@ -68,7 +68,7 @@ async function modelsCommand(home, arguments_, codexAuthFile) {
68
68
  const catalog = await loadCatalog(home);
69
69
  if (action === 'list') {
70
70
  for (const model of catalog.models)
71
- stdout.write(`${model.id}\t${model.name}\t${model.protocol}\t${model.modelId}\tcredential=${model.apiKey === undefined ? 'codex-auth-file' : 'api-key'}\n`);
71
+ stdout.write(`${model.id}\t${model.name}\t${model.protocol}\t${model.modelId}\tcredential=${model.apiKey === undefined ? 'codex-auth-file' : 'api-key'}\tconversation=${model.conversationModel !== false}\tsubagent=${model.subagentModel === true}\n`);
72
72
  return;
73
73
  }
74
74
  const id = arguments_[2];
@@ -171,7 +171,8 @@ async function modelsCommand(home, arguments_, codexAuthFile) {
171
171
  defaultReasoningEffort: existing?.defaultReasoningEffort
172
172
  })
173
173
  : parseReasoningEffort(defaultReasoningEffortOption, '--default-reasoning-effort'),
174
- conversationModel: existing?.conversationModel ?? true,
174
+ conversationModel: booleanFlag(arguments_, '--conversation-model', '--no-conversation-model', existing?.conversationModel ?? true),
175
+ subagentModel: booleanFlag(arguments_, '--subagent-model', '--no-subagent-model', existing?.subagentModel ?? false),
175
176
  titleGeneration: existing?.titleGeneration ?? false,
176
177
  codeMode: booleanFlag(arguments_, '--code-mode', '--no-code-mode', existing?.codeMode ?? false),
177
178
  ...(credential === 'codex-auth-file' && fastMode ? { fastMode: true } : {}),
@@ -278,10 +279,16 @@ async function main() {
278
279
  }
279
280
  });
280
281
  const runtime = runtimeModels(catalog.models, codexAuthFile);
281
- const agent = await createAgent({
282
+ // The CLI-only build contains the independently published extension's same source.
283
+ const extensionPath = './subagent/index.js';
284
+ const extensionModule = (await import(extensionPath));
285
+ let agent;
286
+ const subagent = extensionModule.createSubagentExtension({ agent: () => agent });
287
+ agent = await createAgent({
282
288
  mode: 'normal',
283
289
  host,
284
290
  models: runtime.models,
291
+ extensions: [subagent],
285
292
  ...(modelId === undefined ? {} : { defaultModelId: modelId })
286
293
  });
287
294
  try {
@@ -0,0 +1,56 @@
1
+ import { type Agent, type AgentSession, type ExecutionResult } from '@myagentroam/agent';
2
+ import type { ExtensionOperationContext, SessionExtensionContext } from '@myagentroam/agent/extensions';
3
+ import type { SessionObservations } from './observations.js';
4
+ import type { StartAdmission } from './start.js';
5
+ export declare function target(agent: Agent, actorId: string, id: string): Promise<{
6
+ session: AgentSession;
7
+ summary: import("@myagentroam/agent").AgentSessionSummary & {
8
+ automaticTitlePending: boolean;
9
+ };
10
+ child: boolean;
11
+ parent: boolean;
12
+ }>;
13
+ /** Local operations use the Session owner for all execution and input transitions. */
14
+ export declare function registerControlTools(context: SessionExtensionContext, agent: Agent, observations: SessionObservations, warmObservations: ReadonlyMap<string, SessionObservations>, admission: StartAdmission): void;
15
+ export declare function sendChild(context: SessionExtensionContext, agent: Agent, observations: SessionObservations, warmObservations: ReadonlyMap<string, SessionObservations>, admission: StartAdmission, input: {
16
+ agentId: string;
17
+ content: string;
18
+ delivery?: 'append' | 'replace';
19
+ }, call: ExtensionOperationContext, mode: 'model' | 'native'): Promise<{
20
+ agentId: string;
21
+ delivery: string;
22
+ clientMessageId: `${string}-${string}-${string}-${string}-${string}`;
23
+ }>;
24
+ /** Stop through the Session owner; native callers use their own observation cursor. */
25
+ export declare function stopChild(context: SessionExtensionContext, agent: Agent, observations: SessionObservations, input: {
26
+ agentId: string;
27
+ executionId?: string;
28
+ }, call: ExtensionOperationContext): Promise<{
29
+ updates?: {
30
+ permissionStop?: import("@myagentroam/agent").PermissionStop;
31
+ errorMessage?: string;
32
+ errorCode?: string;
33
+ truncated?: boolean;
34
+ final?: boolean;
35
+ kind: "message" | "terminal";
36
+ value: string;
37
+ }[];
38
+ changedFilesTruncated?: boolean;
39
+ changedFiles?: string[];
40
+ turnId?: string;
41
+ summaryTruncated?: boolean;
42
+ summary?: string;
43
+ permissionStop?: import("@myagentroam/agent").PermissionStop;
44
+ errorCode?: string;
45
+ agentId: string;
46
+ executionId: string;
47
+ status: "completed" | "failed" | "cancelled";
48
+ } | {
49
+ status: "idle" | "running" | "interrupted" | "completed" | "failed" | "cancelled";
50
+ executionId?: string;
51
+ details?: ReturnType<typeof import("./snapshot.js").nativeChildSnapshot>;
52
+ errorCode?: string;
53
+ errorMessage?: string;
54
+ permissionStop?: ExecutionResult["permissionStop"];
55
+ agentId: string;
56
+ }>;
@@ -0,0 +1,219 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { AgentError } from '@myagentroam/agent';
3
+ import { publicChildSnapshot } from './snapshot.js';
4
+ import { historicalExecutionStatus } from './history.js';
5
+ async function relation(agent, actorId, id) {
6
+ const [actor, other] = await Promise.all([agent.getSession(actorId), agent.getSession(id)]);
7
+ if (actor.workspace !== other.workspace || actorId === id)
8
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Session is not available.');
9
+ const child = other.source.parentSessionId === actorId;
10
+ const parent = actor.source.parentSessionId === id;
11
+ if (!child && !parent)
12
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Session is not available.');
13
+ return { summary: other, child, parent };
14
+ }
15
+ export async function target(agent, actorId, id) {
16
+ const resolved = await relation(agent, actorId, id);
17
+ if (!resolved.child)
18
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Session is not available.');
19
+ return { ...resolved, session: await agent.openSession(id) };
20
+ }
21
+ function active(session) {
22
+ const snapshot = session.subscribe(() => { });
23
+ snapshot.unsubscribe();
24
+ return snapshot;
25
+ }
26
+ async function join(promise, signal) {
27
+ signal.throwIfAborted();
28
+ let abort;
29
+ const cancelled = new Promise((_, reject) => {
30
+ abort = () => reject(signal.reason);
31
+ signal.addEventListener('abort', abort, { once: true });
32
+ if (signal.aborted)
33
+ abort();
34
+ });
35
+ try {
36
+ return await Promise.race([promise, cancelled]);
37
+ }
38
+ finally {
39
+ signal.removeEventListener('abort', abort);
40
+ }
41
+ }
42
+ function origin(id, parent, message) {
43
+ const relation = parent ? 'other session' : 'parent session';
44
+ return `<mar_session_origin sessionId="${id}" relation="${parent ? 'other' : 'parent'}">\n <instruction>Message from the ${relation}.</instruction>\n</mar_session_origin>\n\n${message}`;
45
+ }
46
+ /** Local operations use the Session owner for all execution and input transitions. */
47
+ export function registerControlTools(context, agent, observations, warmObservations, admission) {
48
+ context.tools.register({
49
+ name: 'agent_send',
50
+ description: 'Send a message to a direct child or report to the direct parent.',
51
+ allowedCallers: ['direct', 'programmatic'],
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {
55
+ agentId: { type: 'string', minLength: 1 },
56
+ content: { type: 'string', minLength: 1 },
57
+ delivery: { type: 'string', enum: ['append', 'replace'] }
58
+ },
59
+ required: ['agentId', 'content'],
60
+ additionalProperties: false
61
+ },
62
+ async execute(value, call) {
63
+ const input = value;
64
+ const data = await sendChild(context, agent, observations, warmObservations, admission, input, call, 'model');
65
+ return { data, content: JSON.stringify(data) };
66
+ }
67
+ });
68
+ context.tools.register({
69
+ name: 'agent_stop',
70
+ description: 'Stop a direct child execution and wait for its cleanup.',
71
+ allowedCallers: ['direct', 'programmatic'],
72
+ inputSchema: {
73
+ type: 'object',
74
+ properties: {
75
+ agentId: { type: 'string', minLength: 1 },
76
+ executionId: { type: 'string', minLength: 1 }
77
+ },
78
+ required: ['agentId'],
79
+ additionalProperties: false
80
+ },
81
+ async execute(value, call) {
82
+ const input = value;
83
+ const data = await stopChild(context, agent, observations, input, call);
84
+ return { data, content: JSON.stringify(data) };
85
+ }
86
+ });
87
+ }
88
+ export async function sendChild(context, agent, observations, warmObservations, admission, input, call, mode) {
89
+ if (!input.content.trim() || (mode === 'model' && Buffer.byteLength(input.content) > 131072))
90
+ throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Message is invalid.');
91
+ const { summary, child, parent } = await relation(agent, context.sessionId, input.agentId);
92
+ if (parent && input.delivery === 'replace')
93
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Session is not available.');
94
+ call.signal.throwIfAborted();
95
+ if (parent) {
96
+ const receiver = warmObservations.get(input.agentId);
97
+ if (!call.executionId ||
98
+ !receiver?.report(context.sessionId, call.executionId, origin(context.sessionId, true, input.content)))
99
+ throw new AgentError('MAR_AGENT_SUBAGENT_PARENT_IDLE', 'Parent Session is not available for reports.');
100
+ const data = { agentId: input.agentId, delivery: 'reported', clientMessageId: randomUUID() };
101
+ return data;
102
+ }
103
+ const session = await agent.openSession(input.agentId);
104
+ call.signal.throwIfAborted();
105
+ const current = active(session);
106
+ const acknowledgePrevious = observations.acknowledgement(session.id);
107
+ const content = origin(context.sessionId, parent, input.content);
108
+ const clientMessageId = randomUUID();
109
+ if (current.activeExecutionId && !current.terminalEvent && input.delivery !== 'replace') {
110
+ const receipt = await session.steer({
111
+ expectedExecutionId: current.activeExecutionId,
112
+ prompt: content,
113
+ clientMessageId
114
+ });
115
+ if (receipt.status !== 'notAccepted') {
116
+ acknowledgePrevious();
117
+ if (receipt.status === 'accepted')
118
+ (mode === 'native'
119
+ ? observations
120
+ : (warmObservations.get(context.sessionId) ?? observations)).rememberReceipt(session.id, { clientMessageId, executionId: current.activeExecutionId });
121
+ const data = {
122
+ agentId: session.id,
123
+ executionId: current.activeExecutionId,
124
+ delivery: receipt.status,
125
+ clientMessageId,
126
+ ...(receipt.status === 'accepted'
127
+ ? { messageId: clientMessageId, turnId: receipt.entry.turnId, deliveryStatus: 'accepted' }
128
+ : {})
129
+ };
130
+ return data;
131
+ }
132
+ const after = active(session);
133
+ if (after.activeExecutionId && !after.terminalEvent)
134
+ throw new AgentError('MAR_AGENT_SUBAGENT_INPUT_NOT_ACCEPTED', 'Target did not accept the message.');
135
+ }
136
+ if (!child)
137
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Session is not available.');
138
+ if (current.activeExecutionId) {
139
+ const completion = session.joinExecution(current.activeExecutionId);
140
+ if (!current.terminalEvent && input.delivery === 'replace')
141
+ await session.cancel(current.activeExecutionId);
142
+ if (completion)
143
+ await join(completion, call.signal);
144
+ }
145
+ const models = context.models.list();
146
+ const model = models.find((item) => item.id === (summary.modelId ?? models.find((item) => item.isDefault)?.id));
147
+ if (!model?.conversationModel || !model.subagentModel)
148
+ throw new AgentError('MAR_AGENT_SUBAGENT_MODEL_INVALID', 'Target model is not available for child Sessions.');
149
+ const effort = summary.reasoningEffort &&
150
+ model.reasoningEfforts.includes(summary.reasoningEffort)
151
+ ? summary.reasoningEffort
152
+ : model.defaultReasoningEffort;
153
+ call.signal.throwIfAborted();
154
+ const executionInput = {
155
+ prompt: content,
156
+ clientMessageId,
157
+ modelId: model.id,
158
+ reasoningEffort: effort,
159
+ permissionMode: call.permissionMode ?? 'auto'
160
+ };
161
+ const release = admission.reserveResume(agent, context.sessionId);
162
+ let execution;
163
+ try {
164
+ execution =
165
+ summary.source.type === 'subagent' && summary.source.role === 'explore'
166
+ ? session.plan(executionInput)
167
+ : session.run(executionInput);
168
+ }
169
+ finally {
170
+ release();
171
+ }
172
+ acknowledgePrevious();
173
+ (mode === 'native'
174
+ ? observations
175
+ : (warmObservations.get(context.sessionId) ?? observations)).rememberReceipt(session.id, { clientMessageId, executionId: execution.executionId });
176
+ const data = {
177
+ agentId: session.id,
178
+ executionId: execution.executionId,
179
+ delivery: 'started',
180
+ clientMessageId,
181
+ messageId: clientMessageId,
182
+ deliveryStatus: 'accepted',
183
+ ...(execution.turnId === undefined ? {} : { turnId: execution.turnId })
184
+ };
185
+ return data;
186
+ }
187
+ /** Stop through the Session owner; native callers use their own observation cursor. */
188
+ export async function stopChild(context, agent, observations, input, call) {
189
+ const { child, session } = await target(agent, context.sessionId, input.agentId);
190
+ if (!child)
191
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Session is not available.');
192
+ const current = active(session);
193
+ if (!current.activeExecutionId && !input.executionId) {
194
+ const state = await historicalExecutionStatus(agent, session.id, call.signal);
195
+ return { agentId: session.id, ...state };
196
+ }
197
+ if (!current.activeExecutionId ||
198
+ (input.executionId && input.executionId !== current.activeExecutionId))
199
+ throw new AgentError('MAR_AGENT_SUBAGENT_EXECUTION_STALE', 'Execution is not active.');
200
+ const completion = session.joinExecution(current.activeExecutionId);
201
+ if (!completion)
202
+ throw new AgentError('MAR_AGENT_SUBAGENT_EXECUTION_STALE', 'Execution is not active.');
203
+ if (!current.terminalEvent)
204
+ await session.cancel(current.activeExecutionId);
205
+ const result = await join(completion, call.signal);
206
+ call.signal.throwIfAborted();
207
+ const observation = observations.capture(session.id, current.activeExecutionId);
208
+ const data = {
209
+ agentId: session.id,
210
+ executionId: current.activeExecutionId,
211
+ status: result.status,
212
+ ...(result.errorCode ? { errorCode: result.errorCode } : {}),
213
+ ...(result.permissionStop ? { permissionStop: result.permissionStop } : {}),
214
+ ...publicChildSnapshot(observations.snapshot(session.id, current.activeExecutionId)),
215
+ ...(observation?.updates.length ? { updates: observation.updates } : {})
216
+ };
217
+ observation?.acknowledge();
218
+ return data;
219
+ }
@@ -0,0 +1,17 @@
1
+ import { type Agent, type ExecutionResult } from '@myagentroam/agent';
2
+ import { nativeChildSnapshot } from './snapshot.js';
3
+ /** Share only in-flight history pages within one parent scope; completed reads are never cached. */
4
+ export declare function sharedSessionHistory(agent: Pick<Agent, 'sessionHistory'>): Pick<Agent, 'sessionHistory'>;
5
+ /** Read the latest historical Execution without opening the Session. */
6
+ export declare function historicalExecutionStatus(agent: Agent, sessionId: string, signal: AbortSignal, options?: {
7
+ details?: boolean;
8
+ activeExecutionId?: string;
9
+ history?: Pick<Agent, 'sessionHistory'>;
10
+ }): Promise<{
11
+ status: 'idle' | 'running' | 'interrupted' | 'completed' | 'failed' | 'cancelled';
12
+ executionId?: string;
13
+ details?: ReturnType<typeof nativeChildSnapshot>;
14
+ errorCode?: string;
15
+ errorMessage?: string;
16
+ permissionStop?: ExecutionResult['permissionStop'];
17
+ }>;
@@ -0,0 +1,85 @@
1
+ import { AgentError } from '@myagentroam/agent';
2
+ import { nativeChildSnapshot, projectChildEvent } from './snapshot.js';
3
+ /** Share only in-flight history pages within one parent scope; completed reads are never cached. */
4
+ export function sharedSessionHistory(agent) {
5
+ const pending = new Map();
6
+ return {
7
+ sessionHistory(sessionId, options) {
8
+ const key = JSON.stringify([sessionId, options?.limit ?? null, options?.cursor ?? null]);
9
+ const existing = pending.get(key);
10
+ if (existing)
11
+ return existing;
12
+ const request = agent.sessionHistory(sessionId, options);
13
+ pending.set(key, request);
14
+ void request
15
+ .finally(() => {
16
+ if (pending.get(key) === request)
17
+ pending.delete(key);
18
+ })
19
+ .catch(() => { });
20
+ return request;
21
+ }
22
+ };
23
+ }
24
+ /** Read the latest historical Execution without opening the Session. */
25
+ export async function historicalExecutionStatus(agent, sessionId, signal, options = {}) {
26
+ let cursor;
27
+ let latest;
28
+ const pages = [];
29
+ do {
30
+ signal.throwIfAborted();
31
+ const page = await (options.history ?? agent).sessionHistory(sessionId, {
32
+ limit: 100,
33
+ ...(cursor === undefined ? {} : { cursor })
34
+ });
35
+ signal.throwIfAborted();
36
+ const events = page.entries.flatMap((entry) => (entry.kind === 'event' ? [entry.event] : []));
37
+ latest ??= events.findLast((event) => event.type === 'execution.started' ||
38
+ event.type === 'execution.completed' ||
39
+ event.type === 'execution.failed' ||
40
+ event.type === 'execution.cancelled');
41
+ if (latest && !options.details)
42
+ break;
43
+ if (latest) {
44
+ const matching = events.filter((event) => event.executionId === latest.executionId);
45
+ pages.push(matching);
46
+ if (matching.some((event) => event.type === 'execution.started'))
47
+ break;
48
+ }
49
+ cursor = page.nextCursor;
50
+ } while (cursor);
51
+ if (!latest)
52
+ return { status: 'idle' };
53
+ let status;
54
+ if (latest.type === 'execution.started') {
55
+ if (latest.executionId === options.activeExecutionId)
56
+ status = 'running';
57
+ else {
58
+ const interrupted = await agent.sessionExecutionWasInterrupted(sessionId, latest.executionId, { signal });
59
+ signal.throwIfAborted();
60
+ if (!interrupted)
61
+ throw new AgentError('MAR_AGENT_SESSION_EXECUTION_CHANGED', 'Session Execution changed during inspection.');
62
+ status = 'interrupted';
63
+ }
64
+ }
65
+ else
66
+ status = latest.type.slice('execution.'.length);
67
+ const projection = { changedFiles: [] };
68
+ for (const events of pages.reverse())
69
+ for (const event of events)
70
+ projectChildEvent(projection, event, true);
71
+ return {
72
+ status,
73
+ executionId: latest.executionId,
74
+ ...(options.details ? { details: nativeChildSnapshot(projection) } : {}),
75
+ ...(latest.type === 'execution.completed' && latest.permissionStop
76
+ ? { permissionStop: latest.permissionStop }
77
+ : {}),
78
+ ...(latest.type === 'execution.failed'
79
+ ? {
80
+ errorCode: latest.code,
81
+ ...(latest.message === undefined ? {} : { errorMessage: latest.message })
82
+ }
83
+ : {})
84
+ };
85
+ }
@@ -0,0 +1,10 @@
1
+ import type { Agent } from '@myagentroam/agent';
2
+ import { type AgentExtension } from '@myagentroam/agent/extensions';
3
+ export { SUBAGENT_NATIVE_SERVICE } from './native.js';
4
+ export type { NativeAgentGroup, NativeAgentSnapshot, NativeSpawnInput, NativeWaitInput, NativeSubagentClient, NativeSubagentService, ExtensionAgentSnapshot, ExtensionAgentManySnapshot, ExtensionAgentSpawnInput, ExtensionAgentWaitInput, ExtensionAgentsApi } from './native.js';
5
+ export interface SubagentExtensionOptions {
6
+ /** Resolve the Agent after Host composition; it owns all Session execution. */
7
+ readonly agent: () => Agent;
8
+ }
9
+ /** Optional local consumer of Core Session lifecycle and observation APIs. */
10
+ export declare function createSubagentExtension(options: SubagentExtensionOptions): AgentExtension;
@@ -0,0 +1,94 @@
1
+ import { SESSION_CONTEXT_PROVIDER } from '@myagentroam/agent/extensions';
2
+ import { SessionObservations } from './observations.js';
3
+ import { registerStart, StartAdmission } from './start.js';
4
+ import { registerControlTools } from './control.js';
5
+ import { registerWait } from './wait.js';
6
+ import { registerQueryTools } from './query.js';
7
+ import { nativeSubagentService, SUBAGENT_NATIVE_SERVICE } from './native.js';
8
+ import { childResources } from './resources.js';
9
+ import { sharedSessionHistory } from './history.js';
10
+ export { SUBAGENT_NATIVE_SERVICE } from './native.js';
11
+ /** Optional local consumer of Core Session lifecycle and observation APIs. */
12
+ export function createSubagentExtension(options) {
13
+ const admission = new StartAdmission();
14
+ const warmObservations = new Map();
15
+ return {
16
+ id: 'subagent',
17
+ apiVersion: '1.0',
18
+ provides: [{ key: SUBAGENT_NATIVE_SERVICE }],
19
+ async attachSession(context) {
20
+ const agent = options.agent();
21
+ const parent = await agent.getSession(context.sessionId);
22
+ const history = sharedSessionHistory(agent);
23
+ const observations = new SessionObservations(false, history);
24
+ const native = nativeSubagentService(context, agent, admission, warmObservations, parent.workspace, history);
25
+ if (warmObservations.has(context.sessionId))
26
+ throw new Error('SUBAGENT_SESSION_ALREADY_ATTACHED');
27
+ warmObservations.set(context.sessionId, observations);
28
+ let disposed = false;
29
+ let parentUnsubscribe;
30
+ const observe = (session) => {
31
+ if (disposed)
32
+ return;
33
+ if (session.id === context.sessionId) {
34
+ parentUnsubscribe?.();
35
+ const subscription = session.subscribe((event) => {
36
+ if (event.type !== 'execution.completed' &&
37
+ event.type !== 'execution.failed' &&
38
+ event.type !== 'execution.cancelled')
39
+ return;
40
+ admission.finish(event.executionId);
41
+ parentUnsubscribe?.();
42
+ parentUnsubscribe = undefined;
43
+ });
44
+ parentUnsubscribe = subscription.unsubscribe;
45
+ if (subscription.terminalEvent) {
46
+ admission.finish(subscription.terminalEvent.executionId);
47
+ parentUnsubscribe();
48
+ parentUnsubscribe = undefined;
49
+ }
50
+ return;
51
+ }
52
+ if (session.source.parentSessionId === context.sessionId &&
53
+ session.workspace === parent.workspace)
54
+ observations.observe(session);
55
+ };
56
+ const registration = agent.observeSessionExecutions(observe);
57
+ try {
58
+ for (const session of registration.sessions)
59
+ observe(session);
60
+ context.services.provide(SUBAGENT_NATIVE_SERVICE, native.service);
61
+ context.services.provide(SESSION_CONTEXT_PROVIDER, {
62
+ collect: (signal) => observations.collect(signal),
63
+ wake: observations.wake,
64
+ async resources(signal) {
65
+ const resources = await childResources(agent, context.sessionId, signal, () => disposed, history);
66
+ return disposed ? [] : resources;
67
+ }
68
+ });
69
+ registerStart(context, agent, admission);
70
+ registerControlTools(context, agent, observations, warmObservations, admission);
71
+ registerWait(context, agent, observations, history);
72
+ registerQueryTools(context, agent);
73
+ }
74
+ catch (error) {
75
+ disposed = true;
76
+ void native.close().catch(() => { });
77
+ registration.unsubscribe();
78
+ parentUnsubscribe?.();
79
+ warmObservations.delete(context.sessionId);
80
+ observations.close();
81
+ throw error;
82
+ }
83
+ return async () => {
84
+ disposed = true;
85
+ const closing = native.close();
86
+ registration.unsubscribe();
87
+ parentUnsubscribe?.();
88
+ warmObservations.delete(context.sessionId);
89
+ observations.close();
90
+ await closing;
91
+ };
92
+ }
93
+ };
94
+ }
@@ -0,0 +1,91 @@
1
+ import { type Agent, type AgentReasoningEffort, type AgentSessionInputReceipt, type ExecutionResult } from '@myagentroam/agent';
2
+ import type { ExtensionServiceKey, SessionExtensionContext } from '@myagentroam/agent/extensions';
3
+ import { SessionObservations } from './observations.js';
4
+ import { type StartAdmission } from './start.js';
5
+ import { type WaitInput } from './wait.js';
6
+ import { type ChildSnapshot } from './snapshot.js';
7
+ export interface NativeSpawnInput {
8
+ readonly prompt: string;
9
+ readonly description?: string;
10
+ readonly role?: 'general-purpose' | 'explore';
11
+ readonly modelId?: string;
12
+ readonly reasoningEffort?: AgentReasoningEffort;
13
+ readonly background?: boolean;
14
+ readonly waitFor?: 'start' | 'completion';
15
+ readonly signal?: AbortSignal;
16
+ readonly turnSignal?: AbortSignal;
17
+ }
18
+ export interface NativeWaitInput extends WaitInput {
19
+ readonly signal?: AbortSignal;
20
+ }
21
+ export interface NativeAgentSnapshot extends Record<string, unknown> {
22
+ readonly agentId: string;
23
+ readonly description: string;
24
+ readonly status: 'idle' | 'not-started' | 'running' | 'completed' | 'failed' | 'cancelled' | 'interrupted';
25
+ readonly executionId?: string;
26
+ readonly role?: 'general-purpose' | 'explore';
27
+ readonly extensionId?: string;
28
+ readonly modelId?: string;
29
+ readonly modelName?: string;
30
+ readonly reasoningEffort?: string;
31
+ readonly summary?: string;
32
+ readonly turnId?: string;
33
+ readonly receipt?: AgentSessionInputReceipt;
34
+ readonly changedFiles?: readonly string[];
35
+ readonly latestMessage?: ChildSnapshot['latestMessage'];
36
+ readonly latestActivity?: ChildSnapshot['latestActivity'];
37
+ readonly verification?: readonly string[];
38
+ readonly evidence?: readonly {
39
+ path: string;
40
+ startLine?: number;
41
+ endLine?: number;
42
+ }[];
43
+ readonly errorCode?: string;
44
+ readonly errorMessage?: string;
45
+ readonly permissionStop?: ExecutionResult['permissionStop'];
46
+ readonly waitOutcome?: 'snapshot' | 'message' | 'completion' | 'timeout' | 'interrupted';
47
+ }
48
+ export interface NativeAgentGroup extends Record<string, unknown> {
49
+ readonly mode: 'any' | 'all';
50
+ readonly waitFor: 'message' | 'completion';
51
+ readonly waitOutcome: 'snapshot' | 'message' | 'completion' | 'timeout' | 'interrupted';
52
+ readonly agents: readonly NativeAgentSnapshot[];
53
+ readonly pendingAgentIds: readonly string[];
54
+ }
55
+ export interface NativeSubagentClient {
56
+ spawn(input: NativeSpawnInput): Promise<NativeAgentSnapshot>;
57
+ wait(input: NativeWaitInput): Promise<NativeAgentSnapshot | NativeAgentGroup>;
58
+ list(input?: {
59
+ signal?: AbortSignal;
60
+ }): Promise<readonly NativeAgentSnapshot[]>;
61
+ inspect(input: {
62
+ agentId: string;
63
+ signal?: AbortSignal;
64
+ }): Promise<NativeAgentSnapshot>;
65
+ message(input: {
66
+ agentId: string;
67
+ message: string;
68
+ delivery?: 'append' | 'replace';
69
+ signal?: AbortSignal;
70
+ }): Promise<Record<string, unknown>>;
71
+ cancel(input: {
72
+ agentId: string;
73
+ executionId?: string;
74
+ signal?: AbortSignal;
75
+ }): Promise<NativeAgentSnapshot>;
76
+ close(): Promise<void>;
77
+ }
78
+ export interface NativeSubagentService {
79
+ open(): NativeSubagentClient;
80
+ }
81
+ export type ExtensionAgentSnapshot = NativeAgentSnapshot;
82
+ export type ExtensionAgentManySnapshot = NativeAgentGroup;
83
+ export type ExtensionAgentSpawnInput = NativeSpawnInput;
84
+ export type ExtensionAgentWaitInput = NativeWaitInput;
85
+ export type ExtensionAgentsApi = NativeSubagentClient;
86
+ export declare const SUBAGENT_NATIVE_SERVICE: ExtensionServiceKey<NativeSubagentService>;
87
+ /** Each native client owns an independent observation cursor, never a Session Execution. */
88
+ export declare function nativeSubagentService(context: SessionExtensionContext, agent: Agent, admission: StartAdmission, warmObservations: ReadonlyMap<string, SessionObservations>, parentWorkspace: string, historyReader?: Pick<Agent, 'sessionHistory'>): {
89
+ service: NativeSubagentService;
90
+ close(): Promise<void>;
91
+ };