@cydm/pie 2.7.5 → 2.7.7
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/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/index.js +6 -4
- package/dist/builtin/extensions/subagent/index.js +4 -4
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/builtin/skills/chrome-devtools-axi/SKILL.md +2 -2
- package/dist/builtin/skills/computer-use-axi/SKILL.md +1 -1
- package/dist/builtin/skills/tui-use/SKILL.md +1 -1
- package/dist/chunks/{chunk-SUPAWIKP.js → chunk-2FEDB5GR.js} +193 -54
- package/dist/chunks/{chunk-OOUL3DRE.js → chunk-IU5L5DED.js} +325 -56
- package/dist/chunks/{chunk-BFHCZVNO.js → chunk-UPGEXDEQ.js} +336 -59
- package/dist/chunks/{chunk-MLRF72MK.js → chunk-W5B3OXHQ.js} +1 -1
- package/dist/chunks/{src-DV4UYUNJ.js → src-C2CBPEUY.js} +2 -2
- package/dist/chunks/{test-stream-SWO34GSS.js → test-stream-LDVUVSUP.js} +10 -1
- package/dist/cli.js +157 -111
- package/package.json +6 -5
|
@@ -2,7 +2,7 @@ import { createRequire as __createRequire } from "node:module"; const require =
|
|
|
2
2
|
import {
|
|
3
3
|
Agent,
|
|
4
4
|
getToolCallerIdentity
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-W5B3OXHQ.js";
|
|
6
6
|
import {
|
|
7
7
|
AssistantMessageEventStream,
|
|
8
8
|
FileSystemGateway,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
getPlatformConfig,
|
|
13
13
|
hashText,
|
|
14
14
|
streamSimple
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2FEDB5GR.js";
|
|
16
16
|
import {
|
|
17
17
|
Type
|
|
18
18
|
} from "./chunk-XXGYUYXI.js";
|
|
@@ -899,11 +899,6 @@ function shouldPreserveExecutionStateForUserText(text) {
|
|
|
899
899
|
return CONTINUE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
900
900
|
}
|
|
901
901
|
|
|
902
|
-
// ../../packages/shared-headless-capabilities/src/capability.ts
|
|
903
|
-
function defineSharedCapability(definition) {
|
|
904
|
-
return definition;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
902
|
// ../../packages/shared-headless-capabilities/src/tool-policy.ts
|
|
908
903
|
var BUILTIN_TOOL_CAPABILITY_METADATA = {
|
|
909
904
|
search_context: { riskClass: "read_only", concurrencySafe: true, permissionScope: "session", availableInPlanMode: true, allowedForSubagentByDefault: false, maxOutputChars: 2e4 },
|
|
@@ -914,6 +909,7 @@ var BUILTIN_TOOL_CAPABILITY_METADATA = {
|
|
|
914
909
|
write_file: { riskClass: "write", concurrencySafe: false, requiresPermission: true, permissionScope: "filesystem", writesFile: true, highRisk: true },
|
|
915
910
|
edit_file: { riskClass: "write", concurrencySafe: false, requiresPermission: true, permissionScope: "filesystem", writesFile: true, highRisk: true },
|
|
916
911
|
bash: { riskClass: "shell", concurrencySafe: false, requiresPermission: true, permissionScope: "shell", executesShell: true, availableInPlanMode: true, allowedForSubagentByDefault: true, highRisk: true, maxOutputChars: 1e5 },
|
|
912
|
+
shell: { riskClass: "shell", concurrencySafe: false, requiresPermission: true, permissionScope: "shell", executesShell: true, availableInPlanMode: true, allowedForSubagentByDefault: true, highRisk: true, maxOutputChars: 1e5 },
|
|
917
913
|
web_research: { riskClass: "network", concurrencySafe: true, permissionScope: "network", readsNetwork: true, availableInPlanMode: true, allowedForSubagentByDefault: true, maxOutputChars: 1e5 },
|
|
918
914
|
web_search: { riskClass: "network", concurrencySafe: true, permissionScope: "network", readsNetwork: true, availableInPlanMode: true, allowedForSubagentByDefault: true, maxOutputChars: 1e5 },
|
|
919
915
|
web_fetch: { riskClass: "network", concurrencySafe: true, permissionScope: "network", readsNetwork: true, availableInPlanMode: true, allowedForSubagentByDefault: true, maxOutputChars: 1e5 },
|
|
@@ -975,6 +971,11 @@ function getSubagentToolPolicyDecision(tool) {
|
|
|
975
971
|
}
|
|
976
972
|
var DEFAULT_PLAN_MODE_TOOL_NAMES = Object.entries(BUILTIN_TOOL_CAPABILITY_METADATA).filter(([, metadata]) => metadata.availableInPlanMode).map(([name]) => name);
|
|
977
973
|
|
|
974
|
+
// ../../packages/shared-headless-capabilities/src/capability.ts
|
|
975
|
+
function defineSharedCapability(definition) {
|
|
976
|
+
return definition;
|
|
977
|
+
}
|
|
978
|
+
|
|
978
979
|
// ../../packages/shared-headless-capabilities/src/plan.ts
|
|
979
980
|
var import_shell_quote = __toESM(require_shell_quote(), 1);
|
|
980
981
|
var SIMPLE_READ_COMMANDS = /* @__PURE__ */ new Set(["pwd", "ls", "cat", "head", "tail", "wc", "grep", "rg", "which", "type", "echo", "printf", "cd", "true", "false", "tree", "jq", "stat", "file", "du", "df", "basename", "dirname", "date", "uname", "sort", "uniq", "cut", "column", "shasum", "printenv"]);
|
|
@@ -1211,6 +1212,27 @@ function createPlanCapability() {
|
|
|
1211
1212
|
});
|
|
1212
1213
|
}
|
|
1213
1214
|
|
|
1215
|
+
// ../../packages/agent-framework/src/session/remote-failure.ts
|
|
1216
|
+
function classifyRemoteFailure(remote, message) {
|
|
1217
|
+
if (!remote || !["transient", "other", "context_overflow"].includes(remote.category)) return void 0;
|
|
1218
|
+
const overflow = remote.category === "context_overflow";
|
|
1219
|
+
const retryAfterMs = remote.retryAfterMs;
|
|
1220
|
+
return {
|
|
1221
|
+
kind: overflow ? "context_overflow" : remote.category === "transient" ? "provider_transient" : "provider_confirmation",
|
|
1222
|
+
retryable: !overflow,
|
|
1223
|
+
compactable: overflow,
|
|
1224
|
+
userAbort: false,
|
|
1225
|
+
terminal: false,
|
|
1226
|
+
message,
|
|
1227
|
+
...typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs >= 0 ? { retryAfterMs } : {}
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
function resolveRetryDelay(backoffMs, maxDelayMs, retryAfterMs = 0) {
|
|
1231
|
+
const maximumWait = Math.min(maxDelayMs ?? 6e4, 2147483647);
|
|
1232
|
+
if (retryAfterMs > maximumWait) return void 0;
|
|
1233
|
+
return Math.max(maxDelayMs === void 0 ? backoffMs : Math.min(backoffMs, maxDelayMs), retryAfterMs);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1214
1236
|
// ../../packages/agent-framework/src/session/compaction.ts
|
|
1215
1237
|
var COMPACT_PROMPT = `You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task.
|
|
1216
1238
|
|
|
@@ -1577,6 +1599,7 @@ async function createCompactionSummary(messages, options = {}) {
|
|
|
1577
1599
|
throwIfAborted();
|
|
1578
1600
|
return appendFileOperationsToSummary(createSimpleSummary(messages), messages);
|
|
1579
1601
|
}
|
|
1602
|
+
let confirmationRetries = 0;
|
|
1580
1603
|
for (let attempt = 0; ; attempt++) {
|
|
1581
1604
|
throwIfAborted();
|
|
1582
1605
|
let response;
|
|
@@ -1602,17 +1625,22 @@ Return a text-only successor handoff. Do not call any tools.` }],
|
|
|
1602
1625
|
}
|
|
1603
1626
|
).result();
|
|
1604
1627
|
throwIfAborted();
|
|
1605
|
-
if (response.stopReason === "error" && !response.content.some((block) => block.type === "toolCall")) {
|
|
1628
|
+
if (response.stopReason === "error" && (!response.content.some((block) => block.type === "toolCall") || classifyRemoteFailure(response.remoteFailure, response.errorMessage ?? "") !== void 0)) {
|
|
1606
1629
|
const providerError = new Error(response.errorMessage || "Compaction provider error");
|
|
1607
1630
|
const explicitRetryable = response.retryable;
|
|
1608
1631
|
if (typeof explicitRetryable === "boolean") providerError.retryable = explicitRetryable;
|
|
1609
1632
|
if (response.replayEligible === true) providerError.replayEligible = true;
|
|
1633
|
+
providerError.remoteFailure = response.remoteFailure;
|
|
1610
1634
|
throw providerError;
|
|
1611
1635
|
}
|
|
1612
1636
|
} catch (error) {
|
|
1613
1637
|
throwIfAborted();
|
|
1614
|
-
|
|
1615
|
-
|
|
1638
|
+
const remote = classifyRemoteFailure(error?.remoteFailure, error instanceof Error ? error.message : String(error));
|
|
1639
|
+
if (attempt >= 2 || explicitlyNonRetryable(error) || (remote ? !remote.retryable : !isTransientCompactionFailure(error))) throw error;
|
|
1640
|
+
if (remote?.kind === "provider_confirmation" && confirmationRetries++ >= 1) throw error;
|
|
1641
|
+
const delayMs = resolveRetryDelay(250 * 2 ** attempt, 6e4, remote?.retryAfterMs);
|
|
1642
|
+
if (delayMs === void 0) throw new Error(`${remote?.message} (Retry-After exceeds the allowed wait; automatic recovery paused)`);
|
|
1643
|
+
await waitForCompactionRetry(delayMs, signal);
|
|
1616
1644
|
continue;
|
|
1617
1645
|
}
|
|
1618
1646
|
if (response.stopReason !== "stop" || response.content.some((block) => block.type === "toolCall")) {
|
|
@@ -1725,6 +1753,9 @@ function normalizeUserContentBlock(block) {
|
|
|
1725
1753
|
if (block.type === "text") {
|
|
1726
1754
|
return { type: "text", text: block.text };
|
|
1727
1755
|
}
|
|
1756
|
+
if (block.type === "hostContext") {
|
|
1757
|
+
return { type: "text", text: "x".repeat(512) };
|
|
1758
|
+
}
|
|
1728
1759
|
if (block.type === "image") {
|
|
1729
1760
|
return { type: "image", mimeType: block.mimeType };
|
|
1730
1761
|
}
|
|
@@ -1905,6 +1936,107 @@ function getSessionEntryText(entry) {
|
|
|
1905
1936
|
}).filter(Boolean).join("\n");
|
|
1906
1937
|
}
|
|
1907
1938
|
|
|
1939
|
+
// ../../packages/agent-framework/src/session/host-envelope.ts
|
|
1940
|
+
import { createHash } from "node:crypto";
|
|
1941
|
+
function createHostEnvelopeProjectorRegistry(projectors = []) {
|
|
1942
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1943
|
+
const latestByKind = /* @__PURE__ */ new Map();
|
|
1944
|
+
const key = (kind, version) => `${kind}@${version}`;
|
|
1945
|
+
for (const projector of projectors) validateAndIndex(projector);
|
|
1946
|
+
function validateAndIndex(input) {
|
|
1947
|
+
if (!input.kind || typeof input.kind !== "string") throw new Error("Host envelope projector requires a kind.");
|
|
1948
|
+
if (!Number.isInteger(input.version) || input.version < 1) throw new Error(`Host envelope projector ${input.kind} requires an integer version >= 1.`);
|
|
1949
|
+
if (typeof input.project !== "function") throw new Error(`Host envelope projector ${input.kind} requires a project function.`);
|
|
1950
|
+
const projector = Object.freeze({
|
|
1951
|
+
kind: input.kind,
|
|
1952
|
+
version: input.version,
|
|
1953
|
+
project: input.project
|
|
1954
|
+
});
|
|
1955
|
+
const existing = byKey.get(key(projector.kind, projector.version));
|
|
1956
|
+
if (existing && existing.project !== projector.project) {
|
|
1957
|
+
throw new Error(`Duplicate host envelope projector registration for ${projector.kind}@${projector.version}: a version's text must never change after registration.`);
|
|
1958
|
+
}
|
|
1959
|
+
byKey.set(key(projector.kind, projector.version), projector);
|
|
1960
|
+
const currentLatest = latestByKind.get(projector.kind);
|
|
1961
|
+
if (!currentLatest || projector.version > currentLatest.version) latestByKind.set(projector.kind, projector);
|
|
1962
|
+
}
|
|
1963
|
+
return {
|
|
1964
|
+
register: validateAndIndex,
|
|
1965
|
+
resolve: (kind, version) => byKey.get(key(kind, version)),
|
|
1966
|
+
latest: (kind) => latestByKind.get(kind),
|
|
1967
|
+
listPins: () => [...byKey.values()].map(({ kind, version }) => ({ kind, version }))
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
function projectHostContextBlock(block, registry) {
|
|
1971
|
+
let hash;
|
|
1972
|
+
try {
|
|
1973
|
+
hash = createHash("sha256").update(JSON.stringify({ kind: block.kind, version: block.version, data: block.data })).digest("hex").slice(0, 16);
|
|
1974
|
+
} catch {
|
|
1975
|
+
hash = "unhashable";
|
|
1976
|
+
}
|
|
1977
|
+
const projector = registry.resolve(block.kind, block.version);
|
|
1978
|
+
if (!projector) {
|
|
1979
|
+
return {
|
|
1980
|
+
text: `runtime state: unknown (projector missing: ${block.kind}@${block.version})`,
|
|
1981
|
+
degraded: true,
|
|
1982
|
+
audit: { kind: block.kind, version: block.version, hash }
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
try {
|
|
1986
|
+
const text = projector.project(block.data);
|
|
1987
|
+
if (typeof text !== "string" || text.trim().length === 0) throw new Error("projector returned empty text");
|
|
1988
|
+
return { text, degraded: false, audit: { kind: block.kind, version: block.version, hash } };
|
|
1989
|
+
} catch (error) {
|
|
1990
|
+
return {
|
|
1991
|
+
text: `runtime state: unknown (projector failed: ${block.kind}@${block.version}: ${error instanceof Error ? error.message.slice(0, 120) : String(error).slice(0, 120)})`,
|
|
1992
|
+
degraded: true,
|
|
1993
|
+
audit: { kind: block.kind, version: block.version, hash }
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
function leadingHostContextBlock(message) {
|
|
1998
|
+
if (message.role !== "user") return void 0;
|
|
1999
|
+
const content = message.content;
|
|
2000
|
+
if (!Array.isArray(content) || content.length === 0) return void 0;
|
|
2001
|
+
const first = content[0];
|
|
2002
|
+
if (!isHostContextBlock(first)) return void 0;
|
|
2003
|
+
return first;
|
|
2004
|
+
}
|
|
2005
|
+
function projectHostEnvelopes(messages, registry, onProjection) {
|
|
2006
|
+
let changed = false;
|
|
2007
|
+
const next = [];
|
|
2008
|
+
for (const message of messages) {
|
|
2009
|
+
const block = leadingHostContextBlock(message);
|
|
2010
|
+
if (!block) {
|
|
2011
|
+
next.push(message);
|
|
2012
|
+
continue;
|
|
2013
|
+
}
|
|
2014
|
+
const outcome = projectHostContextBlock(block, registry);
|
|
2015
|
+
onProjection?.(outcome);
|
|
2016
|
+
const content = message.content;
|
|
2017
|
+
const rest = content.slice(1);
|
|
2018
|
+
const first = rest[0];
|
|
2019
|
+
let projectedContent;
|
|
2020
|
+
if (first && first.type === "text" && typeof first.text === "string") {
|
|
2021
|
+
const merged = first.text.trim().length > 0 ? `${outcome.text}
|
|
2022
|
+
|
|
2023
|
+
${first.text}` : outcome.text;
|
|
2024
|
+
projectedContent = [{ ...first, text: merged }, ...rest.slice(1)];
|
|
2025
|
+
} else {
|
|
2026
|
+
projectedContent = [{ type: "text", text: outcome.text }, ...rest];
|
|
2027
|
+
}
|
|
2028
|
+
next.push({ ...message, content: projectedContent });
|
|
2029
|
+
changed = true;
|
|
2030
|
+
}
|
|
2031
|
+
return changed ? next : messages;
|
|
2032
|
+
}
|
|
2033
|
+
function isHostContextBlock(value) {
|
|
2034
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2035
|
+
const block = value;
|
|
2036
|
+
const version = block.version;
|
|
2037
|
+
return block.type === "hostContext" && typeof block.kind === "string" && block.kind.length > 0 && block.kind.length <= 128 && typeof version === "number" && Number.isInteger(version) && version >= 1 && !!block.data && typeof block.data === "object" && !Array.isArray(block.data);
|
|
2038
|
+
}
|
|
2039
|
+
|
|
1908
2040
|
// ../../packages/agent-framework/src/session/store.ts
|
|
1909
2041
|
import { createRequire } from "node:module";
|
|
1910
2042
|
import { fileURLToPath } from "node:url";
|
|
@@ -2945,7 +3077,19 @@ function normalizeSessionDisplayName(raw) {
|
|
|
2945
3077
|
}
|
|
2946
3078
|
function isSyntheticEnvelopePreview(preview) {
|
|
2947
3079
|
const trimmed = preview.trimStart();
|
|
2948
|
-
|
|
3080
|
+
if (trimmed.startsWith("<environment_context>") || trimmed.startsWith("<turn_aborted>") || trimmed.startsWith("# AGENTS.md instructions") || trimmed.startsWith("## Project Context\nProject-specific instructions from AGENTS.md")) {
|
|
3081
|
+
return true;
|
|
3082
|
+
}
|
|
3083
|
+
const runtimeMatch = trimmed.match(/^<runtime-mode-context>[\s\S]*?<\/runtime-mode-context>/);
|
|
3084
|
+
if (runtimeMatch) {
|
|
3085
|
+
const rest = trimmed.slice(runtimeMatch[0].length).trim();
|
|
3086
|
+
if (rest.length === 0) return true;
|
|
3087
|
+
return false;
|
|
3088
|
+
}
|
|
3089
|
+
if (trimmed.startsWith("<runtime-mode-context>")) {
|
|
3090
|
+
return true;
|
|
3091
|
+
}
|
|
3092
|
+
return /^HE_INPUT kind=(?:USER_REQUEST|QUEUE_WAKE) source=/.test(trimmed);
|
|
2949
3093
|
}
|
|
2950
3094
|
function isAutoGeneratedSessionName(name) {
|
|
2951
3095
|
return /^Session (\d|[a-z0-9]{6,8}$)/i.test(name.trim());
|
|
@@ -2959,7 +3103,10 @@ function userEntryPreview(entry) {
|
|
|
2959
3103
|
if (!message) return "";
|
|
2960
3104
|
if (typeof message.content === "string") return message.content;
|
|
2961
3105
|
if (!Array.isArray(message.content)) return "";
|
|
2962
|
-
return message.content.map((block) =>
|
|
3106
|
+
return message.content.map((block) => {
|
|
3107
|
+
if (isRecord2(block) && block.type === "hostContext") return "";
|
|
3108
|
+
return isRecord2(block) && block.type === "text" && typeof block.text === "string" ? block.text : "";
|
|
3109
|
+
}).join(" ").trim();
|
|
2963
3110
|
}
|
|
2964
3111
|
function entryJsonBytes(entry) {
|
|
2965
3112
|
try {
|
|
@@ -4705,7 +4852,7 @@ var AgentSessionAutoCompactScheduler = class {
|
|
|
4705
4852
|
this.resolvePending();
|
|
4706
4853
|
return;
|
|
4707
4854
|
}
|
|
4708
|
-
if (this.options.isStreaming()) {
|
|
4855
|
+
if (this.options.isStreaming() || this.runningAutoCompact !== void 0) {
|
|
4709
4856
|
this.pendingAutoCompact = pending;
|
|
4710
4857
|
this.schedule(pending, 100);
|
|
4711
4858
|
return;
|
|
@@ -4717,7 +4864,11 @@ var AgentSessionAutoCompactScheduler = class {
|
|
|
4717
4864
|
if (this.runningAutoCompact === running) {
|
|
4718
4865
|
this.runningAutoCompact = void 0;
|
|
4719
4866
|
this.runningAbortController = void 0;
|
|
4720
|
-
|
|
4867
|
+
try {
|
|
4868
|
+
this.options.onSettled?.();
|
|
4869
|
+
} finally {
|
|
4870
|
+
this.resolvePending();
|
|
4871
|
+
}
|
|
4721
4872
|
}
|
|
4722
4873
|
});
|
|
4723
4874
|
this.runningAutoCompact = running;
|
|
@@ -4742,6 +4893,7 @@ var AgentSessionAutoCompactScheduler = class {
|
|
|
4742
4893
|
});
|
|
4743
4894
|
}
|
|
4744
4895
|
resolvePending() {
|
|
4896
|
+
if (this.hasPendingAutoCompact) return;
|
|
4745
4897
|
const resolve2 = this.resolvePendingWait;
|
|
4746
4898
|
this.pendingWait = void 0;
|
|
4747
4899
|
this.resolvePendingWait = void 0;
|
|
@@ -4885,7 +5037,7 @@ function isUserContentBlock(value) {
|
|
|
4885
5037
|
return false;
|
|
4886
5038
|
}
|
|
4887
5039
|
const type = value.type;
|
|
4888
|
-
return type === "text" || type === "image" || type === "audio" || type === "video" || type === "fileRef";
|
|
5040
|
+
return type === "text" || type === "image" || type === "audio" || type === "video" || type === "fileRef" || type === "hostContext";
|
|
4889
5041
|
}
|
|
4890
5042
|
function normalizePromptInput(input) {
|
|
4891
5043
|
if (typeof input === "string") {
|
|
@@ -5276,7 +5428,8 @@ function createFailureClassification(context, retry, classifyFailure) {
|
|
|
5276
5428
|
if (context.message?.role === "assistant" && context.message.retryable === false) {
|
|
5277
5429
|
return { kind: "unknown", retryable: false, compactable: false, userAbort: context.message.stopReason === "aborted", terminal: true, message: context.errorMessage };
|
|
5278
5430
|
}
|
|
5279
|
-
const
|
|
5431
|
+
const remote = context.message?.role === "assistant" && context.message.stopReason === "error" ? classifyRemoteFailure(context.message.remoteFailure, context.errorMessage) : void 0;
|
|
5432
|
+
const hostClassification = remote ? retry || remote.compactable ? remote : asTerminalFailure(remote) : classifyFailure?.(context);
|
|
5280
5433
|
if (hostClassification) {
|
|
5281
5434
|
return hostClassification;
|
|
5282
5435
|
}
|
|
@@ -5433,22 +5586,23 @@ var AgentSessionRetryController = class {
|
|
|
5433
5586
|
this.resolvePending();
|
|
5434
5587
|
return;
|
|
5435
5588
|
}
|
|
5436
|
-
const maxRetries = this.retry.maxRetriesForFailure?.(failure) ?? this.retry.maxRetries ?? 3;
|
|
5589
|
+
const maxRetries = Math.min(this.retry.maxRetriesForFailure?.(failure) ?? this.retry.maxRetries ?? 3, failure.kind === "provider_confirmation" ? 1 : Infinity);
|
|
5437
5590
|
const kindAttempts = (this.retryAttempts.get(failure.kind) ?? 0) + 1;
|
|
5438
5591
|
this.retryAttempts.set(failure.kind, kindAttempts);
|
|
5439
5592
|
this.totalRetryAttempts += 1;
|
|
5440
5593
|
const totalBudget = this.retry.maxTotalRetries;
|
|
5441
5594
|
const kindExhausted = kindAttempts > maxRetries;
|
|
5442
5595
|
const totalExhausted = totalBudget !== void 0 && this.totalRetryAttempts > totalBudget;
|
|
5443
|
-
|
|
5444
|
-
|
|
5596
|
+
const delayMs = resolveRetryDelay((this.retry.baseDelayMs ?? 2e3) * 2 ** (kindAttempts - 1), this.retry.maxDelayMs, failure.retryAfterMs);
|
|
5597
|
+
if (kindExhausted || totalExhausted || delayMs === void 0) {
|
|
5598
|
+
const terminalFailure = asTerminalFailure(failure, delayMs === void 0 ? `${failure.message} (Retry-After exceeds the allowed wait; automatic recovery paused)` : failure.message);
|
|
5445
5599
|
if (failure.replayExempt === true) {
|
|
5446
5600
|
const outcome = this.replayCircuit.recordReplayExhaustion();
|
|
5447
5601
|
if (outcome.opened) {
|
|
5448
5602
|
this.emit({ type: "retry_exhausted", errorMessage: `systemic timeout-stall: ${outcome.consecutive} consecutive turns ended by timeout-replay exhaustion`, maxRetries: totalExhausted ? totalBudget : maxRetries });
|
|
5449
5603
|
}
|
|
5450
5604
|
}
|
|
5451
|
-
this.emit({ type: "retry_exhausted", errorMessage:
|
|
5605
|
+
this.emit({ type: "retry_exhausted", errorMessage: terminalFailure.message, maxRetries: totalExhausted ? totalBudget : maxRetries });
|
|
5452
5606
|
this.emit({ type: "turn_failed_terminal", failure: terminalFailure, message });
|
|
5453
5607
|
this.emit({ type: "turn_settled" });
|
|
5454
5608
|
this.resetAttempts();
|
|
@@ -5457,8 +5611,6 @@ var AgentSessionRetryController = class {
|
|
|
5457
5611
|
return;
|
|
5458
5612
|
}
|
|
5459
5613
|
const attempt = kindAttempts;
|
|
5460
|
-
const rawDelayMs = (this.retry.baseDelayMs ?? 2e3) * 2 ** (attempt - 1);
|
|
5461
|
-
const delayMs = this.retry.maxDelayMs !== void 0 ? Math.min(rawDelayMs, this.retry.maxDelayMs) : rawDelayMs;
|
|
5462
5614
|
this.ensurePending();
|
|
5463
5615
|
const epoch = this.retryEpoch;
|
|
5464
5616
|
const pending = this.pendingRetry;
|
|
@@ -5510,7 +5662,7 @@ var AgentSessionRetryController = class {
|
|
|
5510
5662
|
userAbort: false,
|
|
5511
5663
|
terminal: true
|
|
5512
5664
|
});
|
|
5513
|
-
const maxRetries = (failure !== void 0 ? this.retry?.maxRetriesForFailure?.(failure) : void 0) ?? this.retry?.maxRetries ?? 3;
|
|
5665
|
+
const maxRetries = Math.min((failure !== void 0 ? this.retry?.maxRetriesForFailure?.(failure) : void 0) ?? this.retry?.maxRetries ?? 3, failure?.kind === "provider_confirmation" ? 1 : Infinity);
|
|
5514
5666
|
this.emit({ type: "retry_exhausted", errorMessage: terminalFailure.message, maxRetries });
|
|
5515
5667
|
this.emit({ type: "turn_failed_terminal", failure: terminalFailure });
|
|
5516
5668
|
this.emit({ type: "turn_settled" });
|
|
@@ -5597,7 +5749,7 @@ function toolOutcomeKind(event) {
|
|
|
5597
5749
|
return typeof kind === "string" ? kind : void 0;
|
|
5598
5750
|
}
|
|
5599
5751
|
function isCommandNonzero(event) {
|
|
5600
|
-
return
|
|
5752
|
+
return toolOutcomeKind(event) === "command_nonzero";
|
|
5601
5753
|
}
|
|
5602
5754
|
function isStateChangingSuccess(event) {
|
|
5603
5755
|
if (event.isError) return false;
|
|
@@ -5605,7 +5757,7 @@ function isStateChangingSuccess(event) {
|
|
|
5605
5757
|
if (outcomeKind === "command_nonzero" || outcomeKind === "timeout" || outcomeKind === "signal") return false;
|
|
5606
5758
|
if (event.toolName === "edit_file" || event.toolName === "write_file") return true;
|
|
5607
5759
|
const details = event.result && typeof event.result === "object" ? event.result.details : void 0;
|
|
5608
|
-
return
|
|
5760
|
+
return details?.commandClass === "filesystem_write";
|
|
5609
5761
|
}
|
|
5610
5762
|
var AgentSessionRuntimeGuard = class {
|
|
5611
5763
|
constructor(trigger, advise) {
|
|
@@ -6211,8 +6363,9 @@ var AgentSessionController = class {
|
|
|
6211
6363
|
getRuntimeGuardTriggered: () => this.runtimeGuard.triggered,
|
|
6212
6364
|
isStreaming: () => this.isStreaming,
|
|
6213
6365
|
runAutoCompact: async (decision, signal) => {
|
|
6214
|
-
await this.runAutoCompact(decision.reason, decision.willRetry, { signal });
|
|
6215
|
-
}
|
|
6366
|
+
await this.runAutoCompact(decision.reason, decision.willRetry, { signal }).finally(() => signal.aborted ? void 0 : this.pendingEndOperations.wait());
|
|
6367
|
+
},
|
|
6368
|
+
onSettled: () => this.pendingEndOperations.track(() => this.handleAgentEndHook(this.agent.state.messages.slice()))
|
|
6216
6369
|
});
|
|
6217
6370
|
this.autoContinue = new AgentSessionAutoContinueController({
|
|
6218
6371
|
agent: this.agent,
|
|
@@ -7882,14 +8035,22 @@ var SessionTraceSnapshotWriter = class {
|
|
|
7882
8035
|
|
|
7883
8036
|
// ../../packages/agent-framework/src/delegation.ts
|
|
7884
8037
|
var DELEGATION_REPORT_MAX_CHARS = 24e3;
|
|
7885
|
-
var DELEGATION_REPORT_INSTRUCTION = 'Return your final response once as a JSON object: {"summary":string,"result":string,"remainingWork"?:string[],"evidenceRefs"?:string[]}. Do not make an extra tool call to report. Evidence references are your claims, not runtime verification.';
|
|
8038
|
+
var DELEGATION_REPORT_INSTRUCTION = 'Return your final response once as a JSON object: {"summary":string,"result":string,"remainingWork"?:string[],"evidenceRefs"?:string[]}. Keep the entire JSON response within 24,000 characters: give concise conclusions and bounded evidence references, not repeated drafting notes. Do not make an extra tool call to report. Evidence references are your claims, not runtime verification.';
|
|
7886
8039
|
function parseDelegationReport(text, runId, truncated = false) {
|
|
7887
8040
|
const raw = (text ?? "").slice(0, DELEGATION_REPORT_MAX_CHARS);
|
|
7888
8041
|
const base = { version: 1, runId: runId.slice(0, 160), raw };
|
|
7889
|
-
|
|
7890
|
-
|
|
8042
|
+
const stopReason = typeof truncated === "string" ? truncated.slice(0, 40) : void 0;
|
|
8043
|
+
const reason = stopReason === "length" ? "provider_length" : ["error", "aborted", "deadline", "cancelled", "no_terminal"].includes(stopReason ?? "") ? stopReason : stopReason && stopReason !== "stop" ? "no_terminal" : truncated === true ? "provider_length" : void 0;
|
|
8044
|
+
if (reason) return { ...base, stopReason, reason, availability: raw ? "truncated" : "missing" };
|
|
8045
|
+
if ((text?.length ?? 0) > DELEGATION_REPORT_MAX_CHARS) return { ...base, availability: "truncated", reason: "char_cap" };
|
|
8046
|
+
if (!raw.trim()) return { ...base, availability: "missing", reason: "empty" };
|
|
8047
|
+
let value;
|
|
8048
|
+
try {
|
|
8049
|
+
value = JSON.parse(raw);
|
|
8050
|
+
} catch {
|
|
8051
|
+
return { ...base, availability: "malformed", reason: "invalid_json" };
|
|
8052
|
+
}
|
|
7891
8053
|
try {
|
|
7892
|
-
const value = JSON.parse(raw);
|
|
7893
8054
|
if (!value || typeof value !== "object" || Array.isArray(value) || typeof value.summary !== "string" || typeof value.result !== "string") throw new Error();
|
|
7894
8055
|
if (Object.keys(value).some((key) => !["summary", "result", "remainingWork", "evidenceRefs"].includes(key))) throw new Error();
|
|
7895
8056
|
for (const key of ["remainingWork", "evidenceRefs"]) {
|
|
@@ -7897,7 +8058,7 @@ function parseDelegationReport(text, runId, truncated = false) {
|
|
|
7897
8058
|
}
|
|
7898
8059
|
return { ...base, availability: "available", report: value };
|
|
7899
8060
|
} catch {
|
|
7900
|
-
return { ...base, availability: "malformed" };
|
|
8061
|
+
return { ...base, availability: "malformed", reason: "invalid_shape" };
|
|
7901
8062
|
}
|
|
7902
8063
|
}
|
|
7903
8064
|
function observeDelegationPhase(observation, phase, workActivity = true) {
|
|
@@ -7926,6 +8087,58 @@ function observeDelegationTool(observation, event) {
|
|
|
7926
8087
|
if (observation.receipts.length > 32) observation.receipts.shift();
|
|
7927
8088
|
}
|
|
7928
8089
|
|
|
8090
|
+
// ../../packages/shared-headless-capabilities/src/subagent-usage.ts
|
|
8091
|
+
function createSubagentUsage(sessionId, runId) {
|
|
8092
|
+
return { sessionId, runId, attempts: 0, reportedAttempts: 0, unknownAttempts: 0, pendingAttempts: 0, status: "pending", reportedTokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cacheWriteLong: 0, totalTokens: 0 }, estimatedCost: null };
|
|
8093
|
+
}
|
|
8094
|
+
function settleSubagentUsage(usage, settled) {
|
|
8095
|
+
usage.status = !settled || usage.pendingAttempts > 0 ? "pending" : usage.attempts === 0 || usage.reportedAttempts === 0 ? "unknown" : usage.unknownAttempts > 0 ? "partial" : "complete";
|
|
8096
|
+
}
|
|
8097
|
+
function beginSubagentUsage(usage, model) {
|
|
8098
|
+
usage.attempts++;
|
|
8099
|
+
usage.pendingAttempts++;
|
|
8100
|
+
let ended = false;
|
|
8101
|
+
return (message) => {
|
|
8102
|
+
if (ended) return;
|
|
8103
|
+
ended = true;
|
|
8104
|
+
usage.pendingAttempts--;
|
|
8105
|
+
const values = message?.usage;
|
|
8106
|
+
const known = message?.usageReported === true && values && [values.input, values.output, values.cacheRead, values.cacheWrite, values.totalTokens].every((n) => Number.isFinite(n) && n >= 0);
|
|
8107
|
+
if (known) {
|
|
8108
|
+
usage.reportedAttempts++;
|
|
8109
|
+
for (const key of Object.keys(usage.reportedTokens)) usage.reportedTokens[key] += values[key] ?? 0;
|
|
8110
|
+
} else usage.unknownAttempts++;
|
|
8111
|
+
const priced = known && ["input", "output", "cacheRead", "cacheWrite"].every((key) => typeof model?.cost?.[key] === "number" && Number.isFinite(model.cost[key]) && model.cost[key] >= 0) && Object.values(model.cost).some((value) => typeof value === "number" && value > 0) && Number.isFinite(values.cost?.total) && values.cost.total >= 0;
|
|
8112
|
+
if (usage.attempts === 1 && priced) usage.estimatedCost = values.cost.total;
|
|
8113
|
+
else if (priced && usage.estimatedCost !== null) usage.estimatedCost += values.cost.total;
|
|
8114
|
+
else usage.estimatedCost = null;
|
|
8115
|
+
};
|
|
8116
|
+
}
|
|
8117
|
+
function restoreSubagentUsageRuns(value) {
|
|
8118
|
+
if (!Array.isArray(value)) return void 0;
|
|
8119
|
+
return value.slice(-4).flatMap((item) => {
|
|
8120
|
+
if (!item || typeof item !== "object" || typeof item.sessionId !== "string" || typeof item.runId !== "string" || item.sessionId.length > 160 || item.runId.length > 160 || !["pending", "partial", "complete", "unknown"].includes(item.status)) return [];
|
|
8121
|
+
if (![item.attempts, item.reportedAttempts, item.unknownAttempts, item.pendingAttempts].every((n) => Number.isSafeInteger(n) && n >= 0)) return [];
|
|
8122
|
+
if (item.reportedAttempts + item.unknownAttempts + item.pendingAttempts !== item.attempts) return [];
|
|
8123
|
+
const tokens = item.reportedTokens;
|
|
8124
|
+
const keys = ["input", "output", "cacheRead", "cacheWrite", "cacheWriteLong", "totalTokens"];
|
|
8125
|
+
if (!tokens || !keys.every((key) => Number.isFinite(tokens[key]) && tokens[key] >= 0)) return [];
|
|
8126
|
+
const expectedStatus = item.pendingAttempts > 0 ? "pending" : item.reportedAttempts === 0 ? "unknown" : item.unknownAttempts > 0 ? "partial" : "complete";
|
|
8127
|
+
if (item.status !== expectedStatus && !(item.status === "pending" && item.attempts === 0)) return [];
|
|
8128
|
+
return [{
|
|
8129
|
+
sessionId: item.sessionId,
|
|
8130
|
+
runId: item.runId,
|
|
8131
|
+
attempts: item.attempts,
|
|
8132
|
+
reportedAttempts: item.reportedAttempts,
|
|
8133
|
+
unknownAttempts: item.unknownAttempts,
|
|
8134
|
+
pendingAttempts: item.pendingAttempts,
|
|
8135
|
+
status: item.status,
|
|
8136
|
+
reportedTokens: Object.fromEntries(keys.map((key) => [key, tokens[key]])),
|
|
8137
|
+
estimatedCost: item.reportedAttempts > 0 && item.unknownAttempts === 0 && item.pendingAttempts === 0 && typeof item.estimatedCost === "number" && Number.isFinite(item.estimatedCost) && item.estimatedCost >= 0 ? item.estimatedCost : null
|
|
8138
|
+
}];
|
|
8139
|
+
});
|
|
8140
|
+
}
|
|
8141
|
+
|
|
7929
8142
|
// ../../packages/shared-headless-capabilities/src/subagent-stream.ts
|
|
7930
8143
|
var SUBAGENT_FINALIZATION_HINT = "The run's soft deadline has arrived. Do not start new tools. Return the requested final report now, including remaining work; already-running tools may finish.";
|
|
7931
8144
|
function subagentSoftDeadlineMs(budgetMs) {
|
|
@@ -7936,6 +8149,10 @@ function withSubagentRunContext(stream, hooks) {
|
|
|
7936
8149
|
if (hooks.hardDeadlineAt !== void 0 && Date.now() >= hooks.hardDeadlineAt) throw new Error("Subagent hard deadline reached");
|
|
7937
8150
|
if (hooks.softDeadlineAt !== void 0 && Date.now() >= hooks.softDeadlineAt) hooks.finalizing = true;
|
|
7938
8151
|
if (hooks.observation) observeDelegationPhase(hooks.observation, hooks.finalizing ? "finalizing" : "request");
|
|
8152
|
+
try {
|
|
8153
|
+
hooks.onPhase?.(hooks.finalizing ? "finalizing" : "request");
|
|
8154
|
+
} catch {
|
|
8155
|
+
}
|
|
7939
8156
|
return stream(model, hooks.finalizing ? { ...context, systemPrompt: `${context.systemPrompt ?? ""}
|
|
7940
8157
|
|
|
7941
8158
|
${SUBAGENT_FINALIZATION_HINT}` } : context, options);
|
|
@@ -7959,11 +8176,17 @@ function cancellableBackoff(ms, signal) {
|
|
|
7959
8176
|
if (signal?.aborted) finish();
|
|
7960
8177
|
});
|
|
7961
8178
|
}
|
|
7962
|
-
function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivity) {
|
|
8179
|
+
function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivity, hooks) {
|
|
7963
8180
|
return async (model, context, options) => {
|
|
7964
8181
|
for (let attempt = 0; ; attempt++) {
|
|
8182
|
+
if (hooks) {
|
|
8183
|
+
hooks.terminalText = void 0;
|
|
8184
|
+
hooks.terminalStopReason = void 0;
|
|
8185
|
+
}
|
|
7965
8186
|
if (options?.signal?.aborted) throw new Error("Subagent aborted by parent");
|
|
8187
|
+
if (hooks?.hardDeadlineAt !== void 0 && Date.now() >= hooks.hardDeadlineAt) throw new Error("Subagent hard deadline reached");
|
|
7966
8188
|
let message;
|
|
8189
|
+
const finishUsage = hooks?.usage ? beginSubagentUsage(hooks.usage, model) : void 0;
|
|
7967
8190
|
try {
|
|
7968
8191
|
const response = await stream(model, context, options);
|
|
7969
8192
|
let terminal;
|
|
@@ -7976,8 +8199,19 @@ function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivi
|
|
|
7976
8199
|
onActivity?.(next.value.type);
|
|
7977
8200
|
} catch {
|
|
7978
8201
|
}
|
|
7979
|
-
if (next.value.type === "done")
|
|
7980
|
-
|
|
8202
|
+
if (next.value.type === "done") {
|
|
8203
|
+
terminal = next.value.message;
|
|
8204
|
+
finishUsage?.(terminal);
|
|
8205
|
+
}
|
|
8206
|
+
if (next.value.type === "error") {
|
|
8207
|
+
terminal = next.value.error;
|
|
8208
|
+
finishUsage?.(terminal);
|
|
8209
|
+
}
|
|
8210
|
+
if (hooks && (next.value.type === "done" || next.value.type === "error")) {
|
|
8211
|
+
const reportTerminal = terminal && ["stop", "length", "error", "aborted"].includes(terminal.stopReason) && !terminal.content.some((block) => block.type === "toolCall");
|
|
8212
|
+
hooks.terminalStopReason = reportTerminal && terminal ? terminal.stopReason : void 0;
|
|
8213
|
+
hooks.terminalText = reportTerminal && terminal ? terminal.content.filter((block) => block.type === "text").map((block) => block.text).join("\n").slice(0, 24001) : void 0;
|
|
8214
|
+
}
|
|
7981
8215
|
}
|
|
7982
8216
|
} finally {
|
|
7983
8217
|
try {
|
|
@@ -7988,6 +8222,7 @@ function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivi
|
|
|
7988
8222
|
if (!terminal) throw new Error("Provider protocol error: response ended without a terminal event");
|
|
7989
8223
|
message = terminal;
|
|
7990
8224
|
} catch (error) {
|
|
8225
|
+
finishUsage?.();
|
|
7991
8226
|
if (!options?.signal?.aborted && attempt < 2 && isRetryableSubagentFailure(error instanceof Error ? error.message : String(error))) {
|
|
7992
8227
|
try {
|
|
7993
8228
|
onRetry?.();
|
|
@@ -8077,6 +8312,7 @@ function toSessionRecord(session, messages) {
|
|
|
8077
8312
|
runCount: session.runCount,
|
|
8078
8313
|
revision: session.revision,
|
|
8079
8314
|
reports: session.reports?.slice(-4),
|
|
8315
|
+
usageRuns: session.usageRuns?.slice(-4).map((usage) => ({ ...usage, reportedTokens: { ...usage.reportedTokens } })),
|
|
8080
8316
|
messages
|
|
8081
8317
|
};
|
|
8082
8318
|
}
|
|
@@ -8151,6 +8387,7 @@ var POWERSHELL_DESTRUCTIVE_PATTERNS = [
|
|
|
8151
8387
|
{ pattern: /\bRemove-Item\b[^;\n|]*(?:HKLM:|HKCU:|Registry::)/i, warning: "may remove registry entries", commandClass: "infra_destructive" },
|
|
8152
8388
|
{ pattern: /\b(Set-ExecutionPolicy|Start-Process\b[^;\n|]*-Verb\s+RunAs)\b/i, warning: "may change execution policy or request elevation", commandClass: "privilege_escalation" },
|
|
8153
8389
|
{ pattern: /\b(Stop-Computer|Restart-Computer)\b/i, warning: "may stop or restart the machine", commandClass: "infra_destructive" },
|
|
8390
|
+
{ pattern: /(^|[;|&\n])\s*(?:Invoke-Expression|iex)\b/i, warning: "evaluates a dynamically constructed string as code", commandClass: "privilege_escalation" },
|
|
8154
8391
|
{ pattern: /\bgit\s+reset\s+--hard\b/i, warning: "may discard uncommitted changes", commandClass: "git_destructive" },
|
|
8155
8392
|
{ pattern: /\bgit\s+push\b[^;&|\n]*[ \t](--force|--force-with-lease|-f)\b/i, warning: "may overwrite remote history", commandClass: "git_destructive" }
|
|
8156
8393
|
];
|
|
@@ -8569,13 +8806,13 @@ function wrapBashGuard(tool, plan) {
|
|
|
8569
8806
|
return {
|
|
8570
8807
|
...tool,
|
|
8571
8808
|
description: `${tool.description}
|
|
8572
|
-
Subagent guarded mode
|
|
8809
|
+
Subagent guarded mode blocks destructive shell commands while preserving the parent's execution checks. Destructive commands are blocked; normal diagnostic, search, test, and build commands are allowed.`,
|
|
8573
8810
|
execute: async (toolCallId, params, signal, onUpdate) => {
|
|
8574
8811
|
const command = typeof params?.command === "string" ? params.command : "";
|
|
8575
8812
|
const classification = classifyBashCommand(command);
|
|
8576
8813
|
if (classification.destructive) {
|
|
8577
|
-
const reason = `destructive
|
|
8578
|
-
plan.toolsExcluded.push({ name:
|
|
8814
|
+
const reason = `destructive shell command blocked for subagent: ${classification.riskWarnings.join("; ")}`;
|
|
8815
|
+
plan.toolsExcluded.push({ name: tool.name, reason });
|
|
8579
8816
|
return {
|
|
8580
8817
|
content: [{ type: "text", text: reason }],
|
|
8581
8818
|
details: {
|
|
@@ -8603,10 +8840,6 @@ function filterToolsForSubagent(runtimeTools, parentTools, plan, mode) {
|
|
|
8603
8840
|
plan.toolsExcluded.push({ name: tool.name, reason: "not available in parent tool set" });
|
|
8604
8841
|
continue;
|
|
8605
8842
|
}
|
|
8606
|
-
if (tool.name === "bash") {
|
|
8607
|
-
filtered.push(wrapBashGuard(tool, plan));
|
|
8608
|
-
continue;
|
|
8609
|
-
}
|
|
8610
8843
|
if (mode === "edit" && (tool.name === "write_file" || tool.name === "edit_file")) {
|
|
8611
8844
|
filtered.push(tool);
|
|
8612
8845
|
continue;
|
|
@@ -8616,7 +8849,7 @@ function filterToolsForSubagent(runtimeTools, parentTools, plan, mode) {
|
|
|
8616
8849
|
plan.toolsExcluded.push({ name: tool.name, reason: policy.reason || "not allowed for subagent" });
|
|
8617
8850
|
continue;
|
|
8618
8851
|
}
|
|
8619
|
-
filtered.push(tool);
|
|
8852
|
+
filtered.push(getToolCapabilityMetadata(tool)?.executesShell === true ? wrapBashGuard(tool, plan) : tool);
|
|
8620
8853
|
}
|
|
8621
8854
|
return filtered;
|
|
8622
8855
|
}
|
|
@@ -8712,12 +8945,13 @@ function createAgentInstance(deps, systemPrompt, tools, model, apiKey, thinkingL
|
|
|
8712
8945
|
}
|
|
8713
8946
|
}), hooks), deps.transientRetryBackoffMs, () => {
|
|
8714
8947
|
if (hooks.observation) observeDelegationPhase(hooks.observation, "response_retry");
|
|
8948
|
+
hooks.onPhase?.("response_retry");
|
|
8715
8949
|
}, () => {
|
|
8716
8950
|
if (hooks.observation) {
|
|
8717
8951
|
hooks.observation.lastWorkAt = Date.now();
|
|
8718
8952
|
hooks.observation.lastActivity = "response_event";
|
|
8719
8953
|
}
|
|
8720
|
-
});
|
|
8954
|
+
}, hooks);
|
|
8721
8955
|
if (deps.createAgent) {
|
|
8722
8956
|
const agent2 = deps.createAgent({
|
|
8723
8957
|
streamFn,
|
|
@@ -8747,7 +8981,7 @@ function createAgentInstance(deps, systemPrompt, tools, model, apiKey, thinkingL
|
|
|
8747
8981
|
return agent;
|
|
8748
8982
|
}
|
|
8749
8983
|
function isParentHandoffRequired(task) {
|
|
8750
|
-
return task.mode === "edit" || task.status === "timeout" || task.status === "failed" || task.confidenceSignals.some((signal) => signal.type === "tool_error" || signal.type === "no_tool_use" || signal.type === "no_text_report");
|
|
8984
|
+
return task.report?.availability !== "available" || task.mode === "edit" || task.status === "timeout" || task.status === "failed" || task.confidenceSignals.some((signal) => signal.type === "tool_error" || signal.type === "no_tool_use" || signal.type === "no_text_report");
|
|
8751
8985
|
}
|
|
8752
8986
|
function shellFromRecord(record) {
|
|
8753
8987
|
return {
|
|
@@ -8761,7 +8995,12 @@ function shellFromRecord(record) {
|
|
|
8761
8995
|
timeoutSeconds: record.timeoutSeconds,
|
|
8762
8996
|
agent: null,
|
|
8763
8997
|
pendingMessages: record.messages,
|
|
8764
|
-
reports: Array.isArray(record.reports) ? record.reports.slice(-4).map((report) =>
|
|
8998
|
+
reports: Array.isArray(record.reports) ? record.reports.slice(-4).map((report) => {
|
|
8999
|
+
const parsed = parseDelegationReport(typeof report?.raw === "string" ? report.raw : void 0, typeof report?.runId === "string" ? report.runId : "legacy", report?.stopReason ?? report?.availability === "truncated");
|
|
9000
|
+
if (["empty", "invalid_json", "invalid_shape", "char_cap", "provider_length", "error", "aborted", "deadline", "cancelled", "no_terminal"].includes(report?.reason ?? "")) parsed.reason = report.reason;
|
|
9001
|
+
return parsed;
|
|
9002
|
+
}) : void 0,
|
|
9003
|
+
usageRuns: restoreSubagentUsageRuns(record.usageRuns),
|
|
8765
9004
|
createdAt: record.createdAt,
|
|
8766
9005
|
lastUsedAt: record.lastUsedAt,
|
|
8767
9006
|
runCount: record.runCount,
|
|
@@ -8843,7 +9082,7 @@ async function persistSubagentSession(deps, session, task) {
|
|
|
8843
9082
|
if (task.status === "completed" && task.result && transcriptChars > SESSION_TRANSCRIPT_WARN_CHARS) {
|
|
8844
9083
|
task.result += `
|
|
8845
9084
|
|
|
8846
|
-
[Session growth warning: session ${session.id} now carries
|
|
9085
|
+
[Session growth warning: session ${session.id} now carries approximately ${transcriptChars} JSON characters of transcript (not billing tokens); full recall carries that history, report recall excludes it. Cost is unknown. For the next round, consider a fresh subagent with a compact brief instead of recalling.]`;
|
|
8847
9086
|
}
|
|
8848
9087
|
if (!deps.sessionStore) return;
|
|
8849
9088
|
if (transcriptChars > SESSION_RECORD_MAX_CHARS) {
|
|
@@ -8875,6 +9114,13 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
|
|
|
8875
9114
|
let heartbeat;
|
|
8876
9115
|
let session;
|
|
8877
9116
|
let runStarted = false;
|
|
9117
|
+
let runUsage;
|
|
9118
|
+
const shownPhases = /* @__PURE__ */ new Set();
|
|
9119
|
+
const phaseProgress = (phase) => {
|
|
9120
|
+
if (shownPhases.has(phase) || shownPhases.size >= 16) return;
|
|
9121
|
+
shownPhases.add(phase);
|
|
9122
|
+
report({ message: `[${task.name}] Phase: ${phase}` });
|
|
9123
|
+
};
|
|
8878
9124
|
let baseline = 0;
|
|
8879
9125
|
let hardDeadlineAt;
|
|
8880
9126
|
let pendingRun;
|
|
@@ -8883,7 +9129,9 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
|
|
|
8883
9129
|
if (!runStarted || !session?.agent) return;
|
|
8884
9130
|
const last = [...session.agent.state.messages.slice(baseline)].reverse().find((m) => m?.role === "assistant");
|
|
8885
9131
|
const terminal = isReportTerminal(last) ? last : void 0;
|
|
8886
|
-
|
|
9132
|
+
const hooks = agentRunHooks.get(session.agent);
|
|
9133
|
+
const capturedText = terminal && hooks?.terminalStopReason === terminal.stopReason ? hooks?.terminalText : void 0;
|
|
9134
|
+
task.report = parseDelegationReport(terminal ? collectLastAssistantText([terminal]) || capturedText || void 0 : void 0, `${task.id}:${session.runCount + 1}`, terminal?.stopReason ?? (timedOut ? "deadline" : signal?.aborted ? "cancelled" : "no_terminal"));
|
|
8887
9135
|
};
|
|
8888
9136
|
let agentSettled = true;
|
|
8889
9137
|
let releaseLease;
|
|
@@ -8926,7 +9174,12 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
|
|
|
8926
9174
|
}
|
|
8927
9175
|
const nextModelOverride = task.modelOverride ?? session.modelOverride;
|
|
8928
9176
|
const nextThinking = task.thinkingLevel ?? session.thinkingLevel;
|
|
8929
|
-
|
|
9177
|
+
const latestReport = session.reports?.at(-1);
|
|
9178
|
+
if (task.resumeMode === "report" && latestReport?.availability !== "available") throw new Error(`Report recall unavailable: session=${session.id}, run=${latestReport?.runId ?? session.runCount}, availability=${latestReport?.availability ?? "missing"}, reason=${latestReport?.reason ?? "legacy_or_missing"}. Explicitly choose resumeMode:"full" (full history; cost unknown), or spawn a new bounded task. No older report or transcript fallback was used.`);
|
|
9179
|
+
const history = session.agent?.state.messages ?? session.pendingMessages ?? [];
|
|
9180
|
+
const historyChars = task.resumeMode === "report" ? 0 : estimateTranscriptChars(history);
|
|
9181
|
+
task.recallInput = { mode: task.resumeMode ?? "full", messageCount: task.resumeMode === "report" ? 0 : history.length, jsonChars: historyChars, warning: historyChars > SESSION_TRANSCRIPT_WARN_CHARS, cost: "unknown" };
|
|
9182
|
+
report({ message: task.resumeMode === "report" ? `[${task.name}] Report recall: old transcript is not carried into the prompt; cost unknown.` : `[${task.name}] Full recall input: ${task.recallInput.messageCount} historical messages, approximately ${task.recallInput.jsonChars} JSON characters (not billing tokens); cost unknown.${task.recallInput.warning ? " Large history: context risk; consider report recall or a new bounded task." : ""}` });
|
|
8930
9183
|
const mustRebuild = task.resumeMode === "report" || session.agent === null || task.requestedMode !== void 0 || task.modelOverride !== void 0 || task.thinkingLevel !== void 0;
|
|
8931
9184
|
if (!mustRebuild) {
|
|
8932
9185
|
subagent = session.agent;
|
|
@@ -9030,6 +9283,14 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
|
|
|
9030
9283
|
if (sessions) retainLiveSession(sessions, session, Math.max(1, deps.maxLiveSessions ?? DEFAULT_MAX_LIVE_SESSIONS));
|
|
9031
9284
|
if (signal?.aborted || sessions?.disposed) throw new Error("Subagent capability disposed or aborted by parent");
|
|
9032
9285
|
const hooks = agentRunHooks.get(subagent);
|
|
9286
|
+
runUsage = createSubagentUsage(session.id, `${task.id}:${session.runCount + 1}`);
|
|
9287
|
+
(task.usageRuns ??= []).push(runUsage);
|
|
9288
|
+
if (hooks) {
|
|
9289
|
+
hooks.usage = runUsage;
|
|
9290
|
+
hooks.onPhase = phaseProgress;
|
|
9291
|
+
hooks.terminalText = void 0;
|
|
9292
|
+
hooks.terminalStopReason = void 0;
|
|
9293
|
+
}
|
|
9033
9294
|
hardDeadlineAt = Date.now() + runTimeoutSeconds * 1e3;
|
|
9034
9295
|
if (hooks) {
|
|
9035
9296
|
hooks.observation = task.observation;
|
|
@@ -9053,6 +9314,7 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
|
|
|
9053
9314
|
softTimeout = setTimeout(() => {
|
|
9054
9315
|
if (hooks) hooks.finalizing = true;
|
|
9055
9316
|
observeDelegationPhase(task.observation, "finalizing", false);
|
|
9317
|
+
phaseProgress("finalizing");
|
|
9056
9318
|
}, subagentSoftDeadlineMs(runTimeoutSeconds * 1e3));
|
|
9057
9319
|
timeout = setTimeout(() => {
|
|
9058
9320
|
timedOut = true;
|
|
@@ -9156,7 +9418,7 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
|
|
|
9156
9418
|
const terminalStopReason = lastAssistant?.stopReason;
|
|
9157
9419
|
const terminalError = typeof lastAssistant?.errorMessage === "string" ? lastAssistant.errorMessage : void 0;
|
|
9158
9420
|
const collectedResponse = isReportTerminal(lastAssistant) ? collectLastAssistantText([lastAssistant]) || void 0 : void 0;
|
|
9159
|
-
task.report = parseDelegationReport(collectedResponse, `${task.id}:${(session?.runCount ?? 0) + 1}`, terminalStopReason
|
|
9421
|
+
task.report = parseDelegationReport(collectedResponse, `${task.id}:${(session?.runCount ?? 0) + 1}`, terminalStopReason ?? "no_terminal");
|
|
9160
9422
|
const streamInterrupted = terminalStopReason === "error" || terminalStopReason === "aborted" || terminalError || !collectedResponse && terminalStopReason !== "stop" || terminalStopReason === "length" && !messageHasOwnText(lastAssistant);
|
|
9161
9423
|
if (streamInterrupted) {
|
|
9162
9424
|
if (terminalStopReason === "length") {
|
|
@@ -9205,6 +9467,7 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
|
|
|
9205
9467
|
refreshReport();
|
|
9206
9468
|
task.status = timedOut ? "timeout" : signal?.aborted ? "cancelled" : "failed";
|
|
9207
9469
|
observeDelegationPhase(task.observation, timedOut ? "deadline" : signal?.aborted ? "cancelled" : "failed");
|
|
9470
|
+
phaseProgress(task.observation.phase);
|
|
9208
9471
|
task.error = errorMessage.slice(0, 2e3);
|
|
9209
9472
|
task.report ??= parseDelegationReport(void 0, task.id);
|
|
9210
9473
|
task.parentHandoffRequired = isParentHandoffRequired(task);
|
|
@@ -9214,14 +9477,21 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
|
|
|
9214
9477
|
if (heartbeat) {
|
|
9215
9478
|
clearInterval(heartbeat);
|
|
9216
9479
|
}
|
|
9480
|
+
if (runUsage) settleSubagentUsage(runUsage, agentSettled);
|
|
9217
9481
|
const finalize = async () => {
|
|
9482
|
+
if (runUsage) settleSubagentUsage(runUsage, true);
|
|
9218
9483
|
try {
|
|
9219
9484
|
if (session) {
|
|
9220
9485
|
const hooks = session.agent ? agentRunHooks.get(session.agent) : void 0;
|
|
9221
|
-
if (hooks && hooks.observation === task.observation)
|
|
9486
|
+
if (hooks && hooks.observation === task.observation) {
|
|
9487
|
+
hooks.observation = void 0;
|
|
9488
|
+
hooks.onPhase = void 0;
|
|
9489
|
+
hooks.usage = void 0;
|
|
9490
|
+
}
|
|
9222
9491
|
if (runStarted) {
|
|
9223
9492
|
refreshReport();
|
|
9224
9493
|
session.runCount++;
|
|
9494
|
+
if (runUsage) session.usageRuns = [...session.usageRuns ?? [], runUsage].slice(-4);
|
|
9225
9495
|
session.reports = [...session.reports ?? [], task.report ?? parseDelegationReport(void 0, task.id)].slice(-4);
|
|
9226
9496
|
}
|
|
9227
9497
|
session.lastUsedAt = Date.now();
|
|
@@ -9272,7 +9542,9 @@ function buildParallelResult(tasks, totalDuration) {
|
|
|
9272
9542
|
if (task.details) {
|
|
9273
9543
|
resultText += `, ${task.details.durationMs}ms, ${task.details.toolCalls} tools`;
|
|
9274
9544
|
}
|
|
9275
|
-
resultText += `, report=${task.report?.availability ?? "missing"})
|
|
9545
|
+
resultText += `, report=${task.report?.availability ?? "missing"}${task.report?.reason ? ` (${task.report.reason})` : ""})
|
|
9546
|
+
`;
|
|
9547
|
+
for (const usage of task.usageRuns ?? []) resultText += `Child usage ${usage.sessionId}/${usage.runId}: ${usage.status}, ${usage.attempts} attempts (${usage.reportedAttempts} reported, ${usage.unknownAttempts} unknown, ${usage.pendingAttempts} pending); reported token subtotal=${usage.reportedTokens.totalTokens}; estimated cost=${usage.estimatedCost ?? "unknown"}. Not added to parent ledger.
|
|
9276
9548
|
`;
|
|
9277
9549
|
if (task.observation) resultText += `Phase: ${task.observation.phase}; last activity: ${task.observation.lastActivity} at ${task.observation.lastWorkAt}
|
|
9278
9550
|
`;
|
|
@@ -9468,7 +9740,7 @@ Default mode="read" is for investigation, search, review, or triage and cannot e
|
|
|
9468
9740
|
thinkingLevel: Type.Optional(Type.Union([Type.Literal("off"), Type.Literal("minimal"), Type.Literal("low"), Type.Literal("medium"), Type.Literal("high"), Type.Literal("xhigh")], { description: "off|minimal|low|medium|high|xhigh. Default medium; override only when the user asks or for cheap lookups (off)." })),
|
|
9469
9741
|
timeout: Type.Optional(Type.Number({ description: "Seconds. Default 1200; set higher for long tasks." })),
|
|
9470
9742
|
resumeMode: Type.Optional(Type.Union([Type.Literal("full"), Type.Literal("report")], { description: "Default full retains prior transcript. Explicit report rebuilds with current tools/model/credentials and only the latest valid historical report as data plus the new task; fails if unavailable." })),
|
|
9471
|
-
sessionId: Type.Optional(Type.String({ description: "Continue a previous subagent session by id (from an earlier result's Session line);
|
|
9743
|
+
sessionId: Type.Optional(Type.String({ description: "Continue a previous subagent session by id (from an earlier result's Session line); full mode retains historical input with unknown cost. Optional mode/model/thinkingLevel reconfigure it; one task at a time; recalls never replay on failure and must target an idle session; prefer a fresh task for independent reviews." }))
|
|
9472
9744
|
}
|
|
9473
9745
|
// No additionalProperties lock: legacy callers may send task aliases
|
|
9474
9746
|
// (description/prompt/objective); normalizeTaskDefs resolves them at
|
|
@@ -9598,6 +9870,8 @@ ${SPAWN_USAGE_HINT}` }],
|
|
|
9598
9870
|
reportedEvidenceCount: task.reportedEvidence.length,
|
|
9599
9871
|
confidenceSignalCount: task.confidenceSignals.length,
|
|
9600
9872
|
report: task.report,
|
|
9873
|
+
recallInput: task.recallInput,
|
|
9874
|
+
usageRuns: task.usageRuns?.map((usage) => ({ ...usage, reportedTokens: { ...usage.reportedTokens } })),
|
|
9601
9875
|
observation: task.observation,
|
|
9602
9876
|
parentHandoffRequired: task.parentHandoffRequired,
|
|
9603
9877
|
failureReason: task.error,
|
|
@@ -10945,7 +11219,7 @@ function createReadTool(cwd, options) {
|
|
|
10945
11219
|
label: "read",
|
|
10946
11220
|
description: `Read a file's contents. Use this before editing, and use offset/limit for large files, or tail=N to read the last N lines when the line count is unknown. Images are attached for the current model when it supports vision; otherwise a configured file-understanding service analyzes them. Video, audio, and documents require a configured understanding service. Pass question to specify what to focus on; it does not force a separate model request. Example: read_file path='Scripts/PlayerController.cs' or read_file path='Data/config.json'. ${pathResolutionDescription} For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). When you need the full file, continue with a larger offset until complete.`,
|
|
10947
11221
|
parameters: readSchema,
|
|
10948
|
-
execute: async (_toolCallId, params, signal) => {
|
|
11222
|
+
execute: async (_toolCallId, params, signal, _onUpdate) => {
|
|
10949
11223
|
const { path: path3, root, question } = params;
|
|
10950
11224
|
const tail = params.tail;
|
|
10951
11225
|
let offset = params.offset;
|
|
@@ -10972,7 +11246,7 @@ function createReadTool(cwd, options) {
|
|
|
10972
11246
|
const message = error instanceof Error ? error.message : String(error);
|
|
10973
11247
|
if (/outside sandbox|outside root|escapes sandbox/i.test(message)) {
|
|
10974
11248
|
return createReadErrorResult(
|
|
10975
|
-
`${message}. read_file is workspace-scoped; for intentional runtime logs or temporary files outside the workspace, use
|
|
11249
|
+
`${message}. read_file is workspace-scoped; for intentional runtime logs or temporary files outside the workspace, use the shell tool with a bounded command such as head, tail, sed -n, or cat on that path.`
|
|
10976
11250
|
);
|
|
10977
11251
|
}
|
|
10978
11252
|
return createReadErrorResult(message);
|
|
@@ -16881,7 +17155,7 @@ function unavailableResult(query, requestedMode, reason) {
|
|
|
16881
17155
|
text: [
|
|
16882
17156
|
`Web search is unavailable for "${query}".`,
|
|
16883
17157
|
reason,
|
|
16884
|
-
"Do not use
|
|
17158
|
+
"Do not use shell, curl, Python, or DuckDuckGo scraping as a fallback; report that web_search is unavailable or ask the user to configure a provider that supports native web search."
|
|
16885
17159
|
].join("\n")
|
|
16886
17160
|
}],
|
|
16887
17161
|
details: {
|
|
@@ -17389,7 +17663,7 @@ function createSharedWebSearchTool(deps) {
|
|
|
17389
17663
|
return {
|
|
17390
17664
|
name: "web_search",
|
|
17391
17665
|
label: "web_search",
|
|
17392
|
-
description: "Search the public web using a provider-native web search capability. Use for current information, online research, or documentation outside the local workspace. Results include sources. Do not use
|
|
17666
|
+
description: "Search the public web using a provider-native web search capability. Use for current information, online research, or documentation outside the local workspace. Results include sources. Do not use shell/curl/python as a web-search fallback. Only set domain filters when the user explicitly asks to limit sources.",
|
|
17393
17667
|
parameters: webSearchSchema,
|
|
17394
17668
|
execute: async (_toolCallId, rawParams, signal) => {
|
|
17395
17669
|
const input = rawParams;
|
|
@@ -17980,7 +18254,7 @@ function createSharedWebFetchTool(deps = {}) {
|
|
|
17980
18254
|
return {
|
|
17981
18255
|
name: "web_fetch",
|
|
17982
18256
|
label: "web_fetch",
|
|
17983
|
-
description: "Fetch and read a specific public web URL. Use web_search to discover sources, then web_fetch to inspect a chosen URL. Do not use
|
|
18257
|
+
description: "Fetch and read a specific public web URL. Use web_search to discover sources, then web_fetch to inspect a chosen URL. Do not use shell/curl/Python as a fallback for web page fetching.",
|
|
17984
18258
|
parameters: webFetchSchema,
|
|
17985
18259
|
capabilityMetadata: {
|
|
17986
18260
|
riskClass: "network",
|
|
@@ -18604,7 +18878,7 @@ function createSharedWebResearchTool(deps) {
|
|
|
18604
18878
|
return {
|
|
18605
18879
|
name: "web_research",
|
|
18606
18880
|
label: "web_research",
|
|
18607
|
-
description: "Canonical web tool. For normal research, pass query. To read a known page, pass url. For low-level search results only, pass query with mode='search'. Use for current information, official docs, news, releases, issues, and webpage summaries. Do not use
|
|
18881
|
+
description: "Canonical web tool. For normal research, pass query. To read a known page, pass url. For low-level search results only, pass query with mode='search'. Use for current information, official docs, news, releases, issues, and webpage summaries. Do not use shell/curl/Python as a web fallback.",
|
|
18608
18882
|
parameters: webResearchSchema,
|
|
18609
18883
|
capabilityMetadata: {
|
|
18610
18884
|
riskClass: "network",
|
|
@@ -19175,6 +19449,8 @@ export {
|
|
|
19175
19449
|
findFirstKeptEntrySummaryId,
|
|
19176
19450
|
createCompactionSummary,
|
|
19177
19451
|
generateEntryId,
|
|
19452
|
+
createHostEnvelopeProjectorRegistry,
|
|
19453
|
+
projectHostEnvelopes,
|
|
19178
19454
|
FileSessionStore,
|
|
19179
19455
|
createSessionManager,
|
|
19180
19456
|
prepareSessionCompaction,
|
|
@@ -19215,6 +19491,7 @@ export {
|
|
|
19215
19491
|
supersedeExecutionState,
|
|
19216
19492
|
shouldPreserveExecutionStateForUserText,
|
|
19217
19493
|
BUILTIN_TOOL_CAPABILITY_METADATA,
|
|
19494
|
+
getToolCapabilityMetadata,
|
|
19218
19495
|
isToolAllowedInPlanMode,
|
|
19219
19496
|
extractPlanTodoItems,
|
|
19220
19497
|
isPlanModeSafeCommand,
|