@bastani/atomic 0.9.45-alpha.1 → 0.9.46-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/builtin/intercom/CHANGELOG.md +11 -0
- package/dist/builtin/intercom/broker/broker.ts +124 -10
- package/dist/builtin/intercom/broker/client.ts +177 -25
- package/dist/builtin/intercom/broker/multinode-router.ts +228 -27
- package/dist/builtin/intercom/broker/multinode-state-store.ts +10 -0
- package/dist/builtin/intercom/broker/recipient-receipts.ts +38 -4
- package/dist/builtin/intercom/broker/send-handler.ts +9 -2
- package/dist/builtin/intercom/broker/sql-outbox-store.ts +29 -4
- package/dist/builtin/intercom/group.ts +25 -0
- package/dist/builtin/intercom/index.bundle.mjs +177 -35
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/retry-policy.ts +7 -0
- package/dist/builtin/intercom/types.ts +21 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +10 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +150 -54
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +45 -0
- package/dist/builtin/workflows/builtin/{chunk-6901nsd6.js → chunk-6jpwc09k.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-vwx023ge.js → chunk-qhm35ntx.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-jvna28mq.js → chunk-zhyqnf5g.js} +331 -68
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +3 -3
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/prompt-engineer/SKILL.md +1 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6.md +2 -2
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_1_sol.md +2 -2
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_1_terra.md +77 -0
- package/dist/builtin/workflows/src/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/journal/internal.d.ts +2 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +7076 -3386
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +11075 -7261
- package/dist/builtin/workflows/src/index.js +6768 -3605
- package/dist/builtin/workflows/src/local-worker-entry.mjs +11075 -7261
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +19 -0
- package/dist/core/agent-session-boundaries.d.ts.map +1 -1
- package/dist/core/agent-session-boundaries.js +1 -0
- package/dist/core/agent-session-boundaries.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +2 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +15 -2
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +3 -2
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts +4 -2
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +13 -8
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-recovery.d.ts.map +1 -1
- package/dist/core/agent-session-recovery.js +92 -1
- package/dist/core/agent-session-recovery.js.map +1 -1
- package/dist/core/agent-session-transfer.d.ts.map +1 -1
- package/dist/core/agent-session-transfer.js +5 -0
- package/dist/core/agent-session-transfer.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +2 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/copilot-errors.d.ts +6 -0
- package/dist/core/copilot-errors.d.ts.map +1 -0
- package/dist/core/copilot-errors.js +17 -0
- package/dist/core/copilot-errors.js.map +1 -0
- package/dist/core/execution-model-router.d.ts.map +1 -1
- package/dist/core/execution-model-router.js +12 -2
- package/dist/core/execution-model-router.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +7 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +6 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/workflow-activity-hub.d.ts +4 -0
- package/dist/core/extensions/workflow-activity-hub.d.ts.map +1 -1
- package/dist/core/extensions/workflow-activity-hub.js +26 -5
- package/dist/core/extensions/workflow-activity-hub.js.map +1 -1
- package/dist/core/extensions/workflow-events.d.ts +5 -0
- package/dist/core/extensions/workflow-events.d.ts.map +1 -1
- package/dist/core/extensions/workflow-events.js.map +1 -1
- package/dist/core/messages.d.ts +5 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-fallback-failures.d.ts +1 -1
- package/dist/core/model-fallback-failures.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.js +37 -3
- package/dist/core/model-fallback-failures.js.map +1 -1
- package/dist/core/model-runtime-streaming.js +2 -2
- package/dist/core/model-runtime-streaming.js.map +1 -1
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +19 -11
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/provider-rate-limits.d.ts +55 -6
- package/dist/core/provider-rate-limits.d.ts.map +1 -1
- package/dist/core/provider-rate-limits.js +182 -29
- package/dist/core/provider-rate-limits.js.map +1 -1
- package/dist/core/provider-request-admission.d.ts.map +1 -1
- package/dist/core/provider-request-admission.js +42 -18
- package/dist/core/provider-request-admission.js.map +1 -1
- package/dist/core/storage/node-sqlite.d.ts.map +1 -1
- package/dist/core/storage/node-sqlite.js +176 -19
- package/dist/core/storage/node-sqlite.js.map +1 -1
- package/dist/core/storage/provider.d.ts +4 -2
- package/dist/core/storage/provider.d.ts.map +1 -1
- package/dist/core/storage/provider.js +94 -5
- package/dist/core/storage/provider.js.map +1 -1
- package/dist/core/structured-output/index.d.ts.map +1 -1
- package/dist/core/structured-output/index.js +15 -8
- package/dist/core/structured-output/index.js.map +1 -1
- package/dist/core/structured-output/provider-failure.d.ts +17 -0
- package/dist/core/structured-output/provider-failure.d.ts.map +1 -0
- package/dist/core/structured-output/provider-failure.js +28 -0
- package/dist/core/structured-output/provider-failure.js.map +1 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +13 -7
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/shell-task-render.d.ts +1 -0
- package/dist/core/tools/shell-task-render.d.ts.map +1 -1
- package/dist/core/tools/shell-task-render.js +7 -2
- package/dist/core/tools/shell-task-render.js.map +1 -1
- package/dist/extensions/tool-search/tool.d.ts +5 -0
- package/dist/extensions/tool-search/tool.d.ts.map +1 -1
- package/dist/extensions/tool-search/tool.js +38 -5
- package/dist/extensions/tool-search/tool.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.js +3 -2
- package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +42 -14
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-context.js +2 -1
- package/dist/modes/interactive/interactive-extension-context.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +5 -6
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/rpc/rpc-client-process.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client-process.js +8 -1
- package/dist/modes/rpc/rpc-client-process.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/utils/ansi.d.ts +2 -0
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js +5 -0
- package/dist/utils/ansi.js.map +1 -1
- package/docs/codemode.md +1 -1
- package/docs/intercom/reference.md +3 -3
- package/docs/models/evals.md +31 -12
- package/docs/models/reference.md +23 -51
- package/docs/models.md +3 -4
- package/docs/providers.md +5 -15
- package/docs/settings.md +1 -1
- package/docs/storage.md +2 -2
- package/docs/tools.md +1 -1
- package/docs/workflows/api-reference.md +20 -1
- package/docs/workflows/operations.md +17 -4
- package/npm-shrinkwrap.json +64 -44
- package/package.json +4 -4
|
@@ -7785,6 +7785,9 @@ function splitKnownThinkingSuffix(model) {
|
|
|
7785
7785
|
thinkingSuffix: `:${suffix}`
|
|
7786
7786
|
};
|
|
7787
7787
|
}
|
|
7788
|
+
function taskRecordModel(model) {
|
|
7789
|
+
return model === undefined ? undefined : splitKnownThinkingSuffix(model).baseModel;
|
|
7790
|
+
}
|
|
7788
7791
|
|
|
7789
7792
|
// dist/builtin/subagents/src/shared/formatters.ts
|
|
7790
7793
|
function formatTokens(n) {
|
|
@@ -9818,6 +9821,14 @@ function resumeRunWorktree(child, setupHook) {
|
|
|
9818
9821
|
}
|
|
9819
9822
|
|
|
9820
9823
|
// dist/builtin/subagents/src/runs/foreground/subagent-child-records.ts
|
|
9824
|
+
function heldTaskExpands(held) {
|
|
9825
|
+
return held.taskExpands ?? true;
|
|
9826
|
+
}
|
|
9827
|
+
function pendingMessagesOfResult(result) {
|
|
9828
|
+
if (!result.pendingMessages?.length)
|
|
9829
|
+
return;
|
|
9830
|
+
return { messages: result.pendingMessages, taskExpands: result.pendingTaskExpands === true };
|
|
9831
|
+
}
|
|
9821
9832
|
function subagentChildInheritance(options) {
|
|
9822
9833
|
return {
|
|
9823
9834
|
...options.currentModel ? { model: options.currentModel } : {},
|
|
@@ -10741,8 +10752,8 @@ async function runAgentTask(input) {
|
|
|
10741
10752
|
...input.options.orchestratorIntercomTarget ? { orchestratorTarget: input.options.orchestratorIntercomTarget } : {},
|
|
10742
10753
|
...worktree ? { worktree } : {},
|
|
10743
10754
|
...terminalStatus ? { terminalStatus } : {},
|
|
10744
|
-
...heldMessages?.length ? { heldMessages: { taskId,
|
|
10745
|
-
...launch ? { launch } : {},
|
|
10755
|
+
...heldMessages?.messages.length ? { heldMessages: { taskId, ...heldMessages } } : {},
|
|
10756
|
+
...launch ? { launch } : heldMessages?.taskExpands && input.options.resumeSession !== true && childSession.childSessionId === undefined ? { launch: subagentChildLaunch(input.task, input.intentTask, input.options) } : {},
|
|
10746
10757
|
inherited: subagentChildInheritance(input.options)
|
|
10747
10758
|
});
|
|
10748
10759
|
};
|
|
@@ -10888,7 +10899,7 @@ ${launch.message}` : input.task,
|
|
|
10888
10899
|
if (child.model !== undefined || child.thinking !== undefined)
|
|
10889
10900
|
context.reportActivity({
|
|
10890
10901
|
reportId: "terminal-model",
|
|
10891
|
-
change: { kind: "model", model: child.model, thinking: child.thinking }
|
|
10902
|
+
change: { kind: "model", model: taskRecordModel(child.model), thinking: child.thinking }
|
|
10892
10903
|
});
|
|
10893
10904
|
const stopped = takeSubagentChildStop(context.ref.taskId);
|
|
10894
10905
|
const stoppedByControl = stopped !== undefined && child.status === "interrupted";
|
|
@@ -10901,11 +10912,11 @@ ${launch.message}` : input.task,
|
|
|
10901
10912
|
if (endedForGood || child.status === "killed")
|
|
10902
10913
|
forgetRunMembership(runId, index);
|
|
10903
10914
|
if (!ownerClosed())
|
|
10904
|
-
recordChild("terminal", stoppedByControl ? stopped : stoppedByStagePause ? SUBAGENT_CHILD_STAGE_PAUSED_STATUS : child.status, stoppedByControl || stoppedByStagePause ? child
|
|
10915
|
+
recordChild("terminal", stoppedByControl ? stopped : stoppedByStagePause ? SUBAGENT_CHILD_STAGE_PAUSED_STATUS : child.status, stoppedByControl || stoppedByStagePause ? pendingMessagesOfResult(child) : undefined);
|
|
10905
10916
|
else if (endedForGood)
|
|
10906
10917
|
recordChild("terminal", SUBAGENT_CHILD_STAGE_CLOSED_STATUS);
|
|
10907
10918
|
else if (child.pendingMessages?.length)
|
|
10908
|
-
recordChild("running", undefined, child
|
|
10919
|
+
recordChild("running", undefined, pendingMessagesOfResult(child));
|
|
10909
10920
|
input.onTerminal?.(child);
|
|
10910
10921
|
const text = input.outputText?.(child) ?? getSingleResultOutput(child);
|
|
10911
10922
|
retainTaskOutput(child, text, context.ref);
|
|
@@ -11070,7 +11081,7 @@ async function runForegroundParallelTasks(input) {
|
|
|
11070
11081
|
const sharedProgress = index === input.behaviors.findIndex((candidate) => candidate.progress) ? join12(input.paramsCwd, "progress.md") : undefined;
|
|
11071
11082
|
const runOptions = {
|
|
11072
11083
|
runningCapacity: input.runningCapacity,
|
|
11073
|
-
getChildSessionOptions: input.ctx.getChildSessionOptions,
|
|
11084
|
+
getChildSessionOptions: input.ctx.getChildSessionOptionsResolver?.() ?? input.ctx.getChildSessionOptions,
|
|
11074
11085
|
resourceLoaderInheritanceSnapshot: input.resourceLoaderInheritanceSnapshot,
|
|
11075
11086
|
cwd: taskCwd,
|
|
11076
11087
|
signal: input.signal,
|
|
@@ -11186,6 +11197,7 @@ async function runForegroundParallelTasks(input) {
|
|
|
11186
11197
|
taskResponse: response
|
|
11187
11198
|
};
|
|
11188
11199
|
}
|
|
11200
|
+
const launchCwd = input.ctx.cwd;
|
|
11189
11201
|
const launched = runOwnerlessChild({
|
|
11190
11202
|
agent: task.agent,
|
|
11191
11203
|
...agentConfig ? { agentSource: agentConfig.source } : {},
|
|
@@ -11194,7 +11206,7 @@ async function runForegroundParallelTasks(input) {
|
|
|
11194
11206
|
options: runOptions,
|
|
11195
11207
|
recordChild: input.recordChild ?? { record() {} },
|
|
11196
11208
|
schedule,
|
|
11197
|
-
runSync: (childTask, options) => input.runtime.runSync(
|
|
11209
|
+
runSync: (childTask, options) => input.runtime.runSync(launchCwd, taskAgents, task.agent, childTask, options)
|
|
11198
11210
|
}).result;
|
|
11199
11211
|
return launched.finally(() => {
|
|
11200
11212
|
if (input.foregroundControl?.currentIndex === index) {
|
|
@@ -11255,8 +11267,8 @@ function runOwnerlessChild(input) {
|
|
|
11255
11267
|
...input.options.orchestratorIntercomTarget ? { orchestratorTarget: input.options.orchestratorIntercomTarget } : {},
|
|
11256
11268
|
...worktree ? { worktree } : {},
|
|
11257
11269
|
...terminalStatus ? { terminalStatus } : {},
|
|
11258
|
-
...held?.length ? { heldMessages: { taskId: launchId,
|
|
11259
|
-
...queuedLaunch ? { launch: queuedLaunch } : {},
|
|
11270
|
+
...held?.messages.length ? { heldMessages: { taskId: launchId, ...held } } : {},
|
|
11271
|
+
...queuedLaunch ? { launch: queuedLaunch } : held?.taskExpands && input.options.resumeSession !== true && childSession.childSessionId === undefined ? { launch: subagentChildLaunch(input.task, undefined, input.options) } : {},
|
|
11260
11272
|
inherited: subagentChildInheritance(input.options)
|
|
11261
11273
|
});
|
|
11262
11274
|
};
|
|
@@ -11333,10 +11345,10 @@ ${resumed.message}` : input.task,
|
|
|
11333
11345
|
forgetRunMembership(runId, index);
|
|
11334
11346
|
if (ownerClosed) {
|
|
11335
11347
|
parkRunWorktree(runId, index);
|
|
11336
|
-
record("running", undefined, child
|
|
11348
|
+
record("running", undefined, pendingMessagesOfResult(child));
|
|
11337
11349
|
return child;
|
|
11338
11350
|
}
|
|
11339
|
-
record("terminal", stoppedByControl ? stopped : child.status, stoppedByControl ? child
|
|
11351
|
+
record("terminal", stoppedByControl ? stopped : child.status, stoppedByControl ? pendingMessagesOfResult(child) : undefined);
|
|
11340
11352
|
return child;
|
|
11341
11353
|
} catch (error) {
|
|
11342
11354
|
if (takeSubagentChildStop(launchId) !== undefined)
|
|
@@ -12849,24 +12861,41 @@ function statsFor(session, fallbackSessionId) {
|
|
|
12849
12861
|
};
|
|
12850
12862
|
}
|
|
12851
12863
|
}
|
|
12852
|
-
function
|
|
12853
|
-
|
|
12864
|
+
function messagesSinceLaunch(manager, launchLeafId) {
|
|
12865
|
+
const branch = manager.getBranch();
|
|
12866
|
+
const launchIndex = launchLeafId === null ? -1 : branch.findIndex((entry) => entry.id === launchLeafId);
|
|
12867
|
+
if (launchLeafId !== null && launchIndex === -1)
|
|
12868
|
+
return [];
|
|
12869
|
+
return branch.slice(launchIndex + 1).flatMap((entry) => entry.type === "message" ? [entry.message] : []);
|
|
12870
|
+
}
|
|
12871
|
+
function outputFor(ownMessages) {
|
|
12872
|
+
return lastNonEmptyAssistantText(ownMessages.filter((message) => message.role === "assistant"));
|
|
12854
12873
|
}
|
|
12855
|
-
function
|
|
12874
|
+
function noSuccessfulTurnCause(ownMessages) {
|
|
12875
|
+
const ownTurns = ownMessages.flatMap((message) => message.role === "assistant" ? [message] : []);
|
|
12876
|
+
if (ownTurns.some((turn) => turn.stopReason !== "error" && turn.stopReason !== "aborted"))
|
|
12877
|
+
return;
|
|
12878
|
+
const errorMessage = ownTurns.findLast((turn) => turn.errorMessage?.trim())?.errorMessage?.trim();
|
|
12879
|
+
return errorMessage || "Child ended without completing an assistant turn of its own.";
|
|
12880
|
+
}
|
|
12881
|
+
function cancelledEnvelope(session, spec, stats, ownMessages) {
|
|
12856
12882
|
return recoverCancelledChildOutput({
|
|
12857
12883
|
progressPath: spec.progressPath,
|
|
12858
|
-
assistantText:
|
|
12884
|
+
assistantText: outputFor(ownMessages),
|
|
12859
12885
|
toolCount: stats.toolCalls,
|
|
12860
12886
|
sessionPath: session?.sessionFile ?? spec.sessionManager?.getSessionFile(),
|
|
12861
12887
|
...spec.progressArtifactPath ? { progressArtifactPath: spec.progressArtifactPath } : {},
|
|
12862
12888
|
...spec.outputArtifactPath ? { outputArtifactPath: spec.outputArtifactPath } : {}
|
|
12863
12889
|
}).text;
|
|
12864
12890
|
}
|
|
12865
|
-
|
|
12891
|
+
var LITERAL_TEXT = { expandPromptTemplates: false };
|
|
12892
|
+
function pendingMessagesOf(session, unsent, taskLiteral) {
|
|
12866
12893
|
const queued = session ? [...session.getSteeringMessages(), ...session.getFollowUpMessages()] : [];
|
|
12867
12894
|
const [task, ...followUps] = unsent;
|
|
12868
12895
|
const pending = task === undefined ? queued : [task, ...queued, ...followUps];
|
|
12869
|
-
|
|
12896
|
+
if (pending.length === 0)
|
|
12897
|
+
return {};
|
|
12898
|
+
return { pendingMessages: pending, ...task !== undefined && !taskLiteral ? { pendingTaskExpands: true } : {} };
|
|
12870
12899
|
}
|
|
12871
12900
|
function attemptStatus(termination) {
|
|
12872
12901
|
if (termination === "interrupt" || termination === PARENT_CANCEL_CAUSE)
|
|
@@ -13073,14 +13102,11 @@ class SubagentControlRuntime {
|
|
|
13073
13102
|
status: killed ? "killed" : "interrupted",
|
|
13074
13103
|
...waitResult === PARENT_CANCEL_CAUSE && !killed ? {
|
|
13075
13104
|
cause: PARENT_CANCEL_CAUSE,
|
|
13076
|
-
...pendingMessagesOf(undefined, [
|
|
13077
|
-
admitted.spec.task,
|
|
13078
|
-
...admitted.spec.resumeFollowUps ?? []
|
|
13079
|
-
])
|
|
13105
|
+
...pendingMessagesOf(undefined, [admitted.spec.task, ...admitted.spec.resumeFollowUps ?? []], admitted.spec.resumeTaskLiteral)
|
|
13080
13106
|
} : {},
|
|
13081
13107
|
stats,
|
|
13082
13108
|
path: admitted.identity.path,
|
|
13083
|
-
envelope: killed ? "Killed. This child cannot be resumed." : waitResult === PARENT_CANCEL_CAUSE ? cancelledEnvelope(undefined, admitted.spec, stats) : INTERRUPTED_ENVELOPE,
|
|
13109
|
+
envelope: killed ? "Killed. This child cannot be resumed." : waitResult === PARENT_CANCEL_CAUSE ? cancelledEnvelope(undefined, admitted.spec, stats, []) : INTERRUPTED_ENVELOPE,
|
|
13084
13110
|
...effectiveModelId === undefined ? {} : { model: effectiveModelId },
|
|
13085
13111
|
...effectiveThinking === undefined ? {} : { thinking: effectiveThinking }
|
|
13086
13112
|
};
|
|
@@ -13115,6 +13141,7 @@ class SubagentControlRuntime {
|
|
|
13115
13141
|
const token = guard.token;
|
|
13116
13142
|
this.attemptTokens.set(admitted.identity.path, token);
|
|
13117
13143
|
let session;
|
|
13144
|
+
let ownMessages = () => [];
|
|
13118
13145
|
const executionEnded = new AbortController;
|
|
13119
13146
|
let termination;
|
|
13120
13147
|
const unsent = [admitted.spec.task, ...admitted.spec.resumeFollowUps ?? []];
|
|
@@ -13163,6 +13190,8 @@ class SubagentControlRuntime {
|
|
|
13163
13190
|
admitted.spec.onChildSession?.(childSession);
|
|
13164
13191
|
if (workflow)
|
|
13165
13192
|
await sessionManager.markSessionInternal(workflow);
|
|
13193
|
+
const launchLeafId = sessionManager.getLeafId();
|
|
13194
|
+
ownMessages = () => messagesSinceLaunch(sessionManager, launchLeafId);
|
|
13166
13195
|
let created;
|
|
13167
13196
|
if (admitted.spec.testSession) {
|
|
13168
13197
|
created = { session: createTestSession(sessionManager, admitted.spec) };
|
|
@@ -13281,7 +13310,7 @@ class SubagentControlRuntime {
|
|
|
13281
13310
|
let activitySequence = 0;
|
|
13282
13311
|
const reportModel = () => taskHooks?.reportActivity({
|
|
13283
13312
|
reportId: `${activityPrefix}:model-${++activitySequence}`,
|
|
13284
|
-
change: { kind: "model", model: effectiveModelId, thinking: effectiveThinking }
|
|
13313
|
+
change: { kind: "model", model: taskRecordModel(effectiveModelId), thinking: effectiveThinking }
|
|
13285
13314
|
});
|
|
13286
13315
|
reportModel();
|
|
13287
13316
|
unsubscribe = session.subscribe((event) => {
|
|
@@ -13355,21 +13384,23 @@ class SubagentControlRuntime {
|
|
|
13355
13384
|
for (const followUp of admitted.spec.resumeFollowUps ?? []) {
|
|
13356
13385
|
if (termination)
|
|
13357
13386
|
break;
|
|
13358
|
-
await session.followUp(followUp);
|
|
13387
|
+
await session.followUp(followUp, undefined, LITERAL_TEXT);
|
|
13359
13388
|
unsent.splice(1, 1);
|
|
13360
13389
|
}
|
|
13361
13390
|
if (!termination) {
|
|
13362
13391
|
unsent.splice(0, 1);
|
|
13363
|
-
await session.prompt(admitted.spec.task);
|
|
13392
|
+
await session.prompt(admitted.spec.task, admitted.spec.resumeTaskLiteral ? LITERAL_TEXT : undefined);
|
|
13364
13393
|
}
|
|
13365
13394
|
await terminating;
|
|
13366
13395
|
const stats = statsFor(session, admitted.identity.path);
|
|
13367
13396
|
const sessionFile = session.sessionFile;
|
|
13368
|
-
const
|
|
13369
|
-
const
|
|
13397
|
+
const producedMessages = ownMessages();
|
|
13398
|
+
const output = outputFor(producedMessages);
|
|
13399
|
+
const noOutput = termination === undefined ? noSuccessfulTurnCause(producedMessages) : undefined;
|
|
13400
|
+
const status = noOutput === undefined ? attemptStatus(termination) : "error";
|
|
13370
13401
|
this.native.publishChildStatus(admitted.identity.path, nativeStatus(status));
|
|
13371
13402
|
this.native.finishChildAttempt(token, nativeStatus(status));
|
|
13372
|
-
const envelope = termination === PARENT_CANCEL_CAUSE ? cancelledEnvelope(session, admitted.spec, stats) : status === "interrupted" ? INTERRUPTED_ENVELOPE : boundedEnvelope(output || termination || "(no output)", undefined);
|
|
13403
|
+
const envelope = termination === PARENT_CANCEL_CAUSE ? cancelledEnvelope(session, admitted.spec, stats, producedMessages) : status === "interrupted" ? INTERRUPTED_ENVELOPE : boundedEnvelope(output || noOutput || termination || "(no output)", undefined);
|
|
13373
13404
|
if (status === "ok")
|
|
13374
13405
|
return {
|
|
13375
13406
|
status,
|
|
@@ -13386,7 +13417,10 @@ class SubagentControlRuntime {
|
|
|
13386
13417
|
if (status === "interrupted")
|
|
13387
13418
|
return {
|
|
13388
13419
|
status: this.killedChildren.has(admitted.identity.path) ? "killed" : status,
|
|
13389
|
-
...termination === PARENT_CANCEL_CAUSE && !this.killedChildren.has(admitted.identity.path) ? {
|
|
13420
|
+
...termination === PARENT_CANCEL_CAUSE && !this.killedChildren.has(admitted.identity.path) ? {
|
|
13421
|
+
cause: PARENT_CANCEL_CAUSE,
|
|
13422
|
+
...pendingMessagesOf(session, unsent, admitted.spec.resumeTaskLiteral)
|
|
13423
|
+
} : {},
|
|
13390
13424
|
stats,
|
|
13391
13425
|
path: admitted.identity.path,
|
|
13392
13426
|
envelope: this.killedChildren.has(admitted.identity.path) ? "Killed. This child cannot be resumed." : envelope,
|
|
@@ -13398,7 +13432,7 @@ class SubagentControlRuntime {
|
|
|
13398
13432
|
};
|
|
13399
13433
|
return {
|
|
13400
13434
|
status,
|
|
13401
|
-
cause: termination ?? "agent session ended without a successful completion",
|
|
13435
|
+
cause: noOutput ?? termination ?? "agent session ended without a successful completion",
|
|
13402
13436
|
stats,
|
|
13403
13437
|
path: admitted.identity.path,
|
|
13404
13438
|
envelope,
|
|
@@ -13419,10 +13453,13 @@ class SubagentControlRuntime {
|
|
|
13419
13453
|
if (status === "interrupted")
|
|
13420
13454
|
return {
|
|
13421
13455
|
status: this.killedChildren.has(admitted.identity.path) ? "killed" : status,
|
|
13422
|
-
...termination === PARENT_CANCEL_CAUSE && !this.killedChildren.has(admitted.identity.path) ? {
|
|
13456
|
+
...termination === PARENT_CANCEL_CAUSE && !this.killedChildren.has(admitted.identity.path) ? {
|
|
13457
|
+
cause: PARENT_CANCEL_CAUSE,
|
|
13458
|
+
...pendingMessagesOf(session, unsent, admitted.spec.resumeTaskLiteral)
|
|
13459
|
+
} : {},
|
|
13423
13460
|
stats,
|
|
13424
13461
|
path: admitted.identity.path,
|
|
13425
|
-
envelope: termination === PARENT_CANCEL_CAUSE && !this.killedChildren.has(admitted.identity.path) ? cancelledEnvelope(session, admitted.spec, stats) : this.killedChildren.has(admitted.identity.path) ? "Killed. This child cannot be resumed." : INTERRUPTED_ENVELOPE,
|
|
13462
|
+
envelope: termination === PARENT_CANCEL_CAUSE && !this.killedChildren.has(admitted.identity.path) ? cancelledEnvelope(session, admitted.spec, stats, ownMessages()) : this.killedChildren.has(admitted.identity.path) ? "Killed. This child cannot be resumed." : INTERRUPTED_ENVELOPE,
|
|
13426
13463
|
sessionFile: session?.sessionFile,
|
|
13427
13464
|
...effectiveModelId === undefined ? {} : { model: effectiveModelId },
|
|
13428
13465
|
...effectiveThinking === undefined ? {} : { thinking: effectiveThinking },
|
|
@@ -13893,7 +13930,10 @@ function resultFromOutcome(agent, task, outcome, startedAt, artifactPaths) {
|
|
|
13893
13930
|
path: outcome.path,
|
|
13894
13931
|
envelope: outcome.status === "killed" ? output : outcome.envelope,
|
|
13895
13932
|
interrupted: status === "interrupted" ? true : undefined,
|
|
13896
|
-
...outcome.status === "interrupted" && outcome.pendingMessages ? {
|
|
13933
|
+
...outcome.status === "interrupted" && outcome.pendingMessages ? {
|
|
13934
|
+
pendingMessages: outcome.pendingMessages,
|
|
13935
|
+
...outcome.pendingTaskExpands ? { pendingTaskExpands: true } : {}
|
|
13936
|
+
} : {},
|
|
13897
13937
|
messages: [],
|
|
13898
13938
|
usage: usageFromStats(outcome.stats),
|
|
13899
13939
|
...model === undefined ? {} : { model },
|
|
@@ -14007,6 +14047,7 @@ ${task}`);
|
|
|
14007
14047
|
sessionManager: options.sessionManager,
|
|
14008
14048
|
...options.resumeSession ? { resumeSession: true } : {},
|
|
14009
14049
|
...options.resumeFollowUps?.length ? { resumeFollowUps: options.resumeFollowUps } : {},
|
|
14050
|
+
...options.resumeTaskLiteral ? { resumeTaskLiteral: true } : {},
|
|
14010
14051
|
...options.onChildSession ? { onChildSession: options.onChildSession } : {},
|
|
14011
14052
|
...options.onChildLaunch ? { onChildLaunch: options.onChildLaunch } : {},
|
|
14012
14053
|
...options.progressPath ? { progressPath: options.progressPath } : {},
|
|
@@ -14131,6 +14172,8 @@ ${task}`);
|
|
|
14131
14172
|
}
|
|
14132
14173
|
persistRequestedOutput(recovered, options, outputSnapshot);
|
|
14133
14174
|
options.onDetachedExit?.(recovered);
|
|
14175
|
+
}).catch((error) => {
|
|
14176
|
+
console.error("atomic-subagents: detached child exit handling failed", error);
|
|
14134
14177
|
});
|
|
14135
14178
|
const continuedModel = running.currentModel ?? candidate;
|
|
14136
14179
|
const continuedThinking = running.currentThinking;
|
|
@@ -14869,15 +14912,23 @@ function resumedPrompts(record, resume) {
|
|
|
14869
14912
|
resume?.continuation,
|
|
14870
14913
|
resume?.message ? undefined : prompt
|
|
14871
14914
|
]);
|
|
14872
|
-
const
|
|
14915
|
+
const recorded = record.heldMessages && record.heldMessages.taskId === record.taskId ? record.heldMessages : undefined;
|
|
14916
|
+
const held = recorded ? recorded.messages.filter((message) => !continuations.has(message)) : [];
|
|
14917
|
+
const taskExpands = recorded !== undefined && heldTaskExpands(recorded) && held.length > 0 && held[0] === recorded.messages[0];
|
|
14873
14918
|
const [task = prompt, ...followUps] = [...held, prompt, ...resume?.continuation ? [resume.continuation] : []];
|
|
14874
|
-
return { task, followUps };
|
|
14919
|
+
return { task, followUps, taskLiteral: !taskExpands };
|
|
14875
14920
|
}
|
|
14876
14921
|
function freshPrompts(record, launch, resume) {
|
|
14877
|
-
const
|
|
14878
|
-
|
|
14922
|
+
const recorded = record.heldMessages && record.heldMessages.taskId === record.taskId ? record.heldMessages : undefined;
|
|
14923
|
+
const held = recorded?.messages ?? [];
|
|
14924
|
+
const heldTask = recorded !== undefined && held[0] === launch.task && heldTaskExpands(recorded);
|
|
14925
|
+
return {
|
|
14926
|
+
task: resume?.message ? `${launch.task}
|
|
14879
14927
|
|
|
14880
|
-
${resume.message}` : launch.task,
|
|
14928
|
+
${resume.message}` : launch.task,
|
|
14929
|
+
followUps: heldTask ? held.slice(1) : held,
|
|
14930
|
+
taskLiteral: false
|
|
14931
|
+
};
|
|
14881
14932
|
}
|
|
14882
14933
|
function rediscoverAgent(deps, cwd, name, source) {
|
|
14883
14934
|
const scopes = source ? ["both", "user", "project"] : [resolveExecutionAgentScope(undefined)];
|
|
@@ -14901,7 +14952,7 @@ async function relaunchChild(deps, ctx, record, hold, release, onReopened, resum
|
|
|
14901
14952
|
const host = ctx.getAgentTaskHost?.();
|
|
14902
14953
|
const queued = record.taskId && host ? takeStoppedQueuedSubagentChild(record.taskId) : undefined;
|
|
14903
14954
|
if (queued && host)
|
|
14904
|
-
return startQueuedChild(deps, ctx, host, record, queued, hold, release, resume);
|
|
14955
|
+
return startedFresh(await startQueuedChild(deps, ctx, host, record, queued, hold, release, resume));
|
|
14905
14956
|
const queuedOwnerless = record.taskId && !host ? takeStoppedQueuedOwnerlessChild(record.taskId) : undefined;
|
|
14906
14957
|
const ownerSignal = host ? undefined : ctx.getCurrentSessionSignal?.() ?? ctx.sessionSignal;
|
|
14907
14958
|
if (queuedOwnerless) {
|
|
@@ -14915,7 +14966,7 @@ async function relaunchChild(deps, ctx, record, hold, release, onReopened, resum
|
|
|
14915
14966
|
...resume?.message ? { message: resume.message } : {},
|
|
14916
14967
|
...supervisorAuthorization ? { supervisorAuthorization } : {}
|
|
14917
14968
|
});
|
|
14918
|
-
return ownerlessRelaunched(deps, record, launched, recordChild, release, hold, supervisorAuthorization, ctx);
|
|
14969
|
+
return startedFresh(await ownerlessRelaunched(deps, record, launched, recordChild, release, hold, supervisorAuthorization, ctx));
|
|
14919
14970
|
}
|
|
14920
14971
|
if (!existsSync15(record.cwd))
|
|
14921
14972
|
return unrestorable(`its working directory no longer exists: ${record.cwd}`);
|
|
@@ -14970,7 +15021,7 @@ async function relaunchChild(deps, ctx, record, hold, release, onReopened, resum
|
|
|
14970
15021
|
const prompts = fresh ? freshPrompts(record, fresh, resume) : resumedPrompts(record, resume);
|
|
14971
15022
|
const options = {
|
|
14972
15023
|
runningCapacity: deps.config.parallel?.runningCapacity,
|
|
14973
|
-
getChildSessionOptions: ctx.getChildSessionOptions,
|
|
15024
|
+
getChildSessionOptions: ctx.getChildSessionOptionsResolver?.() ?? ctx.getChildSessionOptions,
|
|
14974
15025
|
resourceLoaderInheritanceSnapshot: deps.pi.getResourceLoaderInheritanceSnapshot?.(),
|
|
14975
15026
|
cwd: record.cwd,
|
|
14976
15027
|
runId: record.runId,
|
|
@@ -14983,6 +15034,7 @@ async function relaunchChild(deps, ctx, record, hold, release, onReopened, resum
|
|
|
14983
15034
|
...fresh?.maxOutput ? { maxOutput: fresh.maxOutput } : {},
|
|
14984
15035
|
...fresh?.share === undefined ? {} : { share: fresh.share },
|
|
14985
15036
|
...prompts.followUps.length > 0 ? { resumeFollowUps: prompts.followUps } : {},
|
|
15037
|
+
...prompts.taskLiteral ? { resumeTaskLiteral: true } : {},
|
|
14986
15038
|
intercomEvents: deps.pi.events,
|
|
14987
15039
|
parentDepth: getCurrentSubagentDepth(ctx),
|
|
14988
15040
|
workflowStageSubagentGuard: isWorkflowStageOrchestrationContext(ctx),
|
|
@@ -15004,6 +15056,7 @@ async function relaunchChild(deps, ctx, record, hold, release, onReopened, resum
|
|
|
15004
15056
|
const runtime = resolveSubagentExecutorRuntimeDeps(deps.runtime);
|
|
15005
15057
|
const schedule = subagentChildSchedule(record.runId, record.index);
|
|
15006
15058
|
if (!host) {
|
|
15059
|
+
const launchCwd = ctx.cwd;
|
|
15007
15060
|
const launched = runOwnerlessChild({
|
|
15008
15061
|
agent: agent.name,
|
|
15009
15062
|
agentSource: agent.source,
|
|
@@ -15014,9 +15067,10 @@ async function relaunchChild(deps, ctx, record, hold, release, onReopened, resum
|
|
|
15014
15067
|
...ownerSignal ? { ownerSignal } : {},
|
|
15015
15068
|
recordChild,
|
|
15016
15069
|
...schedule ? { schedule } : {},
|
|
15017
|
-
runSync: (task, childOptions) => runtime.runSync(
|
|
15070
|
+
runSync: (task, childOptions) => runtime.runSync(launchCwd, [agent], agent.name, task, childOptions)
|
|
15018
15071
|
});
|
|
15019
|
-
|
|
15072
|
+
const outcome = await ownerlessRelaunched(deps, record, launched, recordChild, release, hold, supervisorAuthorization, ctx);
|
|
15073
|
+
return fresh ? startedFresh(outcome) : outcome;
|
|
15020
15074
|
}
|
|
15021
15075
|
const response = await runAgentTask({
|
|
15022
15076
|
host,
|
|
@@ -15037,7 +15091,11 @@ async function relaunchChild(deps, ctx, record, hold, release, onReopened, resum
|
|
|
15037
15091
|
hold?.release();
|
|
15038
15092
|
}
|
|
15039
15093
|
});
|
|
15040
|
-
|
|
15094
|
+
const outcome = await relaunched(deps, record, response, recordChild, release, supervisorAuthorization, intercomGroup);
|
|
15095
|
+
return fresh ? startedFresh(outcome) : outcome;
|
|
15096
|
+
}
|
|
15097
|
+
function startedFresh(outcome) {
|
|
15098
|
+
return outcome.kind === "restored" ? { ...outcome, fresh: true } : outcome;
|
|
15041
15099
|
}
|
|
15042
15100
|
async function startQueuedChild(deps, ctx, host, record, queued, hold, release, resume) {
|
|
15043
15101
|
const supervisorAuthorization = queued.usesIntercom ? hold?.authorization : undefined;
|
|
@@ -15243,12 +15301,13 @@ function subagentChildRestoreNotices(outcomes) {
|
|
|
15243
15301
|
`${childLabel(outcome.record, outcome.childId)} did not survive the restart and could not be restored: ${outcome.reason}. It is gone: do not send it Intercom messages or wait for its result. Launch a fresh subagent if its work is still needed.`
|
|
15244
15302
|
];
|
|
15245
15303
|
const label = childLabel(outcome.record, outcome.record.childId);
|
|
15304
|
+
const how = outcome.fresh ? "started fresh with its task, since it had not started before the restart," : "relaunched from its own session";
|
|
15246
15305
|
if (outcome.intercomLive === false)
|
|
15247
15306
|
return [
|
|
15248
|
-
`${label} was
|
|
15307
|
+
`${label} was ${how} as task ${outcome.taskId}, but it did not reconnect to Intercom. Treat it as unreachable until it reconnects; its result will still arrive as a task completion.`
|
|
15249
15308
|
];
|
|
15250
15309
|
return [
|
|
15251
|
-
`${label} was interrupted by the restart and has been
|
|
15310
|
+
`${label} was interrupted by the restart and has been ${how} as task ${outcome.taskId}${outcome.intercomLive ? "; it is live on Intercom under the same name" : ""}.`
|
|
15252
15311
|
];
|
|
15253
15312
|
});
|
|
15254
15313
|
}
|
|
@@ -16485,6 +16544,26 @@ class SubagentForkContextError extends Error {
|
|
|
16485
16544
|
function resolveSubagentContext(value) {
|
|
16486
16545
|
return value === "fork" ? "fork" : "fresh";
|
|
16487
16546
|
}
|
|
16547
|
+
function settledForkLeaf(branch, leafId) {
|
|
16548
|
+
const answered = new Set;
|
|
16549
|
+
for (let index = branch.length - 1;index >= 0; index--) {
|
|
16550
|
+
const entry = branch[index];
|
|
16551
|
+
if (entry.type !== "message")
|
|
16552
|
+
continue;
|
|
16553
|
+
const message = entry.message;
|
|
16554
|
+
if (message.role === "toolResult") {
|
|
16555
|
+
answered.add(message.toolCallId);
|
|
16556
|
+
continue;
|
|
16557
|
+
}
|
|
16558
|
+
if (message.role === "user")
|
|
16559
|
+
return leafId;
|
|
16560
|
+
if (message.role !== "assistant")
|
|
16561
|
+
continue;
|
|
16562
|
+
const inFlight = message.content.some((block) => block.type === "toolCall" && !answered.has(block.id));
|
|
16563
|
+
return inFlight ? entry.parentId : leafId;
|
|
16564
|
+
}
|
|
16565
|
+
return leafId;
|
|
16566
|
+
}
|
|
16488
16567
|
function createForkContextResolver(sessionManager, requestedContext) {
|
|
16489
16568
|
if (resolveSubagentContext(requestedContext) !== "fork") {
|
|
16490
16569
|
return { sessionManagerForIndex: async () => {
|
|
@@ -16494,7 +16573,10 @@ function createForkContextResolver(sessionManager, requestedContext) {
|
|
|
16494
16573
|
const parentSessionId = sessionManager.getSessionId();
|
|
16495
16574
|
const store = sessionManager.getSessionStore();
|
|
16496
16575
|
const cwd = sessionManager.getCwd();
|
|
16497
|
-
const
|
|
16576
|
+
const parentLeafId = sessionManager.getLeafId();
|
|
16577
|
+
if (parentLeafId === null)
|
|
16578
|
+
throw new SubagentForkContextError(parentSessionId, "no_leaf");
|
|
16579
|
+
const leafId = settledForkLeaf(sessionManager.getBranch(), parentLeafId);
|
|
16498
16580
|
if (leafId === null)
|
|
16499
16581
|
throw new SubagentForkContextError(parentSessionId, "no_leaf");
|
|
16500
16582
|
const children = new Map;
|
|
@@ -16724,13 +16806,15 @@ async function routeSubagentModel(input) {
|
|
|
16724
16806
|
signal: input.signal,
|
|
16725
16807
|
selection: route.routerSelection
|
|
16726
16808
|
});
|
|
16809
|
+
const { modelRegistry } = ctx;
|
|
16810
|
+
const launchProvider = ctx.model?.provider;
|
|
16727
16811
|
return {
|
|
16728
16812
|
...route,
|
|
16729
16813
|
allowsModel: fallbackRoute.allowsModel,
|
|
16730
16814
|
allowsCandidate: (candidate, defaultEffort) => {
|
|
16731
|
-
const normalized = resolveModelCandidate(candidate,
|
|
16815
|
+
const normalized = resolveModelCandidate(candidate, modelRegistry.getAvailable().map(toModelInfo), launchProvider);
|
|
16732
16816
|
const { baseModel, thinkingSuffix } = splitKnownThinkingSuffix(normalized);
|
|
16733
|
-
const model =
|
|
16817
|
+
const model = modelRegistry.getAvailable().find((m) => `${m.provider}/${m.id}` === baseModel);
|
|
16734
16818
|
return model !== undefined && fallbackRoute.allowsModel(model, thinkingSuffix.slice(1) || defaultEffort);
|
|
16735
16819
|
}
|
|
16736
16820
|
};
|
|
@@ -16892,11 +16976,12 @@ async function runParallelPath(data, deps) {
|
|
|
16892
16976
|
taskTexts[i] = wrapForkTask(taskTexts[i]);
|
|
16893
16977
|
}
|
|
16894
16978
|
}
|
|
16979
|
+
const detachedNotificationScope = { orchestrationContext: ctx.orchestrationContext };
|
|
16895
16980
|
const results = await runForegroundParallelTasks({
|
|
16896
16981
|
wait: params.wait,
|
|
16897
16982
|
schedule: params[BURST_EXECUTION_SCHEDULE],
|
|
16898
16983
|
onDetachedExit: (index, result) => {
|
|
16899
|
-
if (workflowStageAcceptsDetachedNotification(
|
|
16984
|
+
if (workflowStageAcceptsDetachedNotification(detachedNotificationScope)) {
|
|
16900
16985
|
notifyDetachedForegroundChildExit({
|
|
16901
16986
|
pi: deps.pi,
|
|
16902
16987
|
runId,
|
|
@@ -17173,12 +17258,13 @@ async function runSinglePath(data, deps) {
|
|
|
17173
17258
|
};
|
|
17174
17259
|
}
|
|
17175
17260
|
const forwardSingleUpdate = createForwardSingleUpdate(onUpdate, foregroundControl, params.agent, 0);
|
|
17261
|
+
const detachedNotificationScope = { orchestrationContext: ctx.orchestrationContext };
|
|
17176
17262
|
let r;
|
|
17177
17263
|
try {
|
|
17178
17264
|
const supervisorAuthorization = await requestSupervisorAuthorization(deps.pi.events, childIntercomTarget, ctx.getChildSessionOptions?.({ tools: agentConfig.tools }));
|
|
17179
17265
|
const runOptions = {
|
|
17180
17266
|
runningCapacity: deps.config.parallel?.runningCapacity,
|
|
17181
|
-
getChildSessionOptions: ctx.getChildSessionOptions,
|
|
17267
|
+
getChildSessionOptions: ctx.getChildSessionOptionsResolver?.() ?? ctx.getChildSessionOptions,
|
|
17182
17268
|
resourceLoaderInheritanceSnapshot: deps.pi.getResourceLoaderInheritanceSnapshot?.(),
|
|
17183
17269
|
cwd: effectiveCwd,
|
|
17184
17270
|
signal,
|
|
@@ -17214,7 +17300,7 @@ async function runSinglePath(data, deps) {
|
|
|
17214
17300
|
},
|
|
17215
17301
|
onDetachedExit: (result) => {
|
|
17216
17302
|
cleanupTransientProgress(progressDir, artifactConfig.enabled);
|
|
17217
|
-
if (result && workflowStageAcceptsDetachedNotification(
|
|
17303
|
+
if (result && workflowStageAcceptsDetachedNotification(detachedNotificationScope)) {
|
|
17218
17304
|
notifyDetachedForegroundChildExit({ pi: deps.pi, runId, mode: "single", index: 0, result });
|
|
17219
17305
|
}
|
|
17220
17306
|
},
|
|
@@ -17259,6 +17345,7 @@ async function runSinglePath(data, deps) {
|
|
|
17259
17345
|
return await taskToolResultWithOutput(response, ctx.getAgentTaskHost());
|
|
17260
17346
|
r = settledChild;
|
|
17261
17347
|
} else {
|
|
17348
|
+
const launchCwd = ctx.cwd;
|
|
17262
17349
|
r = await runOwnerlessChild({
|
|
17263
17350
|
agent: params.agent,
|
|
17264
17351
|
agentSource: agentConfig.source,
|
|
@@ -17267,7 +17354,7 @@ async function runSinglePath(data, deps) {
|
|
|
17267
17354
|
options: runOptions,
|
|
17268
17355
|
recordChild: createSubagentChildRecordSink(deps.pi, ctx),
|
|
17269
17356
|
...params[BURST_EXECUTION_SCHEDULE] ? { schedule: params[BURST_EXECUTION_SCHEDULE] } : {},
|
|
17270
|
-
runSync: (childTask, options) => deps.runtime.runSync(
|
|
17357
|
+
runSync: (childTask, options) => deps.runtime.runSync(launchCwd, agents, params.agent, childTask, options)
|
|
17271
17358
|
}).result;
|
|
17272
17359
|
}
|
|
17273
17360
|
} catch (error) {
|
|
@@ -17604,7 +17691,16 @@ async function resumeChild(deps, ctx, request) {
|
|
|
17604
17691
|
if (outcome.kind === "unrestorable")
|
|
17605
17692
|
return errorResult(action, `${label(record)} could not be resumed: ${outcome.reason}.`);
|
|
17606
17693
|
const intercom = outcome.intercomLive === true ? " It is live on Intercom under the same name." : outcome.intercomLive === false ? " It has not reconnected to Intercom yet; its result still arrives as a task completion." : "";
|
|
17607
|
-
const
|
|
17694
|
+
const heldMessages = record.heldMessages && record.heldMessages.taskId === record.taskId ? record.heldMessages.messages : [];
|
|
17695
|
+
if (outcome.fresh) {
|
|
17696
|
+
const sent = heldMessages[0] === record.launch?.task ? heldMessages.slice(1) : heldMessages;
|
|
17697
|
+
return controlResult(`Started ${label(record)} fresh with its task as task ${outcome.taskId}, since it stopped before it began${message ? ", with your message added to its task" : ""}.${sent.length > 0 ? " The messages it was sent and had not yet read are delivered after its task, once." : ""}${intercom}`, {
|
|
17698
|
+
action,
|
|
17699
|
+
children: [childDetails(record, "running", outcome.taskId)],
|
|
17700
|
+
...message ? { message } : {}
|
|
17701
|
+
});
|
|
17702
|
+
}
|
|
17703
|
+
const held = heldMessages.length > 0 ? " The messages it had queued and not yet read when it stopped are delivered first, once." : "";
|
|
17608
17704
|
return controlResult(`Resumed ${label(record)} from its own session as task ${outcome.taskId}${message ? interrupted ? ", with your message delivered before it continues its interrupted turn" : ", with your message as its new prompt" : ""}.${held}${intercom}`, {
|
|
17609
17705
|
action,
|
|
17610
17706
|
children: [childDetails(record, "running", outcome.taskId)],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/web-access",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.46-alpha.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
|
|
6
6
|
"contributors": [
|
|
@@ -6,6 +6,51 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.9.46-alpha.1] - 2026-10-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `RunResult.failure` (type `RunFailureClassification`, exported from `@bastani/atomic/workflows`) describes how a failed or blocked run's failure was classified: `kind`, `code`, `retryable`, `resumable`, `recoverability`, `disposition` and `retryAfterMs`. It is absent for runs that did not fail and for results recorded by earlier releases ([#4031](https://github.com/bastani-inc/atomic/issues/4031))
|
|
14
|
+
- Opening a running stage that runs on a local worker (`workflows.localWorkers`) from the workflow graph or `/workflow attach` now shows its live chat: the transcript so far, the reply as it streams, and tool calls, with `on local worker` in the chat header. It no longer shows "Post-mortem chat is available only after the stage completes". Only stages you have open stream to the root, so pools with many workers send nothing extra for stages nobody watches. ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
15
|
+
- Enter (steer) and Ctrl+F (follow-up) in the chat of a stage that runs on a local worker now reach that stage and report the same delivery as an in-process stage. Each message is admitted at most once, even when a reply is lost and the send is retried. The stage's graph node shows `✉ N queued` with no chat open. SDK runs with `localWorkers` now accept `stageControlRegistry`, whose handles for pooled stages steer, queue follow-ups and stream events the same way ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
16
|
+
- A single stage on a local worker can now be paused and resumed (`/workflow pause <run> <stage>`, stage chat, `session.workflows.pause/resume` with `stageId`) without pausing the pool; it keeps its worker's lease while paused. When the worker running an open stage dies or its attempt is fenced, the chat shows `RECONNECTING`, reattaches to the replacement attempt, and redelivers once each, in send order and in their original mode (a steer still steers), the messages the replacement does not have ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
17
|
+
- Children on local workers can now ask for input. Their `ctx.ui` prompts show on their stage and are answered from the stage chat or the workflow tool's `answer` action, and a stage's `ask_user_question` shows its form in the stage chat as for an in-process stage; each answer reaches only the prompt it names on the worker's current attempt. Previously `ctx.ui` in a pooled child failed the child. SDK runs with `localWorkers` now accept `ui`, which answers those prompts (a rejecting `ui` fails the child with its error), and `stageUiBroker`, which receives the stages' `ask_user_question` forms (a broker that rejects a form fails that question in the stage with its error). `ctx.ui.custom` in a pooled child fails it with an error naming the limitation. When a worker dies, the replacement asks again a `ctx.ui` prompt that was still pending, and does not ask again one already answered. A stage's `ask_user_question` still waiting when its worker dies (or when a durable run resumes a stage) shows its form again on the replacement, and the answer becomes that call's result; previously the call was reported to the model as interrupted and the stage could finish without the answer ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
18
|
+
- Added a GPT-6.1 Terra reference to the prompt-engineering skill, based on OpenAI's updated GPT-6 guide, covering when to choose Terra, effort selection, migration from GPT-5.6 Terra, computer-use and writing prompts, and selective use of the shared templates observed on Astra.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Starting a large local worker pool (`workflows.localWorkers`) no longer freezes the root's input and rendering for hundreds of milliseconds while the workers start. When a workflow starts many pooled children at once, such as `Promise.all` over 150 `ctx.workflow` calls, their journal work used to run back to back in one turn of the event loop, and each child's dispatch listed and sorted every checkpoint of the root. While many journal operations are queued, journal work now gives the event loop a turn after 8 ms, checked before each operation, and a child's dispatch looks up only its own boundary. When those children finish at once, completing each one no longer reads every checkpoint of the root, and the sweep for undeliverable stage messages checks run ownership only for runs that have messages to settle, and children finishing together complete in turns of the event loop instead of all at once
|
|
23
|
+
- An Intercom send to a workflow path pattern that matches a live or pending stage, such as `workflow:<run>/**/work-2` for a stage in a child on a local worker, no longer reports `notInKnownSet` ("not in the workflow's known stage set"). The message was delivered, but the known set came only from the launch-time scan of the workflow source, which cannot see stages of children loaded with `loadWorkerWorkflow`. Stages that already exist and can receive messages now count as known; a target that matches neither the scan nor any such stage still gets the warning.
|
|
24
|
+
- Large pooled fan-outs no longer stall the root's main thread when the workflow graph or a stage chat is open: typing in a stage chat during dispatch, wave turnover and run end of 150+ local workers stayed responsive only with nothing attached. Each store change used to re-project, re-freeze, re-expand and re-serialize every run of the root for the graph snapshot, the graph view, the attach pane, the workflow activity bridge and the BACKGROUND widget. Unchanged runs and stages now keep their frozen graph projections and expansions, the graph view and attach pane catch up once before they render or handle input, an open stage chat projects only its own run, the activity bridge publishes each root's final activity once per turn of the event loop (reading published activity, a root run's own lifecycle event and `waitForSettled` always see it first), store-driven widget refreshes spend at most 8 ms per 50 ms after the first four, and the coordinator gets the root's boundaries once per turn instead of once per enqueue and published batch. A pooled child whose worker was replaced also keeps its last known stages, marked `reconnecting`, until the replacement republishes them, instead of briefly showing no stages ([#4009](https://github.com/bastani-inc/atomic/issues/4009))
|
|
25
|
+
- Stages on local workers (`workflows.localWorkers`) now share provider rate limits. When one worker's stage gets an HTTP 429 or an exhausted quota (HTTP 402), the other workers and the root apply the same cooldown or unavailable mark, with the same provider or model scope and the same `Retry-After` end. They hold requests to that provider or model back, or move their fallback walk past it, instead of each sending requests until it gets its own 429. A worker that starts later gets the limits still running. Limits are sent only when a request is rate limited, at most once per provider or model every 250 ms ([#4023](https://github.com/bastani-inc/atomic/issues/4023))
|
|
26
|
+
- Text typed into a stage chat reaches the stage the same way whether the stage is idle, streaming, or still preparing its turn. Only `/skill:` commands expand. Before, Enter or Ctrl+F while the stage was busy expanded prompt templates such as `/review` and ran registered extension commands, or refused them, while the same text sent to an idle stage arrived as typed
|
|
27
|
+
- A message given when resuming a stage that was paused in the middle of its workflow prompt is now delivered as typed, as it is when resuming an idle stage chat. It used to go through the stage prompt's own options, which expand prompt templates and run registered extension commands
|
|
28
|
+
- A failed child workflow (`ctx.workflow`), or a failed stage, no longer has its failure classified from workflow or stage names. Before, a name containing words such as `rate-limit`, `overloaded` or `quota` could turn an ordinary failure, such as a tool that throws `ENOENT`, into a resumable rate limit or provider outage. The root then stayed `running`, blocked for a resume that could not help, and `run()` returned status `running` with the error. A parent now classifies a failed child from the child's own classification (`RunResult.failure`), at any nesting depth, for in-process children and children on local workers, including a child result a resumed root reads back from the journal. A stage's model-resolution failure is classified from the routing error it wraps. A child that really fails on a rate limit still blocks the root resumably. Error messages are unchanged ([#4031](https://github.com/bastani-inc/atomic/issues/4031))
|
|
29
|
+
- A stage whose model hits an exhausted quota (HTTP 402) or a rate limit that resets beyond the retry-delay cap now moves straight to the next fallback model, without spending retries or cooldown-wait rounds on it. A model-specific rate limit no longer delays a fallback on the same provider, and an HTTP 529 overload waits for its `Retry-After` like a 503 ([#3984](https://github.com/bastani-inc/atomic/issues/3984))
|
|
30
|
+
- A stage whose whole model and fallback chain returns HTTP 429 now waits for the earliest recorded provider cooldown and retries, instead of failing after one attempt per model. The wait is bounded: at most `retry.maxRetries` extra rounds per prompt, and only when the earliest cooldown fits the retry-delay cap (`retry.provider.maxRetryDelayMs`, else `retry.maxAgentDelayMs`, else 60 s). A persistent 429 still fails the stage once those rounds are spent ([#3984](https://github.com/bastani-inc/atomic/issues/3984))
|
|
31
|
+
- A workflow run whose stage replaced its session through model fallback no longer fails with `This extension ctx is stale after session replacement` and hides the stage's real error (such as a rate limit). An unhandled stale extension-context rejection no longer fails the run. It is logged to stderr with its stack and the run id, so the code that kept the retired session's context can be found ([#3981](https://github.com/bastani-inc/atomic/issues/3981))
|
|
32
|
+
- Disposing an SDK session right after a workflow run reports `completed`, without awaiting `session.workflows.waitForSettled(runId)`, no longer prints one `atomic-workflows: released stage listener failed Error: SessionClosed` warning per stage ([#3966](https://github.com/bastani-inc/atomic/issues/3966))
|
|
33
|
+
- A workflow stage that has sent its first prompt no longer reads `startup first-dispatch (0s total, 0s on current step; dispatched)` in the workflow tool's `stages` and `stage` output, which looked like a stalled stage for the whole run. The line now reads `startup complete (dispatched after 2s)` ([#3967](https://github.com/bastani-inc/atomic/issues/3967))
|
|
34
|
+
- A local-worker pool no longer fails every in-flight child with `Local worker process cleanup unconfirmed; permits quarantined` when one worker exits while its tool process groups are still running. The coordinator now reaps that worker's orphaned process groups (SIGTERM, then SIGKILL, with a bounded wait), holds the child's work lease so no other worker can claim it meanwhile, releases the permits of groups that are gone or hold only zombies, and replays only that worker's child on a replacement. If a group is still alive when the bound expires, only that child fails, with a retryable `WorkflowInfrastructureError` (`worker_cleanup_unconfirmed`) that names the process groups; their permits stay quarantined and the child is not replayed alongside them, also not by recovery after a stalled worker is stopped or by a durable resume (both read the quarantine from the journal), and closing the pool reports those groups. A durable resume reaps such an attempt's groups with the same bounded sequence, then replays it in the resumed root or fails only that child ([#3986](https://github.com/bastani-inc/atomic/issues/3986))
|
|
35
|
+
- A direct Intercom send to a live stage in a local worker pool no longer routinely fails with `The recipient did not confirm that it saved the message in time` while the pool is busy. Saving a message to a pooled stage's session no longer waits for the worker to re-read the root workflow's journal or for a usage-total transaction; it is confirmed once the coordinator has committed it ([#3983](https://github.com/bastani-inc/atomic/issues/3983))
|
|
36
|
+
- Resuming a quit or crashed root whose children run in `workflows.localWorkers` no longer fails in-flight children with `Session not found`. Resume used to make the previous run's pooled children claimable before the resumed root had enqueued them again, so workers crashed on `Stale work lease`, the pool failed with `Local worker retry limit`, and later children ran in the root process, which cannot see pooled stage sessions. A pooled child is now claimed only after the resumed root enqueues it, a child started after the pool has failed gets the pool's error instead of running in the root process, the three-attempt retry limit counts only crashes within one run of the root (a child resumed many times keeps its full budget), and a worker whose attempt was handed to another worker ends that attempt and serves new work instead of crashing and using up a retry ([#3979](https://github.com/bastani-inc/atomic/issues/3979))
|
|
37
|
+
- Large local worker pools no longer lose child workflows to `database is locked … gave up after waiting 30000 ms for the write lock` after a few hours. Resource admission reconciliation, which every pooled worker runs before it starts a command, read the whole resource permit history twice for every live worker while it held the journal's write lock. With 160 workers and tens of thousands of permits, each pass held the lock for nearly a second. Reconciliation now uses indexes, keeps only the most recent 256 released permits, and takes the write lock only when it has a permit to fence or prune ([#3978](https://github.com/bastani-inc/atomic/issues/3978))
|
|
38
|
+
- Workflow runs and local worker definitions load again after something such as a `/tmp` cleanup deletes the `jiti` transpile cache directory, instead of failing with `ENOENT: no such file or directory, open '/tmp/jiti/workflows-<name>.<hash>.cjs'` until Atomic restarts ([#3985](https://github.com/bastani-inc/atomic/issues/3985))
|
|
39
|
+
- A sticky Intercom send to a workflow pattern target (for example `workflow:<rootRunId>/**`) no longer fails newly starting pooled stages with `Extension startup failed: … pending-stage message <id> changed during delivery`. A stage drain whose transcript snapshot was taken before a slow session append finished used to deliver the already-receipted message again and then fail. The drain now treats an existing delivery receipt for that stage and session as success, so the message reaches each stage exactly once ([#3980](https://github.com/bastani-inc/atomic/issues/3980))
|
|
40
|
+
- A workflow that starts many child workflows at once, such as `Promise.all` over 150 `ctx.workflow` calls, no longer stalls the root session's input and rendering while the children start. Each workflow store change used to re-read every run and stage to detect lifecycle transitions, so a fan-out's cost grew with the square of its width; only the run, stage or tool node that changed is now re-read. Lifecycle events are unchanged
|
|
41
|
+
- A finished stage that ran on a local worker now reopens for post-mortem chat. It used to report `The retained session is missing, unreadable, or invalid` in the same session and SESSION UNAVAILABLE after a restart, because its session lives in the workflow journal rather than the host's session store ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
42
|
+
- A workflow heartbeat no longer goes missing when its wake-up timer fires a moment early. The early wake-up found nothing due and rescheduled, and if the boundary passed while that pass was running, the heartbeat moved to the following interval instead of being sent. The scheduler now reads the clock once per pass, so a boundary that comes due during it is still delivered
|
|
43
|
+
- An idle local worker that meets its root's released lease (between the root's teardown and the pool's close) now ends cleanly instead of crashing, and the pool no longer spawns replacements that crash the same way ([#3979](https://github.com/bastani-inc/atomic/issues/3979))
|
|
44
|
+
- A stage chat open on a `ctx.ui` prompt or `ask_user_question` form of a stage on a local worker (`workflows.localWorkers`) now takes the answer after that worker dies: it shows the prompt the replacement worker asks again and delivers your answer to it once. Before, the open chat kept a prompt that dropped typed text and ignored Enter until you closed and reopened the stage. An answer you submit after the worker died but before the replacement asks again is now kept and given to the replacement's identical prompt instead of being lost, so you are not asked twice. The re-asked `ask_user_question` form no longer shows "Tool execution was interrupted after it started…" above it. A replacement worker whose child asks a different `ctx.ui` question at the same point (its text depends on data) no longer fails the child with `Pool topology scope: direct child source orders are not unique`: the unanswered question of the dead worker is replaced, an open stage chat moves to the new question, and an answer held for the old question is discarded. A child that really takes different steps when it runs again now fails with an error that says so in plain words. The forms of `ask_user_question` and the readiness gate are now built by one helper for in-process and local-worker stages, so they cannot drift apart ([#4022](https://github.com/bastani-inc/atomic/issues/4022))
|
|
45
|
+
- A child on a local worker that loses its worker three times now fails alone with a retryable `WorkflowInfrastructureError` (`worker_retry_limit`) that names its child run and definition export, as #3986 already did for a child whose processes survive cleanup. Before, it failed the whole pool within milliseconds: every other child, including one waiting on a prompt whose worker was never touched, was rejected with `Local worker retry limit`. The same applies to a child whose every attempt the root refused as stale. The failed child, its nested runs and their unfinished stages show failed with its error, so the graph counts none of them as active. Workers that keep crashing without work, worker startup failures and a coordinator that dies still fail the pool. When a pool closes without a quit, the root's unfinished work rows end `failed` instead of staying `ready` or `leased`; quarantined attempts keep their leases, and a resumed root replays failed work it enqueues again ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
46
|
+
- The post-mortem chat of a completed or failed stage that ran on a local worker no longer shows `on local worker` in its header. Post-mortem chat runs in the root, so the header now matches an in-process stage's ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
47
|
+
|
|
48
|
+
## [0.9.45] - 2026-10-25
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- A large local worker pool no longer fails at startup with `Local worker startup timed out after 30000 ms` on a small or busy machine. When `localWorkers.startupTimeoutMs` is not set, the readiness deadline now scales with the number of workers and the 1-minute load average per CPU core, from 30000 ms up to 600000 ms; on an idle machine with spare cores it stays 30000 ms. If workers still miss the deadline, the error now says they were too slow to start and names `workflows.localWorkers.startupTimeoutMs` ([#3962](https://github.com/bastani-inc/atomic/issues/3962))
|
|
53
|
+
|
|
9
54
|
## [0.9.45-alpha.1] - 2026-10-25
|
|
10
55
|
|
|
11
56
|
### Added
|