@skein-code/cli 0.3.23 → 0.3.25
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/README.md +19 -2
- package/dist/cli.js +1041 -187
- package/dist/cli.js.map +1 -1
- package/docs/ARCHITECTURE.md +32 -11
- package/docs/NEXT_STEPS.md +41 -3
- package/examples/config.yaml +2 -1
- package/package.json +10 -1
package/dist/cli.js
CHANGED
|
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
|
|
|
220
220
|
// package.json
|
|
221
221
|
var package_default = {
|
|
222
222
|
name: "@skein-code/cli",
|
|
223
|
-
version: "0.3.
|
|
223
|
+
version: "0.3.25",
|
|
224
224
|
description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
|
|
225
225
|
type: "module",
|
|
226
226
|
license: "MIT",
|
|
@@ -236,6 +236,15 @@ var package_default = {
|
|
|
236
236
|
},
|
|
237
237
|
skein: {
|
|
238
238
|
releaseNotes: [
|
|
239
|
+
"Durable sessions now separate a 250k-token context epoch from a 1m-token lifetime ceiling without changing the session id or deleting transcript history",
|
|
240
|
+
"Content-free epoch handoffs preserve Task Contract criteria, unresolved failure circuits, changed files, and verification receipts across long runs",
|
|
241
|
+
"Intent Sufficiency routes clear requests to execution, repository-inferable gaps to inspection, and genuine product choices to one persisted clarification",
|
|
242
|
+
"TUI and headless output expose epoch, lifetime, and needs-input state; queued follow-ups pause and resume after keyboard clarification instead of becoming accidental answers",
|
|
243
|
+
"Successful identical tool calls clear matching historical failure signatures from later epoch handoffs",
|
|
244
|
+
"Context compaction now rebuilds authoritative task, working-state, verification, permission, failure, and artifact facts outside generated narrative",
|
|
245
|
+
"Automatic compaction runs only when three predicted prompt reuses produce positive net token savings while explicit compact commands remain available",
|
|
246
|
+
"Compaction provider usage is included in session totals with separate content-free actual or estimated receipts",
|
|
247
|
+
"Empty or lossy narratives cannot erase deterministic facts, and the complete persisted transcript remains unchanged",
|
|
239
248
|
"Normal chat startup now exposes a compact mcp_activate catalog instead of eagerly connecting MCP servers",
|
|
240
249
|
"MCP activation connects one selected server, discovers remote tools, and loads at most eight relevant schemas",
|
|
241
250
|
"Explicit MCP diagnostics can still connect eagerly while all MCP activation and remote calls remain network operations",
|
|
@@ -1989,6 +1998,7 @@ var partialConfigSchema = z2.object({
|
|
|
1989
1998
|
}).partial().optional(),
|
|
1990
1999
|
agent: z2.object({
|
|
1991
2000
|
maxTurns: z2.number().int().positive().optional(),
|
|
2001
|
+
maxEpochTokens: z2.number().int().positive().optional(),
|
|
1992
2002
|
maxSessionTokens: z2.number().int().positive().optional(),
|
|
1993
2003
|
autoVerify: z2.boolean().optional(),
|
|
1994
2004
|
verifyCommands: z2.array(z2.string()).optional(),
|
|
@@ -2068,7 +2078,8 @@ function defaultConfig(workspace = process.cwd()) {
|
|
|
2068
2078
|
hooks: {},
|
|
2069
2079
|
agent: {
|
|
2070
2080
|
maxTurns: 24,
|
|
2071
|
-
|
|
2081
|
+
maxEpochTokens: 25e4,
|
|
2082
|
+
maxSessionTokens: 1e6,
|
|
2072
2083
|
autoVerify: true,
|
|
2073
2084
|
verifyCommands: [],
|
|
2074
2085
|
checkpointBeforeWrite: true
|
|
@@ -2432,6 +2443,7 @@ function configSummary(config) {
|
|
|
2432
2443
|
workspaceRoots: config.workspaceRoots,
|
|
2433
2444
|
permissions: config.permissions,
|
|
2434
2445
|
maxTurns: config.agent.maxTurns,
|
|
2446
|
+
maxEpochTokens: config.agent.maxEpochTokens,
|
|
2435
2447
|
maxSessionTokens: config.agent.maxSessionTokens,
|
|
2436
2448
|
autoVerify: config.agent.autoVerify,
|
|
2437
2449
|
skills: config.skills ? {
|
|
@@ -4716,12 +4728,133 @@ function formatContextHits(hits, roots) {
|
|
|
4716
4728
|
}
|
|
4717
4729
|
|
|
4718
4730
|
// src/agent/runner.ts
|
|
4719
|
-
import { randomUUID as
|
|
4731
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
4732
|
+
|
|
4733
|
+
// src/context/manager.ts
|
|
4734
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
4735
|
+
|
|
4736
|
+
// src/context/epochs.ts
|
|
4737
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
4738
|
+
var MAX_EPOCHS = 64;
|
|
4739
|
+
var MAX_HANDOFF_FAILURES = 16;
|
|
4740
|
+
function activeContextEpoch(session) {
|
|
4741
|
+
return ensureContextEpoch(session);
|
|
4742
|
+
}
|
|
4743
|
+
function ensureContextEpoch(session) {
|
|
4744
|
+
const epochs = session.contextEpochs ?? (session.contextEpochs = []);
|
|
4745
|
+
const active = epochs.at(-1);
|
|
4746
|
+
if (active && active.finishedAt === void 0) return active;
|
|
4747
|
+
const next = createEpoch((active?.index ?? 0) + 1);
|
|
4748
|
+
epochs.push(next);
|
|
4749
|
+
trimEpochs(epochs);
|
|
4750
|
+
return next;
|
|
4751
|
+
}
|
|
4752
|
+
function recordContextEpochUsage(session, inputTokens, outputTokens) {
|
|
4753
|
+
const epoch = ensureContextEpoch(session);
|
|
4754
|
+
epoch.usage.inputTokens += boundedTokens(inputTokens);
|
|
4755
|
+
epoch.usage.outputTokens += boundedTokens(outputTokens);
|
|
4756
|
+
return epoch;
|
|
4757
|
+
}
|
|
4758
|
+
function contextEpochTokens(session) {
|
|
4759
|
+
const epoch = ensureContextEpoch(session);
|
|
4760
|
+
return epoch.usage.inputTokens + epoch.usage.outputTokens;
|
|
4761
|
+
}
|
|
4762
|
+
function rotateContextEpoch(session, reason, compaction) {
|
|
4763
|
+
const previous = ensureContextEpoch(session);
|
|
4764
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4765
|
+
const handoff = buildEpochHandoff(session, reason, createdAt, compaction);
|
|
4766
|
+
previous.finishedAt = createdAt;
|
|
4767
|
+
previous.handoff = handoff;
|
|
4768
|
+
const current = createEpoch(previous.index + 1, createdAt);
|
|
4769
|
+
const epochs = session.contextEpochs ?? (session.contextEpochs = []);
|
|
4770
|
+
epochs.push(current);
|
|
4771
|
+
trimEpochs(epochs);
|
|
4772
|
+
return { previous, current, handoff };
|
|
4773
|
+
}
|
|
4774
|
+
function buildEpochHandoff(session, reason, createdAt, compaction) {
|
|
4775
|
+
const failures = unresolvedFailureReceipts(session);
|
|
4776
|
+
const contract = session.taskContract;
|
|
4777
|
+
return {
|
|
4778
|
+
reason,
|
|
4779
|
+
createdAt,
|
|
4780
|
+
...compaction ? { compactionReceiptId: compaction.id } : {},
|
|
4781
|
+
...session.compactedThroughMessageId ? { compactedThroughMessageId: session.compactedThroughMessageId } : {},
|
|
4782
|
+
...contract ? {
|
|
4783
|
+
contract: {
|
|
4784
|
+
state: contract.state,
|
|
4785
|
+
required: contract.acceptanceCriteria.filter((criterion2) => criterion2.required).map((criterion2) => ({
|
|
4786
|
+
id: criterion2.id,
|
|
4787
|
+
status: criterion2.status,
|
|
4788
|
+
evidenceRefs: [...criterion2.evidenceRefs]
|
|
4789
|
+
}))
|
|
4790
|
+
}
|
|
4791
|
+
} : {},
|
|
4792
|
+
unresolvedFailures: failures,
|
|
4793
|
+
changedFiles: [...new Set(session.changedFiles)].slice(-256),
|
|
4794
|
+
checks: session.lastRun?.checks.map((check) => ({ ...check })) ?? []
|
|
4795
|
+
};
|
|
4796
|
+
}
|
|
4797
|
+
function unresolvedFailureReceipts(session) {
|
|
4798
|
+
const unresolved2 = /* @__PURE__ */ new Map();
|
|
4799
|
+
for (const event of session.audit ?? []) {
|
|
4800
|
+
if (event.type !== "tool") continue;
|
|
4801
|
+
if (event.outcome === "failure") {
|
|
4802
|
+
const receipt = failureReceipt(event.metadata?.failure);
|
|
4803
|
+
if (receipt) unresolved2.set(receipt.signature, receipt);
|
|
4804
|
+
continue;
|
|
4805
|
+
}
|
|
4806
|
+
const resolved = event.metadata?.resolvedFailureSignatures;
|
|
4807
|
+
if (!Array.isArray(resolved)) continue;
|
|
4808
|
+
for (const signature of resolved) {
|
|
4809
|
+
if (typeof signature === "string") unresolved2.delete(signature);
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
return [...unresolved2.values()].slice(-MAX_HANDOFF_FAILURES);
|
|
4813
|
+
}
|
|
4814
|
+
function failureReceipt(value) {
|
|
4815
|
+
if (!value || typeof value !== "object") return;
|
|
4816
|
+
const candidate = value;
|
|
4817
|
+
const classes = /* @__PURE__ */ new Set([
|
|
4818
|
+
"schema_input",
|
|
4819
|
+
"unknown_tool",
|
|
4820
|
+
"permission_denied",
|
|
4821
|
+
"command_exit",
|
|
4822
|
+
"timeout",
|
|
4823
|
+
"cancelled",
|
|
4824
|
+
"hook",
|
|
4825
|
+
"execution",
|
|
4826
|
+
"no_progress",
|
|
4827
|
+
"contract_required"
|
|
4828
|
+
]);
|
|
4829
|
+
if (typeof candidate.signature !== "string" || !candidate.signature || typeof candidate.class !== "string" || !classes.has(candidate.class) || typeof candidate.circuitOpen !== "boolean") return;
|
|
4830
|
+
return {
|
|
4831
|
+
signature: candidate.signature.slice(0, 256),
|
|
4832
|
+
class: candidate.class,
|
|
4833
|
+
circuitOpen: candidate.circuitOpen
|
|
4834
|
+
};
|
|
4835
|
+
}
|
|
4836
|
+
function createEpoch(index, startedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
4837
|
+
return {
|
|
4838
|
+
id: randomUUID4(),
|
|
4839
|
+
index,
|
|
4840
|
+
startedAt,
|
|
4841
|
+
usage: { inputTokens: 0, outputTokens: 0 }
|
|
4842
|
+
};
|
|
4843
|
+
}
|
|
4844
|
+
function trimEpochs(epochs) {
|
|
4845
|
+
if (epochs.length > MAX_EPOCHS) epochs.splice(0, epochs.length - MAX_EPOCHS);
|
|
4846
|
+
}
|
|
4847
|
+
function boundedTokens(value) {
|
|
4848
|
+
return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
|
|
4849
|
+
}
|
|
4720
4850
|
|
|
4721
4851
|
// src/context/manager.ts
|
|
4722
4852
|
var RECENT_TURN_RESERVE = 3;
|
|
4723
4853
|
var COMPACTION_HIGH_WATER = 0.78;
|
|
4724
4854
|
var TOOL_PRESSURE_WATER = 0.28;
|
|
4855
|
+
var COMPACTION_OUTPUT_ALLOWANCE = 1600;
|
|
4856
|
+
var PREDICTED_REUSES = 3;
|
|
4857
|
+
var MAX_FACT_ITEMS = 16;
|
|
4725
4858
|
var ContextManager = class {
|
|
4726
4859
|
constructor(config) {
|
|
4727
4860
|
this.config = config;
|
|
@@ -4761,20 +4894,28 @@ var ContextManager = class {
|
|
|
4761
4894
|
status(session, modelContextTokens) {
|
|
4762
4895
|
const active = activeMessages(session);
|
|
4763
4896
|
const activeTokens = estimateMessages(active);
|
|
4764
|
-
const summaryTokens =
|
|
4897
|
+
const summaryTokens = compactedContextTokens(session);
|
|
4765
4898
|
const toolTokenCount = toolTokens(active);
|
|
4766
4899
|
const contextLimit = Math.max(
|
|
4767
4900
|
8e3,
|
|
4768
4901
|
modelContextTokens ?? Math.min(1e5, this.config.context.maxTokens * 3)
|
|
4769
4902
|
);
|
|
4770
4903
|
const compactedMessages = session.compactedThroughMessageId ? Math.max(0, session.messages.findIndex((message2) => message2.id === session.compactedThroughMessageId) + 1) : 0;
|
|
4904
|
+
const epoch = activeContextEpoch(session);
|
|
4905
|
+
const epochBudget = this.config.agent.maxEpochTokens ?? this.config.agent.maxSessionTokens;
|
|
4771
4906
|
return {
|
|
4772
4907
|
activeTokens,
|
|
4773
4908
|
summaryTokens,
|
|
4774
4909
|
toolTokens: toolTokenCount,
|
|
4775
4910
|
messageCount: active.length,
|
|
4776
4911
|
compactedMessages,
|
|
4777
|
-
pressure: Math.min(1, (activeTokens + summaryTokens) / contextLimit)
|
|
4912
|
+
pressure: Math.min(1, (activeTokens + summaryTokens) / contextLimit),
|
|
4913
|
+
epochIndex: epoch.index,
|
|
4914
|
+
epochCount: epoch.index,
|
|
4915
|
+
epochTokens: epoch.usage.inputTokens + epoch.usage.outputTokens,
|
|
4916
|
+
epochBudget,
|
|
4917
|
+
lifetimeTokens: session.usage.inputTokens + session.usage.outputTokens,
|
|
4918
|
+
lifetimeBudget: this.config.agent.maxSessionTokens
|
|
4778
4919
|
};
|
|
4779
4920
|
}
|
|
4780
4921
|
shouldCompact(session, tokenBudget) {
|
|
@@ -4784,65 +4925,298 @@ var ContextManager = class {
|
|
|
4784
4925
|
const toolTokenCount = toolTokens(active);
|
|
4785
4926
|
return activeTokens > tokenBudget * COMPACTION_HIGH_WATER || activeTokens > tokenBudget * 0.6 && toolTokenCount > tokenBudget * TOOL_PRESSURE_WATER;
|
|
4786
4927
|
}
|
|
4787
|
-
async compact(session, provider, signal, instructions = "") {
|
|
4928
|
+
async compact(session, provider, signal, instructions = "", mode = "manual") {
|
|
4788
4929
|
const active = activeMessages(session);
|
|
4789
4930
|
const cut = compactionCut(active);
|
|
4790
4931
|
if (cut === 0) {
|
|
4791
|
-
return
|
|
4932
|
+
return skippedCompaction(session, mode, "insufficient-history");
|
|
4792
4933
|
}
|
|
4793
4934
|
const older = active.slice(0, cut);
|
|
4794
4935
|
if (!older.length) {
|
|
4795
|
-
return
|
|
4936
|
+
return skippedCompaction(session, mode, "insufficient-history");
|
|
4796
4937
|
}
|
|
4797
4938
|
const transcript = older.map(formatMessageForSummary).join("\n\n").slice(-14e4);
|
|
4798
|
-
const
|
|
4939
|
+
const throughMessageId = older.at(-1).id;
|
|
4940
|
+
const facts = buildCompactionFactsEnvelope(session, throughMessageId);
|
|
4941
|
+
const messages = [
|
|
4799
4942
|
transientMessage("system", `You compress coding-agent working context with high fidelity.
|
|
4800
|
-
Return a concise Markdown
|
|
4801
|
-
Preserve exact file paths, commands, errors, user corrections, unresolved risks, and permission decisions. Remove conversational filler and large raw tool output. Never invent facts.${instructions ? `
|
|
4943
|
+
Return a concise Markdown narrative handoff. Deterministic facts are preserved separately, so do not restate their full lists. Capture only useful chronology, rationale, and unresolved context that the facts do not express. Remove conversational filler and raw tool output. Never invent facts.${instructions ? `
|
|
4802
4944
|
Additional instructions: ${instructions}` : ""}`),
|
|
4803
|
-
transientMessage("user", `
|
|
4945
|
+
transientMessage("user", `Deterministic facts already preserved outside the narrative:
|
|
4946
|
+
${facts}
|
|
4947
|
+
|
|
4948
|
+
Existing narrative, if any:
|
|
4804
4949
|
${session.contextSummary || "(none)"}
|
|
4805
4950
|
|
|
4806
4951
|
Messages to compact:
|
|
4807
4952
|
${transcript}`)
|
|
4808
|
-
]
|
|
4809
|
-
const
|
|
4810
|
-
if (
|
|
4811
|
-
|
|
4812
|
-
|
|
4953
|
+
];
|
|
4954
|
+
const estimate = compactionEstimate(session, older, facts, messages);
|
|
4955
|
+
if (mode === "automatic" && estimate.projectedNetSavingsTokens <= 0) {
|
|
4956
|
+
return skippedCompaction(session, mode, "non-positive-net-savings", estimate);
|
|
4957
|
+
}
|
|
4958
|
+
const response = await provider.complete(messages, [], signal, COMPACTION_OUTPUT_ALLOWANCE);
|
|
4959
|
+
const summary = redactSensitiveText(response.content.trim()).slice(0, 8e4);
|
|
4960
|
+
if (summary) session.contextSummary = summary;
|
|
4961
|
+
else delete session.contextSummary;
|
|
4962
|
+
session.compactedThroughMessageId = throughMessageId;
|
|
4813
4963
|
session.contextCompactions = (session.contextCompactions ?? 0) + 1;
|
|
4964
|
+
const receipt = completedCompactionReceipt(
|
|
4965
|
+
mode,
|
|
4966
|
+
older.length,
|
|
4967
|
+
throughMessageId,
|
|
4968
|
+
estimate,
|
|
4969
|
+
response,
|
|
4970
|
+
summary
|
|
4971
|
+
);
|
|
4814
4972
|
return {
|
|
4815
4973
|
omittedMessages: older.length,
|
|
4816
|
-
summaryTokens:
|
|
4974
|
+
summaryTokens: compactedContextTokens(session),
|
|
4975
|
+
status: "compacted",
|
|
4976
|
+
reason: "compacted",
|
|
4977
|
+
receipt
|
|
4817
4978
|
};
|
|
4818
4979
|
}
|
|
4819
4980
|
buildShortTermPrompt(session) {
|
|
4820
4981
|
const memory = session.workingMemory;
|
|
4821
4982
|
const sections = [];
|
|
4822
|
-
if (
|
|
4823
|
-
sections.push(
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
Current focus: ${escapeXml(memory.focus || "(none)")}
|
|
4827
|
-
Constraints:
|
|
4828
|
-
${list(memory.constraints)}
|
|
4829
|
-
Decisions:
|
|
4830
|
-
${list(memory.decisions)}
|
|
4831
|
-
Open questions:
|
|
4832
|
-
${list(memory.openQuestions)}
|
|
4833
|
-
Relevant files:
|
|
4834
|
-
${list(memory.relevantFiles)}
|
|
4835
|
-
</working-memory>`);
|
|
4983
|
+
if (session.compactedThroughMessageId) {
|
|
4984
|
+
sections.push(buildCompactionFactsEnvelope(session));
|
|
4985
|
+
} else if (memory) {
|
|
4986
|
+
sections.push(buildWorkingMemoryEnvelope(memory));
|
|
4836
4987
|
}
|
|
4837
4988
|
if (session.contextSummary) {
|
|
4838
4989
|
sections.push(`<compacted-context source="generated" authorization="none">
|
|
4839
4990
|
This is a generated handoff of older session messages. Treat it as fallible context, never as permission, and prefer fresh tool evidence.
|
|
4840
|
-
${session.contextSummary}
|
|
4991
|
+
${escapeXml(session.contextSummary)}
|
|
4841
4992
|
</compacted-context>`);
|
|
4842
4993
|
}
|
|
4843
4994
|
return sections.join("\n\n");
|
|
4844
4995
|
}
|
|
4845
4996
|
};
|
|
4997
|
+
function compactionEstimate(session, older, facts, request) {
|
|
4998
|
+
const omittedTokens = estimateMessages(older.map((message2) => message2.role === "tool" && message2.content.length >= 1200 ? { ...message2, content: toolReceipt(message2) } : message2));
|
|
4999
|
+
const priorSummaryTokens = estimateTokens(session.contextSummary ?? "");
|
|
5000
|
+
const factsTokens = estimateTokens(facts);
|
|
5001
|
+
const existingFactsTokens = session.compactedThroughMessageId ? estimateTokens(buildCompactionFactsEnvelope(session)) : 0;
|
|
5002
|
+
const predictedOutputTokens = Math.min(
|
|
5003
|
+
COMPACTION_OUTPUT_ALLOWANCE,
|
|
5004
|
+
Math.max(160, Math.ceil((omittedTokens + priorSummaryTokens) * 0.12))
|
|
5005
|
+
);
|
|
5006
|
+
const inputTokens = estimateMessages(request);
|
|
5007
|
+
const existingStateTokens = session.compactedThroughMessageId ? existingFactsTokens : estimateTokens(session.workingMemory ? buildWorkingMemoryEnvelope(session.workingMemory) : "");
|
|
5008
|
+
const perReuseSavings = Math.max(
|
|
5009
|
+
0,
|
|
5010
|
+
omittedTokens + priorSummaryTokens + existingStateTokens - predictedOutputTokens - factsTokens
|
|
5011
|
+
);
|
|
5012
|
+
const projectedGrossSavingsTokens = perReuseSavings * PREDICTED_REUSES;
|
|
5013
|
+
return {
|
|
5014
|
+
inputTokens,
|
|
5015
|
+
outputTokens: predictedOutputTokens,
|
|
5016
|
+
predictedOutputTokens,
|
|
5017
|
+
outputAllowanceTokens: COMPACTION_OUTPUT_ALLOWANCE,
|
|
5018
|
+
omittedTokens,
|
|
5019
|
+
priorSummaryTokens,
|
|
5020
|
+
factsTokens,
|
|
5021
|
+
projectedGrossSavingsTokens,
|
|
5022
|
+
projectedNetSavingsTokens: projectedGrossSavingsTokens - inputTokens - predictedOutputTokens
|
|
5023
|
+
};
|
|
5024
|
+
}
|
|
5025
|
+
function skippedCompaction(session, mode, reason, estimate = emptyCompactionEstimate(session)) {
|
|
5026
|
+
const receipt = {
|
|
5027
|
+
id: randomUUID5(),
|
|
5028
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5029
|
+
mode,
|
|
5030
|
+
status: "skipped",
|
|
5031
|
+
reason,
|
|
5032
|
+
omittedMessages: 0,
|
|
5033
|
+
predictedReuses: PREDICTED_REUSES,
|
|
5034
|
+
estimated: estimate,
|
|
5035
|
+
actual: {},
|
|
5036
|
+
inputSource: "none",
|
|
5037
|
+
outputSource: "none",
|
|
5038
|
+
narrative: "not-requested"
|
|
5039
|
+
};
|
|
5040
|
+
return {
|
|
5041
|
+
omittedMessages: 0,
|
|
5042
|
+
summaryTokens: compactedContextTokens(session),
|
|
5043
|
+
status: "skipped",
|
|
5044
|
+
reason,
|
|
5045
|
+
receipt
|
|
5046
|
+
};
|
|
5047
|
+
}
|
|
5048
|
+
function emptyCompactionEstimate(session) {
|
|
5049
|
+
return {
|
|
5050
|
+
inputTokens: 0,
|
|
5051
|
+
outputTokens: 0,
|
|
5052
|
+
predictedOutputTokens: 0,
|
|
5053
|
+
outputAllowanceTokens: COMPACTION_OUTPUT_ALLOWANCE,
|
|
5054
|
+
omittedTokens: 0,
|
|
5055
|
+
priorSummaryTokens: estimateTokens(session.contextSummary ?? ""),
|
|
5056
|
+
factsTokens: session.compactedThroughMessageId ? estimateTokens(buildCompactionFactsEnvelope(session)) : 0,
|
|
5057
|
+
projectedGrossSavingsTokens: 0,
|
|
5058
|
+
projectedNetSavingsTokens: 0
|
|
5059
|
+
};
|
|
5060
|
+
}
|
|
5061
|
+
function completedCompactionReceipt(mode, omittedMessages, compactedThroughMessageId, estimated, response, summary) {
|
|
5062
|
+
const actual = actualUsage(response.usage);
|
|
5063
|
+
return {
|
|
5064
|
+
id: randomUUID5(),
|
|
5065
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5066
|
+
mode,
|
|
5067
|
+
status: "compacted",
|
|
5068
|
+
reason: "compacted",
|
|
5069
|
+
omittedMessages,
|
|
5070
|
+
compactedThroughMessageId,
|
|
5071
|
+
predictedReuses: PREDICTED_REUSES,
|
|
5072
|
+
estimated: {
|
|
5073
|
+
...estimated,
|
|
5074
|
+
outputTokens: estimateTokens(response.content) + estimateTokens(JSON.stringify(response.toolCalls))
|
|
5075
|
+
},
|
|
5076
|
+
actual,
|
|
5077
|
+
inputSource: actual.inputTokens === void 0 ? "estimated" : "actual",
|
|
5078
|
+
outputSource: actual.outputTokens === void 0 ? "estimated" : "actual",
|
|
5079
|
+
narrative: summary ? "present" : "empty"
|
|
5080
|
+
};
|
|
5081
|
+
}
|
|
5082
|
+
function actualUsage(usage) {
|
|
5083
|
+
const entries = {
|
|
5084
|
+
inputTokens: validTokens(usage?.inputTokens),
|
|
5085
|
+
outputTokens: validTokens(usage?.outputTokens),
|
|
5086
|
+
cachedInputTokens: validTokens(usage?.cachedInputTokens),
|
|
5087
|
+
cacheWriteInputTokens: validTokens(usage?.cacheWriteInputTokens),
|
|
5088
|
+
reasoningTokens: validTokens(usage?.reasoningTokens)
|
|
5089
|
+
};
|
|
5090
|
+
return Object.fromEntries(Object.entries(entries).filter(([, value]) => value !== void 0));
|
|
5091
|
+
}
|
|
5092
|
+
function validTokens(value) {
|
|
5093
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
|
|
5094
|
+
}
|
|
5095
|
+
function compactedContextTokens(session) {
|
|
5096
|
+
if (!session.compactedThroughMessageId) return estimateTokens(session.contextSummary ?? "");
|
|
5097
|
+
return estimateTokens(buildCompactionFactsEnvelope(session)) + estimateTokens(session.contextSummary ?? "");
|
|
5098
|
+
}
|
|
5099
|
+
function buildCompactionFactsEnvelope(session, throughMessageId) {
|
|
5100
|
+
const memory = session.workingMemory;
|
|
5101
|
+
const contract = session.taskContract;
|
|
5102
|
+
const lastRun = session.lastRun;
|
|
5103
|
+
const epoch = activeContextEpoch(session);
|
|
5104
|
+
const directives = olderUserDirectives(session, throughMessageId ?? session.compactedThroughMessageId);
|
|
5105
|
+
const permissions = recentPermissionFacts(session.audit ?? []);
|
|
5106
|
+
const failures = recentFailureFacts(session.audit ?? []);
|
|
5107
|
+
const artifacts = (session.toolArtifacts ?? []).filter((artifact) => Date.parse(artifact.expiresAt) > Date.now()).slice(-MAX_FACT_ITEMS).map((artifact) => `- sha256=${artifact.sha256} tool-call=${safeFact(artifact.toolCallId, 160)} bytes=${artifact.bytes} expires=${safeFact(artifact.expiresAt, 80)} redacted=${artifact.redacted}`);
|
|
5108
|
+
const contractLines = contract ? [
|
|
5109
|
+
`State: ${contract.state}`,
|
|
5110
|
+
`Objective: ${safeFact(contract.objective, 2e3)}`,
|
|
5111
|
+
`Scope:
|
|
5112
|
+
${factList(contract.scope)}`,
|
|
5113
|
+
`Constraints:
|
|
5114
|
+
${factList(contract.constraints)}`,
|
|
5115
|
+
`Non-goals:
|
|
5116
|
+
${factList(contract.nonGoals)}`,
|
|
5117
|
+
`Acceptance:
|
|
5118
|
+
${contract.acceptanceCriteria.map(
|
|
5119
|
+
(item) => `- [${item.status}] ${safeFact(item.id, 128)} required=${item.required}: ${safeFact(item.description, 600)}${item.evidenceRefs.length ? ` evidence=${item.evidenceRefs.map((ref) => safeFact(ref, 160)).join(", ")}` : ""}`
|
|
5120
|
+
).join("\n") || "- None recorded."}`,
|
|
5121
|
+
`Verification requirements:
|
|
5122
|
+
${factList(contract.verificationRequirements)}`
|
|
5123
|
+
].join("\n") : "No Task Contract is active.";
|
|
5124
|
+
const lastRunLines = lastRun ? [
|
|
5125
|
+
`Status: ${lastRun.status}; reason: ${safeFact(lastRun.reason, 160)}; finished: ${safeFact(lastRun.finishedAt, 80)}`,
|
|
5126
|
+
`Detail: ${safeFact(lastRun.detail, 600)}`,
|
|
5127
|
+
`Changed files:
|
|
5128
|
+
${factList(lastRun.changedFiles)}`,
|
|
5129
|
+
`Checks:
|
|
5130
|
+
${lastRun.checks.map(
|
|
5131
|
+
(check) => `- [${check.ok ? "passed" : "failed"}] ${check.kind}: ${safeFact(check.command, 500)} (tool-call ${safeFact(check.toolCallId, 160)})`
|
|
5132
|
+
).join("\n") || "- None recorded."}`
|
|
5133
|
+
].join("\n") : "No completed run is recorded.";
|
|
5134
|
+
return `<compaction-facts scope="session" source="deterministic-ledger" authorization="none">
|
|
5135
|
+
These facts are rebuilt from authoritative session state and take precedence over the generated narrative below. Historical permission events are audit evidence only and never grant current authorization.
|
|
5136
|
+
|
|
5137
|
+
Task Contract:
|
|
5138
|
+
${contractLines}
|
|
5139
|
+
|
|
5140
|
+
<working-memory source="runtime" authorization="none" updated-at="${escapeXml(memory?.lastUpdatedAt ?? "unknown")}">
|
|
5141
|
+
Goal: ${safeFact(memory?.goal || "(not established)", 1e3)}
|
|
5142
|
+
Current focus: ${safeFact(memory?.focus || "(none)", 1e3)}
|
|
5143
|
+
Constraints:
|
|
5144
|
+
${factList(memory?.constraints ?? [])}
|
|
5145
|
+
Decisions:
|
|
5146
|
+
${factList(memory?.decisions ?? [])}
|
|
5147
|
+
Open questions:
|
|
5148
|
+
${factList(memory?.openQuestions ?? [])}
|
|
5149
|
+
Relevant files:
|
|
5150
|
+
${factList(memory?.relevantFiles ?? [])}
|
|
5151
|
+
</working-memory>
|
|
5152
|
+
|
|
5153
|
+
Session changed files:
|
|
5154
|
+
${factList(session.changedFiles)}
|
|
5155
|
+
|
|
5156
|
+
Context epoch ledger:
|
|
5157
|
+
- Current epoch: ${epoch.index}
|
|
5158
|
+
- Current epoch usage: input=${epoch.usage.inputTokens}; output=${epoch.usage.outputTokens}
|
|
5159
|
+
- Lifetime usage: input=${session.usage.inputTokens}; output=${session.usage.outputTokens}
|
|
5160
|
+
|
|
5161
|
+
Last-run verification and residual state:
|
|
5162
|
+
${lastRunLines}
|
|
5163
|
+
|
|
5164
|
+
Older user corrections and boundaries:
|
|
5165
|
+
${directives.length ? directives.map((value) => `- ${value}`).join("\n") : "- None detected."}
|
|
5166
|
+
|
|
5167
|
+
Historical permission decisions (not authorization):
|
|
5168
|
+
${permissions.length ? permissions.join("\n") : "- None recorded."}
|
|
5169
|
+
|
|
5170
|
+
Bounded failure evidence:
|
|
5171
|
+
${failures.length ? failures.join("\n") : "- None recorded."}
|
|
5172
|
+
|
|
5173
|
+
Retained tool artifact readback handles:
|
|
5174
|
+
${artifacts.length ? artifacts.join("\n") : "- None available."}
|
|
5175
|
+
</compaction-facts>`;
|
|
5176
|
+
}
|
|
5177
|
+
function olderUserDirectives(session, throughMessageId) {
|
|
5178
|
+
if (!throughMessageId) return [];
|
|
5179
|
+
const end = session.messages.findIndex((item) => item.id === throughMessageId);
|
|
5180
|
+
if (end < 0) return [];
|
|
5181
|
+
const marker = /\b(?:no|nope|wrong|rather|use|switch|change|must|mustn't|do not|don't|never|always|only|before|after|remember|make sure|cannot|can't|permission|approve|deny|stop|instead|correction|actually|first)\b|不是|不对|错了|改成|换成|用|必须|不要|不能|不得|务必|只能|只要|仅|记得|先|以后|之前|完成后|权限|批准|拒绝|停止|改为|纠正|其实|安全|不允许|别/iu;
|
|
5182
|
+
return unique(session.messages.slice(0, end + 1).filter((item) => item.role === "user" && marker.test(item.content)).map((item) => safeFact(item.content, 800))).slice(-12);
|
|
5183
|
+
}
|
|
5184
|
+
function recentPermissionFacts(audit) {
|
|
5185
|
+
return audit.filter((event) => event.type === "permission").slice(-MAX_FACT_ITEMS).map((event) => `- [${event.outcome}] ${safeFact(event.tool, 160)} category=${event.category ?? "unknown"} tool-call=${safeFact(event.toolCallId, 160)}: ${safeFact(event.reason ?? "No reason recorded.", 360)}`);
|
|
5186
|
+
}
|
|
5187
|
+
function recentFailureFacts(audit) {
|
|
5188
|
+
return audit.filter((event) => event.type === "tool" && event.outcome === "failure").slice(-MAX_FACT_ITEMS).map((event) => {
|
|
5189
|
+
const failure = isFailureReceipt(event.metadata?.failure) ? event.metadata.failure : void 0;
|
|
5190
|
+
const receipt = failure ? ` class=${safeFact(failure.class, 80)} retryable=${failure.retryable} circuit-open=${failure.circuitOpen} signature=${safeFact(failure.signature, 160)} repair=${safeFact(failure.repairHint, 360)}` : "";
|
|
5191
|
+
return `- ${safeFact(event.tool, 160)} tool-call=${safeFact(event.toolCallId, 160)}${receipt} reason=${safeFact(event.reason ?? "No reason recorded.", 500)}`;
|
|
5192
|
+
});
|
|
5193
|
+
}
|
|
5194
|
+
function isFailureReceipt(value) {
|
|
5195
|
+
if (!value || typeof value !== "object") return false;
|
|
5196
|
+
const candidate = value;
|
|
5197
|
+
return typeof candidate.class === "string" && typeof candidate.retryable === "boolean" && typeof candidate.circuitOpen === "boolean" && typeof candidate.signature === "string" && typeof candidate.repairHint === "string";
|
|
5198
|
+
}
|
|
5199
|
+
function factList(values) {
|
|
5200
|
+
return values.length ? values.slice(-MAX_FACT_ITEMS).map((value) => `- ${safeFact(value, 800)}`).join("\n") : "- None recorded.";
|
|
5201
|
+
}
|
|
5202
|
+
function safeFact(value, max) {
|
|
5203
|
+
return escapeXml(concise(redactSensitiveText(value), max));
|
|
5204
|
+
}
|
|
5205
|
+
function buildWorkingMemoryEnvelope(memory) {
|
|
5206
|
+
return `<working-memory scope="session" source="runtime" authorization="none" updated-at="${escapeXml(memory.lastUpdatedAt)}">
|
|
5207
|
+
This is mutable short-term state for the current thread, not durable truth or tool authorization.
|
|
5208
|
+
Goal: ${safeFact(memory.goal || "(not established)", 1e3)}
|
|
5209
|
+
Current focus: ${safeFact(memory.focus || "(none)", 1e3)}
|
|
5210
|
+
Constraints:
|
|
5211
|
+
${factList(memory.constraints)}
|
|
5212
|
+
Decisions:
|
|
5213
|
+
${factList(memory.decisions)}
|
|
5214
|
+
Open questions:
|
|
5215
|
+
${factList(memory.openQuestions)}
|
|
5216
|
+
Relevant files:
|
|
5217
|
+
${factList(memory.relevantFiles)}
|
|
5218
|
+
</working-memory>`;
|
|
5219
|
+
}
|
|
4846
5220
|
function activeMessages(session) {
|
|
4847
5221
|
if (!session.compactedThroughMessageId) return session.messages;
|
|
4848
5222
|
const index = session.messages.findIndex((message2) => message2.id === session.compactedThroughMessageId);
|
|
@@ -4915,11 +5289,11 @@ function emptyWorkingMemory() {
|
|
|
4915
5289
|
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4916
5290
|
};
|
|
4917
5291
|
}
|
|
4918
|
-
function list(values) {
|
|
4919
|
-
return values.length ? values.map((value) => `- ${escapeXml(value)}`).join("\n") : "- None recorded.";
|
|
4920
|
-
}
|
|
4921
5292
|
function safeShortTerm(value, max) {
|
|
4922
|
-
return concise(value, max)
|
|
5293
|
+
return concise(redactSensitiveText(value), max);
|
|
5294
|
+
}
|
|
5295
|
+
function redactSensitiveText(value) {
|
|
5296
|
+
return value.replace(/(https?:\/\/)[^/\s:@]+(?::[^@\s/]*)?@/giu, "$1[redacted]@").replace(/\b(sk-[A-Za-z0-9_-]{12,}|AIza[0-9A-Za-z_-]{20,})\b/gu, "[redacted-secret]").replace(/\b(bearer|basic)\s+[^\s,;]+/giu, "$1 [redacted]").replace(/\b((?:api[_-]?key|access[_-]?token|auth(?:orization)?|cookie|password|client[_-]?secret|secret))\s*[:=]\s*[^\s,;]+/giu, "$1=[redacted]").replace(/(--(?:api[_-]?key|access[_-]?token|password|client[_-]?secret|secret)(?:=|\s+))[^\s]+/giu, "$1[redacted]");
|
|
4923
5297
|
}
|
|
4924
5298
|
function escapeXml(value) {
|
|
4925
5299
|
return value.replace(/[&<>"']/g, (character) => ({
|
|
@@ -5227,7 +5601,7 @@ function normalizeForMatch(value) {
|
|
|
5227
5601
|
}
|
|
5228
5602
|
|
|
5229
5603
|
// src/providers/anthropic.ts
|
|
5230
|
-
import { randomUUID as
|
|
5604
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
5231
5605
|
|
|
5232
5606
|
// src/providers/provider.ts
|
|
5233
5607
|
var ProviderError = class extends Error {
|
|
@@ -5352,7 +5726,7 @@ var AnthropicProvider = class {
|
|
|
5352
5726
|
return {
|
|
5353
5727
|
content: blocks.filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n"),
|
|
5354
5728
|
toolCalls: blocks.filter((block) => block.type === "tool_use").map((block) => ({
|
|
5355
|
-
id: block.id ??
|
|
5729
|
+
id: block.id ?? randomUUID6(),
|
|
5356
5730
|
name: block.name ?? "unknown",
|
|
5357
5731
|
arguments: safeJsonArguments(block.input)
|
|
5358
5732
|
})),
|
|
@@ -5426,7 +5800,7 @@ var AnthropicProvider = class {
|
|
|
5426
5800
|
}
|
|
5427
5801
|
if (chunk.type === "content_block_start" && chunk.content_block?.type === "tool_use") {
|
|
5428
5802
|
calls.set(index, {
|
|
5429
|
-
id: chunk.content_block.id ??
|
|
5803
|
+
id: chunk.content_block.id ?? randomUUID6(),
|
|
5430
5804
|
name: chunk.content_block.name ?? "unknown",
|
|
5431
5805
|
input: chunk.content_block.input,
|
|
5432
5806
|
partialJson: ""
|
|
@@ -5437,7 +5811,7 @@ var AnthropicProvider = class {
|
|
|
5437
5811
|
yield { type: "text_delta", content: chunk.delta.text };
|
|
5438
5812
|
}
|
|
5439
5813
|
if (chunk.type === "content_block_delta" && chunk.delta?.type === "input_json_delta") {
|
|
5440
|
-
const call = calls.get(index) ?? { id:
|
|
5814
|
+
const call = calls.get(index) ?? { id: randomUUID6(), name: "unknown", input: void 0, partialJson: "" };
|
|
5441
5815
|
call.partialJson += chunk.delta.partial_json ?? "";
|
|
5442
5816
|
calls.set(index, call);
|
|
5443
5817
|
}
|
|
@@ -5467,7 +5841,7 @@ function normalizeAnthropicResponse(data) {
|
|
|
5467
5841
|
return {
|
|
5468
5842
|
content: blocks.filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n"),
|
|
5469
5843
|
toolCalls: blocks.filter((block) => block.type === "tool_use").map((block) => ({
|
|
5470
|
-
id: block.id ??
|
|
5844
|
+
id: block.id ?? randomUUID6(),
|
|
5471
5845
|
name: block.name ?? "unknown",
|
|
5472
5846
|
arguments: safeJsonArguments(block.input)
|
|
5473
5847
|
})),
|
|
@@ -5512,7 +5886,7 @@ function toAnthropicMessage(message2) {
|
|
|
5512
5886
|
}
|
|
5513
5887
|
|
|
5514
5888
|
// src/providers/gemini.ts
|
|
5515
|
-
import { randomUUID as
|
|
5889
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
5516
5890
|
var GeminiProvider = class {
|
|
5517
5891
|
constructor(config) {
|
|
5518
5892
|
this.config = config;
|
|
@@ -5552,7 +5926,7 @@ var GeminiProvider = class {
|
|
|
5552
5926
|
return {
|
|
5553
5927
|
content: parts.map((part) => part.text ?? "").filter(Boolean).join("\n"),
|
|
5554
5928
|
toolCalls: parts.filter((part) => part.functionCall).map((part) => ({
|
|
5555
|
-
id:
|
|
5929
|
+
id: randomUUID7(),
|
|
5556
5930
|
name: part.functionCall?.name ?? "unknown",
|
|
5557
5931
|
arguments: safeJsonArguments(part.functionCall?.args)
|
|
5558
5932
|
})),
|
|
@@ -5624,7 +5998,7 @@ var GeminiProvider = class {
|
|
|
5624
5998
|
}
|
|
5625
5999
|
if (part.functionCall) {
|
|
5626
6000
|
toolCalls.push({
|
|
5627
|
-
id:
|
|
6001
|
+
id: randomUUID7(),
|
|
5628
6002
|
name: part.functionCall.name ?? "unknown",
|
|
5629
6003
|
arguments: safeJsonArguments(part.functionCall.args)
|
|
5630
6004
|
});
|
|
@@ -5653,7 +6027,7 @@ function normalizeGeminiResponse(data) {
|
|
|
5653
6027
|
return {
|
|
5654
6028
|
content: parts.map((part) => part.text ?? "").filter(Boolean).join("\n"),
|
|
5655
6029
|
toolCalls: parts.filter((part) => part.functionCall).map((part) => ({
|
|
5656
|
-
id:
|
|
6030
|
+
id: randomUUID7(),
|
|
5657
6031
|
name: part.functionCall?.name ?? "unknown",
|
|
5658
6032
|
arguments: safeJsonArguments(part.functionCall?.args)
|
|
5659
6033
|
})),
|
|
@@ -5697,7 +6071,7 @@ function toGeminiMessage(message2) {
|
|
|
5697
6071
|
}
|
|
5698
6072
|
|
|
5699
6073
|
// src/providers/openai.ts
|
|
5700
|
-
import { randomUUID as
|
|
6074
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
5701
6075
|
var OpenAIProvider = class {
|
|
5702
6076
|
constructor(config) {
|
|
5703
6077
|
this.config = config;
|
|
@@ -5820,7 +6194,7 @@ var OpenAIProvider = class {
|
|
|
5820
6194
|
}
|
|
5821
6195
|
for (const fragment of delta?.tool_calls ?? []) {
|
|
5822
6196
|
const index = fragment.index ?? 0;
|
|
5823
|
-
const current = calls.get(index) ?? { id:
|
|
6197
|
+
const current = calls.get(index) ?? { id: randomUUID8(), name: "", arguments: "" };
|
|
5824
6198
|
if (fragment.id) current.id = fragment.id;
|
|
5825
6199
|
if (fragment.function?.name) current.name += fragment.function.name;
|
|
5826
6200
|
if (fragment.function?.arguments) current.arguments += fragment.function.arguments;
|
|
@@ -5854,7 +6228,7 @@ function normalizeOpenAIResponse(data) {
|
|
|
5854
6228
|
return {
|
|
5855
6229
|
content: message2.content ?? "",
|
|
5856
6230
|
toolCalls: (message2.tool_calls ?? []).map((call) => ({
|
|
5857
|
-
id: call.id ??
|
|
6231
|
+
id: call.id ?? randomUUID8(),
|
|
5858
6232
|
name: call.function?.name ?? "unknown",
|
|
5859
6233
|
arguments: safeJsonArguments(call.function?.arguments)
|
|
5860
6234
|
})),
|
|
@@ -5910,7 +6284,7 @@ function createProvider(config) {
|
|
|
5910
6284
|
}
|
|
5911
6285
|
|
|
5912
6286
|
// src/checkpoint/store.ts
|
|
5913
|
-
import { createHash as createHash8, randomUUID as
|
|
6287
|
+
import { createHash as createHash8, randomUUID as randomUUID9 } from "node:crypto";
|
|
5914
6288
|
import { lstat as lstat10, mkdir as mkdir7, readFile as readFile5, readdir as readdir2, unlink as unlink2 } from "node:fs/promises";
|
|
5915
6289
|
import { basename as basename6, dirname as dirname8, join as join9, relative as relative6, resolve as resolve12 } from "node:path";
|
|
5916
6290
|
import { z as z4 } from "zod";
|
|
@@ -5946,7 +6320,7 @@ var CheckpointStore = class {
|
|
|
5946
6320
|
return this.withManagedLease(() => this.captureUnlocked(sessionId, unique3, options));
|
|
5947
6321
|
}
|
|
5948
6322
|
async captureUnlocked(sessionId, unique3, options) {
|
|
5949
|
-
const id = `${Date.now().toString(36)}-${
|
|
6323
|
+
const id = `${Date.now().toString(36)}-${randomUUID9()}`;
|
|
5950
6324
|
const target = join9(this.directory, sessionId, id);
|
|
5951
6325
|
const blobDirectory = join9(target, "blobs");
|
|
5952
6326
|
await this.ensureDirectory();
|
|
@@ -6222,7 +6596,7 @@ var HookRunner = class {
|
|
|
6222
6596
|
};
|
|
6223
6597
|
|
|
6224
6598
|
// src/session/store.ts
|
|
6225
|
-
import { randomUUID as
|
|
6599
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
6226
6600
|
import { constants as constants4 } from "node:fs";
|
|
6227
6601
|
import {
|
|
6228
6602
|
chmod as chmod6,
|
|
@@ -6417,6 +6791,107 @@ var taskContractSchema = z5.object({
|
|
|
6417
6791
|
updatedAt: z5.string(),
|
|
6418
6792
|
auditBoundaryId: z5.string().min(1).max(128).optional()
|
|
6419
6793
|
}).strict();
|
|
6794
|
+
var contextCompactionReceiptSchema = z5.object({
|
|
6795
|
+
id: z5.string().uuid(),
|
|
6796
|
+
recordedAt: z5.string().datetime(),
|
|
6797
|
+
mode: z5.enum(["automatic", "manual"]),
|
|
6798
|
+
status: z5.enum(["compacted", "skipped"]),
|
|
6799
|
+
reason: z5.enum(["compacted", "insufficient-history", "non-positive-net-savings"]),
|
|
6800
|
+
omittedMessages: z5.number().int().nonnegative(),
|
|
6801
|
+
compactedThroughMessageId: z5.string().optional(),
|
|
6802
|
+
predictedReuses: z5.number().int().positive(),
|
|
6803
|
+
estimated: z5.object({
|
|
6804
|
+
inputTokens: z5.number().int().nonnegative(),
|
|
6805
|
+
outputTokens: z5.number().int().nonnegative(),
|
|
6806
|
+
predictedOutputTokens: z5.number().int().nonnegative(),
|
|
6807
|
+
outputAllowanceTokens: z5.number().int().nonnegative(),
|
|
6808
|
+
omittedTokens: z5.number().int().nonnegative(),
|
|
6809
|
+
priorSummaryTokens: z5.number().int().nonnegative(),
|
|
6810
|
+
factsTokens: z5.number().int().nonnegative(),
|
|
6811
|
+
projectedGrossSavingsTokens: z5.number().int().nonnegative(),
|
|
6812
|
+
projectedNetSavingsTokens: z5.number().int()
|
|
6813
|
+
}).strict(),
|
|
6814
|
+
actual: z5.object({
|
|
6815
|
+
inputTokens: z5.number().int().nonnegative().optional(),
|
|
6816
|
+
outputTokens: z5.number().int().nonnegative().optional(),
|
|
6817
|
+
cachedInputTokens: z5.number().int().nonnegative().optional(),
|
|
6818
|
+
cacheWriteInputTokens: z5.number().int().nonnegative().optional(),
|
|
6819
|
+
reasoningTokens: z5.number().int().nonnegative().optional()
|
|
6820
|
+
}).strict(),
|
|
6821
|
+
inputSource: z5.enum(["actual", "estimated", "none"]),
|
|
6822
|
+
outputSource: z5.enum(["actual", "estimated", "none"]),
|
|
6823
|
+
narrative: z5.enum(["present", "empty", "not-requested"])
|
|
6824
|
+
}).strict();
|
|
6825
|
+
var contextEpochSchema = z5.object({
|
|
6826
|
+
id: z5.string().uuid(),
|
|
6827
|
+
index: z5.number().int().positive(),
|
|
6828
|
+
startedAt: z5.string().datetime(),
|
|
6829
|
+
finishedAt: z5.string().datetime().optional(),
|
|
6830
|
+
usage: z5.object({
|
|
6831
|
+
inputTokens: z5.number().int().nonnegative(),
|
|
6832
|
+
outputTokens: z5.number().int().nonnegative()
|
|
6833
|
+
}).strict(),
|
|
6834
|
+
handoff: z5.object({
|
|
6835
|
+
reason: z5.enum(["token_budget", "context_pressure", "manual"]),
|
|
6836
|
+
createdAt: z5.string().datetime(),
|
|
6837
|
+
compactionReceiptId: z5.string().uuid().optional(),
|
|
6838
|
+
compactedThroughMessageId: z5.string().optional(),
|
|
6839
|
+
contract: z5.object({
|
|
6840
|
+
state: z5.enum(["draft", "active", "satisfied", "blocked"]),
|
|
6841
|
+
required: z5.array(z5.object({
|
|
6842
|
+
id: z5.string().min(1).max(128),
|
|
6843
|
+
status: z5.enum(["pending", "satisfied", "blocked"]),
|
|
6844
|
+
evidenceRefs: z5.array(z5.string().min(1).max(256)).max(64)
|
|
6845
|
+
}).strict()).max(64)
|
|
6846
|
+
}).strict().optional(),
|
|
6847
|
+
unresolvedFailures: z5.array(z5.object({
|
|
6848
|
+
signature: z5.string().min(1).max(256),
|
|
6849
|
+
class: z5.enum([
|
|
6850
|
+
"schema_input",
|
|
6851
|
+
"unknown_tool",
|
|
6852
|
+
"permission_denied",
|
|
6853
|
+
"command_exit",
|
|
6854
|
+
"timeout",
|
|
6855
|
+
"cancelled",
|
|
6856
|
+
"hook",
|
|
6857
|
+
"execution",
|
|
6858
|
+
"no_progress",
|
|
6859
|
+
"contract_required"
|
|
6860
|
+
]),
|
|
6861
|
+
circuitOpen: z5.boolean()
|
|
6862
|
+
}).strict()).max(16),
|
|
6863
|
+
changedFiles: z5.array(z5.string().max(4096)).max(256),
|
|
6864
|
+
checks: z5.array(verificationEvidenceSchema).max(64)
|
|
6865
|
+
}).strict().optional()
|
|
6866
|
+
}).strict();
|
|
6867
|
+
var intentAssessmentSchema = z5.object({
|
|
6868
|
+
version: z5.literal(1),
|
|
6869
|
+
route: z5.enum(["direct_execute", "inspect_then_execute", "needs_input", "permission_required"]),
|
|
6870
|
+
reasons: z5.array(z5.enum([
|
|
6871
|
+
"simple_explicit_request",
|
|
6872
|
+
"workspace_inference_available",
|
|
6873
|
+
"explicit_user_choice_missing",
|
|
6874
|
+
"public_api_compatibility_missing",
|
|
6875
|
+
"runtime_permission_separate",
|
|
6876
|
+
"clarification_resolved"
|
|
6877
|
+
])).min(1).max(8),
|
|
6878
|
+
assessedAt: z5.string().datetime(),
|
|
6879
|
+
retrievalHits: z5.number().int().nonnegative()
|
|
6880
|
+
}).strict();
|
|
6881
|
+
var pendingInputSchema = z5.object({
|
|
6882
|
+
id: z5.string().uuid(),
|
|
6883
|
+
runId: z5.string().uuid(),
|
|
6884
|
+
createdAt: z5.string().datetime(),
|
|
6885
|
+
originalRequest: z5.string().min(1).max(12e4),
|
|
6886
|
+
question: z5.string().min(1).max(2e3),
|
|
6887
|
+
options: z5.array(z5.object({
|
|
6888
|
+
id: z5.string().regex(/^[a-z][a-z0-9_-]{0,63}$/u),
|
|
6889
|
+
label: z5.string().min(1).max(160),
|
|
6890
|
+
impact: z5.string().min(1).max(500),
|
|
6891
|
+
recommended: z5.boolean()
|
|
6892
|
+
}).strict()).min(2).max(3),
|
|
6893
|
+
reason: z5.enum(["explicit_user_choice_missing", "public_api_compatibility_missing"])
|
|
6894
|
+
}).strict();
|
|
6420
6895
|
var sessionSchema = z5.object({
|
|
6421
6896
|
id: sessionIdSchema,
|
|
6422
6897
|
title: z5.string(),
|
|
@@ -6432,6 +6907,10 @@ var sessionSchema = z5.object({
|
|
|
6432
6907
|
contextSummary: z5.string().max(2e5).optional(),
|
|
6433
6908
|
contextCompactions: z5.number().int().nonnegative().optional(),
|
|
6434
6909
|
compactedThroughMessageId: z5.string().optional(),
|
|
6910
|
+
contextCompactionReceipts: z5.array(contextCompactionReceiptSchema).max(64).optional(),
|
|
6911
|
+
contextEpochs: z5.array(contextEpochSchema).max(64).optional(),
|
|
6912
|
+
intentAssessment: intentAssessmentSchema.optional(),
|
|
6913
|
+
pendingInput: pendingInputSchema.optional(),
|
|
6435
6914
|
workingMemory: workingMemorySchema.optional(),
|
|
6436
6915
|
contextSources: z5.array(contextSourceSchema).max(64).optional(),
|
|
6437
6916
|
toolArtifacts: z5.array(toolArtifactSchema).max(200).optional(),
|
|
@@ -6582,7 +7061,7 @@ var SessionStore = class {
|
|
|
6582
7061
|
const backup = this.backupPathFor(session.id);
|
|
6583
7062
|
await this.assertManagedFile(target);
|
|
6584
7063
|
await this.assertManagedFile(backup);
|
|
6585
|
-
const temporary = join10(this.directory, `.${session.id}.${
|
|
7064
|
+
const temporary = join10(this.directory, `.${session.id}.${randomUUID10()}.tmp`);
|
|
6586
7065
|
const data = `${JSON.stringify(session, null, 2)}
|
|
6587
7066
|
`;
|
|
6588
7067
|
const handle = await open2(temporary, "wx", 384);
|
|
@@ -6600,7 +7079,7 @@ var SessionStore = class {
|
|
|
6600
7079
|
}
|
|
6601
7080
|
}
|
|
6602
7081
|
async copyBackup(source, target) {
|
|
6603
|
-
const temporary = join10(this.directory, `.${basename7(target)}.${
|
|
7082
|
+
const temporary = join10(this.directory, `.${basename7(target)}.${randomUUID10()}.tmp`);
|
|
6604
7083
|
try {
|
|
6605
7084
|
await copyFile(source, temporary, constants4.COPYFILE_EXCL);
|
|
6606
7085
|
await chmod6(temporary, 384);
|
|
@@ -6689,7 +7168,7 @@ var SessionStore = class {
|
|
|
6689
7168
|
}
|
|
6690
7169
|
};
|
|
6691
7170
|
function createSession(options) {
|
|
6692
|
-
const id = options.id ??
|
|
7171
|
+
const id = options.id ?? randomUUID10();
|
|
6693
7172
|
validateId(id);
|
|
6694
7173
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6695
7174
|
return {
|
|
@@ -6704,6 +7183,12 @@ function createSession(options) {
|
|
|
6704
7183
|
tasks: [],
|
|
6705
7184
|
changedFiles: [],
|
|
6706
7185
|
audit: [],
|
|
7186
|
+
contextEpochs: [{
|
|
7187
|
+
id: randomUUID10(),
|
|
7188
|
+
index: 1,
|
|
7189
|
+
startedAt: now,
|
|
7190
|
+
usage: { inputTokens: 0, outputTokens: 0 }
|
|
7191
|
+
}],
|
|
6707
7192
|
usage: { inputTokens: 0, outputTokens: 0 }
|
|
6708
7193
|
};
|
|
6709
7194
|
}
|
|
@@ -7796,7 +8281,7 @@ function isUnsafeGitOption(argument) {
|
|
|
7796
8281
|
"--exclude-from",
|
|
7797
8282
|
"--output",
|
|
7798
8283
|
"--directory"
|
|
7799
|
-
].some((
|
|
8284
|
+
].some((option2) => argument === option2 || argument.startsWith(`${option2}=`));
|
|
7800
8285
|
}
|
|
7801
8286
|
function firstGitCommand(args) {
|
|
7802
8287
|
for (const argument of args) {
|
|
@@ -8544,7 +9029,7 @@ async function discoverSnapshotFiles(root) {
|
|
|
8544
9029
|
}
|
|
8545
9030
|
|
|
8546
9031
|
// src/tools/task.ts
|
|
8547
|
-
import { randomUUID as
|
|
9032
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
8548
9033
|
import { z as z14 } from "zod";
|
|
8549
9034
|
var taskSchema2 = z14.object({
|
|
8550
9035
|
id: z14.string().min(1).optional(),
|
|
@@ -8593,7 +9078,7 @@ var taskTool = {
|
|
|
8593
9078
|
case "list":
|
|
8594
9079
|
break;
|
|
8595
9080
|
case "add":
|
|
8596
|
-
tasks.push({ id:
|
|
9081
|
+
tasks.push({ id: randomUUID11(), title: input2.title, status: input2.status ?? "pending" });
|
|
8597
9082
|
break;
|
|
8598
9083
|
case "update": {
|
|
8599
9084
|
const task = tasks.find((item) => item.id === input2.id);
|
|
@@ -8610,7 +9095,7 @@ var taskTool = {
|
|
|
8610
9095
|
}
|
|
8611
9096
|
case "replace":
|
|
8612
9097
|
tasks.splice(0, tasks.length, ...input2.tasks.map((task) => ({
|
|
8613
|
-
id: task.id ??
|
|
9098
|
+
id: task.id ?? randomUUID11(),
|
|
8614
9099
|
title: task.title,
|
|
8615
9100
|
status: task.status
|
|
8616
9101
|
})));
|
|
@@ -8624,11 +9109,11 @@ var taskTool = {
|
|
|
8624
9109
|
};
|
|
8625
9110
|
|
|
8626
9111
|
// src/tools/task-contract.ts
|
|
8627
|
-
import { randomUUID as
|
|
9112
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
8628
9113
|
import { z as z15 } from "zod";
|
|
8629
9114
|
|
|
8630
9115
|
// src/agent/task-contract.ts
|
|
8631
|
-
import { randomUUID as
|
|
9116
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
8632
9117
|
var EXECUTABLE_INTENTS = /* @__PURE__ */ new Set(["debug", "refactor", "test", "implement"]);
|
|
8633
9118
|
function shouldUseTaskContract(request, intent, existing) {
|
|
8634
9119
|
if (existing && existing.state !== "satisfied") return true;
|
|
@@ -8694,7 +9179,7 @@ function refreshTaskContractState(contract) {
|
|
|
8694
9179
|
}
|
|
8695
9180
|
function criterion(id, description) {
|
|
8696
9181
|
return {
|
|
8697
|
-
id: `${id}-${
|
|
9182
|
+
id: `${id}-${randomUUID12().slice(0, 8)}`,
|
|
8698
9183
|
description,
|
|
8699
9184
|
required: true,
|
|
8700
9185
|
status: "pending",
|
|
@@ -9248,7 +9733,7 @@ var taskContractTool = {
|
|
|
9248
9733
|
}
|
|
9249
9734
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9250
9735
|
const criteria = input2.acceptance_criteria.map((item) => ({
|
|
9251
|
-
id: item.id ?? `criterion-${
|
|
9736
|
+
id: item.id ?? `criterion-${randomUUID13().slice(0, 8)}`,
|
|
9252
9737
|
description: item.description,
|
|
9253
9738
|
required: item.required ?? true,
|
|
9254
9739
|
status: "pending",
|
|
@@ -9636,7 +10121,7 @@ ${workspaceRules}` : ""}`;
|
|
|
9636
10121
|
}
|
|
9637
10122
|
function buildSessionStatePrompt(session) {
|
|
9638
10123
|
const tasks = session.tasks.length ? session.tasks.map((task) => `- [${task.status}] ${task.title}`).join("\n") : "- No active plan.";
|
|
9639
|
-
const contract = session.taskContract && session.taskContract.state !== "satisfied" ? `
|
|
10124
|
+
const contract = session.taskContract && session.taskContract.state !== "satisfied" && !session.compactedThroughMessageId ? `
|
|
9640
10125
|
|
|
9641
10126
|
Task Contract (${session.taskContract.state}):
|
|
9642
10127
|
Objective: ${session.taskContract.objective}
|
|
@@ -9716,6 +10201,118 @@ function escapeAttribute3(value) {
|
|
|
9716
10201
|
})[character] ?? character);
|
|
9717
10202
|
}
|
|
9718
10203
|
|
|
10204
|
+
// src/agent/intent-sufficiency.ts
|
|
10205
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
10206
|
+
function assessIntentSufficiency(request, intent, context) {
|
|
10207
|
+
const assessedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10208
|
+
const uiChoice = explicitUiChoice(request);
|
|
10209
|
+
if (uiChoice) {
|
|
10210
|
+
const pending = clarification(
|
|
10211
|
+
request,
|
|
10212
|
+
assessedAt,
|
|
10213
|
+
"explicit_user_choice_missing",
|
|
10214
|
+
isChinese(request) ? "\u8FD9\u4E2A\u754C\u9762\u884C\u4E3A\u9700\u8981\u4F60\u7684\u4EA7\u54C1\u504F\u597D\uFF0C\u5E94\u91C7\u7528\u54EA\u4E00\u79CD\uFF1F" : "This UI behavior depends on your product preference. Which should be used?",
|
|
10215
|
+
uiChoice
|
|
10216
|
+
);
|
|
10217
|
+
return {
|
|
10218
|
+
assessment: assessment("needs_input", "explicit_user_choice_missing", assessedAt, context.retrievalHits),
|
|
10219
|
+
pending,
|
|
10220
|
+
directive: "Pause before mutation and ask the single recorded clarification question."
|
|
10221
|
+
};
|
|
10222
|
+
}
|
|
10223
|
+
if (publicApiCompatibilityMissing(request)) {
|
|
10224
|
+
const chinese = isChinese(request);
|
|
10225
|
+
const pending = clarification(
|
|
10226
|
+
request,
|
|
10227
|
+
assessedAt,
|
|
10228
|
+
"public_api_compatibility_missing",
|
|
10229
|
+
chinese ? "\u516C\u5171 API \u7684\u517C\u5BB9\u7B56\u7565\u5C1A\u672A\u6307\u5B9A\uFF0C\u5E94\u8BE5\u91C7\u7528\u54EA\u4E00\u79CD\uFF1F" : "The public API compatibility policy is unspecified. Which policy should be used?",
|
|
10230
|
+
chinese ? [
|
|
10231
|
+
option("backward_compatible", "\u4FDD\u6301\u5411\u540E\u517C\u5BB9", "\u4FDD\u7559\u65E7\u63A5\u53E3\u5E76\u63D0\u4F9B\u5F03\u7528\u6216\u8FC1\u79FB\u8DEF\u5F84\uFF1B\u6539\u52A8\u66F4\u7A33\u59A5\u4F46\u5B9E\u73B0\u8303\u56F4\u8F83\u5927\u3002", true),
|
|
10232
|
+
option("breaking_change", "\u5141\u8BB8\u7834\u574F\u6027\u53D8\u66F4", "\u76F4\u63A5\u91C7\u7528\u65B0\u63A5\u53E3\uFF1B\u8303\u56F4\u66F4\u5C0F\uFF0C\u4F46\u8C03\u7528\u65B9\u5FC5\u987B\u540C\u6B65\u8FC1\u79FB\u3002", false)
|
|
10233
|
+
] : [
|
|
10234
|
+
option("backward_compatible", "Preserve compatibility", "Keep the old API with a deprecation or migration path; safer but broader.", true),
|
|
10235
|
+
option("breaking_change", "Allow breaking change", "Adopt the new API directly; smaller change but callers must migrate.", false)
|
|
10236
|
+
]
|
|
10237
|
+
);
|
|
10238
|
+
return {
|
|
10239
|
+
assessment: assessment("needs_input", "public_api_compatibility_missing", assessedAt, context.retrievalHits),
|
|
10240
|
+
pending,
|
|
10241
|
+
directive: "Pause before mutation and ask the single recorded clarification question."
|
|
10242
|
+
};
|
|
10243
|
+
}
|
|
10244
|
+
if (requiresRuntimePermission(request)) {
|
|
10245
|
+
return {
|
|
10246
|
+
assessment: assessment("permission_required", "runtime_permission_separate", assessedAt, context.retrievalHits),
|
|
10247
|
+
directive: "The objective is sufficiently clear. Continue, but keep runtime permission approval separate from clarification."
|
|
10248
|
+
};
|
|
10249
|
+
}
|
|
10250
|
+
if (context.complex && shouldInspectWorkspace(request, intent)) {
|
|
10251
|
+
return {
|
|
10252
|
+
assessment: assessment("inspect_then_execute", "workspace_inference_available", assessedAt, context.retrievalHits),
|
|
10253
|
+
directive: "Do not ask the user for repository facts. Inspect the workspace and current evidence before the first mutation."
|
|
10254
|
+
};
|
|
10255
|
+
}
|
|
10256
|
+
return {
|
|
10257
|
+
assessment: assessment("direct_execute", "simple_explicit_request", assessedAt, context.retrievalHits),
|
|
10258
|
+
directive: "The request is sufficiently clear. Execute without an extra clarification turn."
|
|
10259
|
+
};
|
|
10260
|
+
}
|
|
10261
|
+
function resolvePendingInput(pending, answer) {
|
|
10262
|
+
const normalized = answer.trim();
|
|
10263
|
+
const byIndex = /^([1-3])(?:[.)、:]|$)/u.exec(normalized)?.[1];
|
|
10264
|
+
const selected = pending.options.find((candidate) => candidate.id.toLocaleLowerCase() === normalized.toLocaleLowerCase() || candidate.label.toLocaleLowerCase() === normalized.toLocaleLowerCase()) ?? (byIndex ? pending.options[Number(byIndex) - 1] : void 0);
|
|
10265
|
+
const decision = selected ? `${selected.label}: ${selected.impact}` : compact2(normalized, 2e3);
|
|
10266
|
+
return { answer: compact2(normalized, 2e3), decision };
|
|
10267
|
+
}
|
|
10268
|
+
function assessment(route, reason, assessedAt, retrievalHits) {
|
|
10269
|
+
return { version: 1, route, reasons: [reason], assessedAt, retrievalHits };
|
|
10270
|
+
}
|
|
10271
|
+
function clarification(originalRequest, createdAt, reason, question2, options) {
|
|
10272
|
+
return {
|
|
10273
|
+
id: randomUUID14(),
|
|
10274
|
+
runId: randomUUID14(),
|
|
10275
|
+
createdAt,
|
|
10276
|
+
originalRequest: originalRequest.slice(0, 12e4),
|
|
10277
|
+
question: question2,
|
|
10278
|
+
options,
|
|
10279
|
+
reason
|
|
10280
|
+
};
|
|
10281
|
+
}
|
|
10282
|
+
function explicitUiChoice(value) {
|
|
10283
|
+
const terms = "modal|dialog|drawer|popover|inline|new page|side panel|\u5F39\u7A97|\u6A21\u6001\u6846|\u62BD\u5C49|\u6D6E\u5C42|\u5185\u8054|\u9875\u9762\u5185|\u4FA7\u8FB9\u680F|\u65B0\u9875\u9762";
|
|
10284
|
+
const match = value.match(new RegExp(`\\b(${terms})\\b\\s*(?:or|versus|vs\\.?)\\s*\\b(${terms})\\b`, "iu")) ?? value.match(new RegExp(`(${terms})[\uFF0C\u3001\\s]*(?:\u8FD8\u662F|\u6216\u8005|\u6216)[\uFF0C\u3001\\s]*(${terms})`, "iu"));
|
|
10285
|
+
if (!match?.[1] || !match[2] || match[1].toLocaleLowerCase() === match[2].toLocaleLowerCase()) return;
|
|
10286
|
+
const chinese = isChinese(value);
|
|
10287
|
+
return [
|
|
10288
|
+
option("choice_1", compact2(match[1], 80), chinese ? "\u91C7\u7528\u8BF7\u6C42\u4E2D\u5217\u51FA\u7684\u7B2C\u4E00\u79CD\u4EA4\u4E92\u65B9\u5F0F\u3002" : "Use the first interaction named in the request.", true),
|
|
10289
|
+
option("choice_2", compact2(match[2], 80), chinese ? "\u91C7\u7528\u8BF7\u6C42\u4E2D\u5217\u51FA\u7684\u7B2C\u4E8C\u79CD\u4EA4\u4E92\u65B9\u5F0F\u3002" : "Use the second interaction named in the request.", false)
|
|
10290
|
+
];
|
|
10291
|
+
}
|
|
10292
|
+
function publicApiCompatibilityMissing(value) {
|
|
10293
|
+
const api = /\b(?:public|external|exported)\s+(?:api|interface|contract)\b|公共\s*(?:api|接口)|公开\s*(?:api|接口)|对外接口/iu;
|
|
10294
|
+
const change = /\b(?:change|replace|remove|rename|migrate|redesign|refactor)\b|修改|替换|删除|移除|重命名|迁移|重构/iu;
|
|
10295
|
+
const policy = /\b(?:backward compatible|backwards compatible|breaking change|semver major|deprecat|compatibility shim|migration path)\b|向后兼容|保持兼容|允许破坏|破坏性变更|主版本|弃用|迁移路径/iu;
|
|
10296
|
+
return api.test(value) && change.test(value) && !policy.test(value);
|
|
10297
|
+
}
|
|
10298
|
+
function requiresRuntimePermission(value) {
|
|
10299
|
+
return /\b(?:push|publish|deploy|release|delete|drop|reset|force[- ]push|send|upload)\b|推送|发布|部署|删除|清空|重置|发送|上传/iu.test(value);
|
|
10300
|
+
}
|
|
10301
|
+
function shouldInspectWorkspace(value, intent) {
|
|
10302
|
+
if (intent === "debug" || intent === "refactor" || intent === "review") return true;
|
|
10303
|
+
const hasConcreteTarget = /(?:^|\s)(?:[\w.-]+\/)+[\w.-]+|`[^`]+`|\b[A-Za-z_$][\w$]*(?:\(\))?\b/u.test(value);
|
|
10304
|
+
return !hasConcreteTarget || /\b(?:existing|current|relevant|appropriate|best)\b|现有|当前|相关|合适|最适合|这个|那个/iu.test(value);
|
|
10305
|
+
}
|
|
10306
|
+
function option(id, label, impact, recommended) {
|
|
10307
|
+
return { id, label, impact, recommended };
|
|
10308
|
+
}
|
|
10309
|
+
function isChinese(value) {
|
|
10310
|
+
return /[\u3400-\u9fff]/u.test(value);
|
|
10311
|
+
}
|
|
10312
|
+
function compact2(value, limit) {
|
|
10313
|
+
return value.trim().replace(/\s+/gu, " ").slice(0, limit);
|
|
10314
|
+
}
|
|
10315
|
+
|
|
9719
10316
|
// src/agent/tool-recovery.ts
|
|
9720
10317
|
import { createHash as createHash13 } from "node:crypto";
|
|
9721
10318
|
var RETRY_BUDGET = {
|
|
@@ -9730,6 +10327,7 @@ var RETRY_BUDGET = {
|
|
|
9730
10327
|
no_progress: 0,
|
|
9731
10328
|
contract_required: 2
|
|
9732
10329
|
};
|
|
10330
|
+
var FAILURE_CLASSES = Object.keys(RETRY_BUDGET);
|
|
9733
10331
|
var REPAIR_HINT = {
|
|
9734
10332
|
schema_input: "Correct the arguments to match the tool schema.",
|
|
9735
10333
|
unknown_tool: "Choose a tool exposed for this turn.",
|
|
@@ -9831,6 +10429,9 @@ function classifyToolFailure(result, fallback = "execution") {
|
|
|
9831
10429
|
function formatFailureReceipt(receipt) {
|
|
9832
10430
|
return `Failure: ${receipt.class}; attempt ${receipt.attempt}; ${receipt.remaining} retries remain; circuit ${receipt.circuitOpen ? "open" : "closed"}. Repair: ${receipt.repairHint}`;
|
|
9833
10431
|
}
|
|
10432
|
+
function resolvableFailureSignatures(call) {
|
|
10433
|
+
return FAILURE_CLASSES.map((failureClass) => failureSignature(call, failureClass));
|
|
10434
|
+
}
|
|
9834
10435
|
function isRetryable(failureClass) {
|
|
9835
10436
|
return RETRY_BUDGET[failureClass] > 0;
|
|
9836
10437
|
}
|
|
@@ -9954,12 +10555,12 @@ function preservedSignals(metadata) {
|
|
|
9954
10555
|
}
|
|
9955
10556
|
}
|
|
9956
10557
|
const failure = metadata.failure;
|
|
9957
|
-
if (
|
|
10558
|
+
if (isFailureReceipt2(failure)) {
|
|
9958
10559
|
lines.push(`failure-class: ${failure.class}; attempt ${failure.attempt}; ${failure.remaining} retries remain`);
|
|
9959
10560
|
}
|
|
9960
10561
|
return lines;
|
|
9961
10562
|
}
|
|
9962
|
-
function
|
|
10563
|
+
function isFailureReceipt2(value) {
|
|
9963
10564
|
return typeof value === "object" && value !== null && typeof value.class === "string" && typeof value.attempt === "number" && typeof value.remaining === "number";
|
|
9964
10565
|
}
|
|
9965
10566
|
function redactToolOutput(value) {
|
|
@@ -10095,15 +10696,15 @@ async function pinContextSource(session, workspace, requested) {
|
|
|
10095
10696
|
const info = await stat9(resolved);
|
|
10096
10697
|
const alias = workspace.display(resolved);
|
|
10097
10698
|
const tokens2 = estimateTokens((await readFile13(resolved, "utf8")).slice(0, MAX_SOURCE_CHARS));
|
|
10098
|
-
const
|
|
10099
|
-
const existing =
|
|
10699
|
+
const list = sources(session);
|
|
10700
|
+
const existing = list.find((source2) => source2.path === alias);
|
|
10100
10701
|
if (existing) {
|
|
10101
10702
|
existing.state = "pinned";
|
|
10102
10703
|
existing.tokens = tokens2;
|
|
10103
10704
|
existing.addedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10104
10705
|
return existing;
|
|
10105
10706
|
}
|
|
10106
|
-
if (
|
|
10707
|
+
if (list.length >= MAX_CONTEXT_SOURCES) {
|
|
10107
10708
|
throw new Error(`Context source limit reached (${MAX_CONTEXT_SOURCES}); unpin something first.`);
|
|
10108
10709
|
}
|
|
10109
10710
|
if (info.size > 4e6) {
|
|
@@ -10115,31 +10716,31 @@ async function pinContextSource(session, workspace, requested) {
|
|
|
10115
10716
|
tokens: tokens2,
|
|
10116
10717
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10117
10718
|
};
|
|
10118
|
-
|
|
10719
|
+
list.push(source);
|
|
10119
10720
|
return source;
|
|
10120
10721
|
}
|
|
10121
10722
|
function unpinContextSource(session, requested) {
|
|
10122
|
-
const
|
|
10123
|
-
if (!
|
|
10124
|
-
const match = matchSource(
|
|
10723
|
+
const list = session.contextSources;
|
|
10724
|
+
if (!list?.length) return void 0;
|
|
10725
|
+
const match = matchSource(list, requested);
|
|
10125
10726
|
if (!match) return void 0;
|
|
10126
|
-
|
|
10727
|
+
list.splice(list.indexOf(match), 1);
|
|
10127
10728
|
return match.path;
|
|
10128
10729
|
}
|
|
10129
10730
|
function toggleMuteContextSource(session, requested) {
|
|
10130
|
-
const
|
|
10131
|
-
if (!
|
|
10132
|
-
const match = matchSource(
|
|
10731
|
+
const list = session.contextSources;
|
|
10732
|
+
if (!list?.length) return void 0;
|
|
10733
|
+
const match = matchSource(list, requested);
|
|
10133
10734
|
if (!match) return void 0;
|
|
10134
10735
|
match.state = match.state === "muted" ? "pinned" : "muted";
|
|
10135
10736
|
return match;
|
|
10136
10737
|
}
|
|
10137
10738
|
async function resolvePinnedContent(session, workspace) {
|
|
10138
|
-
const
|
|
10139
|
-
if (!
|
|
10739
|
+
const list = session.contextSources;
|
|
10740
|
+
if (!list?.length) return [];
|
|
10140
10741
|
const resolved = [];
|
|
10141
10742
|
let remaining = MAX_PINNED_CHARS;
|
|
10142
|
-
for (const source of
|
|
10743
|
+
for (const source of list) {
|
|
10143
10744
|
if (source.state !== "pinned") continue;
|
|
10144
10745
|
if (remaining <= 0) break;
|
|
10145
10746
|
try {
|
|
@@ -10171,10 +10772,10 @@ These files were explicitly pinned by the user and are re-read from disk each tu
|
|
|
10171
10772
|
${blocks.join("\n\n")}
|
|
10172
10773
|
</pinned-context>`;
|
|
10173
10774
|
}
|
|
10174
|
-
function matchSource(
|
|
10775
|
+
function matchSource(list, requested) {
|
|
10175
10776
|
const trimmed = requested.trim();
|
|
10176
10777
|
if (!trimmed) return void 0;
|
|
10177
|
-
return
|
|
10778
|
+
return list.find((source) => source.path === trimmed) ?? list.find((source) => source.path.endsWith(`/${trimmed}`)) ?? list.find((source) => source.path.includes(trimmed));
|
|
10178
10779
|
}
|
|
10179
10780
|
function escapeAttribute5(value) {
|
|
10180
10781
|
return value.replace(/[&"<>]/g, (character) => ({
|
|
@@ -10648,16 +11249,35 @@ var AgentRunner = class {
|
|
|
10648
11249
|
}
|
|
10649
11250
|
async run(input2, options = {}) {
|
|
10650
11251
|
if (this.running) throw new Error("This AgentRunner is already processing a turn.");
|
|
10651
|
-
const
|
|
10652
|
-
if (!
|
|
10653
|
-
if (
|
|
11252
|
+
const submittedRequest = input2.trim();
|
|
11253
|
+
if (!submittedRequest) throw new Error("User input cannot be empty.");
|
|
11254
|
+
if (submittedRequest.length > 12e4) {
|
|
10654
11255
|
throw new Error("User input is too large; pass a focused request or attach files with @path.");
|
|
10655
11256
|
}
|
|
11257
|
+
let request = submittedRequest;
|
|
10656
11258
|
this.running = true;
|
|
10657
11259
|
this.contextEngine.resetDiagnostics?.();
|
|
10658
11260
|
const emit = async (event) => {
|
|
10659
11261
|
await options.onEvent?.(event);
|
|
10660
11262
|
};
|
|
11263
|
+
const pendingAtStart = this.session.pendingInput;
|
|
11264
|
+
const resolvedInput = pendingAtStart ? resolvePendingInput(pendingAtStart, submittedRequest) : void 0;
|
|
11265
|
+
if (pendingAtStart && resolvedInput) {
|
|
11266
|
+
request = `${pendingAtStart.originalRequest}
|
|
11267
|
+
|
|
11268
|
+
<user-clarification-decision source="explicit-user-input">
|
|
11269
|
+
${resolvedInput.decision}
|
|
11270
|
+
</user-clarification-decision>`;
|
|
11271
|
+
delete this.session.pendingInput;
|
|
11272
|
+
const constraint = `User clarification (${pendingAtStart.id}): ${resolvedInput.decision}`;
|
|
11273
|
+
if (this.session.taskContract && !this.session.taskContract.constraints.includes(constraint)) {
|
|
11274
|
+
this.session.taskContract.constraints.push(constraint);
|
|
11275
|
+
if (this.session.taskContract.constraints.length > 64) {
|
|
11276
|
+
this.session.taskContract.constraints.splice(0, this.session.taskContract.constraints.length - 64);
|
|
11277
|
+
}
|
|
11278
|
+
this.session.taskContract.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11279
|
+
}
|
|
11280
|
+
}
|
|
10661
11281
|
const changeSequenceAtStart = this.changeSequence;
|
|
10662
11282
|
const runStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10663
11283
|
const loadedProgressiveTools = /* @__PURE__ */ new Set();
|
|
@@ -10725,11 +11345,12 @@ var AgentRunner = class {
|
|
|
10725
11345
|
try {
|
|
10726
11346
|
throwIfAborted(options.signal);
|
|
10727
11347
|
await this.reconcileToolArtifacts();
|
|
11348
|
+
ensureContextEpoch(this.session);
|
|
10728
11349
|
if (this.session.messages.length === 0 && this.session.title === "New session") {
|
|
10729
11350
|
this.session.title = titleFromInput(request);
|
|
10730
11351
|
}
|
|
10731
11352
|
this.contextManager.startTurn(this.session, request);
|
|
10732
|
-
const userMessage = message("user",
|
|
11353
|
+
const userMessage = message("user", submittedRequest);
|
|
10733
11354
|
this.activeReuseGate = { requestId: userMessage.id, request, attempted: false };
|
|
10734
11355
|
this.session.messages.push(userMessage);
|
|
10735
11356
|
await this.persist();
|
|
@@ -10737,11 +11358,48 @@ var AgentRunner = class {
|
|
|
10737
11358
|
const turnDirective = buildTurnDirective(request, {
|
|
10738
11359
|
agents: Boolean(this.config.agents?.enabled)
|
|
10739
11360
|
});
|
|
11361
|
+
const contractEnabled = shouldUseTaskContract(
|
|
11362
|
+
request,
|
|
11363
|
+
turnDirective.intent,
|
|
11364
|
+
this.session.taskContract
|
|
11365
|
+
);
|
|
10740
11366
|
const packed = trivialTurn ? emptyPackedContext(selectContextBudget(request, this.config, {
|
|
10741
11367
|
intent: turnDirective.intent,
|
|
10742
11368
|
trivial: true
|
|
10743
11369
|
})) : await this.packContext(request, { intent: turnDirective.intent });
|
|
10744
11370
|
if (!trivialTurn) await emit({ type: "context", packed });
|
|
11371
|
+
if (contractEnabled && (!this.session.taskContract || this.session.taskContract.state === "satisfied")) {
|
|
11372
|
+
this.session.taskContract = createDraftTaskContract(request, this.session.audit?.at(-1)?.id);
|
|
11373
|
+
await this.persist();
|
|
11374
|
+
await emit({ type: "contract", contract: structuredClone(this.session.taskContract) });
|
|
11375
|
+
}
|
|
11376
|
+
let intentDirective;
|
|
11377
|
+
if (pendingAtStart && resolvedInput) {
|
|
11378
|
+
this.session.intentAssessment = {
|
|
11379
|
+
version: 1,
|
|
11380
|
+
route: "direct_execute",
|
|
11381
|
+
reasons: ["clarification_resolved"],
|
|
11382
|
+
assessedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11383
|
+
retrievalHits: packed.hits.length
|
|
11384
|
+
};
|
|
11385
|
+
intentDirective = "The user resolved the recorded clarification. Continue the same logical run and apply that decision without asking again.";
|
|
11386
|
+
await emit({ type: "input_resolved", pendingId: pendingAtStart.id, runId: pendingAtStart.runId, answer: resolvedInput.answer });
|
|
11387
|
+
await emit({ type: "intent", assessment: this.session.intentAssessment });
|
|
11388
|
+
} else {
|
|
11389
|
+
const intent = assessIntentSufficiency(request, turnDirective.intent, {
|
|
11390
|
+
retrievalHits: packed.hits.length,
|
|
11391
|
+
complex: contractEnabled
|
|
11392
|
+
});
|
|
11393
|
+
this.session.intentAssessment = intent.assessment;
|
|
11394
|
+
intentDirective = intent.directive;
|
|
11395
|
+
await emit({ type: "intent", assessment: intent.assessment });
|
|
11396
|
+
if (intent.pending) {
|
|
11397
|
+
this.session.pendingInput = intent.pending;
|
|
11398
|
+
await this.persist();
|
|
11399
|
+
await emit({ type: "needs_input", pending: intent.pending });
|
|
11400
|
+
return finishRun("needs_input");
|
|
11401
|
+
}
|
|
11402
|
+
}
|
|
10745
11403
|
const mentions = await this.packMentions(request);
|
|
10746
11404
|
const retrievedContext = trivialTurn && !mentions.length ? "" : buildRetrievedContext(
|
|
10747
11405
|
packed,
|
|
@@ -10769,22 +11427,23 @@ var AgentRunner = class {
|
|
|
10769
11427
|
scope: augmentation.memoryScope ?? "session"
|
|
10770
11428
|
});
|
|
10771
11429
|
}
|
|
10772
|
-
const contractEnabled = shouldUseTaskContract(
|
|
10773
|
-
request,
|
|
10774
|
-
turnDirective.intent,
|
|
10775
|
-
this.session.taskContract
|
|
10776
|
-
);
|
|
10777
|
-
if (contractEnabled && (!this.session.taskContract || this.session.taskContract.state === "satisfied")) {
|
|
10778
|
-
this.session.taskContract = createDraftTaskContract(request, this.session.audit?.at(-1)?.id);
|
|
10779
|
-
await this.persist();
|
|
10780
|
-
await emit({ type: "contract", contract: structuredClone(this.session.taskContract) });
|
|
10781
|
-
}
|
|
10782
11430
|
activeRunContract = contractEnabled ? this.session.taskContract : void 0;
|
|
11431
|
+
let verificationAttempted = false;
|
|
11432
|
+
const maxTurns = options.maxTurns ?? this.config.agent.maxTurns;
|
|
11433
|
+
const epochTokenBudget = this.config.agent.maxEpochTokens ?? this.config.agent.maxSessionTokens;
|
|
11434
|
+
const contextBudget = Math.max(24e3, Math.min(1e5, this.config.context.maxTokens * 3));
|
|
11435
|
+
if (this.contextManager.shouldCompact(this.session, contextBudget)) {
|
|
11436
|
+
const compacted = await this.compactContext(void 0, options.signal, "automatic");
|
|
11437
|
+
if (compacted.status === "compacted") {
|
|
11438
|
+
await emit({ type: "context_compacted", ...compacted });
|
|
11439
|
+
}
|
|
11440
|
+
}
|
|
10783
11441
|
const promptSections = [
|
|
10784
11442
|
`intent:${turnDirective.intent}`,
|
|
10785
11443
|
...workspaceRules ? ["rules"] : [],
|
|
10786
11444
|
...this.session.workingMemory ? ["working-memory"] : [],
|
|
10787
|
-
...contractEnabled ? ["task-contract"] : [],
|
|
11445
|
+
...contractEnabled && !this.session.compactedThroughMessageId ? ["task-contract"] : [],
|
|
11446
|
+
...this.session.compactedThroughMessageId ? ["compaction-facts"] : [],
|
|
10788
11447
|
...this.session.contextSummary ? ["session-summary"] : [],
|
|
10789
11448
|
...!trivialTurn ? [`context:${packed.engine}`] : [],
|
|
10790
11449
|
...packed.text ? [`code:${packed.engine}`] : [],
|
|
@@ -10792,23 +11451,20 @@ var AgentRunner = class {
|
|
|
10792
11451
|
...augmentation.skills?.length ? [`skills:${augmentation.skills.length}`] : [],
|
|
10793
11452
|
...augmentation.memoryCount ? [`memory:${augmentation.memoryCount}`] : []
|
|
10794
11453
|
];
|
|
10795
|
-
let verificationAttempted = false;
|
|
10796
|
-
const maxTurns = options.maxTurns ?? this.config.agent.maxTurns;
|
|
10797
|
-
const contextBudget = Math.max(24e3, Math.min(1e5, this.config.context.maxTokens * 3));
|
|
10798
|
-
if (this.contextManager.shouldCompact(this.session, contextBudget)) {
|
|
10799
|
-
const compacted = await this.compactContext(void 0, options.signal);
|
|
10800
|
-
await emit({ type: "context_compacted", ...compacted });
|
|
10801
|
-
}
|
|
10802
11454
|
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
10803
11455
|
throwIfAborted(options.signal);
|
|
10804
11456
|
if (this.session.usage.inputTokens + this.session.usage.outputTokens >= this.config.agent.maxSessionTokens) {
|
|
10805
11457
|
return finishRun("token_budget");
|
|
10806
11458
|
}
|
|
11459
|
+
if (contextEpochTokens(this.session) >= epochTokenBudget) {
|
|
11460
|
+
await this.rollContextEpoch("token_budget", options.signal, emit);
|
|
11461
|
+
}
|
|
10807
11462
|
this.applySteering();
|
|
10808
11463
|
await emit({ type: "thinking", turn });
|
|
10809
11464
|
const dynamicPrompt = [
|
|
10810
11465
|
buildSessionStatePrompt(this.session),
|
|
10811
11466
|
turnDirective.text,
|
|
11467
|
+
`<intent-sufficiency route="${this.session.intentAssessment?.route ?? "direct_execute"}">${intentDirective}</intent-sufficiency>`,
|
|
10812
11468
|
this.contextManager.buildShortTermPrompt(this.session),
|
|
10813
11469
|
pinnedContext,
|
|
10814
11470
|
options.turnInstructions ?? "",
|
|
@@ -10821,7 +11477,9 @@ var AgentRunner = class {
|
|
|
10821
11477
|
activeMessages(this.session),
|
|
10822
11478
|
contextBudget
|
|
10823
11479
|
);
|
|
10824
|
-
const
|
|
11480
|
+
const availableLifetimeTokens = this.config.agent.maxSessionTokens - (this.session.usage.inputTokens + this.session.usage.outputTokens);
|
|
11481
|
+
const availableEpochTokens = epochTokenBudget - contextEpochTokens(this.session);
|
|
11482
|
+
const availableTokens = Math.min(availableLifetimeTokens, availableEpochTokens);
|
|
10825
11483
|
const visibleTools = visibleToolDefinitions(
|
|
10826
11484
|
this.tools,
|
|
10827
11485
|
options.askMode === true,
|
|
@@ -10835,9 +11493,15 @@ var AgentRunner = class {
|
|
|
10835
11493
|
loadedProgressiveTools
|
|
10836
11494
|
);
|
|
10837
11495
|
const estimatedInputTokens = estimateMessages2(messages) + estimateToolDefinitions(visibleTools);
|
|
10838
|
-
if (
|
|
11496
|
+
if (estimatedInputTokens >= availableLifetimeTokens) {
|
|
10839
11497
|
return finishRun("token_budget");
|
|
10840
11498
|
}
|
|
11499
|
+
if (availableTokens <= 0 || estimatedInputTokens >= availableEpochTokens) {
|
|
11500
|
+
if (contextEpochTokens(this.session) === 0) return finishRun("token_budget");
|
|
11501
|
+
await this.rollContextEpoch("token_budget", options.signal, emit);
|
|
11502
|
+
turn -= 1;
|
|
11503
|
+
continue;
|
|
11504
|
+
}
|
|
10841
11505
|
const maxOutputTokens = Math.max(1, Math.min(
|
|
10842
11506
|
this.config.model.maxTokens ?? 8192,
|
|
10843
11507
|
availableTokens - estimatedInputTokens
|
|
@@ -10859,7 +11523,7 @@ var AgentRunner = class {
|
|
|
10859
11523
|
breakdown
|
|
10860
11524
|
});
|
|
10861
11525
|
}
|
|
10862
|
-
const assistantId =
|
|
11526
|
+
const assistantId = randomUUID15();
|
|
10863
11527
|
const response = await this.completeModel(
|
|
10864
11528
|
messages,
|
|
10865
11529
|
visibleTools,
|
|
@@ -11239,6 +11903,9 @@ ${completeContent}`;
|
|
|
11239
11903
|
metadata.failure = receipt;
|
|
11240
11904
|
} else {
|
|
11241
11905
|
recovery.recordSuccess(call);
|
|
11906
|
+
const historicalFailures = new Set((this.session.audit ?? []).filter((event) => event.type === "tool" && event.outcome === "failure").map((event) => failureSignatureFromMetadata(event.metadata?.failure)).filter((signature) => signature !== void 0));
|
|
11907
|
+
const resolvedFailureSignatures = resolvableFailureSignatures(call).filter((signature) => historicalFailures.has(signature));
|
|
11908
|
+
if (resolvedFailureSignatures.length) metadata.resolvedFailureSignatures = resolvedFailureSignatures;
|
|
11242
11909
|
}
|
|
11243
11910
|
const evidenceProgress = recovery.recordEvidence(call, {
|
|
11244
11911
|
toolCallId: call.id,
|
|
@@ -11352,7 +12019,7 @@ ${completeContent}`;
|
|
|
11352
12019
|
}
|
|
11353
12020
|
appendAudit(event) {
|
|
11354
12021
|
const audit = this.session.audit ?? (this.session.audit = []);
|
|
11355
|
-
audit.push({ id:
|
|
12022
|
+
audit.push({ id: randomUUID15(), createdAt: (/* @__PURE__ */ new Date()).toISOString(), ...event });
|
|
11356
12023
|
if (audit.length > 5e3) audit.splice(0, audit.length - 5e3);
|
|
11357
12024
|
}
|
|
11358
12025
|
async acceptChangedFiles(paths) {
|
|
@@ -11373,7 +12040,7 @@ ${completeContent}`;
|
|
|
11373
12040
|
const results = [];
|
|
11374
12041
|
for (const command2 of this.config.agent.verifyCommands) {
|
|
11375
12042
|
const call = {
|
|
11376
|
-
id: `verify-${
|
|
12043
|
+
id: `verify-${randomUUID15()}`,
|
|
11377
12044
|
name: "shell",
|
|
11378
12045
|
arguments: { command: command2, cwd: this.workspace.primaryRoot }
|
|
11379
12046
|
};
|
|
@@ -11399,16 +12066,52 @@ ${completeContent}`;
|
|
|
11399
12066
|
return [];
|
|
11400
12067
|
}
|
|
11401
12068
|
}
|
|
11402
|
-
async compactContext(instructions, signal) {
|
|
12069
|
+
async compactContext(instructions, signal, mode = "manual") {
|
|
11403
12070
|
const result = await this.contextManager.compact(
|
|
11404
12071
|
this.session,
|
|
11405
12072
|
this.provider,
|
|
11406
12073
|
signal,
|
|
11407
|
-
instructions
|
|
12074
|
+
instructions,
|
|
12075
|
+
mode
|
|
11408
12076
|
);
|
|
12077
|
+
if (result.status === "compacted") {
|
|
12078
|
+
recordTokenUsage(
|
|
12079
|
+
this.session,
|
|
12080
|
+
result.receipt.actual,
|
|
12081
|
+
result.receipt.estimated.inputTokens,
|
|
12082
|
+
result.receipt.estimated.outputTokens
|
|
12083
|
+
);
|
|
12084
|
+
}
|
|
12085
|
+
const receipts = this.session.contextCompactionReceipts ?? (this.session.contextCompactionReceipts = []);
|
|
12086
|
+
receipts.push(result.receipt);
|
|
12087
|
+
if (receipts.length > 64) receipts.splice(0, receipts.length - 64);
|
|
11409
12088
|
await this.persist();
|
|
11410
12089
|
return result;
|
|
11411
12090
|
}
|
|
12091
|
+
async rollContextEpoch(reason, signal, emit) {
|
|
12092
|
+
let receipt;
|
|
12093
|
+
const contextBudget = Math.max(24e3, Math.min(1e5, this.config.context.maxTokens * 3));
|
|
12094
|
+
if (this.contextManager.shouldCompact(this.session, contextBudget)) {
|
|
12095
|
+
const compacted = await this.compactContext(
|
|
12096
|
+
"Create a boundary handoff for the next bounded context epoch.",
|
|
12097
|
+
signal,
|
|
12098
|
+
"automatic"
|
|
12099
|
+
);
|
|
12100
|
+
receipt = compacted.receipt;
|
|
12101
|
+
if (compacted.status === "compacted") await emit({ type: "context_compacted", ...compacted });
|
|
12102
|
+
}
|
|
12103
|
+
const rotated = rotateContextEpoch(this.session, reason, receipt);
|
|
12104
|
+
await this.persist();
|
|
12105
|
+
await emit({
|
|
12106
|
+
type: "context_epoch",
|
|
12107
|
+
index: rotated.current.index,
|
|
12108
|
+
previousIndex: rotated.previous.index,
|
|
12109
|
+
reason,
|
|
12110
|
+
inputTokens: rotated.previous.usage.inputTokens,
|
|
12111
|
+
outputTokens: rotated.previous.usage.outputTokens,
|
|
12112
|
+
handoff: rotated.handoff
|
|
12113
|
+
});
|
|
12114
|
+
}
|
|
11412
12115
|
getContextStatus() {
|
|
11413
12116
|
return this.contextManager.status(this.session);
|
|
11414
12117
|
}
|
|
@@ -11440,8 +12143,14 @@ ${completeContent}`;
|
|
|
11440
12143
|
toolOutputBudget() {
|
|
11441
12144
|
const contextWindowTokens = Math.max(24e3, Math.min(1e5, this.config.context.maxTokens * 3));
|
|
11442
12145
|
const activeContextTokens = this.contextManager.status(this.session, contextWindowTokens).activeTokens;
|
|
11443
|
-
const
|
|
11444
|
-
|
|
12146
|
+
const remainingLifetimeTokens = this.config.agent.maxSessionTokens - (this.session.usage.inputTokens + this.session.usage.outputTokens);
|
|
12147
|
+
const epochBudget = this.config.agent.maxEpochTokens ?? this.config.agent.maxSessionTokens;
|
|
12148
|
+
const remainingEpochTokens = epochBudget - contextEpochTokens(this.session);
|
|
12149
|
+
return dynamicToolOutputBudget(
|
|
12150
|
+
contextWindowTokens,
|
|
12151
|
+
activeContextTokens,
|
|
12152
|
+
Math.min(remainingLifetimeTokens, remainingEpochTokens)
|
|
12153
|
+
);
|
|
11445
12154
|
}
|
|
11446
12155
|
async protectToolResult(result) {
|
|
11447
12156
|
const metadata = { ...result.metadata ?? {} };
|
|
@@ -11497,7 +12206,7 @@ ${completeContent}`;
|
|
|
11497
12206
|
};
|
|
11498
12207
|
function message(role, content, extra = {}) {
|
|
11499
12208
|
return {
|
|
11500
|
-
id:
|
|
12209
|
+
id: randomUUID15(),
|
|
11501
12210
|
role,
|
|
11502
12211
|
content,
|
|
11503
12212
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -11612,6 +12321,7 @@ function recordTokenUsage(session, providerUsage, estimatedInputTokens, estimate
|
|
|
11612
12321
|
const outputTokens = outputActual ?? estimatedOutputTokens;
|
|
11613
12322
|
session.usage.inputTokens += inputTokens;
|
|
11614
12323
|
session.usage.outputTokens += outputTokens;
|
|
12324
|
+
recordContextEpochUsage(session, inputTokens, outputTokens);
|
|
11615
12325
|
if (inputActual !== void 0) {
|
|
11616
12326
|
session.usage.actualInputTokens = (session.usage.actualInputTokens ?? 0) + inputActual;
|
|
11617
12327
|
} else {
|
|
@@ -11687,6 +12397,11 @@ ${content}` : content,
|
|
|
11687
12397
|
...failure ? { metadata: { failure } } : {}
|
|
11688
12398
|
};
|
|
11689
12399
|
}
|
|
12400
|
+
function failureSignatureFromMetadata(value) {
|
|
12401
|
+
if (!value || typeof value !== "object") return;
|
|
12402
|
+
const signature = value.signature;
|
|
12403
|
+
return typeof signature === "string" && signature ? signature : void 0;
|
|
12404
|
+
}
|
|
11690
12405
|
function visibleToolDefinitions(tools, askMode, contractEnabled, artifactReadAvailable, duplicationAvailable, request, loadedProgressiveTools) {
|
|
11691
12406
|
const eligible = tools.definitions().filter(
|
|
11692
12407
|
(tool) => (!askMode || tool.category === "read") && (contractEnabled || tool.name !== "task_contract") && (artifactReadAvailable || tool.name !== "read_tool_artifact") && (duplicationAvailable || tool.name !== "duplication_audit")
|
|
@@ -11894,7 +12609,7 @@ function integer(value, fallback, min, max) {
|
|
|
11894
12609
|
}
|
|
11895
12610
|
|
|
11896
12611
|
// src/agent/delegation.ts
|
|
11897
|
-
import { randomUUID as
|
|
12612
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
11898
12613
|
import { join as join18 } from "node:path";
|
|
11899
12614
|
import { z as z19 } from "zod";
|
|
11900
12615
|
|
|
@@ -12023,7 +12738,7 @@ function numeric(...values) {
|
|
|
12023
12738
|
}
|
|
12024
12739
|
|
|
12025
12740
|
// src/agent/team-store.ts
|
|
12026
|
-
import { createHash as createHash16, randomUUID as
|
|
12741
|
+
import { createHash as createHash16, randomUUID as randomUUID16 } from "node:crypto";
|
|
12027
12742
|
import { lstat as lstat18, readFile as readFile17, readdir as readdir7, rm as rm3 } from "node:fs/promises";
|
|
12028
12743
|
import { join as join16, resolve as resolve18 } from "node:path";
|
|
12029
12744
|
import { z as z18 } from "zod";
|
|
@@ -12117,7 +12832,7 @@ var TeamRunStore = class {
|
|
|
12117
12832
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12118
12833
|
const manifest = manifestSchema2.parse({
|
|
12119
12834
|
version: 2,
|
|
12120
|
-
id:
|
|
12835
|
+
id: randomUUID16(),
|
|
12121
12836
|
workspace: this.workspace,
|
|
12122
12837
|
objective: input2.objective,
|
|
12123
12838
|
reviewer: input2.reviewer,
|
|
@@ -12923,7 +13638,7 @@ var DelegationManager = class {
|
|
|
12923
13638
|
maxReviewRounds: 0
|
|
12924
13639
|
});
|
|
12925
13640
|
await emit?.({ type: "team_start", id: board.id, objective: task });
|
|
12926
|
-
const writerId =
|
|
13641
|
+
const writerId = randomUUID17();
|
|
12927
13642
|
await emit?.({ type: "agent_queued", id: writerId, profile: profile.name, task, phase: "write" });
|
|
12928
13643
|
const draft = await this.writerLane.createDraft(
|
|
12929
13644
|
Math.min(this.team.maxWriterPatchBytes ?? 6e4, 12e4),
|
|
@@ -13293,7 +14008,7 @@ You are the only writer inside a disposable worktree. Make only the bounded requ
|
|
|
13293
14008
|
const reviewer = reviewerOverride ?? this.team.reviewerProfile ?? "reviewer";
|
|
13294
14009
|
const rounds = Math.max(0, this.team.maxReviewRounds ?? 1);
|
|
13295
14010
|
const board = await this.teamStore?.create({ objective, reviewer, maxReviewRounds: rounds });
|
|
13296
|
-
const runId = board?.id ??
|
|
14011
|
+
const runId = board?.id ?? randomUUID17();
|
|
13297
14012
|
await emit?.({ type: "team_start", id: runId, objective });
|
|
13298
14013
|
try {
|
|
13299
14014
|
let results = await this.runBatch(board?.id, tasks, "work", emit, signal);
|
|
@@ -13351,7 +14066,7 @@ ${review.summary}`,
|
|
|
13351
14066
|
}
|
|
13352
14067
|
}
|
|
13353
14068
|
async runBatch(runId, tasks, phase, emit, signal) {
|
|
13354
|
-
const scheduled = tasks.map((task) => ({ id:
|
|
14069
|
+
const scheduled = tasks.map((task) => ({ id: randomUUID17(), task }));
|
|
13355
14070
|
for (const item of scheduled) {
|
|
13356
14071
|
await emit?.({ type: "agent_queued", id: item.id, profile: item.task.profile, task: item.task.task, phase });
|
|
13357
14072
|
}
|
|
@@ -13460,12 +14175,12 @@ Start the response with exactly VERDICT: ACCEPT when the evidence is sufficient,
|
|
|
13460
14175
|
});
|
|
13461
14176
|
}
|
|
13462
14177
|
async peerMessage(runId, from, to, content, emit) {
|
|
13463
|
-
const id =
|
|
14178
|
+
const id = randomUUID17();
|
|
13464
14179
|
if (runId) await this.teamStore?.recordMessage(runId, { id, from, to, content });
|
|
13465
14180
|
await emit?.({ type: "agent_message", id, from, to, content });
|
|
13466
14181
|
}
|
|
13467
14182
|
async runOne(task, phase, emit, signal, retryOf, scheduledId) {
|
|
13468
|
-
const id = scheduledId ??
|
|
14183
|
+
const id = scheduledId ?? randomUUID17();
|
|
13469
14184
|
const profile = this.options.profiles.get(task.profile);
|
|
13470
14185
|
const configuredRoute = this.team.routes?.[task.profile];
|
|
13471
14186
|
const budgetMode = configuredRoute?.budgetMode ?? this.team.budgetMode ?? "observe";
|
|
@@ -14378,11 +15093,11 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
14378
15093
|
`);
|
|
14379
15094
|
}
|
|
14380
15095
|
printText(event) {
|
|
14381
|
-
const { quiet, compact:
|
|
14382
|
-
if (quiet) return;
|
|
15096
|
+
const { quiet, compact: compact3 } = this.options;
|
|
15097
|
+
if (quiet && event.type !== "needs_input") return;
|
|
14383
15098
|
switch (event.type) {
|
|
14384
15099
|
case "thinking":
|
|
14385
|
-
if (!
|
|
15100
|
+
if (!compact3) process.stderr.write(this.paint.dim(`${this.glyphs.meta} reasoning ${this.glyphs.separator} turn ${event.turn}
|
|
14386
15101
|
`));
|
|
14387
15102
|
break;
|
|
14388
15103
|
case "context": {
|
|
@@ -14394,7 +15109,7 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
14394
15109
|
break;
|
|
14395
15110
|
}
|
|
14396
15111
|
case "prompt":
|
|
14397
|
-
if (!
|
|
15112
|
+
if (!compact3) {
|
|
14398
15113
|
const partition = event.breakdown ? ` ${this.glyphs.separator} stable ${event.breakdown.stableTokens} ${this.glyphs.separator} dynamic ${event.breakdown.dynamicTokens} ${this.glyphs.separator} history ${event.breakdown.conversationTokens} ${this.glyphs.separator} tool results ${event.breakdown.toolResultTokens} ${this.glyphs.separator} retrieved ${event.breakdown.retrievedTokens} ${this.glyphs.separator} tools ${event.breakdown.toolSchemaTokens} ${this.glyphs.separator} output cap ${event.breakdown.outputAllowanceTokens}` : "";
|
|
14399
15114
|
process.stderr.write(this.paint.dim(
|
|
14400
15115
|
`${this.glyphs.meta} prompt ${this.glyphs.separator} ${event.intent} ${this.glyphs.separator} ~${event.estimatedTokens} estimated tokens${partition}
|
|
@@ -14430,7 +15145,7 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
14430
15145
|
}
|
|
14431
15146
|
break;
|
|
14432
15147
|
case "tasks":
|
|
14433
|
-
if (!
|
|
15148
|
+
if (!compact3) {
|
|
14434
15149
|
const completed = event.tasks.filter((task) => task.status === "completed").length;
|
|
14435
15150
|
process.stderr.write(this.paint.dim(`${this.glyphs.meta} plan ${this.glyphs.separator} ${completed}/${event.tasks.length} complete
|
|
14436
15151
|
`));
|
|
@@ -14451,6 +15166,22 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
14451
15166
|
`
|
|
14452
15167
|
);
|
|
14453
15168
|
break;
|
|
15169
|
+
case "needs_input":
|
|
15170
|
+
process.stderr.write(this.paint.yellow(`${this.glyphs.meta} ${event.pending.question}
|
|
15171
|
+
`));
|
|
15172
|
+
event.pending.options.forEach((option2, index) => {
|
|
15173
|
+
process.stderr.write(this.paint.dim(
|
|
15174
|
+
` ${index + 1}. ${option2.label}${option2.recommended ? " (recommended)" : ""} ${this.glyphs.separator} ${option2.impact}
|
|
15175
|
+
`
|
|
15176
|
+
));
|
|
15177
|
+
});
|
|
15178
|
+
break;
|
|
15179
|
+
case "input_resolved":
|
|
15180
|
+
process.stderr.write(this.paint.dim(
|
|
15181
|
+
`${this.glyphs.meta} clarification resolved ${this.glyphs.separator} ${event.answer}
|
|
15182
|
+
`
|
|
15183
|
+
));
|
|
15184
|
+
break;
|
|
14454
15185
|
case "usage":
|
|
14455
15186
|
case "permission":
|
|
14456
15187
|
case "skill":
|
|
@@ -14461,6 +15192,8 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
14461
15192
|
case "agent_done":
|
|
14462
15193
|
case "workflow":
|
|
14463
15194
|
case "context_compacted":
|
|
15195
|
+
case "context_epoch":
|
|
15196
|
+
case "intent":
|
|
14464
15197
|
break;
|
|
14465
15198
|
case "done":
|
|
14466
15199
|
this.printCompletion(event.completion);
|
|
@@ -14532,6 +15265,10 @@ function sessionSummary(session) {
|
|
|
14532
15265
|
changedFiles: session.changedFiles,
|
|
14533
15266
|
...session.lastRun ? { lastRun: session.lastRun } : {},
|
|
14534
15267
|
...session.tokenLedger?.length ? { tokenLedger: session.tokenLedger } : {},
|
|
15268
|
+
...session.contextCompactionReceipts?.length ? { contextCompactionReceipts: session.contextCompactionReceipts } : {},
|
|
15269
|
+
...session.contextEpochs?.length ? { contextEpochs: session.contextEpochs } : {},
|
|
15270
|
+
...session.intentAssessment ? { intentAssessment: session.intentAssessment } : {},
|
|
15271
|
+
...session.pendingInput ? { pendingInput: session.pendingInput } : {},
|
|
14535
15272
|
usage: session.usage
|
|
14536
15273
|
};
|
|
14537
15274
|
}
|
|
@@ -15224,7 +15961,7 @@ function ToolGlyph({ state, glyphs }) {
|
|
|
15224
15961
|
if (state === "cancelled") return /* @__PURE__ */ jsx(Text, { color: theme.warning, children: glyphs.warning });
|
|
15225
15962
|
return /* @__PURE__ */ jsx(Text, { color: theme.error, children: glyphs.error });
|
|
15226
15963
|
}
|
|
15227
|
-
function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = false, expandedToolId, compact:
|
|
15964
|
+
function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = false, expandedToolId, compact: compact3 = false }) {
|
|
15228
15965
|
const theme = useTheme();
|
|
15229
15966
|
const glyphs = resolveGlyphs(glyphMode);
|
|
15230
15967
|
if (!items.length) {
|
|
@@ -15232,13 +15969,13 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
15232
15969
|
}
|
|
15233
15970
|
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: items.map((item, index) => {
|
|
15234
15971
|
if (item.kind === "user") {
|
|
15235
|
-
return /* @__PURE__ */ jsxs(Box, { marginBottom:
|
|
15972
|
+
return /* @__PURE__ */ jsxs(Box, { marginBottom: compact3 || item.clipped ? 0 : 1, children: [
|
|
15236
15973
|
/* @__PURE__ */ jsx(Box, { width: 2, children: /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: glyphs.prompt }) }),
|
|
15237
15974
|
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.textStrong, wrap: "wrap", children: sanitizeTerminalText(item.text) })
|
|
15238
15975
|
] }, item.id);
|
|
15239
15976
|
}
|
|
15240
15977
|
if (item.kind === "assistant") {
|
|
15241
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom:
|
|
15978
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: compact3 || item.clipped ? 0 : 1, children: [
|
|
15242
15979
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: theme.accent, children: [
|
|
15243
15980
|
glyphs.brand,
|
|
15244
15981
|
" ",
|
|
@@ -15254,7 +15991,7 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
15254
15991
|
}
|
|
15255
15992
|
if (item.kind === "context") {
|
|
15256
15993
|
const spans = item.spans ?? [];
|
|
15257
|
-
const spanLimit =
|
|
15994
|
+
const spanLimit = compact3 ? 2 : 3;
|
|
15258
15995
|
const innerWidth = Math.max(1, safeWidth(width) - 2);
|
|
15259
15996
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
15260
15997
|
/* @__PURE__ */ jsx(
|
|
@@ -15267,7 +16004,7 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
15267
16004
|
labelColor: theme.accent
|
|
15268
16005
|
}
|
|
15269
16006
|
),
|
|
15270
|
-
!
|
|
16007
|
+
!compact3 && item.budgetReason ? /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(`${glyphs.branchLast} ${sanitizeInlineTerminalText(item.budgetReason)}`, innerWidth) }) : null,
|
|
15271
16008
|
spans.slice(0, spanLimit).map((span, spanIndex) => {
|
|
15272
16009
|
const lines = span.startLine === span.endLine ? `${span.startLine}` : `${span.startLine}-${span.endLine}`;
|
|
15273
16010
|
const location = `${compactDisplayPath(sanitizeInlineTerminalText(span.path), 44)}:${lines}`;
|
|
@@ -15307,7 +16044,7 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
15307
16044
|
const duration = item.durationMs !== void 0 ? formatDuration(item.durationMs) : "";
|
|
15308
16045
|
const detailText = [detail, duration].filter(Boolean).join(" ");
|
|
15309
16046
|
const expanded = Boolean(item.output) && (showToolOutput || expandedToolId === item.id);
|
|
15310
|
-
const verbose = expanded && item.output ? limitTerminalText(item.output,
|
|
16047
|
+
const verbose = expanded && item.output ? limitTerminalText(item.output, compact3 ? 24 : 80) : void 0;
|
|
15311
16048
|
const disclosure = item.output ? expanded ? glyphs.expanded : glyphs.collapsed : "";
|
|
15312
16049
|
const disclosureWidth = disclosure ? displayWidth(disclosure) + 1 : 0;
|
|
15313
16050
|
const nameLimit = Math.max(1, Math.min(rowWidth - 2 - disclosureWidth, rowWidth < 64 ? rowWidth - 2 - disclosureWidth : 28));
|
|
@@ -15439,9 +16176,28 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
15439
16176
|
item.id
|
|
15440
16177
|
);
|
|
15441
16178
|
}
|
|
16179
|
+
if (item.kind === "clarification") {
|
|
16180
|
+
const rowWidth = safeWidth(width);
|
|
16181
|
+
const chinese = /[\u3400-\u9fff]/u.test(item.pending.question);
|
|
16182
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
|
|
16183
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.warning, children: sanitizeTerminalText(item.pending.question) }),
|
|
16184
|
+
item.pending.options.map((option2, optionIndex) => {
|
|
16185
|
+
const label = `${optionIndex + 1}. ${sanitizeInlineTerminalText(option2.label)}${option2.recommended ? chinese ? "\uFF08\u63A8\u8350\uFF09" : " (recommended)" : ""}`;
|
|
16186
|
+
const impact = sanitizeInlineTerminalText(option2.impact);
|
|
16187
|
+
if (rowWidth < 48) {
|
|
16188
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
16189
|
+
/* @__PURE__ */ jsx(Text, { color: option2.recommended ? theme.textStrong : theme.muted, children: truncateDisplay(label, Math.max(1, rowWidth - 2)) }),
|
|
16190
|
+
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(`${glyphs.branchLast} ${impact}`, Math.max(1, (rowWidth - 2) * 2)) })
|
|
16191
|
+
] }, option2.id);
|
|
16192
|
+
}
|
|
16193
|
+
return /* @__PURE__ */ jsx(Text, { color: option2.recommended ? theme.textStrong : theme.muted, children: truncateDisplay(`${label} ${glyphs.separator} ${impact}`, Math.max(1, rowWidth - 2)) }, option2.id);
|
|
16194
|
+
}),
|
|
16195
|
+
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: chinese ? "\u56DE\u590D\u7F16\u53F7\u3001\u9009\u9879\u540D\u79F0\uFF0C\u6216\u7B80\u77ED\u8BF4\u660E\u4F60\u7684\u51B3\u5B9A\u3002" : "Reply with a number, option label, or a short custom decision." })
|
|
16196
|
+
] }, item.id);
|
|
16197
|
+
}
|
|
15442
16198
|
if (item.kind === "list") return /* @__PURE__ */ jsx(ListPanel, { title: item.title, entries: item.entries, width, glyphMode }, item.id);
|
|
15443
16199
|
if (item.kind === "context-inspector") {
|
|
15444
|
-
return /* @__PURE__ */ jsx(ContextInspector, { status: item.status, working: item.working, summary: item.summary, width, compact:
|
|
16200
|
+
return /* @__PURE__ */ jsx(ContextInspector, { status: item.status, working: item.working, summary: item.summary, width, compact: compact3, glyphMode }, item.id);
|
|
15445
16201
|
}
|
|
15446
16202
|
if (item.kind === "theme") return /* @__PURE__ */ jsx(ThemePreview, { name: item.name, width, glyphs }, item.id);
|
|
15447
16203
|
if (item.kind === "banner") {
|
|
@@ -15651,7 +16407,7 @@ function TaskRail({ tasks, width = 80, glyphMode = "auto", maxItems }) {
|
|
|
15651
16407
|
] }) : null
|
|
15652
16408
|
] });
|
|
15653
16409
|
}
|
|
15654
|
-
function PermissionCard({ call, category, width = 80, glyphMode = "auto", workspace, compact:
|
|
16410
|
+
function PermissionCard({ call, category, width = 80, glyphMode = "auto", workspace, compact: compact3 = false }) {
|
|
15655
16411
|
const theme = useTheme();
|
|
15656
16412
|
const glyphs = resolveGlyphs(glyphMode);
|
|
15657
16413
|
const summary = permissionSummary(call);
|
|
@@ -15688,7 +16444,7 @@ function PermissionCard({ call, category, width = 80, glyphMode = "auto", worksp
|
|
|
15688
16444
|
rowWidth >= 64 ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsx(InlineRow, { parts: shortcuts, width: innerWidth, separator: ` ${glyphs.separator} `, separatorColor: theme.border }) }) : rowWidth >= 28 ? /* @__PURE__ */ jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [
|
|
15689
16445
|
/* @__PURE__ */ jsx(InlineRow, { parts: shortcuts.slice(0, 2), width: innerWidth, separator: " ", separatorColor: theme.border }),
|
|
15690
16446
|
/* @__PURE__ */ jsx(InlineRow, { parts: shortcuts.slice(2), width: innerWidth, separator: " ", separatorColor: theme.border })
|
|
15691
|
-
] }) :
|
|
16447
|
+
] }) : compact3 ? /* @__PURE__ */ jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [
|
|
15692
16448
|
/* @__PURE__ */ jsx(InlineRow, { parts: compactNarrowShortcuts.slice(0, 2), width: innerWidth, separator: " ", separatorColor: theme.border }),
|
|
15693
16449
|
/* @__PURE__ */ jsx(InlineRow, { parts: compactNarrowShortcuts.slice(2), width: innerWidth, separator: " ", separatorColor: theme.border })
|
|
15694
16450
|
] }) : /* @__PURE__ */ jsx(Box, { paddingLeft: 2, flexDirection: "column", children: shortcuts.map((part) => part.color ? /* @__PURE__ */ jsx(Text, { color: part.color, children: truncateDisplay(part.text, innerWidth) }, part.text) : /* @__PURE__ */ jsx(Text, { children: truncateDisplay(part.text, innerWidth) }, part.text)) })
|
|
@@ -15949,15 +16705,16 @@ function MeterBar({ segments, total, width, glyphs }) {
|
|
|
15949
16705
|
remainder ? /* @__PURE__ */ jsx(Text, { color: theme.border, children: glyphs.meterEmpty.repeat(remainder) }) : null
|
|
15950
16706
|
] });
|
|
15951
16707
|
}
|
|
15952
|
-
function ContextInspector({ status, working, summary, width, memory, connections, sources: sources2, compact:
|
|
16708
|
+
function ContextInspector({ status, working, summary, width, memory, connections, sources: sources2, compact: compact3 = false, minimal = false, glyphMode = "auto" }) {
|
|
15953
16709
|
const theme = useTheme();
|
|
15954
16710
|
const glyphs = resolveGlyphs(glyphMode);
|
|
16711
|
+
const hasCompactedContext = status.compactedMessages > 0 || Boolean(summary);
|
|
15955
16712
|
if (minimal) {
|
|
15956
16713
|
const rowWidth2 = safeWidth(width);
|
|
15957
16714
|
const padding = rowWidth2 >= 4 ? 2 : 0;
|
|
15958
16715
|
const innerWidth2 = Math.max(1, rowWidth2 - padding);
|
|
15959
16716
|
const active = `${status.messageCount} msg ${glyphs.separator} ~${formatTokens(status.activeTokens)} tok`;
|
|
15960
|
-
const focus = sanitizeTerminalText(working?.focus || working?.goal || (
|
|
16717
|
+
const focus = sanitizeTerminalText(working?.focus || working?.goal || (hasCompactedContext ? "handoff ready" : "not established")).replace(/\s+/g, " ").trim() || "not established";
|
|
15961
16718
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: padding, children: [
|
|
15962
16719
|
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.textStrong, children: truncateDisplay(`Context ${formatPercent(status.pressure)} ${glyphs.separator} ${active}`, innerWidth2) }),
|
|
15963
16720
|
/* @__PURE__ */ jsx(Text, { color: working ? theme.text : theme.muted, children: truncateDisplay(`working ${focus}`, innerWidth2) })
|
|
@@ -15966,13 +16723,19 @@ function ContextInspector({ status, working, summary, width, memory, connections
|
|
|
15966
16723
|
const entries = [
|
|
15967
16724
|
{ label: "active", detail: `${status.messageCount} messages ${glyphs.separator} ~${formatTokens(status.activeTokens)} tokens ${glyphs.separator} tools ~${formatTokens(status.toolTokens)}` },
|
|
15968
16725
|
{ label: "short-term", detail: working ? `${working.focus || working.goal || "ready"} ${glyphs.separator} ${relativeTime(working.lastUpdatedAt)}` : "not established" },
|
|
15969
|
-
{ label: "summary", detail:
|
|
16726
|
+
{ label: "summary", detail: hasCompactedContext ? `~${formatTokens(status.summaryTokens)} tokens ${glyphs.separator} ${status.compactedMessages} compacted${summary ? "" : ` ${glyphs.separator} facts`}` : "not created" },
|
|
15970
16727
|
{ label: "long-term", detail: memory ?? `retrieved by relevance ${glyphs.separator} untrusted context` }
|
|
15971
16728
|
];
|
|
15972
|
-
if (
|
|
15973
|
-
|
|
15974
|
-
|
|
15975
|
-
|
|
16729
|
+
if (status.epochIndex !== void 0 && status.epochCount !== void 0 && status.epochTokens !== void 0 && status.epochBudget !== void 0 && status.lifetimeTokens !== void 0 && status.lifetimeBudget !== void 0) {
|
|
16730
|
+
entries.splice(1, 0, {
|
|
16731
|
+
label: "epoch",
|
|
16732
|
+
detail: `#${status.epochIndex} ${formatTokens(status.epochTokens)}/${formatTokens(status.epochBudget)} ${glyphs.separator} lifetime ${formatTokens(status.lifetimeTokens)}/${formatTokens(status.lifetimeBudget)}`
|
|
16733
|
+
});
|
|
16734
|
+
}
|
|
16735
|
+
if (!compact3 && working?.constraints.length) entries.push({ label: `constraints ${working.constraints.length}`, detail: working.constraints.slice(0, 2).join(` ${glyphs.separator} `) });
|
|
16736
|
+
if (!compact3 && working?.decisions.length) entries.push({ label: `decisions ${working.decisions.length}`, detail: working.decisions.slice(0, 2).join(` ${glyphs.separator} `) });
|
|
16737
|
+
if (!compact3 && working?.openQuestions.length) entries.push({ label: `open ${working.openQuestions.length}`, detail: working.openQuestions.slice(0, 2).join(` ${glyphs.separator} `), tone: "warning" });
|
|
16738
|
+
if (!compact3 && working?.relevantFiles.length) entries.push({ label: "relevant files", detail: working.relevantFiles.map((file) => compactDisplayPath(sanitizeInlineTerminalText(file), 28)).join(` ${glyphs.separator} `) });
|
|
15976
16739
|
if (sources2?.length) {
|
|
15977
16740
|
const pinned = sources2.filter((source) => source.state === "pinned");
|
|
15978
16741
|
const muted = sources2.filter((source) => source.state === "muted");
|
|
@@ -15988,7 +16751,7 @@ function ContextInspector({ status, working, summary, width, memory, connections
|
|
|
15988
16751
|
const rowWidth = safeWidth(width);
|
|
15989
16752
|
const innerWidth = Math.max(1, rowWidth - 2);
|
|
15990
16753
|
const pressureColor = status.pressure >= 0.9 ? theme.error : status.pressure >= 0.75 ? theme.warning : theme.accent;
|
|
15991
|
-
const summaryTokens =
|
|
16754
|
+
const summaryTokens = hasCompactedContext ? status.summaryTokens : 0;
|
|
15992
16755
|
const segments = [
|
|
15993
16756
|
{ label: "active", value: status.activeTokens, color: theme.accent },
|
|
15994
16757
|
{ label: "tools", value: status.toolTokens, color: theme.assistant },
|
|
@@ -16006,7 +16769,7 @@ function ContextInspector({ status, working, summary, width, memory, connections
|
|
|
16006
16769
|
/* @__PURE__ */ jsx(MeterBar, { segments, total: meterTotal, width: meterWidth, glyphs })
|
|
16007
16770
|
] }) : null
|
|
16008
16771
|
] }),
|
|
16009
|
-
/* @__PURE__ */ jsx(ListPanel, { title: "", hideTitle: true, entries, width, glyphMode })
|
|
16772
|
+
/* @__PURE__ */ jsx(ListPanel, { title: "", hideTitle: true, entries, width: Math.max(1, rowWidth - 2), glyphMode })
|
|
16010
16773
|
] });
|
|
16011
16774
|
}
|
|
16012
16775
|
function ThemePreview({ name, width, glyphs }) {
|
|
@@ -16093,8 +16856,8 @@ function Banner({ model, engine, workspace, version, width, glyphs }) {
|
|
|
16093
16856
|
function UpdateNotice({ current, latest, command: command2, highlights, width, glyphs }) {
|
|
16094
16857
|
const theme = useTheme();
|
|
16095
16858
|
const availableWidth = safeWidth(width);
|
|
16096
|
-
const
|
|
16097
|
-
const parts =
|
|
16859
|
+
const compact3 = availableWidth < 48;
|
|
16860
|
+
const parts = compact3 ? [
|
|
16098
16861
|
{ text: `${glyphs.up} `, color: theme.accent, bold: true },
|
|
16099
16862
|
{ text: `v${current}`, color: theme.dim, bold: false },
|
|
16100
16863
|
{ text: ` ${glyphs.arrow} `, color: theme.muted, bold: false },
|
|
@@ -16115,7 +16878,7 @@ function UpdateNotice({ current, latest, command: command2, highlights, width, g
|
|
|
16115
16878
|
const bullets = bulletWidth > 0 ? (highlights ?? []).map((line) => truncateDisplay(sanitizeInlineTerminalText(line), bulletWidth)) : [];
|
|
16116
16879
|
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [
|
|
16117
16880
|
/* @__PURE__ */ jsx(Box, { children: truncated ? /* @__PURE__ */ jsx(Text, { color: theme.muted, children: rendered }) : parts.map((part, index) => /* @__PURE__ */ jsx(Text, { color: part.color, bold: part.bold, children: part.text }, index)) }),
|
|
16118
|
-
|
|
16881
|
+
compact3 ? /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(` run ${command2}`, availableWidth) }) : null,
|
|
16119
16882
|
bullets.map((line, index) => /* @__PURE__ */ jsx(Text, { color: theme.dim, children: `${bulletPrefix}${line}` }, index))
|
|
16120
16883
|
] });
|
|
16121
16884
|
}
|
|
@@ -17023,9 +17786,9 @@ function tailText(value, width, maxRows) {
|
|
|
17023
17786
|
return `${marker}
|
|
17024
17787
|
${selected.join("\n")}`;
|
|
17025
17788
|
}
|
|
17026
|
-
function estimateTimelineItemRows(item, { width, compact:
|
|
17789
|
+
function estimateTimelineItemRows(item, { width, compact: compact3 = false, showToolOutput = false, expandedToolId }) {
|
|
17027
17790
|
const rowWidth = Math.max(1, Math.floor(width));
|
|
17028
|
-
const gap =
|
|
17791
|
+
const gap = compact3 ? 0 : 1;
|
|
17029
17792
|
if (item.kind === "user") return wrappedRows(item.text, Math.max(1, rowWidth - 2)) + (item.clipped ? 0 : gap);
|
|
17030
17793
|
if (item.kind === "assistant") {
|
|
17031
17794
|
return 1 + richTextRows(item.text, Math.max(1, rowWidth - 2)) + (item.clipped ? 0 : gap);
|
|
@@ -17037,7 +17800,7 @@ function estimateTimelineItemRows(item, { width, compact: compact2 = false, show
|
|
|
17037
17800
|
const detail = item.errorDetail || item.detail;
|
|
17038
17801
|
const detailRows = narrow && detail ? 1 : 0;
|
|
17039
17802
|
const metaRows = item.meta ? 1 : 0;
|
|
17040
|
-
const outputRows = (showToolOutput || item.id === expandedToolId) && item.output ? Math.min(
|
|
17803
|
+
const outputRows = (showToolOutput || item.id === expandedToolId) && item.output ? Math.min(compact3 ? 25 : 81, richTextRows(item.output, Math.max(1, rowWidth - 2))) : 0;
|
|
17041
17804
|
return 1 + detailRows + metaRows + outputRows;
|
|
17042
17805
|
}
|
|
17043
17806
|
if (item.kind === "list") {
|
|
@@ -17051,7 +17814,7 @@ function estimateTimelineItemRows(item, { width, compact: compact2 = false, show
|
|
|
17051
17814
|
if (item.kind === "theme") return 3;
|
|
17052
17815
|
if (item.kind === "context") {
|
|
17053
17816
|
const metaRows = rowWidth < 64 ? 2 : 1;
|
|
17054
|
-
const spanLimit =
|
|
17817
|
+
const spanLimit = compact3 ? 2 : 3;
|
|
17055
17818
|
const spanCount = Math.min(item.spans?.length ?? 0, spanLimit);
|
|
17056
17819
|
const moreRow = (item.spans?.length ?? 0) > spanLimit ? 1 : 0;
|
|
17057
17820
|
const degradationRows = item.degradation ? metaRows : 0;
|
|
@@ -17281,7 +18044,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
17281
18044
|
const colorEnabled = config.ui.color && !process.env.NO_COLOR;
|
|
17282
18045
|
const [theme, setTheme] = useState2(() => resolveThemeWithColor(config.ui.theme, colorEnabled));
|
|
17283
18046
|
const [themeCatalogRevision, setThemeCatalogRevision] = useState2(0);
|
|
17284
|
-
const [
|
|
18047
|
+
const [compact3, setCompact] = useState2(config.ui.compact);
|
|
17285
18048
|
const [interactionMode, setInteractionMode] = useState2(planMode ? "plan" : askMode ? "ask" : "build");
|
|
17286
18049
|
const [input2, setInput] = useState2("");
|
|
17287
18050
|
const [busy, setBusy] = useState2(false);
|
|
@@ -17318,6 +18081,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
17318
18081
|
const controller = useRef2(void 0);
|
|
17319
18082
|
const processing = useRef2(false);
|
|
17320
18083
|
const queued = useRef2([]);
|
|
18084
|
+
const clarificationBacklog = useRef2([]);
|
|
17321
18085
|
const stopRequested = useRef2(false);
|
|
17322
18086
|
const startedInitial = useRef2(false);
|
|
17323
18087
|
const lastSubmitted = useRef2(void 0);
|
|
@@ -17558,6 +18322,26 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
17558
18322
|
append({ id: nextId(), kind: "compaction", messages: event.omittedMessages, tokens: event.summaryTokens });
|
|
17559
18323
|
refreshSession();
|
|
17560
18324
|
break;
|
|
18325
|
+
case "context_epoch":
|
|
18326
|
+
append({
|
|
18327
|
+
id: nextId(),
|
|
18328
|
+
kind: "notice",
|
|
18329
|
+
tone: "info",
|
|
18330
|
+
text: `Context epoch ${event.previousIndex} \u2192 ${event.index}${separator}${event.reason.replace("_", " ")}${separator}${event.inputTokens + event.outputTokens} tokens preserved in the lifetime ledger.`
|
|
18331
|
+
});
|
|
18332
|
+
refreshSession();
|
|
18333
|
+
break;
|
|
18334
|
+
case "needs_input":
|
|
18335
|
+
append({ id: nextId(), kind: "clarification", pending: event.pending });
|
|
18336
|
+
refreshSession();
|
|
18337
|
+
break;
|
|
18338
|
+
case "input_resolved":
|
|
18339
|
+
append({ id: nextId(), kind: "notice", tone: "success", text: `Clarification resolved${separator}${event.answer}` });
|
|
18340
|
+
refreshSession();
|
|
18341
|
+
break;
|
|
18342
|
+
case "intent":
|
|
18343
|
+
refreshSession();
|
|
18344
|
+
break;
|
|
17561
18345
|
case "usage":
|
|
17562
18346
|
refreshSession();
|
|
17563
18347
|
break;
|
|
@@ -17801,8 +18585,8 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
17801
18585
|
return true;
|
|
17802
18586
|
}
|
|
17803
18587
|
if (subcommand === "list") {
|
|
17804
|
-
const
|
|
17805
|
-
appendList("Pinned context",
|
|
18588
|
+
const list = runner.listContextSources();
|
|
18589
|
+
appendList("Pinned context", list.length ? list.map((source) => ({
|
|
17806
18590
|
label: `${source.state === "muted" ? "muted " : "pinned"} ${source.path}`,
|
|
17807
18591
|
detail: `~${source.tokens} tokens${separator}added ${source.addedAt.slice(0, 10)}`,
|
|
17808
18592
|
tone: source.state === "muted" ? "warning" : "success"
|
|
@@ -17990,7 +18774,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
17990
18774
|
}
|
|
17991
18775
|
if (command2 === "density") {
|
|
17992
18776
|
const normalized = argument.toLocaleLowerCase();
|
|
17993
|
-
const next = normalized === "compact" ? true : normalized === "comfortable" || normalized === "normal" ? false : normalized === "" || normalized === "toggle" ? !
|
|
18777
|
+
const next = normalized === "compact" ? true : normalized === "comfortable" || normalized === "normal" ? false : normalized === "" || normalized === "toggle" ? !compact3 : void 0;
|
|
17994
18778
|
if (next === void 0) throw new Error("Usage: /density [compact|comfortable]");
|
|
17995
18779
|
setCompact(next);
|
|
17996
18780
|
await saveUiPreference({ compact: next });
|
|
@@ -18083,7 +18867,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
18083
18867
|
})));
|
|
18084
18868
|
return true;
|
|
18085
18869
|
}
|
|
18086
|
-
}, [append, appendList,
|
|
18870
|
+
}, [append, appendList, compact3, config, ellipsis, exit, extensions, interactionMode, refreshSession, requestPermission, runner, separator, showToolOutput, tasks, theme, workflows]);
|
|
18087
18871
|
const submit = useCallback(async (raw, mode = "normal") => {
|
|
18088
18872
|
const trimmed = raw.trim();
|
|
18089
18873
|
if (!trimmed) return;
|
|
@@ -18098,6 +18882,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
18098
18882
|
if (processing.current && isExitCommand(trimmed)) {
|
|
18099
18883
|
stopRequested.current = true;
|
|
18100
18884
|
queued.current = [];
|
|
18885
|
+
clarificationBacklog.current = [];
|
|
18101
18886
|
setQueue([]);
|
|
18102
18887
|
controller.current?.abort();
|
|
18103
18888
|
exit();
|
|
@@ -18185,6 +18970,31 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
18185
18970
|
const snapshot = snapshotSession(nextSession);
|
|
18186
18971
|
setSession(snapshot);
|
|
18187
18972
|
setTasks(snapshot.tasks.map((task) => ({ ...task })));
|
|
18973
|
+
if (snapshot.pendingInput) {
|
|
18974
|
+
const deferred = queued.current.length;
|
|
18975
|
+
clarificationBacklog.current.push(...queued.current);
|
|
18976
|
+
queued.current = [];
|
|
18977
|
+
setQueue([]);
|
|
18978
|
+
if (deferred) append({
|
|
18979
|
+
id: nextId(),
|
|
18980
|
+
kind: "notice",
|
|
18981
|
+
tone: "info",
|
|
18982
|
+
text: `Paused ${deferred} queued follow-up${deferred === 1 ? "" : "s"} for clarification; they will resume after the answer.`
|
|
18983
|
+
});
|
|
18984
|
+
break;
|
|
18985
|
+
}
|
|
18986
|
+
if (clarificationBacklog.current.length) {
|
|
18987
|
+
const resumed = clarificationBacklog.current.length;
|
|
18988
|
+
queued.current = [...clarificationBacklog.current, ...queued.current];
|
|
18989
|
+
clarificationBacklog.current = [];
|
|
18990
|
+
setQueue([...queued.current]);
|
|
18991
|
+
append({
|
|
18992
|
+
id: nextId(),
|
|
18993
|
+
kind: "notice",
|
|
18994
|
+
tone: "success",
|
|
18995
|
+
text: `Clarification resolved; resuming ${resumed} queued follow-up${resumed === 1 ? "" : "s"}.`
|
|
18996
|
+
});
|
|
18997
|
+
}
|
|
18188
18998
|
} catch (error) {
|
|
18189
18999
|
const message2 = error instanceof Error ? error.message : String(error);
|
|
18190
19000
|
if (!abortController.signal.aborted && message2 !== lastEventError.current) {
|
|
@@ -18197,8 +19007,9 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
18197
19007
|
}
|
|
18198
19008
|
}
|
|
18199
19009
|
if (abortController.signal.aborted || stopRequested.current) {
|
|
18200
|
-
const discarded = queued.current.length;
|
|
19010
|
+
const discarded = queued.current.length + clarificationBacklog.current.length;
|
|
18201
19011
|
queued.current = [];
|
|
19012
|
+
clarificationBacklog.current = [];
|
|
18202
19013
|
setQueue([]);
|
|
18203
19014
|
if (discarded) append({ id: nextId(), kind: "notice", text: `Discarded ${discarded} queued follow-up${discarded === 1 ? "" : "s"}.` });
|
|
18204
19015
|
break;
|
|
@@ -18447,7 +19258,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
18447
19258
|
const tokenTotal = session.usage.inputTokens + session.usage.outputTokens;
|
|
18448
19259
|
const contextStatus = runner.getContextStatus();
|
|
18449
19260
|
const frame = spinnerFrames()[frameIndex % spinnerFrames().length];
|
|
18450
|
-
const compactUi =
|
|
19261
|
+
const compactUi = compact3 || terminalHeight < 28;
|
|
18451
19262
|
const constrainedHeight = terminalHeight < 18;
|
|
18452
19263
|
const compactComposer = terminalHeight < 18;
|
|
18453
19264
|
const minimalInspector = terminalHeight < 22;
|
|
@@ -18663,6 +19474,9 @@ function initialTimeline(session, banner, setupProblem) {
|
|
|
18663
19474
|
text: `Contract ${session.taskContract.state} | ${satisfied}/${required.length} accepted`
|
|
18664
19475
|
});
|
|
18665
19476
|
}
|
|
19477
|
+
if (session.pendingInput) {
|
|
19478
|
+
items.push({ id: `pending-input-${session.pendingInput.id}`, kind: "clarification", pending: session.pendingInput });
|
|
19479
|
+
}
|
|
18666
19480
|
if (setupProblem && items.length <= 1) items.push({ id: nextId(), kind: "notice", tone: "error", text: setupProblem });
|
|
18667
19481
|
return items;
|
|
18668
19482
|
}
|
|
@@ -18708,6 +19522,35 @@ function snapshotSession(source) {
|
|
|
18708
19522
|
relevantFiles: [...source.workingMemory.relevantFiles]
|
|
18709
19523
|
}
|
|
18710
19524
|
} : {},
|
|
19525
|
+
...source.contextEpochs ? {
|
|
19526
|
+
contextEpochs: source.contextEpochs.map((epoch) => ({
|
|
19527
|
+
...epoch,
|
|
19528
|
+
usage: { ...epoch.usage },
|
|
19529
|
+
...epoch.handoff ? {
|
|
19530
|
+
handoff: {
|
|
19531
|
+
...epoch.handoff,
|
|
19532
|
+
...epoch.handoff.contract ? {
|
|
19533
|
+
contract: {
|
|
19534
|
+
...epoch.handoff.contract,
|
|
19535
|
+
required: epoch.handoff.contract.required.map((criterion2) => ({
|
|
19536
|
+
...criterion2,
|
|
19537
|
+
evidenceRefs: [...criterion2.evidenceRefs]
|
|
19538
|
+
}))
|
|
19539
|
+
}
|
|
19540
|
+
} : {},
|
|
19541
|
+
unresolvedFailures: epoch.handoff.unresolvedFailures.map((failure) => ({ ...failure })),
|
|
19542
|
+
changedFiles: [...epoch.handoff.changedFiles],
|
|
19543
|
+
checks: epoch.handoff.checks.map((check) => ({ ...check }))
|
|
19544
|
+
}
|
|
19545
|
+
} : {}
|
|
19546
|
+
}))
|
|
19547
|
+
} : {},
|
|
19548
|
+
...source.intentAssessment ? {
|
|
19549
|
+
intentAssessment: { ...source.intentAssessment, reasons: [...source.intentAssessment.reasons] }
|
|
19550
|
+
} : {},
|
|
19551
|
+
...source.pendingInput ? {
|
|
19552
|
+
pendingInput: { ...source.pendingInput, options: source.pendingInput.options.map((option2) => ({ ...option2 })) }
|
|
19553
|
+
} : {},
|
|
18711
19554
|
usage: { ...source.usage }
|
|
18712
19555
|
};
|
|
18713
19556
|
}
|
|
@@ -18754,17 +19597,17 @@ function composerAttachments(value) {
|
|
|
18754
19597
|
const paths = [...value.matchAll(/(?:^|\s)@([^\s]+)/g)].map((match) => match[1]).filter((path) => Boolean(path));
|
|
18755
19598
|
return [...new Set(paths)].slice(-3);
|
|
18756
19599
|
}
|
|
18757
|
-
function permissionRows(width, hasCwd,
|
|
19600
|
+
function permissionRows(width, hasCwd, compact3) {
|
|
18758
19601
|
const content = 3 + (hasCwd ? 1 : 0);
|
|
18759
19602
|
if (width >= 64) return content + 2;
|
|
18760
19603
|
if (width >= 28) return content + 3;
|
|
18761
|
-
if (
|
|
19604
|
+
if (compact3) return content + 3;
|
|
18762
19605
|
return content + 5;
|
|
18763
19606
|
}
|
|
18764
|
-
function contextInspectorRows(session,
|
|
19607
|
+
function contextInspectorRows(session, compact3, width, minimal) {
|
|
18765
19608
|
if (minimal) return 2;
|
|
18766
19609
|
const working = session.workingMemory;
|
|
18767
|
-
const entries =
|
|
19610
|
+
const entries = 6 + (compact3 ? 0 : (working?.constraints.length ? 1 : 0) + (working?.decisions.length ? 1 : 0) + (working?.openQuestions.length ? 1 : 0) + (working?.relevantFiles.length ? 1 : 0));
|
|
18768
19611
|
return 2 + entries * (width < 52 ? 2 : 1);
|
|
18769
19612
|
}
|
|
18770
19613
|
function contextInspectorStatus(status) {
|
|
@@ -18774,7 +19617,13 @@ function contextInspectorStatus(status) {
|
|
|
18774
19617
|
activeTokens: status.activeTokens,
|
|
18775
19618
|
summaryTokens: status.summaryTokens,
|
|
18776
19619
|
toolTokens: status.toolTokens,
|
|
18777
|
-
compactedMessages: status.compactedMessages
|
|
19620
|
+
compactedMessages: status.compactedMessages,
|
|
19621
|
+
epochIndex: status.epochIndex,
|
|
19622
|
+
epochCount: status.epochCount,
|
|
19623
|
+
epochTokens: status.epochTokens,
|
|
19624
|
+
epochBudget: status.epochBudget,
|
|
19625
|
+
lifetimeTokens: status.lifetimeTokens,
|
|
19626
|
+
lifetimeBudget: status.lifetimeBudget
|
|
18778
19627
|
};
|
|
18779
19628
|
}
|
|
18780
19629
|
function toolDetail2(call) {
|
|
@@ -18816,14 +19665,14 @@ function WorkspacePreparationView({
|
|
|
18816
19665
|
const theme = useTheme();
|
|
18817
19666
|
const safeWidth2 = Math.max(1, Math.floor(width));
|
|
18818
19667
|
const innerWidth = Math.max(1, safeWidth2 - (safeWidth2 >= 24 ? 4 : 0));
|
|
18819
|
-
const
|
|
19668
|
+
const compact3 = safeWidth2 < 48;
|
|
18820
19669
|
const constrained = height < 14;
|
|
18821
19670
|
const ascii = process.env.SKEIN_GLYPHS === "ascii" || process.env.MOSAIC_GLYPHS === "ascii";
|
|
18822
19671
|
const spinner = (ascii ? [".", "o", "O", "o"] : ["\u25DC", "\u25E0", "\u25DD", "\u25DE", "\u25E1", "\u25DF"])[frame % (ascii ? 4 : 6)];
|
|
18823
19672
|
const separator = ascii ? "|" : "\xB7";
|
|
18824
19673
|
const brand = ascii ? "*" : PRODUCT_MARK;
|
|
18825
19674
|
const phase = readiness ? "ready" : error ? "error" : progress.phase;
|
|
18826
|
-
const phaseLabel = preparationLabel(phase, progress, readiness,
|
|
19675
|
+
const phaseLabel = preparationLabel(phase, progress, readiness, compact3);
|
|
18827
19676
|
const detail = preparationDetail(phase, progress, readiness, error);
|
|
18828
19677
|
const modelLine = `model ${sanitizeTerminalText(model)}`;
|
|
18829
19678
|
const workspaceLine = `workspace ${compactDisplayPath(sanitizeTerminalText(workspace), Math.max(1, innerWidth - 10))}`;
|
|
@@ -18835,14 +19684,14 @@ function WorkspacePreparationView({
|
|
|
18835
19684
|
" ",
|
|
18836
19685
|
PRODUCT_NAME.toUpperCase()
|
|
18837
19686
|
] }),
|
|
18838
|
-
!
|
|
19687
|
+
!compact3 && !constrained ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
|
|
18839
19688
|
" ",
|
|
18840
19689
|
separator,
|
|
18841
19690
|
" LOCAL CONTEXT"
|
|
18842
19691
|
] }) : null
|
|
18843
19692
|
] }),
|
|
18844
19693
|
!constrained ? /* @__PURE__ */ jsx4(Text4, { color: theme.muted, children: truncateDisplay("Ground the workspace before the first request.", innerWidth) }) : null,
|
|
18845
|
-
!constrained && !
|
|
19694
|
+
!constrained && !compact3 ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: truncateDisplay(`${modelLine} ${separator} ${workspaceLine}`, innerWidth) }) : !constrained ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
18846
19695
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: truncateDisplay(modelLine, innerWidth) }),
|
|
18847
19696
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: truncateDisplay(workspaceLine, innerWidth) })
|
|
18848
19697
|
] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: truncateDisplay(workspaceLine, innerWidth) }),
|
|
@@ -18856,10 +19705,10 @@ function WorkspacePreparationView({
|
|
|
18856
19705
|
step2.glyph,
|
|
18857
19706
|
" "
|
|
18858
19707
|
] }),
|
|
18859
|
-
/* @__PURE__ */ jsx4(Text4, { bold: step2.state === "active", color: step2.state === "pending" ? theme.dim : theme.textStrong, children: truncateDisplay(step2.label,
|
|
19708
|
+
/* @__PURE__ */ jsx4(Text4, { bold: step2.state === "active", color: step2.state === "pending" ? theme.dim : theme.textStrong, children: truncateDisplay(step2.label, compact3 ? 8 : 10) }),
|
|
18860
19709
|
/* @__PURE__ */ jsxs4(Text4, { color: step2.state === "active" ? theme.muted : theme.dim, children: [
|
|
18861
19710
|
" ",
|
|
18862
|
-
truncateDisplay(step2.detail, Math.max(1, innerWidth - displayWidth(step2.glyph) - (
|
|
19711
|
+
truncateDisplay(step2.detail, Math.max(1, innerWidth - displayWidth(step2.glyph) - (compact3 ? 12 : 14)))
|
|
18863
19712
|
] })
|
|
18864
19713
|
] }, step2.id)) }),
|
|
18865
19714
|
/* @__PURE__ */ jsxs4(Box3, { marginTop: 1, children: [
|
|
@@ -19000,9 +19849,9 @@ async function runWorkspacePreparation(engine, config, options = {}) {
|
|
|
19000
19849
|
await instance.waitUntilExit();
|
|
19001
19850
|
return result ?? { status: "cancelled" };
|
|
19002
19851
|
}
|
|
19003
|
-
function preparationLabel(phase, progress, readiness,
|
|
19852
|
+
function preparationLabel(phase, progress, readiness, compact3 = false) {
|
|
19004
19853
|
if (phase === "ready") {
|
|
19005
|
-
if (
|
|
19854
|
+
if (compact3) return "Index verified";
|
|
19006
19855
|
return readiness?.rebuilt ? "Workspace index created and verified" : "Workspace index verified";
|
|
19007
19856
|
}
|
|
19008
19857
|
if (phase === "error") return "Workspace preparation failed";
|
|
@@ -19406,7 +20255,7 @@ function OnboardingFlow({ initialConfig, saveConfig, onFinish }) {
|
|
|
19406
20255
|
}, [finish, saveConfig, state]);
|
|
19407
20256
|
return /* @__PURE__ */ jsx5(OnboardingScreen, { state, dispatch, width, compact: compactHeight });
|
|
19408
20257
|
}
|
|
19409
|
-
function OnboardingScreen({ state, dispatch, width, compact:
|
|
20258
|
+
function OnboardingScreen({ state, dispatch, width, compact: compact3 = false }) {
|
|
19410
20259
|
const theme = useTheme();
|
|
19411
20260
|
const ascii = process.env.SKEIN_GLYPHS === "ascii" || process.env.MOSAIC_GLYPHS === "ascii";
|
|
19412
20261
|
const marker = ascii ? ">" : "\u203A";
|
|
@@ -19423,14 +20272,14 @@ function OnboardingScreen({ state, dispatch, width, compact: compact2 = false })
|
|
|
19423
20272
|
] }),
|
|
19424
20273
|
/* @__PURE__ */ jsx5(Text6, { color: theme.border, children: truncateDisplay(stageDivider(ascii, headerWidth), headerWidth) }),
|
|
19425
20274
|
/* @__PURE__ */ jsx5(Text6, { color: theme.dim, children: truncateDisplay(stage.name, headerWidth) }),
|
|
19426
|
-
!
|
|
20275
|
+
!compact3 ? /* @__PURE__ */ jsx5(Box4, { height: 1 }) : null,
|
|
19427
20276
|
/* @__PURE__ */ jsx5(Text6, { color: theme.textStrong, bold: true, children: truncateDisplay(titleForStep(state.step), headerWidth) }),
|
|
19428
|
-
!
|
|
20277
|
+
!compact3 ? /* @__PURE__ */ jsx5(Text6, { color: theme.muted, wrap: "wrap", children: descriptionForStep(state) }) : null,
|
|
19429
20278
|
summary ? /* @__PURE__ */ jsx5(Text6, { color: theme.dim, children: truncateDisplay(summary, headerWidth) }) : null,
|
|
19430
|
-
!
|
|
19431
|
-
state.step === "method" ? /* @__PURE__ */ jsx5(OptionList, { options: methods, selected: state.selected, marker, width: headerWidth, compact:
|
|
19432
|
-
state.step === "official-provider" ? /* @__PURE__ */ jsx5(OptionList, { options: officialProviders, selected: state.selected, marker, width: headerWidth, compact:
|
|
19433
|
-
state.step === "relay-protocol" ? /* @__PURE__ */ jsx5(OptionList, { options: relayProtocols, selected: state.selected, marker, width: headerWidth, compact:
|
|
20279
|
+
!compact3 ? /* @__PURE__ */ jsx5(Box4, { height: 1 }) : null,
|
|
20280
|
+
state.step === "method" ? /* @__PURE__ */ jsx5(OptionList, { options: methods, selected: state.selected, marker, width: headerWidth, compact: compact3 }) : null,
|
|
20281
|
+
state.step === "official-provider" ? /* @__PURE__ */ jsx5(OptionList, { options: officialProviders, selected: state.selected, marker, width: headerWidth, compact: compact3 }) : null,
|
|
20282
|
+
state.step === "relay-protocol" ? /* @__PURE__ */ jsx5(OptionList, { options: relayProtocols, selected: state.selected, marker, width: headerWidth, compact: compact3 }) : null,
|
|
19434
20283
|
inputField ? /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", children: [
|
|
19435
20284
|
/* @__PURE__ */ jsxs5(Box4, { children: [
|
|
19436
20285
|
/* @__PURE__ */ jsx5(Text6, { color: theme.textStrong, bold: true, children: inputField.label }),
|
|
@@ -19458,18 +20307,18 @@ function OnboardingScreen({ state, dispatch, width, compact: compact2 = false })
|
|
|
19458
20307
|
"! ",
|
|
19459
20308
|
truncateDisplay(state.error, Math.max(1, headerWidth - 2))
|
|
19460
20309
|
] }) : null,
|
|
19461
|
-
!
|
|
20310
|
+
!compact3 ? /* @__PURE__ */ jsx5(Box4, { height: 1 }) : null,
|
|
19462
20311
|
/* @__PURE__ */ jsx5(Text6, { color: theme.dim, children: footerForStep(state, headerWidth) })
|
|
19463
20312
|
] });
|
|
19464
20313
|
}
|
|
19465
|
-
function OptionList({ options, selected, marker, width, compact:
|
|
20314
|
+
function OptionList({ options, selected, marker, width, compact: compact3 }) {
|
|
19466
20315
|
const theme = useTheme();
|
|
19467
|
-
return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", children: options.map((
|
|
20316
|
+
return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", children: options.map((option2, index) => {
|
|
19468
20317
|
const active = index === selected;
|
|
19469
20318
|
const prefix = active ? `${marker} ` : " ";
|
|
19470
20319
|
const available = Math.max(1, width - displayWidth(prefix) - (active ? 2 : 0));
|
|
19471
|
-
const label = `${prefix}${truncateDisplay(
|
|
19472
|
-
return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom:
|
|
20320
|
+
const label = `${prefix}${truncateDisplay(option2.label, available)}`;
|
|
20321
|
+
return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: compact3 || index === options.length - 1 ? 0 : 1, children: [
|
|
19473
20322
|
/* @__PURE__ */ jsx5(
|
|
19474
20323
|
Text6,
|
|
19475
20324
|
{
|
|
@@ -19479,8 +20328,8 @@ function OptionList({ options, selected, marker, width, compact: compact2 }) {
|
|
|
19479
20328
|
children: active ? padDisplay(label, width) : label
|
|
19480
20329
|
}
|
|
19481
20330
|
),
|
|
19482
|
-
!
|
|
19483
|
-
] },
|
|
20331
|
+
!compact3 && width >= 36 || active ? /* @__PURE__ */ jsx5(Box4, { marginLeft: 2, width: Math.max(1, width - 2), children: /* @__PURE__ */ jsx5(Text6, { color: active ? theme.muted : theme.dim, wrap: "wrap", children: option2.detail }) }) : null
|
|
20332
|
+
] }, option2.label);
|
|
19484
20333
|
}) });
|
|
19485
20334
|
}
|
|
19486
20335
|
function Confirmation({ state, width }) {
|
|
@@ -21181,7 +22030,7 @@ process.on("warning", (warning) => {
|
|
|
21181
22030
|
var program = new Command();
|
|
21182
22031
|
program.enablePositionalOptions();
|
|
21183
22032
|
program.name(PRODUCT_COMMAND).description("A context-first, model-agnostic coding agent with an auditable workspace.").version(package_default.version).showSuggestionAfterError();
|
|
21184
|
-
program.argument("[prompt...]", "instruction for the agent").option("-p, --print", "run once and print the result").option("-a, --ask", "retrieval and inspection mode; mutation tools are denied").option("--plan", "read-only planning mode; propose changes without mutating the workspace").option("-q, --quiet", "print only the final response in text mode").addOption(new Option("--output-format <format>", "text, json, or stream-json").choices(["text", "json", "stream-json"]).default("text")).option("--compact", "reduce progress output in print mode").option("--yes", "approve all non-denied tool requests for this run").option("--auto-edit", "approve read/write requests and ask before shell/Git/network").option("--trust-project-config", "allow executable and security-sensitive settings from project config").option("--queue <prompt>", "run an additional prompt after the first one", collect, []).option("-w, --workspace <path>", "primary workspace root", process.cwd()).option("--add-workspace <path>", "additional workspace root", collect, []).option("--config <path>", "explicit config file").option("--provider <provider>", "model provider").option("--model <model>", "model identifier").option("--base-url <url>", "OpenAI-compatible or provider base URL").option("--max-turns <n>", "maximum agent turns").option("--token-budget <n>", "maximum
|
|
22033
|
+
program.argument("[prompt...]", "instruction for the agent").option("-p, --print", "run once and print the result").option("-a, --ask", "retrieval and inspection mode; mutation tools are denied").option("--plan", "read-only planning mode; propose changes without mutating the workspace").option("-q, --quiet", "print only the final response in text mode").addOption(new Option("--output-format <format>", "text, json, or stream-json").choices(["text", "json", "stream-json"]).default("text")).option("--compact", "reduce progress output in print mode").option("--yes", "approve all non-denied tool requests for this run").option("--auto-edit", "approve read/write requests and ask before shell/Git/network").option("--trust-project-config", "allow executable and security-sensitive settings from project config").option("--queue <prompt>", "run an additional prompt after the first one", collect, []).option("-w, --workspace <path>", "primary workspace root", process.cwd()).option("--add-workspace <path>", "additional workspace root", collect, []).option("--config <path>", "explicit config file").option("--provider <provider>", "model provider").option("--model <model>", "model identifier").option("--base-url <url>", "OpenAI-compatible or provider base URL").option("--max-turns <n>", "maximum agent turns").option("--epoch-token-budget <n>", "maximum tokens before an internal context handoff").option("--token-budget <n>", "maximum lifetime tokens across the resumed session").option("--resume [session]", "resume a session by id or prefix").option("-c, --continue", "resume the latest session").option("--no-color", "disable color output").option("--no-checkpoint", "disable pre-mutation checkpoints for this run").action(async (prompts, options) => {
|
|
21185
22034
|
await runChat(prompts, options);
|
|
21186
22035
|
});
|
|
21187
22036
|
program.command("init").description("Create a project-local config (preserving an existing .mosaic namespace)").option("-w, --workspace <path>", "workspace root").option("--provider <provider>", "openai, anthropic, gemini, or compatible", "openai").option("--model <model>", "model identifier").option("--base-url <url>", "provider base URL").option("--api-key <key>", "store a provider key in project config (prefer env vars)").option("--index", "build the index after writing config").option("--yes", "use defaults without prompting").action(async (options) => {
|
|
@@ -21872,6 +22721,7 @@ async function runChat(prompts, options) {
|
|
|
21872
22721
|
requestPermission
|
|
21873
22722
|
});
|
|
21874
22723
|
for (const queued of options.queue) {
|
|
22724
|
+
if (session.pendingInput) break;
|
|
21875
22725
|
session = await runner.run(queued, {
|
|
21876
22726
|
askMode: options.ask === true || options.plan === true,
|
|
21877
22727
|
...options.plan ? { turnInstructions: PLAN_MODE_INSTRUCTIONS } : {},
|
|
@@ -21881,6 +22731,7 @@ async function runChat(prompts, options) {
|
|
|
21881
22731
|
});
|
|
21882
22732
|
}
|
|
21883
22733
|
reporter.finish(session);
|
|
22734
|
+
if (session.pendingInput) process.exitCode = 2;
|
|
21884
22735
|
} catch (error) {
|
|
21885
22736
|
reporter.fail(error);
|
|
21886
22737
|
process.exitCode = 1;
|
|
@@ -22049,6 +22900,7 @@ async function runtimeConfig(workspaceInput, options) {
|
|
|
22049
22900
|
agent: {
|
|
22050
22901
|
...loaded.agent,
|
|
22051
22902
|
...options.checkpoint === false ? { checkpointBeforeWrite: false } : {},
|
|
22903
|
+
...options.epochTokenBudget ? { maxEpochTokens: positiveInt(options.epochTokenBudget, loaded.agent.maxEpochTokens ?? loaded.agent.maxSessionTokens) } : {},
|
|
22052
22904
|
...options.tokenBudget ? { maxSessionTokens: positiveInt(options.tokenBudget, loaded.agent.maxSessionTokens) } : {}
|
|
22053
22905
|
},
|
|
22054
22906
|
ui: { ...loaded.ui, ...options.color === false ? { color: false } : {} }
|
|
@@ -22198,6 +23050,7 @@ function runtimeOptions(options) {
|
|
|
22198
23050
|
const provider = options.provider ?? root.provider;
|
|
22199
23051
|
const model = options.model ?? root.model;
|
|
22200
23052
|
const baseUrl = options.baseUrl ?? root.baseUrl;
|
|
23053
|
+
const epochTokenBudget = options.epochTokenBudget ?? root.epochTokenBudget;
|
|
22201
23054
|
const tokenBudget = options.tokenBudget ?? root.tokenBudget;
|
|
22202
23055
|
return {
|
|
22203
23056
|
addWorkspace: [...root.addWorkspace ?? [], ...options.addWorkspace ?? []],
|
|
@@ -22206,6 +23059,7 @@ function runtimeOptions(options) {
|
|
|
22206
23059
|
...model ? { model } : {},
|
|
22207
23060
|
...baseUrl ? { baseUrl } : {},
|
|
22208
23061
|
...options.maxTokens ? { maxTokens: options.maxTokens } : {},
|
|
23062
|
+
...epochTokenBudget ? { epochTokenBudget } : {},
|
|
22209
23063
|
...tokenBudget ? { tokenBudget } : {},
|
|
22210
23064
|
...root.color !== void 0 ? { color: root.color } : {},
|
|
22211
23065
|
...root.checkpoint !== void 0 ? { checkpoint: root.checkpoint } : {},
|