@bastani/atomic 0.9.57 → 0.9.58
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 +57 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +18 -0
- package/dist/builtin/subagents/README.md +3 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +202 -38
- package/dist/builtin/web-access/CHANGELOG.md +12 -0
- package/dist/builtin/web-access/index.bundle.mjs +2 -2
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +42 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +885 -228
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +933 -175
- package/dist/builtin/workflows/src/index.js +630 -162
- package/dist/builtin/workflows/src/local-worker-entry.mjs +933 -175
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +1 -1
- package/dist/builtin/workflows/src/shared/workflow-infrastructure-error.d.ts +13 -0
- package/dist/builtin/workflows/src/workers/public-options.d.ts +10 -0
- package/dist/core/agent-session-persistent-custom-messages.d.ts +5 -0
- package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.js +16 -0
- package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
- package/dist/core/agent-session-tasks.d.ts.map +1 -1
- package/dist/core/agent-session-tasks.js +20 -0
- package/dist/core/agent-session-tasks.js.map +1 -1
- package/dist/core/child-session-options.d.ts.map +1 -1
- package/dist/core/child-session-options.js +3 -1
- package/dist/core/child-session-options.js.map +1 -1
- package/dist/core/sdk-types.d.ts +6 -1
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/sdk.js +4 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +4 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +4 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager-resource-accessors.d.ts +2 -0
- package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-resource-accessors.js +6 -0
- package/dist/core/settings-manager-resource-accessors.js.map +1 -1
- package/dist/core/settings-schema.d.ts +9 -0
- package/dist/core/settings-schema.d.ts.map +1 -1
- package/dist/core/settings-schema.js +11 -0
- package/dist/core/settings-schema.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tasks/agent-adapter.d.ts +10 -1
- package/dist/core/tasks/agent-adapter.d.ts.map +1 -1
- package/dist/core/tasks/agent-adapter.js +20 -0
- package/dist/core/tasks/agent-adapter.js.map +1 -1
- package/dist/core/tasks/contracts.d.ts +6 -0
- package/dist/core/tasks/contracts.d.ts.map +1 -1
- package/dist/core/tasks/contracts.js.map +1 -1
- package/dist/core/tasks/heartbeat-notice.d.ts +64 -0
- package/dist/core/tasks/heartbeat-notice.d.ts.map +1 -0
- package/dist/core/tasks/heartbeat-notice.js +98 -0
- package/dist/core/tasks/heartbeat-notice.js.map +1 -0
- package/dist/core/tasks/heartbeat.d.ts +75 -0
- package/dist/core/tasks/heartbeat.d.ts.map +1 -0
- package/dist/core/tasks/heartbeat.js +440 -0
- package/dist/core/tasks/heartbeat.js.map +1 -0
- package/dist/core/tasks/supervisor.d.ts +2 -0
- package/dist/core/tasks/supervisor.d.ts.map +1 -1
- package/dist/core/tasks/supervisor.js +32 -4
- package/dist/core/tasks/supervisor.js.map +1 -1
- package/dist/core/tasks/task-view-source.d.ts +72 -0
- package/dist/core/tasks/task-view-source.d.ts.map +1 -0
- package/dist/core/tasks/task-view-source.js +155 -0
- package/dist/core/tasks/task-view-source.js.map +1 -0
- package/dist/core/tools/bash-pty-native.d.ts +2 -0
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
- package/dist/core/tools/bash-pty-native.js +1 -0
- package/dist/core/tools/bash-pty-native.js.map +1 -1
- package/dist/core/tools/bash.d.ts +3 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +20 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -8
- package/dist/core/tools/powershell.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.js +7 -0
- package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.d.ts +3 -0
- package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.js +4 -0
- package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-types.d.ts +11 -0
- package/dist/modes/interactive/components/chat-session-host-types.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-types.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +2 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +15 -8
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +12 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/task-detail.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-detail.js +5 -0
- package/dist/modes/interactive/components/task-detail.js.map +1 -1
- package/dist/modes/interactive/components/task-heartbeat-message.d.ts +14 -0
- package/dist/modes/interactive/components/task-heartbeat-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/task-heartbeat-message.js +76 -0
- package/dist/modes/interactive/components/task-heartbeat-message.js.map +1 -0
- package/dist/modes/interactive/components/task-inspector.d.ts +6 -3
- package/dist/modes/interactive/components/task-inspector.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-inspector.js +53 -100
- package/dist/modes/interactive/components/task-inspector.js.map +1 -1
- package/dist/modes/interactive/skill-command-autocomplete.d.ts +22 -2
- package/dist/modes/interactive/skill-command-autocomplete.d.ts.map +1 -1
- package/dist/modes/interactive/skill-command-autocomplete.js +28 -9
- package/dist/modes/interactive/skill-command-autocomplete.js.map +1 -1
- package/docs/background-tasks.md +65 -1
- package/docs/sdk.md +3 -1
- package/docs/settings.md +12 -0
- package/docs/subagents/reference.md +2 -0
- package/docs/subagents.md +2 -0
- package/docs/tools.md +2 -1
- package/docs/workflows/operations.md +5 -3
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- package/schemas/settings.schema.json +24 -0
|
@@ -8939,6 +8939,13 @@ function subagentChildCardRecord(childId, taskId) {
|
|
|
8939
8939
|
const id = childId ?? childIdsByTaskId.get(taskId);
|
|
8940
8940
|
return id === undefined ? undefined : latest.get(id);
|
|
8941
8941
|
}
|
|
8942
|
+
function observedSubagentRunIds(ownerSessionId) {
|
|
8943
|
+
const runIds = new Set;
|
|
8944
|
+
for (const record of latest.values())
|
|
8945
|
+
if (record.owner?.sessionId === ownerSessionId)
|
|
8946
|
+
runIds.add(record.runId);
|
|
8947
|
+
return runIds;
|
|
8948
|
+
}
|
|
8942
8949
|
function watchSubagentChildCard(card, invalidate, key, ownerSessionId) {
|
|
8943
8950
|
invalidators.set(card, invalidate);
|
|
8944
8951
|
const cards = watchers.get(key) ?? new Set;
|
|
@@ -10820,6 +10827,7 @@ ${launch.message}` : input.task,
|
|
|
10820
10827
|
},
|
|
10821
10828
|
wait: { kind: "background" },
|
|
10822
10829
|
runtime: input.runtime,
|
|
10830
|
+
...input.heartbeatMs === undefined ? {} : { heartbeatMs: input.heartbeatMs },
|
|
10823
10831
|
onTerminal: launch.onTerminal,
|
|
10824
10832
|
...input.schedule ? { schedule: input.schedule } : {},
|
|
10825
10833
|
...input.outputText ? { outputText: input.outputText } : {},
|
|
@@ -10834,7 +10842,8 @@ ${launch.message}` : input.task,
|
|
|
10834
10842
|
agent: input.agent,
|
|
10835
10843
|
task: input.intentTask ?? input.task,
|
|
10836
10844
|
cwd: input.options.cwd ?? input.cwd,
|
|
10837
|
-
...input.options.modelRoute ? { routerSelection: input.options.modelRoute.routerSelection } : {}
|
|
10845
|
+
...input.options.modelRoute ? { routerSelection: input.options.modelRoute.routerSelection } : {},
|
|
10846
|
+
...input.heartbeatMs === undefined ? {} : { heartbeatMs: input.heartbeatMs }
|
|
10838
10847
|
}, input.operation ?? `${input.options.runId}:${input.options.index ?? 0}`, (context) => {
|
|
10839
10848
|
dispatched = true;
|
|
10840
10849
|
queuedChildren.delete(context.ref.taskId);
|
|
@@ -11182,6 +11191,7 @@ async function runForegroundParallelTasks(input) {
|
|
|
11182
11191
|
options: runOptions,
|
|
11183
11192
|
runtime: input.runtime,
|
|
11184
11193
|
wait: input.wait,
|
|
11194
|
+
...(task.heartbeatMs ?? input.heartbeatMs) === undefined ? {} : { heartbeatMs: task.heartbeatMs ?? input.heartbeatMs },
|
|
11185
11195
|
schedule,
|
|
11186
11196
|
onTerminal: (child) => {
|
|
11187
11197
|
terminalChild = child;
|
|
@@ -11822,7 +11832,10 @@ import {
|
|
|
11822
11832
|
import {
|
|
11823
11833
|
SubagentControl
|
|
11824
11834
|
} from "@bastani/atomic-natives";
|
|
11825
|
-
import {
|
|
11835
|
+
import {
|
|
11836
|
+
clampThinkingLevel,
|
|
11837
|
+
getSupportedThinkingLevels
|
|
11838
|
+
} from "@bastani/pi-ai/compat";
|
|
11826
11839
|
|
|
11827
11840
|
// dist/builtin/subagents/src/agents/skills.ts
|
|
11828
11841
|
import * as fs14 from "node:fs";
|
|
@@ -12563,6 +12576,23 @@ function createInProcessChildPromptBehavior(policy) {
|
|
|
12563
12576
|
}
|
|
12564
12577
|
|
|
12565
12578
|
// dist/builtin/subagents/src/runs/inprocess/runner.ts
|
|
12579
|
+
var SUBAGENT_SESSION_START_TIMEOUT_MS = 5 * 60000;
|
|
12580
|
+
function formatStartTimeout(ms) {
|
|
12581
|
+
return ms % 60000 === 0 ? `${ms / 60000} min` : `${Math.max(1, Math.round(ms / 1000))}s`;
|
|
12582
|
+
}
|
|
12583
|
+
var DEFAULT_THINKING_LEVEL = "medium";
|
|
12584
|
+
function explicitEffortFor(model, requested, settings) {
|
|
12585
|
+
if (getSupportedThinkingLevels(model).includes(requested))
|
|
12586
|
+
return requested;
|
|
12587
|
+
return clampThinkingLevel(model, settings.getModelThinkingLevel(model.provider, model.id) ?? settings.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
|
|
12588
|
+
}
|
|
12589
|
+
function pinFallbackEffort(entry, requested, settings, resolve) {
|
|
12590
|
+
const resolved = resolve?.(entry);
|
|
12591
|
+
if (!resolved)
|
|
12592
|
+
return entry;
|
|
12593
|
+
const { baseModel } = splitKnownThinkingSuffix(entry.trim());
|
|
12594
|
+
return `${baseModel}:${explicitEffortFor(resolved.model, resolved.thinkingLevel ?? requested, settings)}`;
|
|
12595
|
+
}
|
|
12566
12596
|
function modelIdForCandidate(candidate, fallbackModel) {
|
|
12567
12597
|
return candidate.modelId ?? (candidate.model ? `${candidate.model.provider}/${candidate.model.id}` : undefined) ?? (fallbackModel ? `${fallbackModel.provider}/${fallbackModel.id}` : undefined);
|
|
12568
12598
|
}
|
|
@@ -13015,12 +13045,23 @@ class SubagentControlRuntime {
|
|
|
13015
13045
|
delivered = new Set;
|
|
13016
13046
|
deliveredEnvelopes = new Map;
|
|
13017
13047
|
killedChildren = new Set;
|
|
13048
|
+
childIds = new Map;
|
|
13018
13049
|
nextAttemptId = 1;
|
|
13019
13050
|
constructor(parent, sessionRoot, runningCapacity) {
|
|
13020
13051
|
this.parent = { ...parent };
|
|
13021
13052
|
this.sessionRoot = trustedSessionRoot(this.parent, sessionRoot);
|
|
13022
13053
|
this.native = new SubagentControl(this.parent.path, runningCapacity);
|
|
13023
13054
|
}
|
|
13055
|
+
childIdOf(pathValue) {
|
|
13056
|
+
return this.childIds.get(pathValue);
|
|
13057
|
+
}
|
|
13058
|
+
pathOfChildId(childId) {
|
|
13059
|
+
let latest;
|
|
13060
|
+
for (const [pathValue, id] of this.childIds)
|
|
13061
|
+
if (id === childId)
|
|
13062
|
+
latest = pathValue;
|
|
13063
|
+
return latest;
|
|
13064
|
+
}
|
|
13024
13065
|
registerAgents(agents) {
|
|
13025
13066
|
for (const agent of agents)
|
|
13026
13067
|
this.native.registerAgent(agent.name);
|
|
@@ -13038,6 +13079,8 @@ class SubagentControlRuntime {
|
|
|
13038
13079
|
if (!native.child)
|
|
13039
13080
|
return refusal(native);
|
|
13040
13081
|
const identity = native.child;
|
|
13082
|
+
if (spec.childId !== undefined)
|
|
13083
|
+
this.childIds.set(identity.path, spec.childId);
|
|
13041
13084
|
const childModePolicy = resolveChildModePolicy(spec);
|
|
13042
13085
|
const sessionDir = spec.sessionManager?.getSessionDir() || sessionDirectory(this.sessionRoot, identity.path);
|
|
13043
13086
|
mkdirSync7(sessionDir, { recursive: true });
|
|
@@ -13180,9 +13223,34 @@ class SubagentControlRuntime {
|
|
|
13180
13223
|
signals.interrupt.addEventListener("abort", interruptListener, {
|
|
13181
13224
|
once: true
|
|
13182
13225
|
});
|
|
13226
|
+
const startTimeoutMs = admitted.spec.sessionStartTimeoutMs ?? SUBAGENT_SESSION_START_TIMEOUT_MS;
|
|
13227
|
+
const startDeadline = new AbortController;
|
|
13228
|
+
let startPhase = "opening its session";
|
|
13229
|
+
const startTimer = setTimeout(() => startDeadline.abort(new Error(`Subagent session did not start within ${formatStartTimeout(startTimeoutMs)} (still ${startPhase}); it was stopped before its task ran.`)), startTimeoutMs);
|
|
13230
|
+
const beforeStartDeadline = (phase, work, abandon) => {
|
|
13231
|
+
startPhase = phase;
|
|
13232
|
+
return new Promise((resolve, reject) => {
|
|
13233
|
+
const expire = () => {
|
|
13234
|
+
work.then((late) => abandon?.(late), () => {
|
|
13235
|
+
return;
|
|
13236
|
+
});
|
|
13237
|
+
reject(startDeadline.signal.reason);
|
|
13238
|
+
};
|
|
13239
|
+
if (startDeadline.signal.aborted)
|
|
13240
|
+
return expire();
|
|
13241
|
+
startDeadline.signal.addEventListener("abort", expire, { once: true });
|
|
13242
|
+
work.then((value) => {
|
|
13243
|
+
startDeadline.signal.removeEventListener("abort", expire);
|
|
13244
|
+
resolve(value);
|
|
13245
|
+
}, (error) => {
|
|
13246
|
+
startDeadline.signal.removeEventListener("abort", expire);
|
|
13247
|
+
reject(error);
|
|
13248
|
+
});
|
|
13249
|
+
});
|
|
13250
|
+
};
|
|
13183
13251
|
try {
|
|
13184
13252
|
const workflow = workflowMetadataFromContext(admitted.spec.parent?.orchestrationContext);
|
|
13185
|
-
const sessionManager = admitted.sessionManager ?
|
|
13253
|
+
const sessionManager = await beforeStartDeadline("opening its session", admitted.sessionManager ? SessionManager3.open(admitted.sessionManager.getSessionStore(), admitted.sessionManager.getSessionId(), admitted.policy.cwd) : SessionManager3.create(admitted.policy.cwd, admitted.sessionDir, workflow ? { internal: true, workflow } : { internal: true }));
|
|
13186
13254
|
transcriptManager = sessionManager;
|
|
13187
13255
|
taskHooks?.bindTranscript?.(sessionManager);
|
|
13188
13256
|
const childSession = {
|
|
@@ -13192,7 +13260,7 @@ class SubagentControlRuntime {
|
|
|
13192
13260
|
taskHooks?.bindChildSession?.(childSession);
|
|
13193
13261
|
admitted.spec.onChildSession?.(childSession);
|
|
13194
13262
|
if (workflow)
|
|
13195
|
-
await sessionManager.markSessionInternal(workflow);
|
|
13263
|
+
await beforeStartDeadline("opening its session", sessionManager.markSessionInternal(workflow));
|
|
13196
13264
|
const launchLeafId = sessionManager.getLeafId();
|
|
13197
13265
|
ownMessages = () => messagesSinceLaunch(sessionManager, launchLeafId);
|
|
13198
13266
|
let created;
|
|
@@ -13218,6 +13286,13 @@ class SubagentControlRuntime {
|
|
|
13218
13286
|
} : inherited.extensionBindings;
|
|
13219
13287
|
const agentDir = inherited.agentDir ?? getAgentDir2();
|
|
13220
13288
|
const settingsManager = inherited.settingsManager ?? SettingsManager.create(admitted.policy.cwd, agentDir);
|
|
13289
|
+
const requested = admitted.spec.requestedThinking;
|
|
13290
|
+
const startSuffix = splitKnownThinkingSuffix(candidate.modelId ?? "").thinkingSuffix.slice(1);
|
|
13291
|
+
const startEffort = THINKING_LEVELS.find((level) => level === startSuffix) ?? requested;
|
|
13292
|
+
const explicitEffort = requested === undefined || startEffort === undefined ? {} : {
|
|
13293
|
+
...inherited.model ? { thinkingLevel: explicitEffortFor(inherited.model, startEffort, settingsManager) } : {},
|
|
13294
|
+
fallbackModels: (inherited.fallbackModels ?? settingsManager.getFallbackModels() ?? []).map((entry) => pinFallbackEffort(entry, requested, settingsManager, admitted.spec.resolveCandidateModel))
|
|
13295
|
+
};
|
|
13221
13296
|
const resourceLoader = new DefaultResourceLoader(inProcessChildResourceLoaderOptions({
|
|
13222
13297
|
cwd: admitted.policy.cwd,
|
|
13223
13298
|
agentDir,
|
|
@@ -13227,7 +13302,7 @@ class SubagentControlRuntime {
|
|
|
13227
13302
|
resourceLoaderInheritanceSnapshot: admitted.spec.parent?.resourceLoaderInheritanceSnapshot,
|
|
13228
13303
|
orchestrationContext: admitted.spec.parent?.orchestrationContext
|
|
13229
13304
|
}));
|
|
13230
|
-
await resourceLoader.reload();
|
|
13305
|
+
await beforeStartDeadline("loading its resources", resourceLoader.reload());
|
|
13231
13306
|
const selectedSkills = resolveSkillsFromCatalog([...admitted.policy.skills], resourceLoader.getSkillCatalog(), admitted.policy.cwd);
|
|
13232
13307
|
skillReport = {
|
|
13233
13308
|
...selectedSkills.resolved.length > 0 ? { skills: selectedSkills.resolved.map((skill) => skill.name) } : {},
|
|
@@ -13239,26 +13314,28 @@ class SubagentControlRuntime {
|
|
|
13239
13314
|
const skillInjection = buildSkillInjection(selectedSkills.resolved);
|
|
13240
13315
|
const promptBehavior = createInProcessChildPromptBehavior(admitted.policy);
|
|
13241
13316
|
const systemPromptTransform = createInProcessChildSystemPromptTransform(admitted.policy, skillInjection);
|
|
13242
|
-
created = {
|
|
13243
|
-
|
|
13244
|
-
|
|
13245
|
-
|
|
13246
|
-
|
|
13247
|
-
|
|
13248
|
-
|
|
13249
|
-
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
|
|
13259
|
-
|
|
13260
|
-
|
|
13317
|
+
created = await beforeStartDeadline("creating its agent session, which includes any wait for a resource-admission permit", createAgentSession({
|
|
13318
|
+
...inherited,
|
|
13319
|
+
...explicitEffort,
|
|
13320
|
+
extensionBindings,
|
|
13321
|
+
customTools: admitted.policy.customTools ?? inherited.customTools,
|
|
13322
|
+
resourceLoader,
|
|
13323
|
+
resourceAdmissionSignal: AbortSignal.any([signals.abort, signals.interrupt, startDeadline.signal]),
|
|
13324
|
+
subagentPolicy: {
|
|
13325
|
+
...admitted.policy,
|
|
13326
|
+
executionEnded: executionEnded.signal,
|
|
13327
|
+
...!childAllowsIntercom(inherited) ? { intercom: undefined, intercomGroup: undefined } : {}
|
|
13328
|
+
},
|
|
13329
|
+
settingsManager,
|
|
13330
|
+
orchestrationContext: admitted.spec.parent?.orchestrationContext,
|
|
13331
|
+
sessionManager,
|
|
13332
|
+
systemPromptTransform,
|
|
13333
|
+
initialContextTransform: promptBehavior.initialContextTransform
|
|
13334
|
+
}), (late) => void late.session.dispose().catch(() => {
|
|
13335
|
+
return;
|
|
13336
|
+
}));
|
|
13261
13337
|
}
|
|
13338
|
+
clearTimeout(startTimer);
|
|
13262
13339
|
session = created.session;
|
|
13263
13340
|
const transcriptSession = session;
|
|
13264
13341
|
let observedStreamingMessage;
|
|
@@ -13490,6 +13567,7 @@ class SubagentControlRuntime {
|
|
|
13490
13567
|
...skillReport
|
|
13491
13568
|
};
|
|
13492
13569
|
} finally {
|
|
13570
|
+
clearTimeout(startTimer);
|
|
13493
13571
|
executionEnded.abort();
|
|
13494
13572
|
signals.abort.removeEventListener("abort", abortListener);
|
|
13495
13573
|
signals.interrupt.removeEventListener("abort", interruptListener);
|
|
@@ -13677,11 +13755,24 @@ function findSubagentControl(id) {
|
|
|
13677
13755
|
if (direct)
|
|
13678
13756
|
return direct;
|
|
13679
13757
|
for (const control of controls.values()) {
|
|
13758
|
+
if (control.pathOfChildId(id) !== undefined)
|
|
13759
|
+
return control;
|
|
13680
13760
|
if (control.native.listChildren().some((child) => child.path === id))
|
|
13681
13761
|
return control;
|
|
13682
13762
|
}
|
|
13683
13763
|
return;
|
|
13684
13764
|
}
|
|
13765
|
+
function subagentChildPath(control, id) {
|
|
13766
|
+
return control.pathOfChildId(id) ?? id;
|
|
13767
|
+
}
|
|
13768
|
+
function subagentChildIdOfPath(id) {
|
|
13769
|
+
for (const control of controls.values()) {
|
|
13770
|
+
const childId = control.childIdOf(id);
|
|
13771
|
+
if (childId !== undefined)
|
|
13772
|
+
return childId;
|
|
13773
|
+
}
|
|
13774
|
+
return;
|
|
13775
|
+
}
|
|
13685
13776
|
function listSubagentControls() {
|
|
13686
13777
|
return [...controls.values()];
|
|
13687
13778
|
}
|
|
@@ -14027,6 +14118,8 @@ async function runSingleInProcess(runtimeCwd, agent, task, options) {
|
|
|
14027
14118
|
const resolvedCandidate = candidate ? options.resolveCandidateModel?.(candidate) : undefined;
|
|
14028
14119
|
if (options.modelRoute && !resolvedCandidate)
|
|
14029
14120
|
throw new Error("Subagent auto-selected model could not be resolved before execution. Retry explicitly with the current catalog.");
|
|
14121
|
+
const requestedSuffix = splitKnownThinkingSuffix(options.modelOverride ?? agent.model ?? "").thinkingSuffix.slice(1);
|
|
14122
|
+
const requestedThinking = options.modelRoute ? undefined : THINKING_LEVELS.find((level) => level === requestedSuffix);
|
|
14030
14123
|
const orchestrationContext = workflowOrchestrationContext(options);
|
|
14031
14124
|
const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
|
|
14032
14125
|
const parent = {
|
|
@@ -14053,6 +14146,7 @@ ${task}`);
|
|
|
14053
14146
|
const testSession = options.testSession ?? defaultTestSession();
|
|
14054
14147
|
const spec = {
|
|
14055
14148
|
taskName: agent.name,
|
|
14149
|
+
childId: `${options.runId}:${options.index ?? 0}`,
|
|
14056
14150
|
task,
|
|
14057
14151
|
agent,
|
|
14058
14152
|
cwd,
|
|
@@ -14066,6 +14160,11 @@ ${task}`);
|
|
|
14066
14160
|
...options.progressPath ? { progressPath: options.progressPath } : {},
|
|
14067
14161
|
...options.progressArtifactPath ? { progressArtifactPath: options.progressArtifactPath } : options.progressPath && artifactsDir ? { progressArtifactPath: options.progressPath } : {},
|
|
14068
14162
|
...artifactPaths?.outputPath ? { outputArtifactPath: artifactPaths.outputPath } : {},
|
|
14163
|
+
...options.sessionStartTimeoutMs === undefined ? {} : { sessionStartTimeoutMs: options.sessionStartTimeoutMs },
|
|
14164
|
+
...requestedThinking === undefined ? {} : {
|
|
14165
|
+
requestedThinking,
|
|
14166
|
+
...options.resolveCandidateModel ? { resolveCandidateModel: options.resolveCandidateModel } : {}
|
|
14167
|
+
},
|
|
14069
14168
|
tools: agent.tools,
|
|
14070
14169
|
mcpDirectTools: agent.mcpDirectTools,
|
|
14071
14170
|
skills: options.skills ?? agent.skills,
|
|
@@ -15334,6 +15433,9 @@ async function recordReportedGoneSubagentChildren(pi, outcomes) {
|
|
|
15334
15433
|
}
|
|
15335
15434
|
}
|
|
15336
15435
|
|
|
15436
|
+
// dist/builtin/subagents/src/runs/foreground/subagent-executor.ts
|
|
15437
|
+
import { validateTaskHeartbeatMs } from "@bastani/atomic";
|
|
15438
|
+
|
|
15337
15439
|
// dist/builtin/subagents/src/agents/agent-management.ts
|
|
15338
15440
|
import * as fs15 from "node:fs";
|
|
15339
15441
|
import * as path16 from "node:path";
|
|
@@ -15892,34 +15994,52 @@ function handleSubagentControlNotice(input) {
|
|
|
15892
15994
|
function canonicalChildren(control) {
|
|
15893
15995
|
return [...control.listChildren()].sort((left, right) => left.path.localeCompare(right.path));
|
|
15894
15996
|
}
|
|
15997
|
+
function settledCause(control, path, status) {
|
|
15998
|
+
if (status !== "error" && status !== "interrupted")
|
|
15999
|
+
return;
|
|
16000
|
+
const cause = control.getDeliveredResult(path)?.cause?.replace(/\s+/g, " ").trim();
|
|
16001
|
+
return cause || undefined;
|
|
16002
|
+
}
|
|
15895
16003
|
function childLines(control, id) {
|
|
15896
|
-
if (!control)
|
|
15897
|
-
return [];
|
|
15898
16004
|
if (id) {
|
|
15899
|
-
const
|
|
16005
|
+
const path = subagentChildPath(control, id);
|
|
16006
|
+
const child = control.findChild(path);
|
|
15900
16007
|
if (!child)
|
|
15901
16008
|
return [];
|
|
15902
|
-
const delivered = control.getDeliveredResult(
|
|
16009
|
+
const delivered = control.getDeliveredResult(path);
|
|
16010
|
+
const childId = control.childIdOf(path);
|
|
16011
|
+
const cause = settledCause(control, path, child.status);
|
|
15903
16012
|
return [
|
|
16013
|
+
...childId ? [`Id: ${childId}`] : [],
|
|
15904
16014
|
`Child: ${child.path}`,
|
|
15905
16015
|
`Parent: ${child.parentPath}`,
|
|
15906
16016
|
`Task: ${child.taskName}`,
|
|
15907
16017
|
`Depth: ${child.depth}`,
|
|
15908
16018
|
`Status: ${child.status}`,
|
|
16019
|
+
...cause ? [`Cause: ${cause}`] : [],
|
|
15909
16020
|
`Residency: ${child.loaded ? "loaded" : "cold"}`,
|
|
15910
16021
|
...delivered?.sessionFile ? [`Session: ${delivered.sessionFile}`] : []
|
|
15911
16022
|
];
|
|
15912
16023
|
}
|
|
15913
|
-
return control.listChildren().map((child) =>
|
|
16024
|
+
return control.listChildren().map((child) => {
|
|
16025
|
+
const childId = control.childIdOf(child.path);
|
|
16026
|
+
const cause = settledCause(control, child.path, child.status);
|
|
16027
|
+
return `${child.path} — ${child.status} (${child.loaded ? "loaded" : "cold"})${childId ? ` · id ${childId}` : ""}${cause ? ` · ${cause}` : ""}`;
|
|
16028
|
+
});
|
|
15914
16029
|
}
|
|
15915
16030
|
function statusGroup(control, id) {
|
|
16031
|
+
const path = id === undefined ? undefined : subagentChildPath(control, id);
|
|
15916
16032
|
return {
|
|
15917
16033
|
parentPath: control.parent.path,
|
|
15918
|
-
children: canonicalChildren(control).filter((child) => !
|
|
16034
|
+
children: canonicalChildren(control).filter((child) => !path || path === control.parent.path || child.path === path).map((child) => {
|
|
15919
16035
|
const delivered = control.getDeliveredResult(child.path);
|
|
15920
16036
|
const metadata = control.getChildMetadata(child.path) ?? delivered;
|
|
16037
|
+
const childId = control.childIdOf(child.path);
|
|
16038
|
+
const failure = settledCause(control, child.path, child.status);
|
|
15921
16039
|
return {
|
|
15922
16040
|
...child,
|
|
16041
|
+
...childId === undefined ? {} : { childId },
|
|
16042
|
+
...failure === undefined ? {} : { failure },
|
|
15923
16043
|
sessionFile: delivered?.sessionFile,
|
|
15924
16044
|
...metadata?.model === undefined ? {} : { model: metadata.model },
|
|
15925
16045
|
...metadata?.thinking === undefined ? {} : { thinking: metadata.thinking }
|
|
@@ -15927,10 +16047,11 @@ function statusGroup(control, id) {
|
|
|
15927
16047
|
})
|
|
15928
16048
|
};
|
|
15929
16049
|
}
|
|
15930
|
-
function inspectInProcessChildStatus(id) {
|
|
16050
|
+
function inspectInProcessChildStatus(id, ownsRun) {
|
|
16051
|
+
const owned = (control) => ownsRun?.(control.parent.path) ?? true;
|
|
15931
16052
|
if (id) {
|
|
15932
16053
|
const control = findSubagentControl(id);
|
|
15933
|
-
if (!control)
|
|
16054
|
+
if (!control || !owned(control))
|
|
15934
16055
|
return;
|
|
15935
16056
|
const text = id === control.parent.path ? [`Parent: ${control.parent.path}`, ...childLines(control)].join(`
|
|
15936
16057
|
`) : childLines(control, id).join(`
|
|
@@ -15942,7 +16063,7 @@ function inspectInProcessChildStatus(id) {
|
|
|
15942
16063
|
details: { mode: "management", results: [], statusGroups: [statusGroup(control, id)] }
|
|
15943
16064
|
};
|
|
15944
16065
|
}
|
|
15945
|
-
const controls = listSubagentControls();
|
|
16066
|
+
const controls = listSubagentControls().filter(owned);
|
|
15946
16067
|
const lines = controls.flatMap((control) => [`Parent: ${control.parent.path}`, ...childLines(control)]);
|
|
15947
16068
|
if (lines.length === 0)
|
|
15948
16069
|
return;
|
|
@@ -16257,7 +16378,8 @@ function topLevelSingleTask(item) {
|
|
|
16257
16378
|
...item.params.modelConstraints !== undefined ? { modelConstraints: item.params.modelConstraints } : {},
|
|
16258
16379
|
...item.params.taskNeeds !== undefined ? { taskNeeds: item.params.taskNeeds } : {},
|
|
16259
16380
|
...item.params.skill !== undefined ? { skill: item.params.skill } : {},
|
|
16260
|
-
...item.params.group !== undefined ? { group: item.params.group } : {}
|
|
16381
|
+
...item.params.group !== undefined ? { group: item.params.group } : {},
|
|
16382
|
+
...item.params.heartbeatMs !== undefined ? { heartbeatMs: item.params.heartbeatMs } : {}
|
|
16261
16383
|
};
|
|
16262
16384
|
}
|
|
16263
16385
|
function existingParallelTask(item, task) {
|
|
@@ -16266,7 +16388,8 @@ function existingParallelTask(item, task) {
|
|
|
16266
16388
|
...task,
|
|
16267
16389
|
cwd: originatingTaskCwd(callCwd, task.cwd),
|
|
16268
16390
|
[BURST_TASK_DISCOVERY_CWD]: callCwd,
|
|
16269
|
-
...task.group === undefined && item.params.group !== undefined ? { group: item.params.group } : {}
|
|
16391
|
+
...task.group === undefined && item.params.group !== undefined ? { group: item.params.group } : {},
|
|
16392
|
+
...task.heartbeatMs === undefined && item.params.heartbeatMs !== undefined ? { heartbeatMs: item.params.heartbeatMs } : {}
|
|
16270
16393
|
};
|
|
16271
16394
|
}
|
|
16272
16395
|
function mergeBurst(items) {
|
|
@@ -16992,6 +17115,7 @@ async function runParallelPath(data, deps) {
|
|
|
16992
17115
|
const detachedNotificationScope = { orchestrationContext: ctx.orchestrationContext };
|
|
16993
17116
|
const results = await runForegroundParallelTasks({
|
|
16994
17117
|
wait: params.wait,
|
|
17118
|
+
...params.heartbeatMs === undefined ? {} : { heartbeatMs: params.heartbeatMs },
|
|
16995
17119
|
schedule: params[BURST_EXECUTION_SCHEDULE],
|
|
16996
17120
|
onDetachedExit: (index, result) => {
|
|
16997
17121
|
if (workflowStageAcceptsDetachedNotification(detachedNotificationScope)) {
|
|
@@ -17340,6 +17464,7 @@ async function runSinglePath(data, deps) {
|
|
|
17340
17464
|
intentTask: handoffTaskContext,
|
|
17341
17465
|
options: runOptions,
|
|
17342
17466
|
wait: params.wait,
|
|
17467
|
+
...params.heartbeatMs === undefined ? {} : { heartbeatMs: params.heartbeatMs },
|
|
17343
17468
|
schedule: params[BURST_EXECUTION_SCHEDULE],
|
|
17344
17469
|
runtime: deps.runtime,
|
|
17345
17470
|
recordChild: createSubagentChildRecordSink(deps.pi, ctx),
|
|
@@ -17551,10 +17676,22 @@ function liveTaskIds(host) {
|
|
|
17551
17676
|
function ownedRecords(ctx) {
|
|
17552
17677
|
return readSubagentChildRecords(ctx.sessionManager.getEntries(), subagentChildOwner(ctx).sessionId);
|
|
17553
17678
|
}
|
|
17679
|
+
async function subagentRunsLaunchedBy(ctx) {
|
|
17680
|
+
const owner = subagentChildOwner(ctx).sessionId;
|
|
17681
|
+
await subagentChildRecordsAppended(owner);
|
|
17682
|
+
const runIds = observedSubagentRunIds(owner);
|
|
17683
|
+
for (const record of ownedRecords(ctx).values())
|
|
17684
|
+
runIds.add(record.runId);
|
|
17685
|
+
return runIds;
|
|
17686
|
+
}
|
|
17554
17687
|
function resolveRecord(records, id) {
|
|
17555
17688
|
const direct = findSubagentChildRecord(records, id);
|
|
17556
17689
|
if (direct)
|
|
17557
17690
|
return direct;
|
|
17691
|
+
const listedAs = subagentChildIdOfPath(id.trim());
|
|
17692
|
+
const byPath = listedAs === undefined ? undefined : records.get(listedAs);
|
|
17693
|
+
if (byPath)
|
|
17694
|
+
return byPath;
|
|
17558
17695
|
const inRun = [...records.values()].filter((record) => record.runId === id.trim());
|
|
17559
17696
|
if (inRun.length === 1)
|
|
17560
17697
|
return inRun[0];
|
|
@@ -17895,7 +18032,8 @@ async function handleManagementRequest(input) {
|
|
|
17895
18032
|
if (action === "status") {
|
|
17896
18033
|
const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
|
|
17897
18034
|
const foreground = getForegroundControl(deps.state, targetRunId);
|
|
17898
|
-
const
|
|
18035
|
+
const launched = await subagentRunsLaunchedBy(ctx);
|
|
18036
|
+
const live = inspectInProcessChildStatus(targetRunId, (runId) => launched.has(runId)) ?? (foreground ? foregroundStatusResult(foreground) : undefined);
|
|
17899
18037
|
if (awaitingResume.length > 0) {
|
|
17900
18038
|
const liveText = live?.content.map((part) => part.type === "text" ? part.text : "").join(`
|
|
17901
18039
|
`);
|
|
@@ -17926,6 +18064,22 @@ function inferExecutionMode(params) {
|
|
|
17926
18064
|
return "parallel";
|
|
17927
18065
|
return "single";
|
|
17928
18066
|
}
|
|
18067
|
+
function invalidHeartbeatResult(params) {
|
|
18068
|
+
try {
|
|
18069
|
+
if (params.heartbeatMs !== undefined)
|
|
18070
|
+
validateTaskHeartbeatMs(params.heartbeatMs);
|
|
18071
|
+
for (const [index, task] of (params.tasks ?? []).entries())
|
|
18072
|
+
if (task.heartbeatMs !== undefined)
|
|
18073
|
+
validateTaskHeartbeatMs(task.heartbeatMs, `tasks[${index}].heartbeatMs`);
|
|
18074
|
+
return;
|
|
18075
|
+
} catch (error) {
|
|
18076
|
+
return {
|
|
18077
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
18078
|
+
isError: true,
|
|
18079
|
+
details: { mode: inferExecutionMode(params), results: [] }
|
|
18080
|
+
};
|
|
18081
|
+
}
|
|
18082
|
+
}
|
|
17929
18083
|
function duplicateSubagentCallResult(params) {
|
|
17930
18084
|
return {
|
|
17931
18085
|
content: [
|
|
@@ -17999,7 +18153,7 @@ function createSubagentExecutor(rawDeps) {
|
|
|
17999
18153
|
duplicateResult: duplicateSubagentCallResult
|
|
18000
18154
|
});
|
|
18001
18155
|
return {
|
|
18002
|
-
execute: (id, params, signal, onUpdate, ctx) => params.action ? execute(id, params, signal, onUpdate, ctx) : executeWithBurstCollection(id, params, signal, onUpdate, ctx)
|
|
18156
|
+
execute: async (id, params, signal, onUpdate, ctx) => params.action ? execute(id, params, signal, onUpdate, ctx) : invalidHeartbeatResult(params) ?? executeWithBurstCollection(id, params, signal, onUpdate, ctx)
|
|
18003
18157
|
};
|
|
18004
18158
|
}
|
|
18005
18159
|
|
|
@@ -19237,11 +19391,13 @@ function childStatusLines(child, expanded, theme, width) {
|
|
|
19237
19391
|
return [
|
|
19238
19392
|
heading,
|
|
19239
19393
|
...metadata,
|
|
19394
|
+
...child.childId ? [theme.fg("dim", ` Id: ${displayText(child.childId)}`)] : [],
|
|
19240
19395
|
theme.fg("dim", ` Path: ${displayText(child.path)}`),
|
|
19241
19396
|
theme.fg("dim", ` Parent: ${displayText(child.parentPath)} · Depth: ${child.depth}`),
|
|
19242
19397
|
theme.fg("dim", ` Task: ${displayText(child.taskName)}`),
|
|
19243
19398
|
theme.fg("dim", ` Residency: ${child.loaded ? "loaded" : "cold"}`),
|
|
19244
19399
|
...child.cause ? [theme.fg("muted", ` Cause: ${displayText(child.cause)}`)] : [],
|
|
19400
|
+
...child.failure ? [theme.fg("error", ` Error: ${displayText(child.failure)}`)] : [],
|
|
19245
19401
|
...child.sessionFile ? [theme.fg("dim", ` Session: ${displayText(child.sessionFile)}`)] : []
|
|
19246
19402
|
];
|
|
19247
19403
|
}
|
|
@@ -19709,6 +19865,12 @@ var MaxOutputSchema = Type.Object({
|
|
|
19709
19865
|
bytes: Type.Optional(Type.Number()),
|
|
19710
19866
|
lines: Type.Optional(Type.Number())
|
|
19711
19867
|
}, { additionalProperties: false });
|
|
19868
|
+
function heartbeatSchema(scope) {
|
|
19869
|
+
return Type.Number({
|
|
19870
|
+
minimum: 0,
|
|
19871
|
+
description: `Heartbeat cadence in ms for ${scope} while it runs (default 15 min; 0 disables; min 60000).`
|
|
19872
|
+
});
|
|
19873
|
+
}
|
|
19712
19874
|
var GroupSchema = Type.Union([Type.String(), Type.Boolean()], {
|
|
19713
19875
|
description: "Intercom group for spawned children. A named string joins that group; boolean `true` or the trimmed, case-insensitive string sentinel `true`/`auto` auto-generates one shared UUID group per parallel set. The names `true` and `auto` are reserved; use a different literal group name. Defaults to the current session/stage's group. Only applied when the child has intercom access; contact_supervisor still reaches the supervisor across groups."
|
|
19714
19876
|
});
|
|
@@ -19727,7 +19889,8 @@ var TaskItem = Type.Object({
|
|
|
19727
19889
|
modelConstraints: Type.Optional(ModelConstraintsSchema),
|
|
19728
19890
|
taskNeeds: Type.Optional(TaskNeedsSchema),
|
|
19729
19891
|
skill: Type.Optional(SkillOverride),
|
|
19730
|
-
group: Type.Optional(GroupSchema)
|
|
19892
|
+
group: Type.Optional(GroupSchema),
|
|
19893
|
+
heartbeatMs: Type.Optional(heartbeatSchema("this task"))
|
|
19731
19894
|
});
|
|
19732
19895
|
var ControlOverrides = Type.Object({
|
|
19733
19896
|
enabled: Type.Optional(Type.Boolean({ description: "Enable/disable subagent control attention tracking for this run" })),
|
|
@@ -19764,6 +19927,7 @@ var SubagentParams = Type.Object({
|
|
|
19764
19927
|
budgetMs: Type.Optional(Type.Number({ description: "Foreground wait budget in milliseconds (kind='foreground' only)." }))
|
|
19765
19928
|
}, { additionalProperties: false })),
|
|
19766
19929
|
budgetMs: Type.Optional(Type.Number({ description: "Foreground wait budget in milliseconds." })),
|
|
19930
|
+
heartbeatMs: Type.Optional(heartbeatSchema("each child; a task's own wins")),
|
|
19767
19931
|
agent: Type.Optional(Type.String({ description: "Agent name (SINGLE mode) or target for management get/update/delete" })),
|
|
19768
19932
|
task: Type.Optional(Type.String({ description: "Task (SINGLE mode, optional for self-contained agents)" })),
|
|
19769
19933
|
action: Type.Optional(Type.String({
|
|
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.58] - 2026-11-01
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- PDF text extraction no longer prints pdf.js font warnings such as `Warning: TT: undefined function: 32` over the interactive terminal UI when fetching PDFs with unusual embedded fonts. Extraction errors are still reported.
|
|
12
|
+
|
|
13
|
+
## [0.9.58-alpha.1] - 2026-11-01
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- PDF text extraction no longer prints pdf.js font warnings such as `Warning: TT: undefined function: 32` over the interactive terminal UI when fetching PDFs with unusual embedded fonts. Extraction errors are still reported.
|
|
18
|
+
|
|
7
19
|
## [0.9.52] - 2026-10-29
|
|
8
20
|
|
|
9
21
|
### Fixed
|
|
@@ -72560,7 +72560,7 @@ async function extractPDFToMarkdown(buffer, url, options = {}) {
|
|
|
72560
72560
|
filename
|
|
72561
72561
|
} = options;
|
|
72562
72562
|
const safeMaxPages = Number.isFinite(maxPages) ? Math.max(1, Math.floor(maxPages)) : DEFAULT_MAX_PAGES;
|
|
72563
|
-
const pdf = await getDocumentProxy(new Uint8Array(buffer));
|
|
72563
|
+
const pdf = await getDocumentProxy(new Uint8Array(buffer), { verbosity: PDF_JS_VERBOSITY_ERRORS });
|
|
72564
72564
|
const metadata = await pdf.getMetadata();
|
|
72565
72565
|
const metadataInfo = metadata.info && typeof metadata.info === "object" ? metadata.info : null;
|
|
72566
72566
|
const metaTitle = typeof metadataInfo?.Title === "string" ? metadataInfo.Title : undefined;
|
|
@@ -72650,7 +72650,7 @@ function isPDF(url, contentType) {
|
|
|
72650
72650
|
return false;
|
|
72651
72651
|
}
|
|
72652
72652
|
}
|
|
72653
|
-
var DEFAULT_MAX_PAGES = 100, DEFAULT_OUTPUT_DIR;
|
|
72653
|
+
var DEFAULT_MAX_PAGES = 100, PDF_JS_VERBOSITY_ERRORS = 0, DEFAULT_OUTPUT_DIR;
|
|
72654
72654
|
var init_pdf_extract = __esm(() => {
|
|
72655
72655
|
init_dist();
|
|
72656
72656
|
DEFAULT_OUTPUT_DIR = join4(homedir4(), "Downloads");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/web-access",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.58",
|
|
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,48 @@ 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.58] - 2026-11-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `workflows.localWorkers.maxLoadPerCpu` (SDK `localWorkers.maxLoadPerCpu`): while the machine's load per CPU is above it, the stages of pooled children start one at a time, at most one every 5 seconds in the order they asked, instead of all at once while earlier ones are still starting. Running stages and their subagents and tool processes are not held. Default `3`; `0` turns it off. It is not saved with the root. ([#4146](https://github.com/bastani-inc/atomic/pull/4146))
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- A message sent to a stage on a local worker (`workflows.localWorkers`) while its session was still starting no longer fails the stage with "generation closed before the stage finished; its tool calls were refused" when the stage has `fallbackModels`.
|
|
18
|
+
- The chat of a stage on a local worker completes `/skill:` commands with that stage's skills, as an in-process stage chat does, instead of warning "Skill discovery unavailable: this stage host does not expose its session command metadata." While the stage's session is still starting, it says its skills are loading.
|
|
19
|
+
- In the chat of a stage on a local worker, `/compact` compacts that stage's session and a `!` command runs on the worker in the stage's working directory, with its output streaming into the chat and Escape stopping it even while the pool is paused, as in the chat of an in-process stage. After `/compact` the open chat shows the compacted transcript. Before, `/compact` was sent to the model as plain text and a `!` command failed with "no live agent session on this stage". `/tasks` there lists the stage's agents and shells on its worker with their live status, opens their output and transcripts, sends them input and stops them, and the chat's task footer counts them, as in the chat of an in-process stage. An agent task's reply shows while it streams, tasks can be inspected and stopped while the pool is paused, and a stop that cannot reach the worker says so. Before, `/tasks` showed an empty list.
|
|
20
|
+
- A stage on a local worker reads as queued, with what it is doing (`creating session 32s`), on its graph node, in the graph header, the stage list, `/workflow status` and the `workflow` tool's stage list until its session has started, instead of reading as running. Its chat says it is starting on its local worker instead of showing a blank transcript, streams the stage as soon as the session starts, and opens the live view by itself when the worker cannot serve one yet. A message sent meanwhile waits for the session instead of failing with "The connection to stage … was lost" and "1 message not delivered".
|
|
21
|
+
- A root that runs children on local workers (`workflows.localWorkers`) no longer fails all of them with `Stale lease for run <id>` when its own process renews the root's lease late. A busy session, such as one that reloads its workflows and starts several other runs with in-process agent stages, could push the root's renewal past the pool's 90-second window. The pool treated that late renewal as a lost lease: it failed every running child, and every child the root started afterwards failed at once, while the root itself kept running. The pool now waits for the renewal and its workers keep their children; only a lease that was released or taken over by another holder ends them. Each run also renews its lease on its own, so a busy or blocked run no longer delays another run's renewal, and a renewal that fails without losing the lease, such as one that meets a briefly locked database, is retried on the next heartbeat instead of giving up the run. ([#4138](https://github.com/bastani-inc/atomic/pull/4138))
|
|
22
|
+
- With `workflows.localWorkers` configured, a root starts its pool's coordinator process when it runs its first pooled `ctx.workflow` child, or when it resumes a pool that already ran. A root that only runs `ctx.task` stages and `ctx.tool` steps no longer starts one, so launching such runs no longer leaves an idle coordinator process for each of them. ([#4138](https://github.com/bastani-inc/atomic/pull/4138))
|
|
23
|
+
- Stages on local workers (`workflows.localWorkers`) no longer fail with `Worker exited before delivery acknowledgement` while their worker is still running. On a busy machine a stage's startup delivery of queued Intercom messages could make no progress for a minute; the root then ended the worker's delivery request while the delivery kept going, and the next message reached a closed connection, failing the stage at `preparing message delivery` with an unknown, terminal failure. The delivery now keeps its connection to the worker until it finishes, and the stage still starts after a minute without progress, as an in-process stage does. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
24
|
+
- A worker whose work lease ran out only because the machine was too busy to process its renewal in time keeps its child. Its next journal write, usage report or completion is no longer refused with `Stale work lease`, which ended the child's attempt and could fail the child as `killed`; a session it starts no longer fails with `Stale resource admission attempt`, and waits for the renewal instead. Only an attempt that recovery handed to another worker is refused. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
25
|
+
- A fault of the pool itself that reaches a running pooled child, such as a journal write the database refused, replays the child from its last checkpoint (up to the usual three attempts) instead of failing its stage. A stage that does fail on a pool fault is classified with that fault as its failure code (`worker_lost`, `stale_work_lease`, …), recoverable and retryable, instead of `unknown`, and resume runs it again. A child whose attempt was fenced is no longer completed with the aborted run's result. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
26
|
+
- A stage on a local worker that ended keeps the worker lease it ended under: its worker's later lease renewals for the child's other stages no longer show it as renewed after it ended. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
27
|
+
- `/workflow pause <run>` and the `workflow` tool's `pause` pause a root's stages on local workers even while the root's own `ctx.tool` steps or in-process stages run, instead of reporting `No active stages to pause`. Pause, `pause --all` and resume no longer fail with `Worker control acknowledgement unresolved` when workers confirm slowly on a busy machine: the pause is recorded at once, workers pause their stages as they next check in, and the result says how many stages have not confirmed yet. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
28
|
+
- Resuming a root that runs children on local workers runs again the children an earlier process failed on a fault of the pool itself (a stage that lost its worker's delivery or lease on a busy machine, recorded as `Worker exited before delivery acknowledgement`, `Stale work lease` or `Stale resource admission attempt`), instead of replaying their failure. This includes runs paused or failed by 0.9.57. Pausing a root whose children have durable progress now says it can be resumed from a new process once this one exits, instead of `Resume with /workflow resume on this live process … Cross-process resume requires durable checkpoint or pending prompt progress`. ([#4146](https://github.com/bastani-inc/atomic/pull/4146))
|
|
29
|
+
|
|
30
|
+
## [0.9.58-alpha.1] - 2026-11-01
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- `workflows.localWorkers.maxLoadPerCpu` (SDK `localWorkers.maxLoadPerCpu`): while the machine's load per CPU is above it, the stages of pooled children start one at a time, at most one every 5 seconds in the order they asked, instead of all at once while earlier ones are still starting. Running stages and their subagents and tool processes are not held. Default `3`; `0` turns it off. It is not saved with the root. ([#4146](https://github.com/bastani-inc/atomic/pull/4146))
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- A message sent to a stage on a local worker (`workflows.localWorkers`) while its session was still starting no longer fails the stage with "generation closed before the stage finished; its tool calls were refused" when the stage has `fallbackModels`.
|
|
39
|
+
- The chat of a stage on a local worker completes `/skill:` commands with that stage's skills, as an in-process stage chat does, instead of warning "Skill discovery unavailable: this stage host does not expose its session command metadata." While the stage's session is still starting, it says its skills are loading.
|
|
40
|
+
- In the chat of a stage on a local worker, `/compact` compacts that stage's session and a `!` command runs on the worker in the stage's working directory, with its output streaming into the chat and Escape stopping it even while the pool is paused, as in the chat of an in-process stage. After `/compact` the open chat shows the compacted transcript. Before, `/compact` was sent to the model as plain text and a `!` command failed with "no live agent session on this stage". `/tasks` there lists the stage's agents and shells on its worker with their live status, opens their output and transcripts, sends them input and stops them, and the chat's task footer counts them, as in the chat of an in-process stage. An agent task's reply shows while it streams, tasks can be inspected and stopped while the pool is paused, and a stop that cannot reach the worker says so. Before, `/tasks` showed an empty list.
|
|
41
|
+
- A stage on a local worker reads as queued, with what it is doing (`creating session 32s`), on its graph node, in the graph header, the stage list, `/workflow status` and the `workflow` tool's stage list until its session has started, instead of reading as running. Its chat says it is starting on its local worker instead of showing a blank transcript, streams the stage as soon as the session starts, and opens the live view by itself when the worker cannot serve one yet. A message sent meanwhile waits for the session instead of failing with "The connection to stage … was lost" and "1 message not delivered".
|
|
42
|
+
- A root that runs children on local workers (`workflows.localWorkers`) no longer fails all of them with `Stale lease for run <id>` when its own process renews the root's lease late. A busy session, such as one that reloads its workflows and starts several other runs with in-process agent stages, could push the root's renewal past the pool's 90-second window. The pool treated that late renewal as a lost lease: it failed every running child, and every child the root started afterwards failed at once, while the root itself kept running. The pool now waits for the renewal and its workers keep their children; only a lease that was released or taken over by another holder ends them. Each run also renews its lease on its own, so a busy or blocked run no longer delays another run's renewal, and a renewal that fails without losing the lease, such as one that meets a briefly locked database, is retried on the next heartbeat instead of giving up the run. ([#4138](https://github.com/bastani-inc/atomic/pull/4138))
|
|
43
|
+
- With `workflows.localWorkers` configured, a root starts its pool's coordinator process when it runs its first pooled `ctx.workflow` child, or when it resumes a pool that already ran. A root that only runs `ctx.task` stages and `ctx.tool` steps no longer starts one, so launching such runs no longer leaves an idle coordinator process for each of them. ([#4138](https://github.com/bastani-inc/atomic/pull/4138))
|
|
44
|
+
- Stages on local workers (`workflows.localWorkers`) no longer fail with `Worker exited before delivery acknowledgement` while their worker is still running. On a busy machine a stage's startup delivery of queued Intercom messages could make no progress for a minute; the root then ended the worker's delivery request while the delivery kept going, and the next message reached a closed connection, failing the stage at `preparing message delivery` with an unknown, terminal failure. The delivery now keeps its connection to the worker until it finishes, and the stage still starts after a minute without progress, as an in-process stage does. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
45
|
+
- A worker whose work lease ran out only because the machine was too busy to process its renewal in time keeps its child. Its next journal write, usage report or completion is no longer refused with `Stale work lease`, which ended the child's attempt and could fail the child as `killed`; a session it starts no longer fails with `Stale resource admission attempt`, and waits for the renewal instead. Only an attempt that recovery handed to another worker is refused. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
46
|
+
- A fault of the pool itself that reaches a running pooled child, such as a journal write the database refused, replays the child from its last checkpoint (up to the usual three attempts) instead of failing its stage. A stage that does fail on a pool fault is classified with that fault as its failure code (`worker_lost`, `stale_work_lease`, …), recoverable and retryable, instead of `unknown`, and resume runs it again. A child whose attempt was fenced is no longer completed with the aborted run's result. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
47
|
+
- A stage on a local worker that ended keeps the worker lease it ended under: its worker's later lease renewals for the child's other stages no longer show it as renewed after it ended. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
48
|
+
- `/workflow pause <run>` and the `workflow` tool's `pause` pause a root's stages on local workers even while the root's own `ctx.tool` steps or in-process stages run, instead of reporting `No active stages to pause`. Pause, `pause --all` and resume no longer fail with `Worker control acknowledgement unresolved` when workers confirm slowly on a busy machine: the pause is recorded at once, workers pause their stages as they next check in, and the result says how many stages have not confirmed yet. ([#4145](https://github.com/bastani-inc/atomic/pull/4145))
|
|
49
|
+
- Resuming a root that runs children on local workers runs again the children an earlier process failed on a fault of the pool itself (a stage that lost its worker's delivery or lease on a busy machine, recorded as `Worker exited before delivery acknowledgement`, `Stale work lease` or `Stale resource admission attempt`), instead of replaying their failure. This includes runs paused or failed by 0.9.57. Pausing a root whose children have durable progress now says it can be resumed from a new process once this one exits, instead of `Resume with /workflow resume on this live process … Cross-process resume requires durable checkpoint or pending prompt progress`. ([#4146](https://github.com/bastani-inc/atomic/pull/4146))
|
|
50
|
+
|
|
9
51
|
## [0.9.57] - 2026-10-31
|
|
10
52
|
|
|
11
53
|
### Changed
|