@skein-code/cli 0.3.9 → 0.3.10
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 +1 -1
- package/dist/cli.js +1066 -419
- package/dist/cli.js.map +1 -1
- package/docs/NEXT_STEPS.md +9 -8
- package/package.json +4 -4
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.10",
|
|
224
224
|
description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
|
|
225
225
|
type: "module",
|
|
226
226
|
license: "MIT",
|
|
@@ -236,9 +236,9 @@ var package_default = {
|
|
|
236
236
|
},
|
|
237
237
|
skein: {
|
|
238
238
|
releaseNotes: [
|
|
239
|
-
"
|
|
240
|
-
"
|
|
241
|
-
"
|
|
239
|
+
"Complex executable requests now use a durable Task Contract with evidence-bound acceptance criteria",
|
|
240
|
+
"Completion stays unverified while required acceptance or final verification remains unresolved",
|
|
241
|
+
"Structured tool failure receipts add concise repair hints, retry budgets, cancellation handling, and identical-call circuit breaking"
|
|
242
242
|
]
|
|
243
243
|
},
|
|
244
244
|
bin: {
|
|
@@ -3279,7 +3279,7 @@ function formatContextHits(hits, roots) {
|
|
|
3279
3279
|
}
|
|
3280
3280
|
|
|
3281
3281
|
// src/agent/runner.ts
|
|
3282
|
-
import { randomUUID as
|
|
3282
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
3283
3283
|
|
|
3284
3284
|
// src/context/manager.ts
|
|
3285
3285
|
var RECENT_TURN_RESERVE = 3;
|
|
@@ -4974,6 +4974,19 @@ var lastRunSchema = z5.object({
|
|
|
4974
4974
|
checks: z5.array(verificationEvidenceSchema),
|
|
4975
4975
|
detail: z5.string(),
|
|
4976
4976
|
mutationTracking: z5.enum(["complete", "unknown"]).optional(),
|
|
4977
|
+
acceptance: z5.object({
|
|
4978
|
+
state: z5.enum(["draft", "active", "satisfied", "blocked"]),
|
|
4979
|
+
total: z5.number().int().nonnegative(),
|
|
4980
|
+
satisfied: z5.number().int().nonnegative(),
|
|
4981
|
+
pending: z5.number().int().nonnegative(),
|
|
4982
|
+
blocked: z5.number().int().nonnegative(),
|
|
4983
|
+
missingVerification: z5.array(z5.string()),
|
|
4984
|
+
unresolved: z5.array(z5.object({
|
|
4985
|
+
id: z5.string(),
|
|
4986
|
+
description: z5.string(),
|
|
4987
|
+
status: z5.enum(["pending", "satisfied", "blocked"])
|
|
4988
|
+
}).strict())
|
|
4989
|
+
}).strict().optional(),
|
|
4977
4990
|
reason: z5.string(),
|
|
4978
4991
|
finishedAt: z5.string()
|
|
4979
4992
|
}).strict();
|
|
@@ -4986,6 +4999,27 @@ var workingMemorySchema = z5.object({
|
|
|
4986
4999
|
relevantFiles: z5.array(z5.string()),
|
|
4987
5000
|
lastUpdatedAt: z5.string()
|
|
4988
5001
|
}).strict();
|
|
5002
|
+
var taskContractCriterionSchema = z5.object({
|
|
5003
|
+
id: z5.string().min(1).max(128),
|
|
5004
|
+
description: z5.string().min(1).max(2e3),
|
|
5005
|
+
required: z5.boolean(),
|
|
5006
|
+
status: z5.enum(["pending", "satisfied", "blocked"]),
|
|
5007
|
+
evidenceRefs: z5.array(z5.string().min(1).max(256)).max(64),
|
|
5008
|
+
note: z5.string().max(2e3).optional()
|
|
5009
|
+
}).strict();
|
|
5010
|
+
var taskContractSchema = z5.object({
|
|
5011
|
+
version: z5.literal(1),
|
|
5012
|
+
state: z5.enum(["draft", "active", "satisfied", "blocked"]),
|
|
5013
|
+
objective: z5.string().min(1).max(2e4),
|
|
5014
|
+
scope: z5.array(z5.string().min(1).max(2e3)).max(64),
|
|
5015
|
+
constraints: z5.array(z5.string().min(1).max(2e3)).max(64),
|
|
5016
|
+
nonGoals: z5.array(z5.string().min(1).max(2e3)).max(64),
|
|
5017
|
+
acceptanceCriteria: z5.array(taskContractCriterionSchema).min(1).max(64),
|
|
5018
|
+
verificationRequirements: z5.array(z5.string().min(1).max(2e3)).max(64),
|
|
5019
|
+
createdAt: z5.string(),
|
|
5020
|
+
updatedAt: z5.string(),
|
|
5021
|
+
auditBoundaryId: z5.string().min(1).max(128).optional()
|
|
5022
|
+
}).strict();
|
|
4989
5023
|
var sessionSchema = z5.object({
|
|
4990
5024
|
id: sessionIdSchema,
|
|
4991
5025
|
title: z5.string(),
|
|
@@ -5003,6 +5037,7 @@ var sessionSchema = z5.object({
|
|
|
5003
5037
|
compactedThroughMessageId: z5.string().optional(),
|
|
5004
5038
|
workingMemory: workingMemorySchema.optional(),
|
|
5005
5039
|
contextSources: z5.array(contextSourceSchema).max(64).optional(),
|
|
5040
|
+
taskContract: taskContractSchema.optional(),
|
|
5006
5041
|
lastRun: lastRunSchema.optional(),
|
|
5007
5042
|
usage: z5.object({
|
|
5008
5043
|
inputTokens: z5.number().nonnegative(),
|
|
@@ -6751,120 +6786,91 @@ var taskTool = {
|
|
|
6751
6786
|
}
|
|
6752
6787
|
};
|
|
6753
6788
|
|
|
6754
|
-
// src/tools/
|
|
6789
|
+
// src/tools/task-contract.ts
|
|
6790
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
6755
6791
|
import { z as z13 } from "zod";
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
"set_focus",
|
|
6777
|
-
"add_constraint",
|
|
6778
|
-
"add_decision",
|
|
6779
|
-
"add_question",
|
|
6780
|
-
"resolve_question",
|
|
6781
|
-
"add_file",
|
|
6782
|
-
"clear"
|
|
6783
|
-
] },
|
|
6784
|
-
value: { type: "string", description: "Text for a goal, focus, constraint, decision, or question." },
|
|
6785
|
-
path: { type: "string", description: "Workspace-relative relevant file path." },
|
|
6786
|
-
field: { type: "string", enum: ["constraints", "decisions", "openQuestions", "relevantFiles"] }
|
|
6787
|
-
}, ["action"])
|
|
6788
|
-
},
|
|
6789
|
-
async execute(arguments_, context) {
|
|
6790
|
-
const input2 = inputSchema9.parse(arguments_);
|
|
6791
|
-
const memory = context.session.workingMemory ?? emptyWorkingMemory2();
|
|
6792
|
-
switch (input2.action) {
|
|
6793
|
-
case "show":
|
|
6794
|
-
break;
|
|
6795
|
-
case "set_goal":
|
|
6796
|
-
memory.goal = clean(input2.value);
|
|
6797
|
-
break;
|
|
6798
|
-
case "set_focus":
|
|
6799
|
-
memory.focus = clean(input2.value);
|
|
6800
|
-
break;
|
|
6801
|
-
case "add_constraint":
|
|
6802
|
-
pushBounded2(memory.constraints, input2.value, 12);
|
|
6803
|
-
break;
|
|
6804
|
-
case "add_decision":
|
|
6805
|
-
pushBounded2(memory.decisions, input2.value, 12);
|
|
6806
|
-
break;
|
|
6807
|
-
case "add_question":
|
|
6808
|
-
pushBounded2(memory.openQuestions, input2.value, 12);
|
|
6809
|
-
break;
|
|
6810
|
-
case "resolve_question":
|
|
6811
|
-
removeMatching(memory.openQuestions, input2.value);
|
|
6812
|
-
break;
|
|
6813
|
-
case "add_file": {
|
|
6814
|
-
const safe = await context.workspace.resolvePath(input2.path, { expect: "any" });
|
|
6815
|
-
pushBounded2(memory.relevantFiles, safe, 24);
|
|
6816
|
-
break;
|
|
6817
|
-
}
|
|
6818
|
-
case "clear":
|
|
6819
|
-
memory[input2.field] = [];
|
|
6820
|
-
break;
|
|
6821
|
-
}
|
|
6822
|
-
memory.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6823
|
-
context.session.workingMemory = memory;
|
|
6824
|
-
return { content: render(memory), metadata: { workingMemory: { ...memory } } };
|
|
6825
|
-
}
|
|
6826
|
-
};
|
|
6827
|
-
function emptyWorkingMemory2() {
|
|
6792
|
+
|
|
6793
|
+
// src/agent/task-contract.ts
|
|
6794
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
6795
|
+
var EXECUTABLE_INTENTS = /* @__PURE__ */ new Set(["debug", "refactor", "test", "implement"]);
|
|
6796
|
+
function shouldUseTaskContract(request, intent, existing) {
|
|
6797
|
+
if (existing && existing.state !== "satisfied") return true;
|
|
6798
|
+
if (!EXECUTABLE_INTENTS.has(intent)) return false;
|
|
6799
|
+
const value = request.trim();
|
|
6800
|
+
if (value.length < 180) return false;
|
|
6801
|
+
const requirements = value.split(/(?:\n+|[;;。]\s*|\b(?:and|then|also|plus)\b|以及|并且|同时|还要|另外)/iu).filter((item) => item.trim().length >= 12);
|
|
6802
|
+
const complexitySignals = [
|
|
6803
|
+
/\b(?:refactor|migrate|redesign|architecture|end[- ]to[- ]end|across|multiple)\b/iu,
|
|
6804
|
+
/重构|迁移|架构|完整|全链路|多个|跨模块|兼容|同时/iu,
|
|
6805
|
+
/(?:^|\n)\s*(?:[-*]|\d+[.)、])\s+/mu
|
|
6806
|
+
].filter((pattern) => pattern.test(value)).length;
|
|
6807
|
+
return requirements.length >= 3 || complexitySignals >= 2 || value.length >= 500;
|
|
6808
|
+
}
|
|
6809
|
+
function createDraftTaskContract(request, auditBoundaryId) {
|
|
6810
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6811
|
+
const objective = compact(request, 2e3);
|
|
6828
6812
|
return {
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6813
|
+
version: 1,
|
|
6814
|
+
state: "draft",
|
|
6815
|
+
objective,
|
|
6816
|
+
scope: ["Configured workspace roots and the files required by the objective."],
|
|
6817
|
+
constraints: [
|
|
6818
|
+
"Preserve unrelated user changes.",
|
|
6819
|
+
"Use successful tool evidence for acceptance claims."
|
|
6820
|
+
],
|
|
6821
|
+
nonGoals: [],
|
|
6822
|
+
acceptanceCriteria: [
|
|
6823
|
+
criterion("requested-outcome", `Implement the requested outcome: ${compact(request, 500)}`),
|
|
6824
|
+
criterion("verification", "Run the smallest relevant deterministic verification after the final mutation.")
|
|
6825
|
+
],
|
|
6826
|
+
verificationRequirements: [
|
|
6827
|
+
"Record at least one successful test, typecheck, lint, build, check, or diff check after the final mutation."
|
|
6828
|
+
],
|
|
6829
|
+
createdAt: now,
|
|
6830
|
+
updatedAt: now,
|
|
6831
|
+
...auditBoundaryId ? { auditBoundaryId } : {}
|
|
6836
6832
|
};
|
|
6837
6833
|
}
|
|
6838
|
-
function
|
|
6839
|
-
const
|
|
6840
|
-
const
|
|
6841
|
-
if (
|
|
6842
|
-
|
|
6843
|
-
|
|
6834
|
+
function successfulContractEvidence(session) {
|
|
6835
|
+
const refs = /* @__PURE__ */ new Set();
|
|
6836
|
+
const contract = session.taskContract;
|
|
6837
|
+
if (!contract) return refs;
|
|
6838
|
+
for (const event of eventsSinceContract(session.audit ?? [], contract)) {
|
|
6839
|
+
if (event.type !== "tool" || event.outcome !== "success") continue;
|
|
6840
|
+
if (event.tool === "task_contract" || event.tool === "task" || event.tool === "working_memory") continue;
|
|
6841
|
+
refs.add(event.id);
|
|
6842
|
+
refs.add(event.toolCallId);
|
|
6843
|
+
}
|
|
6844
|
+
return refs;
|
|
6844
6845
|
}
|
|
6845
|
-
function
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
if (candidate.toLocaleLowerCase() === query || candidate.toLocaleLowerCase().includes(query)) {
|
|
6850
|
-
values.splice(index, 1);
|
|
6851
|
-
}
|
|
6846
|
+
function eventsSinceContract(audit, contract) {
|
|
6847
|
+
if (contract.auditBoundaryId) {
|
|
6848
|
+
const boundary = audit.findIndex((event) => event.id === contract.auditBoundaryId);
|
|
6849
|
+
if (boundary >= 0) return audit.slice(boundary + 1);
|
|
6852
6850
|
}
|
|
6851
|
+
return audit.filter((event) => event.createdAt >= contract.createdAt);
|
|
6853
6852
|
}
|
|
6854
|
-
function
|
|
6855
|
-
|
|
6853
|
+
function refreshTaskContractState(contract) {
|
|
6854
|
+
const required = contract.acceptanceCriteria.filter((item) => item.required);
|
|
6855
|
+
contract.state = required.some((item) => item.status === "blocked") ? "blocked" : required.length > 0 && required.every((item) => item.status === "satisfied") ? "satisfied" : "active";
|
|
6856
|
+
contract.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6856
6857
|
}
|
|
6857
|
-
function
|
|
6858
|
-
return
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6858
|
+
function criterion(id, description) {
|
|
6859
|
+
return {
|
|
6860
|
+
id: `${id}-${randomUUID10().slice(0, 8)}`,
|
|
6861
|
+
description,
|
|
6862
|
+
required: true,
|
|
6863
|
+
status: "pending",
|
|
6864
|
+
evidenceRefs: []
|
|
6865
|
+
};
|
|
6866
|
+
}
|
|
6867
|
+
function compact(value, limit) {
|
|
6868
|
+
return value.trim().replace(/\s+/gu, " ").slice(0, limit);
|
|
6866
6869
|
}
|
|
6867
6870
|
|
|
6871
|
+
// src/agent/completion-gate.ts
|
|
6872
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
6873
|
+
|
|
6868
6874
|
// src/tools/permissions.ts
|
|
6869
6875
|
import { createHash as createHash8, createHmac, randomBytes } from "node:crypto";
|
|
6870
6876
|
var permissionScopeSecret = randomBytes(32);
|
|
@@ -7025,27 +7031,529 @@ function normalizeFullUrl(value) {
|
|
|
7025
7031
|
return normalized.slice(0, 8e3);
|
|
7026
7032
|
}
|
|
7027
7033
|
}
|
|
7028
|
-
function scopeFingerprint(value) {
|
|
7029
|
-
return createHmac("sha256", permissionScopeSecret).update(stableScopeValue(value)).digest("hex").slice(0, 24);
|
|
7034
|
+
function scopeFingerprint(value) {
|
|
7035
|
+
return createHmac("sha256", permissionScopeSecret).update(stableScopeValue(value)).digest("hex").slice(0, 24);
|
|
7036
|
+
}
|
|
7037
|
+
function stableScopeValue(value) {
|
|
7038
|
+
if (value === null) return "null";
|
|
7039
|
+
if (typeof value === "string") return `string:${JSON.stringify(value)}`;
|
|
7040
|
+
if (typeof value === "number") return `number:${String(value)}`;
|
|
7041
|
+
if (typeof value === "boolean") return `boolean:${String(value)}`;
|
|
7042
|
+
if (typeof value === "undefined") return "undefined";
|
|
7043
|
+
if (Array.isArray(value)) return `array:[${value.map(stableScopeValue).join(",")}]`;
|
|
7044
|
+
if (typeof value === "object") {
|
|
7045
|
+
const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableScopeValue(item)}`);
|
|
7046
|
+
return `object:{${entries.join(",")}}`;
|
|
7047
|
+
}
|
|
7048
|
+
return `${typeof value}:${String(value)}`;
|
|
7049
|
+
}
|
|
7050
|
+
function containsShellControl(command2) {
|
|
7051
|
+
return /(?:[;&|<>`\n\r]|\$\(|\$\{)/.test(command2);
|
|
7052
|
+
}
|
|
7053
|
+
function escapeRegExp(value) {
|
|
7054
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7055
|
+
}
|
|
7056
|
+
|
|
7057
|
+
// src/agent/completion-gate.ts
|
|
7058
|
+
function captureVerification(call, result, changeSequence, configuredCommands) {
|
|
7059
|
+
if (call.name !== "shell" && call.name !== "git") return void 0;
|
|
7060
|
+
const command2 = commandForCall(call);
|
|
7061
|
+
if (!command2) return void 0;
|
|
7062
|
+
const normalized = normalizeCommand2(command2);
|
|
7063
|
+
const configured = new Set(configuredCommands.map(normalizeCommand2));
|
|
7064
|
+
const kind = configured.has(normalized) ? "configured" : classifyVerificationCommand(normalized);
|
|
7065
|
+
if (!kind) return void 0;
|
|
7066
|
+
return {
|
|
7067
|
+
toolCallId: call.id,
|
|
7068
|
+
tool: call.name,
|
|
7069
|
+
command: redactCommand(command2),
|
|
7070
|
+
kind,
|
|
7071
|
+
ok: result.ok,
|
|
7072
|
+
changeSequence,
|
|
7073
|
+
commandKey: createHash9("sha256").update(normalized).digest("hex")
|
|
7074
|
+
};
|
|
7075
|
+
}
|
|
7076
|
+
function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = []) {
|
|
7077
|
+
const files = [...new Set(changedFiles)];
|
|
7078
|
+
const acceptance = taskContract ? buildAcceptance(taskContract, audit, evidence, currentChangeSequence) : void 0;
|
|
7079
|
+
if (mutationTracking === "unknown") {
|
|
7080
|
+
return {
|
|
7081
|
+
status: "unverified",
|
|
7082
|
+
changedFiles: files,
|
|
7083
|
+
checks: [],
|
|
7084
|
+
detail: files.length ? `Workspace changes were observed, but a dynamic shell command prevented complete mutation tracking for ${fileCount(files.length)}.` : "A dynamic shell command may have changed workspace files, but reliable mutation tracking was unavailable.",
|
|
7085
|
+
mutationTracking,
|
|
7086
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "unverified") } : {}
|
|
7087
|
+
};
|
|
7088
|
+
}
|
|
7089
|
+
if (!files.length) {
|
|
7090
|
+
if (acceptance && acceptanceUnresolved(acceptance)) {
|
|
7091
|
+
return {
|
|
7092
|
+
status: "unverified",
|
|
7093
|
+
changedFiles: [],
|
|
7094
|
+
checks: [],
|
|
7095
|
+
detail: acceptanceDetail(acceptance),
|
|
7096
|
+
acceptance: acceptanceForCompletion(acceptance, "unverified")
|
|
7097
|
+
};
|
|
7098
|
+
}
|
|
7099
|
+
return {
|
|
7100
|
+
status: "no_changes",
|
|
7101
|
+
changedFiles: [],
|
|
7102
|
+
checks: [],
|
|
7103
|
+
detail: "No workspace files changed in this run.",
|
|
7104
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "no_changes") } : {}
|
|
7105
|
+
};
|
|
7106
|
+
}
|
|
7107
|
+
const latestByCommand = /* @__PURE__ */ new Map();
|
|
7108
|
+
for (const item of evidence) {
|
|
7109
|
+
if (item.changeSequence === currentChangeSequence) {
|
|
7110
|
+
latestByCommand.set(item.commandKey, item);
|
|
7111
|
+
}
|
|
7112
|
+
}
|
|
7113
|
+
const checks = [...latestByCommand.values()].map(publicEvidence);
|
|
7114
|
+
if (!checks.length) {
|
|
7115
|
+
return {
|
|
7116
|
+
status: "unverified",
|
|
7117
|
+
changedFiles: files,
|
|
7118
|
+
checks,
|
|
7119
|
+
detail: `No successful verification was recorded after the last change to ${fileCount(files.length)}.`,
|
|
7120
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "unverified") } : {}
|
|
7121
|
+
};
|
|
7122
|
+
}
|
|
7123
|
+
const failures = checks.filter((check) => !check.ok);
|
|
7124
|
+
if (failures.length) {
|
|
7125
|
+
return {
|
|
7126
|
+
status: "verification_failed",
|
|
7127
|
+
changedFiles: files,
|
|
7128
|
+
checks,
|
|
7129
|
+
detail: `${failures.length} of ${checks.length} current verification ${checks.length === 1 ? "check" : "checks"} failed.`,
|
|
7130
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "verification_failed") } : {}
|
|
7131
|
+
};
|
|
7132
|
+
}
|
|
7133
|
+
if (acceptance && acceptanceUnresolved(acceptance)) {
|
|
7134
|
+
return {
|
|
7135
|
+
status: "unverified",
|
|
7136
|
+
changedFiles: files,
|
|
7137
|
+
checks,
|
|
7138
|
+
detail: acceptanceDetail(acceptance),
|
|
7139
|
+
acceptance: acceptanceForCompletion(acceptance, "unverified")
|
|
7140
|
+
};
|
|
7141
|
+
}
|
|
7142
|
+
return {
|
|
7143
|
+
status: "verified",
|
|
7144
|
+
changedFiles: files,
|
|
7145
|
+
checks,
|
|
7146
|
+
detail: `${checks.length} current verification ${checks.length === 1 ? "check" : "checks"} passed for ${fileCount(files.length)}.`,
|
|
7147
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "verified") } : {}
|
|
7148
|
+
};
|
|
7149
|
+
}
|
|
7150
|
+
function completionRecoveryDirective(completion) {
|
|
7151
|
+
if (completion.acceptance && acceptanceUnresolved(completion.acceptance)) {
|
|
7152
|
+
const unresolved = completion.acceptance.unresolved.map((item) => `- [${item.status}] ${item.id}: ${item.description}`).join("\n");
|
|
7153
|
+
const failed = completion.checks.filter((check) => !check.ok).map((check) => `- ${check.command} (tool call ${check.toolCallId})`).join("\n");
|
|
7154
|
+
return `<runtime-completion-gate status="acceptance_unresolved" authorization="none">
|
|
7155
|
+
The run cannot be marked complete while required Task Contract criteria remain unresolved:
|
|
7156
|
+
${unresolved || "- Acceptance criteria are satisfied."}${completion.acceptance.missingVerification.length ? `
|
|
7157
|
+
Missing required verification:
|
|
7158
|
+
${completion.acceptance.missingVerification.map((item) => `- ${item}`).join("\n")}` : ""}${failed ? `
|
|
7159
|
+
Current failed checks:
|
|
7160
|
+
${failed}` : ""}
|
|
7161
|
+
Complete or explicitly block only these criteria. Mark a criterion satisfied through task_contract only with successful tool audit evidence or a successful tool-call id. Run the smallest missing verification after the final mutation. Do not repeat the final summary or claim acceptance from prose alone.
|
|
7162
|
+
</runtime-completion-gate>`;
|
|
7163
|
+
}
|
|
7164
|
+
if (completion.status === "verification_failed") {
|
|
7165
|
+
const failed = completion.checks.filter((check) => !check.ok).map((check) => `- ${check.command} (tool call ${check.toolCallId})`).join("\n");
|
|
7166
|
+
return `<runtime-completion-gate status="verification_failed" authorization="none">
|
|
7167
|
+
The run cannot be marked complete because current verification failed:
|
|
7168
|
+
${failed}
|
|
7169
|
+
Inspect the recorded tool output, correct the underlying problem, and rerun the smallest relevant check. Do not repeat the final summary or claim success without a new successful tool result. If the failure cannot be resolved safely, state the exact blocker and leave the result unverified.
|
|
7170
|
+
</runtime-completion-gate>`;
|
|
7171
|
+
}
|
|
7172
|
+
const changeSummary = completion.mutationTracking === "unknown" ? "A dynamic shell command could not be mapped to a complete set of workspace changes." : `The run changed ${fileCount(completion.changedFiles.length)}, but no successful verification command was recorded after the last change.`;
|
|
7173
|
+
return `<runtime-completion-gate status="unverified" authorization="none">
|
|
7174
|
+
${changeSummary}
|
|
7175
|
+
Run the smallest relevant test, typecheck, lint, build, or git diff --check now. Do not repeat the final summary or claim a check passed without a successful tool result. If verification cannot be run safely, state the exact reason and leave the result unverified.
|
|
7176
|
+
</runtime-completion-gate>`;
|
|
7177
|
+
}
|
|
7178
|
+
function buildAcceptance(contract, audit, evidence, currentChangeSequence) {
|
|
7179
|
+
const contractEvents = eventsSinceContract(audit, contract);
|
|
7180
|
+
const successfulEvents = contractEvents.filter(
|
|
7181
|
+
(event) => event.type === "tool" && event.outcome === "success" && event.tool !== "task_contract" && event.tool !== "task" && event.tool !== "working_memory"
|
|
7182
|
+
);
|
|
7183
|
+
const latestMutationIndex = contractEvents.reduce((latest, event, index) => {
|
|
7184
|
+
const changedFiles = event.metadata?.changedFiles;
|
|
7185
|
+
return Array.isArray(changedFiles) && changedFiles.length > 0 ? index : latest;
|
|
7186
|
+
}, -1);
|
|
7187
|
+
const successfulRefs = /* @__PURE__ */ new Map();
|
|
7188
|
+
for (const event of successfulEvents) {
|
|
7189
|
+
const index = contractEvents.indexOf(event);
|
|
7190
|
+
successfulRefs.set(event.id, { event, index });
|
|
7191
|
+
successfulRefs.set(event.toolCallId, { event, index });
|
|
7192
|
+
}
|
|
7193
|
+
const required = contract.acceptanceCriteria.filter((item) => item.required);
|
|
7194
|
+
const normalized = required.map((item) => {
|
|
7195
|
+
const evidenceValid = item.evidenceRefs.some((ref) => {
|
|
7196
|
+
const event = successfulRefs.get(ref);
|
|
7197
|
+
return event !== void 0 && event.index >= latestMutationIndex;
|
|
7198
|
+
});
|
|
7199
|
+
const status = item.status === "satisfied" && !evidenceValid ? "pending" : item.status;
|
|
7200
|
+
return { id: item.id, description: item.description, status };
|
|
7201
|
+
});
|
|
7202
|
+
const satisfied = normalized.filter((item) => item.status === "satisfied").length;
|
|
7203
|
+
const pending = normalized.filter((item) => item.status === "pending").length;
|
|
7204
|
+
const blocked = normalized.filter((item) => item.status === "blocked").length;
|
|
7205
|
+
const currentChecks = evidence.filter((item) => item.changeSequence === currentChangeSequence && item.ok);
|
|
7206
|
+
const requirements = contract.verificationRequirements.length ? contract.verificationRequirements : ["Record at least one successful test, typecheck, lint, build, check, or diff check after the final mutation."];
|
|
7207
|
+
const missingVerification = requirements.filter(
|
|
7208
|
+
(requirement) => !verificationRequirementMet(requirement, currentChecks)
|
|
7209
|
+
);
|
|
7210
|
+
return {
|
|
7211
|
+
state: contract.state === "draft" ? "draft" : blocked > 0 ? "blocked" : pending > 0 || missingVerification.length > 0 ? "active" : "satisfied",
|
|
7212
|
+
total: normalized.length,
|
|
7213
|
+
satisfied,
|
|
7214
|
+
pending,
|
|
7215
|
+
blocked,
|
|
7216
|
+
missingVerification,
|
|
7217
|
+
unresolved: normalized.filter((item) => item.status !== "satisfied")
|
|
7218
|
+
};
|
|
7219
|
+
}
|
|
7220
|
+
function acceptanceDetail(acceptance) {
|
|
7221
|
+
const parts = [
|
|
7222
|
+
acceptance.pending ? `${acceptance.pending} pending` : "",
|
|
7223
|
+
acceptance.blocked ? `${acceptance.blocked} blocked` : "",
|
|
7224
|
+
acceptance.missingVerification.length ? `${acceptance.missingVerification.length} verification requirements missing` : ""
|
|
7225
|
+
].filter(Boolean).join(" and ");
|
|
7226
|
+
const unresolved = acceptance.pending + acceptance.blocked;
|
|
7227
|
+
return `Task Contract acceptance is unresolved: ${parts} required ${unresolved === 1 ? "criterion" : "criteria"}.`;
|
|
7228
|
+
}
|
|
7229
|
+
function verificationRequirementMet(requirement, checks) {
|
|
7230
|
+
const normalized = normalizeCommand2(requirement);
|
|
7231
|
+
const broad = normalized.match(/^Record at least one successful (.+) after the final mutation\.?$/iu);
|
|
7232
|
+
if (broad) return checks.length > 0;
|
|
7233
|
+
const commandKey = createHash9("sha256").update(normalized).digest("hex");
|
|
7234
|
+
return checks.some((item) => item.commandKey === commandKey);
|
|
7235
|
+
}
|
|
7236
|
+
function acceptanceUnresolved(acceptance) {
|
|
7237
|
+
return acceptance.pending > 0 || acceptance.blocked > 0 || acceptance.missingVerification.length > 0;
|
|
7238
|
+
}
|
|
7239
|
+
function acceptanceForCompletion(acceptance, status) {
|
|
7240
|
+
if (acceptance.state === "draft" || acceptance.state === "blocked" || status === "verified" || status === "no_changes") return acceptance;
|
|
7241
|
+
return acceptance.state === "active" ? acceptance : { ...acceptance, state: "active" };
|
|
7242
|
+
}
|
|
7243
|
+
function classifyVerificationCommand(command2) {
|
|
7244
|
+
const normalized = normalizeCommand2(command2).toLocaleLowerCase();
|
|
7245
|
+
const segments = normalized.split(/\s*(?:&&|\|\||;)\s*/u).filter(Boolean);
|
|
7246
|
+
if (segments.length > 1) {
|
|
7247
|
+
const kinds = segments.map(classifySingleVerificationCommand);
|
|
7248
|
+
if (kinds.every((kind) => kind !== void 0)) {
|
|
7249
|
+
return kinds.every((kind) => kind === kinds[0]) ? kinds[0] : "check";
|
|
7250
|
+
}
|
|
7251
|
+
return void 0;
|
|
7252
|
+
}
|
|
7253
|
+
return classifySingleVerificationCommand(normalized);
|
|
7254
|
+
}
|
|
7255
|
+
function classifySingleVerificationCommand(command2) {
|
|
7256
|
+
const value = command2.replace(/^(?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|[^\s]+)\s+)+/u, "");
|
|
7257
|
+
if (/^git\s+diff\b.*(?:^|\s)--check(?:\s|$)/u.test(value)) return "diff";
|
|
7258
|
+
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:test(?::[^\s]+)?|test|vitest|jest)(?:\s|$)/u.test(value) || /^(?:npx|pnpx|bunx)\s+(?:vitest|jest)(?:\s|$)/u.test(value) || /^(?:python(?:\d+(?:\.\d+)*)?\s+-m\s+)?pytest(?:\s|$)/u.test(value) || /^(?:cargo|go|dotnet|mvn|mvnw|gradle|gradlew)\s+(?:test|verify)(?:\s|$)/u.test(value) || /^(?:make|just|task)\s+(?:test|verify)(?:\s|$)/u.test(value) || /^node\s+--test(?:\s|$)/u.test(value)) return "test";
|
|
7259
|
+
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:typecheck|type-check|check:types)(?:\s|$)/u.test(value) || /^(?:npx|pnpx|bunx)\s+(?:tsc|pyright|mypy)(?:\s|$)/u.test(value) || /^(?:tsc|pyright|mypy)(?:\s|$)/u.test(value) || /^cargo\s+check(?:\s|$)/u.test(value)) return "typecheck";
|
|
7260
|
+
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?lint(?:\s|$)/u.test(value) || /^(?:npx|pnpx|bunx)\s+(?:eslint|biome|ruff)(?:\s|$)/u.test(value) || /^(?:eslint|biome\s+check|ruff\s+check|cargo\s+clippy)(?:\s|$)/u.test(value)) return "lint";
|
|
7261
|
+
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:build|compile)(?:\s|$)/u.test(value) || /^(?:cargo|go|dotnet|mvn|mvnw|gradle|gradlew)\s+build(?:\s|$)/u.test(value) || /^(?:make|just|task)\s+(?:build|compile)(?:\s|$)/u.test(value)) return "build";
|
|
7262
|
+
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?check(?:\s|$)/u.test(value) || /^(?:make|just|task|gradle|gradlew)\s+check(?:\s|$)/u.test(value)) return "check";
|
|
7263
|
+
return void 0;
|
|
7264
|
+
}
|
|
7265
|
+
function publicEvidence(item) {
|
|
7266
|
+
return {
|
|
7267
|
+
toolCallId: item.toolCallId,
|
|
7268
|
+
tool: item.tool,
|
|
7269
|
+
command: item.command,
|
|
7270
|
+
kind: item.kind,
|
|
7271
|
+
ok: item.ok
|
|
7272
|
+
};
|
|
7273
|
+
}
|
|
7274
|
+
function normalizeCommand2(command2) {
|
|
7275
|
+
return command2.trim().replace(/[\t ]+/gu, " ");
|
|
7276
|
+
}
|
|
7277
|
+
function redactCommand(command2) {
|
|
7278
|
+
return command2.replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ").replace(/\b((?:api[_-]?key|access[_-]?token|authorization|password|secret|token))\s*=\s*([^\s]+)/giu, "$1=[redacted]").replace(/\b(sk-[A-Za-z0-9_-]{12,}|AIza[0-9A-Za-z_-]{20,})\b/gu, "[redacted-secret]").trim().slice(0, 2e3);
|
|
7279
|
+
}
|
|
7280
|
+
function fileCount(count) {
|
|
7281
|
+
return `${count} workspace ${count === 1 ? "file" : "files"}`;
|
|
7282
|
+
}
|
|
7283
|
+
|
|
7284
|
+
// src/tools/task-contract.ts
|
|
7285
|
+
var criterionSchema = z13.object({
|
|
7286
|
+
id: z13.string().min(1).max(128).optional(),
|
|
7287
|
+
description: z13.string().min(1).max(2e3),
|
|
7288
|
+
required: z13.boolean().optional()
|
|
7289
|
+
}).strict();
|
|
7290
|
+
var inputSchema9 = z13.discriminatedUnion("action", [
|
|
7291
|
+
z13.object({ action: z13.literal("show") }).strict(),
|
|
7292
|
+
z13.object({
|
|
7293
|
+
action: z13.literal("replace"),
|
|
7294
|
+
objective: z13.string().min(1).max(2e4),
|
|
7295
|
+
scope: z13.array(z13.string().min(1).max(2e3)).max(64),
|
|
7296
|
+
constraints: z13.array(z13.string().min(1).max(2e3)).max(64),
|
|
7297
|
+
non_goals: z13.array(z13.string().min(1).max(2e3)).max(64),
|
|
7298
|
+
acceptance_criteria: z13.array(criterionSchema).min(1).max(64),
|
|
7299
|
+
verification_requirements: z13.array(z13.string().min(1).max(2e3)).min(1).max(64)
|
|
7300
|
+
}).strict(),
|
|
7301
|
+
z13.object({ action: z13.literal("activate") }).strict(),
|
|
7302
|
+
z13.object({
|
|
7303
|
+
action: z13.literal("update_criterion"),
|
|
7304
|
+
id: z13.string().min(1).max(128),
|
|
7305
|
+
status: z13.enum(["pending", "satisfied", "blocked"]),
|
|
7306
|
+
evidence_refs: z13.array(z13.string().min(1).max(256)).max(64).optional(),
|
|
7307
|
+
note: z13.string().max(2e3).optional()
|
|
7308
|
+
}).strict()
|
|
7309
|
+
]);
|
|
7310
|
+
var taskContractTool = {
|
|
7311
|
+
definition: {
|
|
7312
|
+
name: "task_contract",
|
|
7313
|
+
description: "Define and update the durable acceptance contract for a complex executable request. Satisfied criteria require successful tool audit evidence IDs or tool-call IDs.",
|
|
7314
|
+
category: "read",
|
|
7315
|
+
inputSchema: {
|
|
7316
|
+
type: "object",
|
|
7317
|
+
properties: {
|
|
7318
|
+
action: { type: "string", enum: ["show", "replace", "activate", "update_criterion"] },
|
|
7319
|
+
objective: { type: "string" },
|
|
7320
|
+
scope: { type: "array", items: { type: "string" } },
|
|
7321
|
+
constraints: { type: "array", items: { type: "string" } },
|
|
7322
|
+
non_goals: { type: "array", items: { type: "string" } },
|
|
7323
|
+
acceptance_criteria: { type: "array", items: {
|
|
7324
|
+
type: "object",
|
|
7325
|
+
properties: {
|
|
7326
|
+
id: { type: "string" },
|
|
7327
|
+
description: { type: "string" },
|
|
7328
|
+
required: { type: "boolean" }
|
|
7329
|
+
},
|
|
7330
|
+
required: ["description"],
|
|
7331
|
+
additionalProperties: false
|
|
7332
|
+
} },
|
|
7333
|
+
verification_requirements: {
|
|
7334
|
+
type: "array",
|
|
7335
|
+
minItems: 1,
|
|
7336
|
+
items: { type: "string" },
|
|
7337
|
+
description: "Required verification commands or the standard any-successful-check requirement."
|
|
7338
|
+
},
|
|
7339
|
+
id: { type: "string" },
|
|
7340
|
+
status: { type: "string", enum: ["pending", "satisfied", "blocked"] },
|
|
7341
|
+
evidence_refs: { type: "array", items: { type: "string" } },
|
|
7342
|
+
note: { type: "string" }
|
|
7343
|
+
},
|
|
7344
|
+
required: ["action"],
|
|
7345
|
+
additionalProperties: false
|
|
7346
|
+
}
|
|
7347
|
+
},
|
|
7348
|
+
async execute(arguments_, context) {
|
|
7349
|
+
const input2 = inputSchema9.parse(arguments_);
|
|
7350
|
+
let contract = context.session.taskContract;
|
|
7351
|
+
if (!contract) throw new Error("No Task Contract is active for this session.");
|
|
7352
|
+
if (input2.action === "replace") {
|
|
7353
|
+
if (contract.state !== "draft") {
|
|
7354
|
+
throw new Error("Only a draft Task Contract can be replaced.");
|
|
7355
|
+
}
|
|
7356
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7357
|
+
const criteria = input2.acceptance_criteria.map((item) => ({
|
|
7358
|
+
id: item.id ?? `criterion-${randomUUID11().slice(0, 8)}`,
|
|
7359
|
+
description: item.description,
|
|
7360
|
+
required: item.required ?? true,
|
|
7361
|
+
status: "pending",
|
|
7362
|
+
evidenceRefs: []
|
|
7363
|
+
}));
|
|
7364
|
+
if (new Set(criteria.map((item) => item.id)).size !== criteria.length) {
|
|
7365
|
+
throw new Error("Task Contract criterion ids must be unique.");
|
|
7366
|
+
}
|
|
7367
|
+
if (!criteria.some((item) => item.required)) {
|
|
7368
|
+
throw new Error("Task Contract requires at least one required acceptance criterion.");
|
|
7369
|
+
}
|
|
7370
|
+
for (const existing of contract.acceptanceCriteria.filter((item) => item.required)) {
|
|
7371
|
+
const retained = criteria.find((item) => item.id === existing.id);
|
|
7372
|
+
if (!retained || !retained.required || retained.description !== existing.description) {
|
|
7373
|
+
throw new Error(`Required criterion cannot be removed or weakened: ${existing.id}`);
|
|
7374
|
+
}
|
|
7375
|
+
}
|
|
7376
|
+
for (const existing of contract.verificationRequirements) {
|
|
7377
|
+
if (!input2.verification_requirements.includes(existing)) {
|
|
7378
|
+
throw new Error(`Verification requirement cannot be removed: ${existing}`);
|
|
7379
|
+
}
|
|
7380
|
+
}
|
|
7381
|
+
for (const requirement of input2.verification_requirements) {
|
|
7382
|
+
validateVerificationRequirement(requirement);
|
|
7383
|
+
}
|
|
7384
|
+
Object.assign(contract, {
|
|
7385
|
+
version: 1,
|
|
7386
|
+
state: "active",
|
|
7387
|
+
objective: input2.objective,
|
|
7388
|
+
scope: input2.scope,
|
|
7389
|
+
constraints: input2.constraints,
|
|
7390
|
+
nonGoals: input2.non_goals,
|
|
7391
|
+
acceptanceCriteria: criteria,
|
|
7392
|
+
verificationRequirements: input2.verification_requirements,
|
|
7393
|
+
createdAt: contract.createdAt,
|
|
7394
|
+
updatedAt: now,
|
|
7395
|
+
...contract.auditBoundaryId ? { auditBoundaryId: contract.auditBoundaryId } : {}
|
|
7396
|
+
});
|
|
7397
|
+
} else if (input2.action === "activate") {
|
|
7398
|
+
refreshTaskContractState(contract);
|
|
7399
|
+
} else if (input2.action === "update_criterion") {
|
|
7400
|
+
if (contract.state === "draft") {
|
|
7401
|
+
throw new Error("Activate the draft Task Contract before updating acceptance criteria.");
|
|
7402
|
+
}
|
|
7403
|
+
const criterion2 = contract.acceptanceCriteria.find((item) => item.id === input2.id);
|
|
7404
|
+
if (!criterion2) throw new Error(`Unknown Task Contract criterion id: ${input2.id}`);
|
|
7405
|
+
const refs = [...new Set(input2.evidence_refs ?? [])];
|
|
7406
|
+
if (input2.status === "satisfied") {
|
|
7407
|
+
if (!refs.length) throw new Error("Satisfied criteria require at least one evidence_refs entry.");
|
|
7408
|
+
const valid = successfulContractEvidence(context.session);
|
|
7409
|
+
const invalid = refs.filter((ref) => !valid.has(ref));
|
|
7410
|
+
if (invalid.length) {
|
|
7411
|
+
throw new Error(`Unknown or unsuccessful evidence refs: ${invalid.join(", ")}`);
|
|
7412
|
+
}
|
|
7413
|
+
}
|
|
7414
|
+
criterion2.status = input2.status;
|
|
7415
|
+
criterion2.evidenceRefs = input2.status === "satisfied" ? refs : [];
|
|
7416
|
+
if (input2.note === void 0) delete criterion2.note;
|
|
7417
|
+
else criterion2.note = input2.note;
|
|
7418
|
+
refreshTaskContractState(contract);
|
|
7419
|
+
}
|
|
7420
|
+
return {
|
|
7421
|
+
content: formatContract(contract),
|
|
7422
|
+
metadata: { taskContract: structuredClone(contract) }
|
|
7423
|
+
};
|
|
7424
|
+
}
|
|
7425
|
+
};
|
|
7426
|
+
function formatContract(contract) {
|
|
7427
|
+
const satisfied = contract.acceptanceCriteria.filter((item) => item.status === "satisfied").length;
|
|
7428
|
+
const criteria = contract.acceptanceCriteria.map(
|
|
7429
|
+
(item) => `- [${item.status}] ${item.id}: ${item.description}`
|
|
7430
|
+
).join("\n");
|
|
7431
|
+
return `Task Contract: ${contract.state} (${satisfied}/${contract.acceptanceCriteria.length} satisfied)
|
|
7432
|
+
Objective: ${contract.objective}
|
|
7433
|
+
${criteria}`;
|
|
7434
|
+
}
|
|
7435
|
+
function validateVerificationRequirement(requirement) {
|
|
7436
|
+
if (/^Record at least one successful (.+) after the final mutation\.?$/iu.test(requirement)) return;
|
|
7437
|
+
if (/\b(?:api[_-]?key|authorization|password|secret|token)\s*=/iu.test(requirement)) {
|
|
7438
|
+
throw new Error("Verification requirements cannot contain credentials.");
|
|
7439
|
+
}
|
|
7440
|
+
if (!classifyVerificationCommand(requirement)) {
|
|
7441
|
+
throw new Error(`Verification requirement is not a recognized deterministic check: ${requirement}`);
|
|
7442
|
+
}
|
|
7443
|
+
}
|
|
7444
|
+
|
|
7445
|
+
// src/tools/working-memory.ts
|
|
7446
|
+
import { z as z14 } from "zod";
|
|
7447
|
+
var inputSchema10 = z14.discriminatedUnion("action", [
|
|
7448
|
+
z14.object({ action: z14.literal("show") }).strict(),
|
|
7449
|
+
z14.object({ action: z14.literal("set_goal"), value: z14.string().min(1).max(1e3) }).strict(),
|
|
7450
|
+
z14.object({ action: z14.literal("set_focus"), value: z14.string().min(1).max(1e3) }).strict(),
|
|
7451
|
+
z14.object({ action: z14.literal("add_constraint"), value: z14.string().min(1).max(1e3) }).strict(),
|
|
7452
|
+
z14.object({ action: z14.literal("add_decision"), value: z14.string().min(1).max(1e3) }).strict(),
|
|
7453
|
+
z14.object({ action: z14.literal("add_question"), value: z14.string().min(1).max(1e3) }).strict(),
|
|
7454
|
+
z14.object({ action: z14.literal("resolve_question"), value: z14.string().min(1).max(1e3) }).strict(),
|
|
7455
|
+
z14.object({ action: z14.literal("add_file"), path: z14.string().min(1).max(4e3) }).strict(),
|
|
7456
|
+
z14.object({ action: z14.literal("clear"), field: z14.enum(["constraints", "decisions", "openQuestions", "relevantFiles"]) }).strict()
|
|
7457
|
+
]);
|
|
7458
|
+
var workingMemoryTool = {
|
|
7459
|
+
definition: {
|
|
7460
|
+
name: "working_memory",
|
|
7461
|
+
description: "Read or update short-term thread state: goal, focus, constraints, decisions, open questions, and relevant files. This state is temporary and is not durable memory.",
|
|
7462
|
+
category: "read",
|
|
7463
|
+
inputSchema: jsonSchema({
|
|
7464
|
+
action: { type: "string", enum: [
|
|
7465
|
+
"show",
|
|
7466
|
+
"set_goal",
|
|
7467
|
+
"set_focus",
|
|
7468
|
+
"add_constraint",
|
|
7469
|
+
"add_decision",
|
|
7470
|
+
"add_question",
|
|
7471
|
+
"resolve_question",
|
|
7472
|
+
"add_file",
|
|
7473
|
+
"clear"
|
|
7474
|
+
] },
|
|
7475
|
+
value: { type: "string", description: "Text for a goal, focus, constraint, decision, or question." },
|
|
7476
|
+
path: { type: "string", description: "Workspace-relative relevant file path." },
|
|
7477
|
+
field: { type: "string", enum: ["constraints", "decisions", "openQuestions", "relevantFiles"] }
|
|
7478
|
+
}, ["action"])
|
|
7479
|
+
},
|
|
7480
|
+
async execute(arguments_, context) {
|
|
7481
|
+
const input2 = inputSchema10.parse(arguments_);
|
|
7482
|
+
const memory = context.session.workingMemory ?? emptyWorkingMemory2();
|
|
7483
|
+
switch (input2.action) {
|
|
7484
|
+
case "show":
|
|
7485
|
+
break;
|
|
7486
|
+
case "set_goal":
|
|
7487
|
+
memory.goal = clean(input2.value);
|
|
7488
|
+
break;
|
|
7489
|
+
case "set_focus":
|
|
7490
|
+
memory.focus = clean(input2.value);
|
|
7491
|
+
break;
|
|
7492
|
+
case "add_constraint":
|
|
7493
|
+
pushBounded2(memory.constraints, input2.value, 12);
|
|
7494
|
+
break;
|
|
7495
|
+
case "add_decision":
|
|
7496
|
+
pushBounded2(memory.decisions, input2.value, 12);
|
|
7497
|
+
break;
|
|
7498
|
+
case "add_question":
|
|
7499
|
+
pushBounded2(memory.openQuestions, input2.value, 12);
|
|
7500
|
+
break;
|
|
7501
|
+
case "resolve_question":
|
|
7502
|
+
removeMatching(memory.openQuestions, input2.value);
|
|
7503
|
+
break;
|
|
7504
|
+
case "add_file": {
|
|
7505
|
+
const safe = await context.workspace.resolvePath(input2.path, { expect: "any" });
|
|
7506
|
+
pushBounded2(memory.relevantFiles, safe, 24);
|
|
7507
|
+
break;
|
|
7508
|
+
}
|
|
7509
|
+
case "clear":
|
|
7510
|
+
memory[input2.field] = [];
|
|
7511
|
+
break;
|
|
7512
|
+
}
|
|
7513
|
+
memory.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7514
|
+
context.session.workingMemory = memory;
|
|
7515
|
+
return { content: render(memory), metadata: { workingMemory: { ...memory } } };
|
|
7516
|
+
}
|
|
7517
|
+
};
|
|
7518
|
+
function emptyWorkingMemory2() {
|
|
7519
|
+
return {
|
|
7520
|
+
goal: "",
|
|
7521
|
+
focus: "",
|
|
7522
|
+
constraints: [],
|
|
7523
|
+
decisions: [],
|
|
7524
|
+
openQuestions: [],
|
|
7525
|
+
relevantFiles: [],
|
|
7526
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7527
|
+
};
|
|
7528
|
+
}
|
|
7529
|
+
function pushBounded2(values, value, limit) {
|
|
7530
|
+
const normalized = clean(value);
|
|
7531
|
+
const existing = values.indexOf(normalized);
|
|
7532
|
+
if (existing >= 0) values.splice(existing, 1);
|
|
7533
|
+
values.push(normalized);
|
|
7534
|
+
if (values.length > limit) values.splice(0, values.length - limit);
|
|
7030
7535
|
}
|
|
7031
|
-
function
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
if (typeof value === "object") {
|
|
7039
|
-
const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableScopeValue(item)}`);
|
|
7040
|
-
return `object:{${entries.join(",")}}`;
|
|
7536
|
+
function removeMatching(values, value) {
|
|
7537
|
+
const query = value.toLocaleLowerCase().trim();
|
|
7538
|
+
for (let index = values.length - 1; index >= 0; index -= 1) {
|
|
7539
|
+
const candidate = values[index];
|
|
7540
|
+
if (candidate.toLocaleLowerCase() === query || candidate.toLocaleLowerCase().includes(query)) {
|
|
7541
|
+
values.splice(index, 1);
|
|
7542
|
+
}
|
|
7041
7543
|
}
|
|
7042
|
-
return `${typeof value}:${String(value)}`;
|
|
7043
7544
|
}
|
|
7044
|
-
function
|
|
7045
|
-
return /(?:[
|
|
7545
|
+
function clean(value) {
|
|
7546
|
+
return value.trim().replace(/\b(sk-[A-Za-z0-9_-]{12,}|AIza[0-9A-Za-z_-]{20,})\b/g, "[redacted-secret]").replace(/\b((?:api[_-]?key|access[_-]?token|auth(?:orization)?|password|secret))\s*[:=]\s*[^\s,;]+/gi, "$1=[redacted]").replace(/\s+/g, " ").slice(0, 1e3);
|
|
7046
7547
|
}
|
|
7047
|
-
function
|
|
7048
|
-
return
|
|
7548
|
+
function render(memory) {
|
|
7549
|
+
return [
|
|
7550
|
+
`Goal: ${memory.goal || "(none)"}`,
|
|
7551
|
+
`Focus: ${memory.focus || "(none)"}`,
|
|
7552
|
+
`Constraints: ${memory.constraints.length ? memory.constraints.join(" \xB7 ") : "(none)"}`,
|
|
7553
|
+
`Decisions: ${memory.decisions.length ? memory.decisions.join(" \xB7 ") : "(none)"}`,
|
|
7554
|
+
`Open questions: ${memory.openQuestions.length ? memory.openQuestions.join(" \xB7 ") : "(none)"}`,
|
|
7555
|
+
`Relevant files: ${memory.relevantFiles.length ? memory.relevantFiles.join(" \xB7 ") : "(none)"}`
|
|
7556
|
+
].join("\n");
|
|
7049
7557
|
}
|
|
7050
7558
|
|
|
7051
7559
|
// src/tools/index.ts
|
|
@@ -7059,6 +7567,7 @@ function createDefaultToolRegistry(_options = {}) {
|
|
|
7059
7567
|
shellTool,
|
|
7060
7568
|
gitTool,
|
|
7061
7569
|
taskTool,
|
|
7570
|
+
taskContractTool,
|
|
7062
7571
|
workingMemoryTool
|
|
7063
7572
|
]);
|
|
7064
7573
|
}
|
|
@@ -7098,11 +7607,19 @@ ${workspaceRules}` : ""}`;
|
|
|
7098
7607
|
}
|
|
7099
7608
|
function buildSessionStatePrompt(session) {
|
|
7100
7609
|
const tasks = session.tasks.length ? session.tasks.map((task) => `- [${task.status}] ${task.title}`).join("\n") : "- No active plan.";
|
|
7610
|
+
const contract = session.taskContract && session.taskContract.state !== "satisfied" ? `
|
|
7611
|
+
|
|
7612
|
+
Task Contract (${session.taskContract.state}):
|
|
7613
|
+
Objective: ${session.taskContract.objective}
|
|
7614
|
+
${session.taskContract.acceptanceCriteria.map(
|
|
7615
|
+
(item) => `- [${item.status}] ${item.id}: ${item.description}`
|
|
7616
|
+
).join("\n")}
|
|
7617
|
+
Use task_contract to activate or update acceptance. A satisfied criterion requires successful tool evidence.` : "";
|
|
7101
7618
|
return `<session-state scope="session" authorization="none">
|
|
7102
7619
|
This is mutable execution state, not a permission grant or higher-priority instruction.
|
|
7103
7620
|
|
|
7104
7621
|
Current saved plan:
|
|
7105
|
-
${tasks}
|
|
7622
|
+
${tasks}${contract}
|
|
7106
7623
|
</session-state>`;
|
|
7107
7624
|
}
|
|
7108
7625
|
function buildRetrievedContext(packed, mentions, primaryRoot, roots = [primaryRoot]) {
|
|
@@ -7170,130 +7687,120 @@ function escapeAttribute3(value) {
|
|
|
7170
7687
|
})[character] ?? character);
|
|
7171
7688
|
}
|
|
7172
7689
|
|
|
7173
|
-
// src/agent/
|
|
7174
|
-
import { createHash as
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7690
|
+
// src/agent/tool-recovery.ts
|
|
7691
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
7692
|
+
var RETRY_BUDGET = {
|
|
7693
|
+
schema_input: 3,
|
|
7694
|
+
unknown_tool: 2,
|
|
7695
|
+
permission_denied: 0,
|
|
7696
|
+
command_exit: 3,
|
|
7697
|
+
timeout: 2,
|
|
7698
|
+
cancelled: 0,
|
|
7699
|
+
hook: 1,
|
|
7700
|
+
execution: 3,
|
|
7701
|
+
contract_required: 2
|
|
7702
|
+
};
|
|
7703
|
+
var REPAIR_HINT = {
|
|
7704
|
+
schema_input: "Correct the arguments to match the tool schema.",
|
|
7705
|
+
unknown_tool: "Choose a tool exposed for this turn.",
|
|
7706
|
+
permission_denied: "Do not retry unless the user or configuration changes permission.",
|
|
7707
|
+
command_exit: "Inspect the exit output, change the command or fix the cause, then retry once.",
|
|
7708
|
+
timeout: "Narrow the operation or increase its allowed timeout.",
|
|
7709
|
+
cancelled: "Stop work and preserve the current state.",
|
|
7710
|
+
hook: "Fix the hook failure before relying on the tool result.",
|
|
7711
|
+
execution: "Use the error detail to change the inputs or approach.",
|
|
7712
|
+
contract_required: "Activate the Task Contract before any workspace mutation."
|
|
7713
|
+
};
|
|
7714
|
+
var ToolRecoveryController = class {
|
|
7715
|
+
signatures = /* @__PURE__ */ new Map();
|
|
7716
|
+
classFailures = /* @__PURE__ */ new Map();
|
|
7717
|
+
toolClasses = /* @__PURE__ */ new Map();
|
|
7718
|
+
preflight(call) {
|
|
7719
|
+
const callKey = callSignature(call);
|
|
7720
|
+
const signatureState = this.signatures.get(callKey);
|
|
7721
|
+
if (signatureState && (signatureState.failures >= 2 || !isRetryable(signatureState.failureClass))) {
|
|
7722
|
+
return this.receipt(
|
|
7723
|
+
call,
|
|
7724
|
+
signatureState.failureClass,
|
|
7725
|
+
signatureState.failures + 1,
|
|
7726
|
+
true
|
|
7727
|
+
);
|
|
7728
|
+
}
|
|
7729
|
+
const lastClass = this.toolClasses.get(call.name);
|
|
7730
|
+
if (lastClass && (this.classFailures.get(lastClass) ?? 0) >= RETRY_BUDGET[lastClass] && RETRY_BUDGET[lastClass] > 0) {
|
|
7731
|
+
return this.receipt(call, lastClass, (signatureState?.failures ?? 0) + 1, true);
|
|
7732
|
+
}
|
|
7733
|
+
return void 0;
|
|
7203
7734
|
}
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7735
|
+
recordFailure(call, failureClass) {
|
|
7736
|
+
const callKey = callSignature(call);
|
|
7737
|
+
const current = this.signatures.get(callKey);
|
|
7738
|
+
const failures = current?.failureClass === failureClass ? current.failures + 1 : 1;
|
|
7739
|
+
this.signatures.set(callKey, { failureClass, failures });
|
|
7740
|
+
this.toolClasses.set(call.name, failureClass);
|
|
7741
|
+
this.classFailures.set(failureClass, (this.classFailures.get(failureClass) ?? 0) + 1);
|
|
7742
|
+
return this.receipt(
|
|
7743
|
+
call,
|
|
7744
|
+
failureClass,
|
|
7745
|
+
failures,
|
|
7746
|
+
failures >= 2 || !isRetryable(failureClass)
|
|
7747
|
+
);
|
|
7211
7748
|
}
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
latestByCommand.set(item.commandKey, item);
|
|
7216
|
-
}
|
|
7749
|
+
recordSuccess(call) {
|
|
7750
|
+
this.signatures.delete(callSignature(call));
|
|
7751
|
+
this.toolClasses.delete(call.name);
|
|
7217
7752
|
}
|
|
7218
|
-
|
|
7219
|
-
|
|
7753
|
+
receipt(call, failureClass, attempt, circuitOpen) {
|
|
7754
|
+
const budget = RETRY_BUDGET[failureClass];
|
|
7755
|
+
const consumed = this.classFailures.get(failureClass) ?? 0;
|
|
7220
7756
|
return {
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7757
|
+
class: failureClass,
|
|
7758
|
+
retryable: isRetryable(failureClass) && consumed < budget && !circuitOpen,
|
|
7759
|
+
repairHint: REPAIR_HINT[failureClass],
|
|
7760
|
+
attempt,
|
|
7761
|
+
remaining: Math.max(0, budget - consumed),
|
|
7762
|
+
circuitOpen,
|
|
7763
|
+
signature: failureSignature(call, failureClass)
|
|
7225
7764
|
};
|
|
7226
7765
|
}
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
changedFiles: files,
|
|
7232
|
-
checks,
|
|
7233
|
-
detail: `${failures.length} of ${checks.length} current verification ${checks.length === 1 ? "check" : "checks"} failed.`
|
|
7234
|
-
};
|
|
7766
|
+
};
|
|
7767
|
+
function classifyToolFailure(result, fallback = "execution") {
|
|
7768
|
+
if (result.metadata?.failureClass && isFailureClass(result.metadata.failureClass)) {
|
|
7769
|
+
return result.metadata.failureClass;
|
|
7235
7770
|
}
|
|
7236
|
-
return
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
};
|
|
7242
|
-
}
|
|
7243
|
-
function completionRecoveryDirective(completion) {
|
|
7244
|
-
if (completion.status === "verification_failed") {
|
|
7245
|
-
const failed = completion.checks.filter((check) => !check.ok).map((check) => `- ${check.command} (tool call ${check.toolCallId})`).join("\n");
|
|
7246
|
-
return `<runtime-completion-gate status="verification_failed" authorization="none">
|
|
7247
|
-
The run cannot be marked complete because current verification failed:
|
|
7248
|
-
${failed}
|
|
7249
|
-
Inspect the recorded tool output, correct the underlying problem, and rerun the smallest relevant check. Do not repeat the final summary or claim success without a new successful tool result. If the failure cannot be resolved safely, state the exact blocker and leave the result unverified.
|
|
7250
|
-
</runtime-completion-gate>`;
|
|
7771
|
+
if (result.metadata?.aborted === true) return "cancelled";
|
|
7772
|
+
if (result.metadata?.timedOut === true) return "timeout";
|
|
7773
|
+
if (result.metadata?.hookError) return "hook";
|
|
7774
|
+
if (typeof result.metadata?.exitCode === "number" && result.metadata.exitCode !== 0) {
|
|
7775
|
+
return "command_exit";
|
|
7251
7776
|
}
|
|
7252
|
-
|
|
7253
|
-
return `<runtime-completion-gate status="unverified" authorization="none">
|
|
7254
|
-
${changeSummary}
|
|
7255
|
-
Run the smallest relevant test, typecheck, lint, build, or git diff --check now. Do not repeat the final summary or claim a check passed without a successful tool result. If verification cannot be run safely, state the exact reason and leave the result unverified.
|
|
7256
|
-
</runtime-completion-gate>`;
|
|
7777
|
+
return fallback;
|
|
7257
7778
|
}
|
|
7258
|
-
function
|
|
7259
|
-
|
|
7260
|
-
const segments = normalized.split(/\s*(?:&&|\|\||;)\s*/u).filter(Boolean);
|
|
7261
|
-
if (segments.length > 1) {
|
|
7262
|
-
const kinds = segments.map(classifySingleVerificationCommand);
|
|
7263
|
-
if (kinds.every((kind) => kind !== void 0)) {
|
|
7264
|
-
return kinds.every((kind) => kind === kinds[0]) ? kinds[0] : "check";
|
|
7265
|
-
}
|
|
7266
|
-
return void 0;
|
|
7267
|
-
}
|
|
7268
|
-
return classifySingleVerificationCommand(normalized);
|
|
7779
|
+
function formatFailureReceipt(receipt) {
|
|
7780
|
+
return `Failure: ${receipt.class}; attempt ${receipt.attempt}; ${receipt.remaining} retries remain; circuit ${receipt.circuitOpen ? "open" : "closed"}. Repair: ${receipt.repairHint}`;
|
|
7269
7781
|
}
|
|
7270
|
-
function
|
|
7271
|
-
|
|
7272
|
-
if (/^git\s+diff\b.*(?:^|\s)--check(?:\s|$)/u.test(value)) return "diff";
|
|
7273
|
-
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:test(?::[^\s]+)?|test|vitest|jest)(?:\s|$)/u.test(value) || /^(?:npx|pnpx|bunx)\s+(?:vitest|jest)(?:\s|$)/u.test(value) || /^(?:python(?:\d+(?:\.\d+)*)?\s+-m\s+)?pytest(?:\s|$)/u.test(value) || /^(?:cargo|go|dotnet|mvn|mvnw|gradle|gradlew)\s+(?:test|verify)(?:\s|$)/u.test(value) || /^(?:make|just|task)\s+(?:test|verify)(?:\s|$)/u.test(value) || /^node\s+--test(?:\s|$)/u.test(value)) return "test";
|
|
7274
|
-
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:typecheck|type-check|check:types)(?:\s|$)/u.test(value) || /^(?:npx|pnpx|bunx)\s+(?:tsc|pyright|mypy)(?:\s|$)/u.test(value) || /^(?:tsc|pyright|mypy)(?:\s|$)/u.test(value) || /^cargo\s+check(?:\s|$)/u.test(value)) return "typecheck";
|
|
7275
|
-
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?lint(?:\s|$)/u.test(value) || /^(?:npx|pnpx|bunx)\s+(?:eslint|biome|ruff)(?:\s|$)/u.test(value) || /^(?:eslint|biome\s+check|ruff\s+check|cargo\s+clippy)(?:\s|$)/u.test(value)) return "lint";
|
|
7276
|
-
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:build|compile)(?:\s|$)/u.test(value) || /^(?:cargo|go|dotnet|mvn|mvnw|gradle|gradlew)\s+build(?:\s|$)/u.test(value) || /^(?:make|just|task)\s+(?:build|compile)(?:\s|$)/u.test(value)) return "build";
|
|
7277
|
-
if (/^(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?check(?:\s|$)/u.test(value) || /^(?:make|just|task|gradle|gradlew)\s+check(?:\s|$)/u.test(value)) return "check";
|
|
7278
|
-
return void 0;
|
|
7782
|
+
function isRetryable(failureClass) {
|
|
7783
|
+
return RETRY_BUDGET[failureClass] > 0;
|
|
7279
7784
|
}
|
|
7280
|
-
function
|
|
7281
|
-
return {
|
|
7282
|
-
toolCallId: item.toolCallId,
|
|
7283
|
-
tool: item.tool,
|
|
7284
|
-
command: item.command,
|
|
7285
|
-
kind: item.kind,
|
|
7286
|
-
ok: item.ok
|
|
7287
|
-
};
|
|
7785
|
+
function callSignature(call) {
|
|
7786
|
+
return createHash10("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}`).digest("hex");
|
|
7288
7787
|
}
|
|
7289
|
-
function
|
|
7290
|
-
return
|
|
7788
|
+
function failureSignature(call, failureClass) {
|
|
7789
|
+
return createHash10("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}\0${failureClass}`).digest("hex");
|
|
7291
7790
|
}
|
|
7292
|
-
function
|
|
7293
|
-
|
|
7791
|
+
function redact(value, key = "") {
|
|
7792
|
+
if (/api[_-]?key|authorization|cookie|password|secret|token/i.test(key)) return "[redacted]";
|
|
7793
|
+
if (Array.isArray(value)) return value.map((item) => redact(item));
|
|
7794
|
+
if (value && typeof value === "object") {
|
|
7795
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([name, item]) => [name, redact(item, name)]));
|
|
7796
|
+
}
|
|
7797
|
+
return value;
|
|
7294
7798
|
}
|
|
7295
|
-
function
|
|
7296
|
-
return
|
|
7799
|
+
function stableJson(value) {
|
|
7800
|
+
return JSON.stringify(value) ?? String(value);
|
|
7801
|
+
}
|
|
7802
|
+
function isFailureClass(value) {
|
|
7803
|
+
return typeof value === "string" && Object.hasOwn(RETRY_BUDGET, value);
|
|
7297
7804
|
}
|
|
7298
7805
|
|
|
7299
7806
|
// src/agent/rules.ts
|
|
@@ -7538,6 +8045,8 @@ var AgentRunner = class {
|
|
|
7538
8045
|
const changeSequenceAtStart = this.changeSequence;
|
|
7539
8046
|
const runChangedFiles = /* @__PURE__ */ new Set();
|
|
7540
8047
|
const verificationEvidence = [];
|
|
8048
|
+
const toolRecovery = new ToolRecoveryController();
|
|
8049
|
+
let activeRunContract = this.session.taskContract;
|
|
7541
8050
|
let mutationTracking = "complete";
|
|
7542
8051
|
let completionRecoveryAttempted = false;
|
|
7543
8052
|
const recordExecution = (call, result) => {
|
|
@@ -7556,12 +8065,21 @@ var AgentRunner = class {
|
|
|
7556
8065
|
);
|
|
7557
8066
|
if (evidence) verificationEvidence.push(evidence);
|
|
7558
8067
|
};
|
|
7559
|
-
const completionReport = () =>
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
8068
|
+
const completionReport = () => {
|
|
8069
|
+
const completion = buildRunCompletion(
|
|
8070
|
+
runChangedFiles,
|
|
8071
|
+
verificationEvidence,
|
|
8072
|
+
this.changeSequence,
|
|
8073
|
+
mutationTracking,
|
|
8074
|
+
activeRunContract,
|
|
8075
|
+
this.session.audit ?? []
|
|
8076
|
+
);
|
|
8077
|
+
if (completion.acceptance && activeRunContract && activeRunContract.state !== "draft") {
|
|
8078
|
+
activeRunContract.state = completion.acceptance.state;
|
|
8079
|
+
activeRunContract.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8080
|
+
}
|
|
8081
|
+
return completion;
|
|
8082
|
+
};
|
|
7565
8083
|
const finishRun = async (reason, completion = completionReport()) => {
|
|
7566
8084
|
this.session.lastRun = {
|
|
7567
8085
|
...completion,
|
|
@@ -7569,6 +8087,10 @@ var AgentRunner = class {
|
|
|
7569
8087
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7570
8088
|
};
|
|
7571
8089
|
await this.persist();
|
|
8090
|
+
const finalContract = activeRunContract && structuredClone(activeRunContract);
|
|
8091
|
+
if (finalContract && finalContract.state !== "satisfied") {
|
|
8092
|
+
await emit({ type: "contract", contract: finalContract });
|
|
8093
|
+
}
|
|
7572
8094
|
await emit({ type: "done", reason, completion });
|
|
7573
8095
|
return this.session;
|
|
7574
8096
|
};
|
|
@@ -7613,10 +8135,22 @@ var AgentRunner = class {
|
|
|
7613
8135
|
const turnDirective = buildTurnDirective(request, {
|
|
7614
8136
|
agents: Boolean(this.config.agents?.enabled)
|
|
7615
8137
|
});
|
|
8138
|
+
const contractEnabled = shouldUseTaskContract(
|
|
8139
|
+
request,
|
|
8140
|
+
turnDirective.intent,
|
|
8141
|
+
this.session.taskContract
|
|
8142
|
+
);
|
|
8143
|
+
if (contractEnabled && (!this.session.taskContract || this.session.taskContract.state === "satisfied")) {
|
|
8144
|
+
this.session.taskContract = createDraftTaskContract(request, this.session.audit?.at(-1)?.id);
|
|
8145
|
+
await this.persist();
|
|
8146
|
+
await emit({ type: "contract", contract: structuredClone(this.session.taskContract) });
|
|
8147
|
+
}
|
|
8148
|
+
activeRunContract = contractEnabled ? this.session.taskContract : void 0;
|
|
7616
8149
|
const promptSections = [
|
|
7617
8150
|
`intent:${turnDirective.intent}`,
|
|
7618
8151
|
...workspaceRules ? ["rules"] : [],
|
|
7619
8152
|
...this.session.workingMemory ? ["working-memory"] : [],
|
|
8153
|
+
...contractEnabled ? ["task-contract"] : [],
|
|
7620
8154
|
...this.session.contextSummary ? ["session-summary"] : [],
|
|
7621
8155
|
...!trivialTurn ? [`context:${packed.engine}`] : [],
|
|
7622
8156
|
...packed.text ? [`code:${packed.engine}`] : [],
|
|
@@ -7669,7 +8203,7 @@ var AgentRunner = class {
|
|
|
7669
8203
|
);
|
|
7670
8204
|
const availableTokens = this.config.agent.maxSessionTokens - (this.session.usage.inputTokens + this.session.usage.outputTokens);
|
|
7671
8205
|
const estimatedInputTokens = estimateMessages2(messages) + estimateToolDefinitions(
|
|
7672
|
-
|
|
8206
|
+
visibleToolDefinitions(this.tools, options.askMode === true, contractEnabled)
|
|
7673
8207
|
);
|
|
7674
8208
|
if (availableTokens <= 0 || estimatedInputTokens >= availableTokens) {
|
|
7675
8209
|
return finishRun("token_budget");
|
|
@@ -7678,8 +8212,8 @@ var AgentRunner = class {
|
|
|
7678
8212
|
this.config.model.maxTokens ?? 8192,
|
|
7679
8213
|
availableTokens - estimatedInputTokens
|
|
7680
8214
|
));
|
|
7681
|
-
const visibleTools =
|
|
7682
|
-
const assistantId =
|
|
8215
|
+
const visibleTools = visibleToolDefinitions(this.tools, options.askMode === true, contractEnabled);
|
|
8216
|
+
const assistantId = randomUUID12();
|
|
7683
8217
|
const response = await this.completeModel(
|
|
7684
8218
|
messages,
|
|
7685
8219
|
visibleTools,
|
|
@@ -7722,9 +8256,17 @@ var AgentRunner = class {
|
|
|
7722
8256
|
return finishRun("token_budget");
|
|
7723
8257
|
}
|
|
7724
8258
|
if (response.toolCalls.length) {
|
|
8259
|
+
const visibleToolNames = new Set(visibleTools.map((tool) => tool.name));
|
|
7725
8260
|
for (const call of response.toolCalls) {
|
|
7726
8261
|
throwIfAborted(options.signal);
|
|
7727
|
-
const result = await this.executeTool(
|
|
8262
|
+
const result = await this.executeTool(
|
|
8263
|
+
call,
|
|
8264
|
+
options,
|
|
8265
|
+
emit,
|
|
8266
|
+
toolRecovery,
|
|
8267
|
+
visibleToolNames
|
|
8268
|
+
);
|
|
8269
|
+
if (call.name === "task_contract") activeRunContract = this.session.taskContract;
|
|
7728
8270
|
recordExecution(call, result);
|
|
7729
8271
|
this.session.messages.push(message("tool", result.content, {
|
|
7730
8272
|
toolCallId: result.toolCallId,
|
|
@@ -7753,8 +8295,11 @@ Review these results, correct any failures if needed, then provide the final ans
|
|
|
7753
8295
|
continue;
|
|
7754
8296
|
}
|
|
7755
8297
|
const completion = completionReport();
|
|
7756
|
-
if (this.config.agent.autoVerify && (completion.status === "unverified" || completion.status === "verification_failed") && !completionRecoveryAttempted && turn < maxTurns) {
|
|
8298
|
+
if ((this.config.agent.autoVerify || Boolean(completion.acceptance)) && (completion.status === "unverified" || completion.status === "verification_failed") && !completionRecoveryAttempted && turn < maxTurns) {
|
|
7757
8299
|
completionRecoveryAttempted = true;
|
|
8300
|
+
if (activeRunContract) {
|
|
8301
|
+
await emit({ type: "contract", contract: structuredClone(activeRunContract) });
|
|
8302
|
+
}
|
|
7758
8303
|
this.session.messages.push(message("user", completionRecoveryDirective(completion)));
|
|
7759
8304
|
await this.persist();
|
|
7760
8305
|
await this.runAfterTurnHook(turn, [], options.signal);
|
|
@@ -7822,10 +8367,29 @@ ${input2}`
|
|
|
7822
8367
|
if (final) return final;
|
|
7823
8368
|
return { content, toolCalls: [] };
|
|
7824
8369
|
}
|
|
7825
|
-
async executeTool(call, options, emit) {
|
|
8370
|
+
async executeTool(call, options, emit, recovery = new ToolRecoveryController(), visibleToolNames) {
|
|
8371
|
+
const preflight = recovery.preflight(call);
|
|
8372
|
+
if (preflight) {
|
|
8373
|
+
const result = failedResult(call, "Tool call rejected by the recovery circuit.", preflight);
|
|
8374
|
+
this.recordToolResult(result);
|
|
8375
|
+
await emit({ type: "tool_result", result });
|
|
8376
|
+
return result;
|
|
8377
|
+
}
|
|
8378
|
+
if (visibleToolNames && !visibleToolNames.has(call.name)) {
|
|
8379
|
+
const receipt = recovery.recordFailure(call, "unknown_tool");
|
|
8380
|
+
const result = failedResult(
|
|
8381
|
+
call,
|
|
8382
|
+
`Tool is not exposed for this turn: ${call.name}`,
|
|
8383
|
+
receipt
|
|
8384
|
+
);
|
|
8385
|
+
this.recordToolResult(result);
|
|
8386
|
+
await emit({ type: "tool_result", result });
|
|
8387
|
+
return result;
|
|
8388
|
+
}
|
|
7826
8389
|
const tool = this.tools.get(call.name);
|
|
7827
8390
|
if (!tool) {
|
|
7828
|
-
const
|
|
8391
|
+
const receipt = recovery.recordFailure(call, "unknown_tool");
|
|
8392
|
+
const result = failedResult(call, `Unknown tool: ${call.name}`, receipt);
|
|
7829
8393
|
this.recordToolResult(result);
|
|
7830
8394
|
await emit({ type: "tool_result", result });
|
|
7831
8395
|
return result;
|
|
@@ -7836,7 +8400,20 @@ ${input2}`
|
|
|
7836
8400
|
tool.permissionCategories?.(call.arguments) ?? [tool.definition.category]
|
|
7837
8401
|
);
|
|
7838
8402
|
} catch (error) {
|
|
7839
|
-
const
|
|
8403
|
+
const failureClass = classifyThrownToolFailure(error, options.signal);
|
|
8404
|
+
const receipt = recovery.recordFailure(call, failureClass);
|
|
8405
|
+
const result = failedResult(call, formatToolError(error), receipt);
|
|
8406
|
+
this.recordToolResult(result, tool.definition.category);
|
|
8407
|
+
await emit({ type: "tool_result", result });
|
|
8408
|
+
return result;
|
|
8409
|
+
}
|
|
8410
|
+
if (categories.some((category) => category !== "read") && this.session.taskContract?.state === "draft") {
|
|
8411
|
+
const receipt = recovery.recordFailure(call, "contract_required");
|
|
8412
|
+
const result = failedResult(
|
|
8413
|
+
call,
|
|
8414
|
+
"Potentially mutating work is paused until the draft Task Contract is activated.",
|
|
8415
|
+
receipt
|
|
8416
|
+
);
|
|
7840
8417
|
this.recordToolResult(result, tool.definition.category);
|
|
7841
8418
|
await emit({ type: "tool_result", result });
|
|
7842
8419
|
return result;
|
|
@@ -7844,7 +8421,8 @@ ${input2}`
|
|
|
7844
8421
|
for (const category of categories) {
|
|
7845
8422
|
const allowed = await this.authorize(call, category, options, emit);
|
|
7846
8423
|
if (!allowed) {
|
|
7847
|
-
const
|
|
8424
|
+
const receipt = recovery.recordFailure(call, "permission_denied");
|
|
8425
|
+
const result = failedResult(call, `Permission denied for ${category} operation.`, receipt);
|
|
7848
8426
|
this.recordToolResult(result, category);
|
|
7849
8427
|
await emit({ type: "tool_result", result });
|
|
7850
8428
|
return result;
|
|
@@ -7906,15 +8484,30 @@ Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : executio
|
|
|
7906
8484
|
...afterHookError ? { toolSucceeded: true, hookError: afterHookError.message } : {}
|
|
7907
8485
|
}
|
|
7908
8486
|
};
|
|
8487
|
+
if (!result.ok) {
|
|
8488
|
+
const failureClass = options.signal?.aborted ? "cancelled" : classifyToolFailure(result);
|
|
8489
|
+
const receipt = recovery.recordFailure(call, failureClass);
|
|
8490
|
+
result.content = truncateToolOutput(`${formatFailureReceipt(receipt)}
|
|
8491
|
+
${result.content}`);
|
|
8492
|
+
result.metadata = { ...result.metadata, failure: receipt };
|
|
8493
|
+
} else {
|
|
8494
|
+
recovery.recordSuccess(call);
|
|
8495
|
+
}
|
|
7909
8496
|
this.contextManager.recordTool(this.session, call, result);
|
|
7910
8497
|
if (JSON.stringify(this.session.tasks) !== tasksBefore || call.name === "task") {
|
|
7911
8498
|
await emit({ type: "tasks", tasks: this.session.tasks.map((task) => ({ ...task })) });
|
|
7912
8499
|
}
|
|
8500
|
+
if (call.name === "task_contract" && this.session.taskContract) {
|
|
8501
|
+
await emit({ type: "contract", contract: structuredClone(this.session.taskContract) });
|
|
8502
|
+
}
|
|
7913
8503
|
this.recordToolResult(result, tool.definition.category);
|
|
7914
8504
|
await emit({ type: "tool_result", result });
|
|
7915
8505
|
return result;
|
|
7916
8506
|
} catch (error) {
|
|
7917
|
-
const
|
|
8507
|
+
const normalized = toError(error);
|
|
8508
|
+
const failureClass = classifyThrownToolFailure(normalized, options.signal);
|
|
8509
|
+
const receipt = recovery.recordFailure(call, failureClass);
|
|
8510
|
+
const result = failedResult(call, formatToolError(error), receipt);
|
|
7918
8511
|
this.recordToolResult(result, tool.definition.category);
|
|
7919
8512
|
await emit({ type: "tool_result", result });
|
|
7920
8513
|
return result;
|
|
@@ -7983,7 +8576,7 @@ Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : executio
|
|
|
7983
8576
|
}
|
|
7984
8577
|
appendAudit(event) {
|
|
7985
8578
|
const audit = this.session.audit ?? (this.session.audit = []);
|
|
7986
|
-
audit.push({ id:
|
|
8579
|
+
audit.push({ id: randomUUID12(), createdAt: (/* @__PURE__ */ new Date()).toISOString(), ...event });
|
|
7987
8580
|
if (audit.length > 5e3) audit.splice(0, audit.length - 5e3);
|
|
7988
8581
|
}
|
|
7989
8582
|
async acceptChangedFiles(paths) {
|
|
@@ -8004,7 +8597,7 @@ Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : executio
|
|
|
8004
8597
|
const results = [];
|
|
8005
8598
|
for (const command2 of this.config.agent.verifyCommands) {
|
|
8006
8599
|
const call = {
|
|
8007
|
-
id: `verify-${
|
|
8600
|
+
id: `verify-${randomUUID12()}`,
|
|
8008
8601
|
name: "shell",
|
|
8009
8602
|
arguments: { command: command2, cwd: this.workspace.primaryRoot }
|
|
8010
8603
|
};
|
|
@@ -8071,7 +8664,7 @@ Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : executio
|
|
|
8071
8664
|
};
|
|
8072
8665
|
function message(role, content, extra = {}) {
|
|
8073
8666
|
return {
|
|
8074
|
-
id:
|
|
8667
|
+
id: randomUUID12(),
|
|
8075
8668
|
role,
|
|
8076
8669
|
content,
|
|
8077
8670
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -8142,14 +8735,21 @@ function estimateResponseTokens(response) {
|
|
|
8142
8735
|
function uniqueCategories(categories) {
|
|
8143
8736
|
return [...new Set(categories)];
|
|
8144
8737
|
}
|
|
8145
|
-
function failedResult(call, content) {
|
|
8738
|
+
function failedResult(call, content, failure) {
|
|
8146
8739
|
return {
|
|
8147
8740
|
toolCallId: call.id,
|
|
8148
8741
|
name: call.name,
|
|
8149
8742
|
ok: false,
|
|
8150
|
-
content: truncateToolOutput(
|
|
8743
|
+
content: truncateToolOutput(failure ? `${formatFailureReceipt(failure)}
|
|
8744
|
+
${content}` : content),
|
|
8745
|
+
...failure ? { metadata: { failure } } : {}
|
|
8151
8746
|
};
|
|
8152
8747
|
}
|
|
8748
|
+
function visibleToolDefinitions(tools, askMode, contractEnabled) {
|
|
8749
|
+
return tools.definitions().filter(
|
|
8750
|
+
(tool) => (!askMode || tool.category === "read") && (contractEnabled || tool.name !== "task_contract")
|
|
8751
|
+
);
|
|
8752
|
+
}
|
|
8153
8753
|
function truncateToolOutput(content) {
|
|
8154
8754
|
const max = 8e4;
|
|
8155
8755
|
return content.length <= max ? content : `${content.slice(0, max)}
|
|
@@ -8160,6 +8760,13 @@ function formatToolError(error) {
|
|
|
8160
8760
|
if (normalized.name === "ZodError") return `Invalid tool arguments: ${normalized.message}`;
|
|
8161
8761
|
return normalized.message;
|
|
8162
8762
|
}
|
|
8763
|
+
function classifyThrownToolFailure(error, signal) {
|
|
8764
|
+
const normalized = toError(error);
|
|
8765
|
+
if (isAbortError(normalized) || signal?.aborted) return "cancelled";
|
|
8766
|
+
if (normalized.name === "HookError") return "hook";
|
|
8767
|
+
if (normalized.name === "ZodError" || normalized.name === "ToolInputError") return "schema_input";
|
|
8768
|
+
return "execution";
|
|
8769
|
+
}
|
|
8163
8770
|
function titleFromInput(input2) {
|
|
8164
8771
|
return input2.trim().replace(/\s+/g, " ").slice(0, 80) || "New session";
|
|
8165
8772
|
}
|
|
@@ -8335,9 +8942,9 @@ function integer(value, fallback, min, max) {
|
|
|
8335
8942
|
}
|
|
8336
8943
|
|
|
8337
8944
|
// src/agent/delegation.ts
|
|
8338
|
-
import { randomUUID as
|
|
8945
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
8339
8946
|
import { join as join17 } from "node:path";
|
|
8340
|
-
import { z as
|
|
8947
|
+
import { z as z16 } from "zod";
|
|
8341
8948
|
|
|
8342
8949
|
// src/agent/external-runtime.ts
|
|
8343
8950
|
async function runExternalAgent(request) {
|
|
@@ -8464,86 +9071,86 @@ function numeric(...values) {
|
|
|
8464
9071
|
}
|
|
8465
9072
|
|
|
8466
9073
|
// src/agent/team-store.ts
|
|
8467
|
-
import { createHash as
|
|
9074
|
+
import { createHash as createHash11, randomUUID as randomUUID13 } from "node:crypto";
|
|
8468
9075
|
import { lstat as lstat17, readFile as readFile14, readdir as readdir6, rm as rm2 } from "node:fs/promises";
|
|
8469
9076
|
import { join as join15, resolve as resolve15 } from "node:path";
|
|
8470
|
-
import { z as
|
|
8471
|
-
var runIdSchema =
|
|
8472
|
-
var hashSchema =
|
|
8473
|
-
var artifactSchema =
|
|
9077
|
+
import { z as z15 } from "zod";
|
|
9078
|
+
var runIdSchema = z15.string().uuid();
|
|
9079
|
+
var hashSchema = z15.string().regex(/^[a-f0-9]{64}$/u);
|
|
9080
|
+
var artifactSchema = z15.object({
|
|
8474
9081
|
sha256: hashSchema,
|
|
8475
|
-
bytes:
|
|
9082
|
+
bytes: z15.number().int().nonnegative().max(5e5)
|
|
8476
9083
|
}).strict();
|
|
8477
|
-
var phaseSchema =
|
|
8478
|
-
var agentRecordSchema =
|
|
8479
|
-
id:
|
|
8480
|
-
profile:
|
|
8481
|
-
provider:
|
|
8482
|
-
model:
|
|
9084
|
+
var phaseSchema = z15.enum(["work", "review", "revision", "write"]);
|
|
9085
|
+
var agentRecordSchema = z15.object({
|
|
9086
|
+
id: z15.string().uuid(),
|
|
9087
|
+
profile: z15.string(),
|
|
9088
|
+
provider: z15.string(),
|
|
9089
|
+
model: z15.string(),
|
|
8483
9090
|
phase: phaseSchema,
|
|
8484
|
-
ok:
|
|
8485
|
-
createdAt:
|
|
8486
|
-
startedAt:
|
|
8487
|
-
endedAt:
|
|
8488
|
-
durationMs:
|
|
8489
|
-
toolCalls:
|
|
8490
|
-
usage:
|
|
8491
|
-
inputTokens:
|
|
8492
|
-
outputTokens:
|
|
9091
|
+
ok: z15.boolean(),
|
|
9092
|
+
createdAt: z15.string(),
|
|
9093
|
+
startedAt: z15.string().optional(),
|
|
9094
|
+
endedAt: z15.string().optional(),
|
|
9095
|
+
durationMs: z15.number().int().nonnegative().optional(),
|
|
9096
|
+
toolCalls: z15.number().int().nonnegative().optional(),
|
|
9097
|
+
usage: z15.object({
|
|
9098
|
+
inputTokens: z15.number().int().nonnegative(),
|
|
9099
|
+
outputTokens: z15.number().int().nonnegative()
|
|
8493
9100
|
}).strict().optional(),
|
|
8494
9101
|
report: artifactSchema
|
|
8495
9102
|
}).strict();
|
|
8496
|
-
var messageRecordSchema =
|
|
8497
|
-
id:
|
|
8498
|
-
from:
|
|
8499
|
-
to:
|
|
8500
|
-
createdAt:
|
|
9103
|
+
var messageRecordSchema = z15.object({
|
|
9104
|
+
id: z15.string().uuid(),
|
|
9105
|
+
from: z15.string(),
|
|
9106
|
+
to: z15.string(),
|
|
9107
|
+
createdAt: z15.string(),
|
|
8501
9108
|
content: artifactSchema
|
|
8502
9109
|
}).strict();
|
|
8503
|
-
var writerIntegrationSchema =
|
|
8504
|
-
status:
|
|
8505
|
-
checkedAt:
|
|
8506
|
-
detail:
|
|
8507
|
-
checkpoint:
|
|
8508
|
-
sessionId:
|
|
8509
|
-
checkpointId:
|
|
9110
|
+
var writerIntegrationSchema = z15.object({
|
|
9111
|
+
status: z15.enum(["ready", "conflict", "integrated"]),
|
|
9112
|
+
checkedAt: z15.string(),
|
|
9113
|
+
detail: z15.string().max(2e4),
|
|
9114
|
+
checkpoint: z15.object({
|
|
9115
|
+
sessionId: z15.string(),
|
|
9116
|
+
checkpointId: z15.string()
|
|
8510
9117
|
}).strict().optional(),
|
|
8511
|
-
integratedAt:
|
|
9118
|
+
integratedAt: z15.string().optional()
|
|
8512
9119
|
}).strict();
|
|
8513
|
-
var writerLaneSchema =
|
|
8514
|
-
profile:
|
|
8515
|
-
reviewer:
|
|
8516
|
-
baseCommit:
|
|
8517
|
-
outcome:
|
|
9120
|
+
var writerLaneSchema = z15.object({
|
|
9121
|
+
profile: z15.string(),
|
|
9122
|
+
reviewer: z15.string(),
|
|
9123
|
+
baseCommit: z15.string().regex(/^[a-f0-9]{40,64}$/u),
|
|
9124
|
+
outcome: z15.enum(["accepted", "rejected", "failed", "cancelled"]),
|
|
8518
9125
|
patch: artifactSchema,
|
|
8519
|
-
files:
|
|
8520
|
-
worktreeCleaned:
|
|
9126
|
+
files: z15.array(z15.string().min(1).max(4e3)).max(2e3),
|
|
9127
|
+
worktreeCleaned: z15.boolean(),
|
|
8521
9128
|
review: artifactSchema.optional(),
|
|
8522
9129
|
integration: writerIntegrationSchema.optional()
|
|
8523
9130
|
}).strict();
|
|
8524
9131
|
var manifestFields = {
|
|
8525
9132
|
id: runIdSchema,
|
|
8526
|
-
workspace:
|
|
8527
|
-
objective:
|
|
8528
|
-
reviewer:
|
|
8529
|
-
createdAt:
|
|
8530
|
-
updatedAt:
|
|
8531
|
-
status:
|
|
8532
|
-
maxReviewRounds:
|
|
8533
|
-
reviewRounds:
|
|
8534
|
-
agents:
|
|
8535
|
-
messages:
|
|
9133
|
+
workspace: z15.string(),
|
|
9134
|
+
objective: z15.string().max(3e4),
|
|
9135
|
+
reviewer: z15.string(),
|
|
9136
|
+
createdAt: z15.string(),
|
|
9137
|
+
updatedAt: z15.string(),
|
|
9138
|
+
status: z15.enum(["running", "accepted", "rejected", "failed"]),
|
|
9139
|
+
maxReviewRounds: z15.number().int().min(0).max(3),
|
|
9140
|
+
reviewRounds: z15.number().int().min(0).max(3),
|
|
9141
|
+
agents: z15.array(agentRecordSchema).max(256),
|
|
9142
|
+
messages: z15.array(messageRecordSchema).max(512)
|
|
8536
9143
|
};
|
|
8537
|
-
var manifestV1Schema =
|
|
8538
|
-
version:
|
|
9144
|
+
var manifestV1Schema = z15.object({
|
|
9145
|
+
version: z15.literal(1),
|
|
8539
9146
|
...manifestFields
|
|
8540
9147
|
}).strict();
|
|
8541
|
-
var manifestV2Schema =
|
|
8542
|
-
version:
|
|
9148
|
+
var manifestV2Schema = z15.object({
|
|
9149
|
+
version: z15.literal(2),
|
|
8543
9150
|
...manifestFields,
|
|
8544
9151
|
writer: writerLaneSchema.optional()
|
|
8545
9152
|
}).strict();
|
|
8546
|
-
var manifestSchema2 =
|
|
9153
|
+
var manifestSchema2 = z15.discriminatedUnion("version", [manifestV1Schema, manifestV2Schema]);
|
|
8547
9154
|
var TeamRunStore = class {
|
|
8548
9155
|
workspace;
|
|
8549
9156
|
directory;
|
|
@@ -8558,7 +9165,7 @@ var TeamRunStore = class {
|
|
|
8558
9165
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8559
9166
|
const manifest = manifestSchema2.parse({
|
|
8560
9167
|
version: 2,
|
|
8561
|
-
id:
|
|
9168
|
+
id: randomUUID13(),
|
|
8562
9169
|
workspace: this.workspace,
|
|
8563
9170
|
objective: input2.objective,
|
|
8564
9171
|
reviewer: input2.reviewer,
|
|
@@ -8722,7 +9329,7 @@ var TeamRunStore = class {
|
|
|
8722
9329
|
async writeArtifact(runId, content, truncate = true) {
|
|
8723
9330
|
const data = boundedArtifactText(content, 5e5, truncate);
|
|
8724
9331
|
const bytes = Buffer.byteLength(data);
|
|
8725
|
-
const sha256 =
|
|
9332
|
+
const sha256 = createHash11("sha256").update(data).digest("hex");
|
|
8726
9333
|
const directory = join15(this.runDirectory(runId), "blobs");
|
|
8727
9334
|
await ensureWorkspaceStorageDirectory(this.workspace, directory, {
|
|
8728
9335
|
requireActiveNamespace: this.managedDirectory
|
|
@@ -8731,7 +9338,7 @@ var TeamRunStore = class {
|
|
|
8731
9338
|
try {
|
|
8732
9339
|
await this.assertRegularFile(path);
|
|
8733
9340
|
const existing = await readFile14(path);
|
|
8734
|
-
if (
|
|
9341
|
+
if (createHash11("sha256").update(existing).digest("hex") !== sha256) {
|
|
8735
9342
|
throw new Error(`Team artifact hash collision or corruption: ${sha256}`);
|
|
8736
9343
|
}
|
|
8737
9344
|
} catch (error) {
|
|
@@ -8752,7 +9359,7 @@ var TeamRunStore = class {
|
|
|
8752
9359
|
const path = this.artifactPath(runId, artifact.sha256);
|
|
8753
9360
|
await this.assertRegularFile(path);
|
|
8754
9361
|
const data = await readFile14(path);
|
|
8755
|
-
const hash =
|
|
9362
|
+
const hash = createHash11("sha256").update(data).digest("hex");
|
|
8756
9363
|
if (hash !== artifact.sha256 || data.byteLength !== artifact.bytes) {
|
|
8757
9364
|
throw new Error(`Team artifact integrity check failed: ${artifact.sha256}`);
|
|
8758
9365
|
}
|
|
@@ -8815,7 +9422,7 @@ function resolveAgentModelRoute(team, parent, profile) {
|
|
|
8815
9422
|
}
|
|
8816
9423
|
|
|
8817
9424
|
// src/agent/writer-lane.ts
|
|
8818
|
-
import { createHash as
|
|
9425
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
8819
9426
|
import { lstat as lstat18, mkdtemp, realpath as realpath7, rm as rm3 } from "node:fs/promises";
|
|
8820
9427
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
8821
9428
|
import { isAbsolute as isAbsolute5, join as join16, resolve as resolve16 } from "node:path";
|
|
@@ -8913,7 +9520,7 @@ var WriterLane = class {
|
|
|
8913
9520
|
return {
|
|
8914
9521
|
baseCommit,
|
|
8915
9522
|
patch,
|
|
8916
|
-
patchSha256:
|
|
9523
|
+
patchSha256: createHash12("sha256").update(patch).digest("hex"),
|
|
8917
9524
|
files,
|
|
8918
9525
|
worktreeCleaned,
|
|
8919
9526
|
value
|
|
@@ -9143,14 +9750,14 @@ async function pathExists2(path) {
|
|
|
9143
9750
|
}
|
|
9144
9751
|
|
|
9145
9752
|
// src/agent/delegation.ts
|
|
9146
|
-
var writerRunInputSchema =
|
|
9147
|
-
task:
|
|
9148
|
-
profile:
|
|
9149
|
-
reviewer:
|
|
9753
|
+
var writerRunInputSchema = z16.object({
|
|
9754
|
+
task: z16.string().min(1).max(2e4),
|
|
9755
|
+
profile: z16.string().max(64).optional(),
|
|
9756
|
+
reviewer: z16.string().max(64).optional()
|
|
9150
9757
|
}).strict();
|
|
9151
|
-
var writerIntegrateInputSchema =
|
|
9152
|
-
run_id:
|
|
9153
|
-
patch_sha256:
|
|
9758
|
+
var writerIntegrateInputSchema = z16.object({
|
|
9759
|
+
run_id: z16.string().uuid(),
|
|
9760
|
+
patch_sha256: z16.string().regex(/^[a-f0-9]{64}$/u)
|
|
9154
9761
|
}).strict();
|
|
9155
9762
|
var DelegationManager = class {
|
|
9156
9763
|
constructor(options) {
|
|
@@ -9211,10 +9818,10 @@ var DelegationManager = class {
|
|
|
9211
9818
|
},
|
|
9212
9819
|
async execute(arguments_, context) {
|
|
9213
9820
|
if (!manager.team.enabled) return { ok: false, content: "Multi-agent delegation is disabled." };
|
|
9214
|
-
const input2 =
|
|
9215
|
-
tasks:
|
|
9216
|
-
profile:
|
|
9217
|
-
task:
|
|
9821
|
+
const input2 = z16.object({
|
|
9822
|
+
tasks: z16.array(z16.object({
|
|
9823
|
+
profile: z16.string().max(64).optional(),
|
|
9824
|
+
task: z16.string().min(1).max(2e4)
|
|
9218
9825
|
})).min(1).max(manager.team.maxDelegations)
|
|
9219
9826
|
}).parse(arguments_);
|
|
9220
9827
|
const tasks = input2.tasks.map((task) => ({
|
|
@@ -9260,13 +9867,13 @@ var DelegationManager = class {
|
|
|
9260
9867
|
},
|
|
9261
9868
|
async execute(arguments_, context) {
|
|
9262
9869
|
if (!manager.team.enabled) return { ok: false, content: "Multi-agent teams are disabled." };
|
|
9263
|
-
const input2 =
|
|
9264
|
-
objective:
|
|
9265
|
-
tasks:
|
|
9266
|
-
profile:
|
|
9267
|
-
task:
|
|
9870
|
+
const input2 = z16.object({
|
|
9871
|
+
objective: z16.string().min(1).max(3e4),
|
|
9872
|
+
tasks: z16.array(z16.object({
|
|
9873
|
+
profile: z16.string().max(64).optional(),
|
|
9874
|
+
task: z16.string().min(1).max(2e4)
|
|
9268
9875
|
})).min(1).max(manager.team.maxDelegations),
|
|
9269
|
-
reviewer:
|
|
9876
|
+
reviewer: z16.string().max(64).optional()
|
|
9270
9877
|
}).parse(arguments_);
|
|
9271
9878
|
const tasks = input2.tasks.map((task) => ({
|
|
9272
9879
|
profile: task.profile ?? manager.team.defaultProfile,
|
|
@@ -9364,7 +9971,7 @@ var DelegationManager = class {
|
|
|
9364
9971
|
maxReviewRounds: 0
|
|
9365
9972
|
});
|
|
9366
9973
|
await emit?.({ type: "team_start", id: board.id, objective: task });
|
|
9367
|
-
const writerId =
|
|
9974
|
+
const writerId = randomUUID14();
|
|
9368
9975
|
await emit?.({ type: "agent_queued", id: writerId, profile: profile.name, task, phase: "write" });
|
|
9369
9976
|
const draft = await this.writerLane.createDraft(
|
|
9370
9977
|
Math.min(this.team.maxWriterPatchBytes ?? 6e4, 12e4),
|
|
@@ -9734,7 +10341,7 @@ You are the only writer inside a disposable worktree. Make only the bounded requ
|
|
|
9734
10341
|
const reviewer = reviewerOverride ?? this.team.reviewerProfile ?? "reviewer";
|
|
9735
10342
|
const rounds = Math.max(0, this.team.maxReviewRounds ?? 1);
|
|
9736
10343
|
const board = await this.teamStore?.create({ objective, reviewer, maxReviewRounds: rounds });
|
|
9737
|
-
const runId = board?.id ??
|
|
10344
|
+
const runId = board?.id ?? randomUUID14();
|
|
9738
10345
|
await emit?.({ type: "team_start", id: runId, objective });
|
|
9739
10346
|
try {
|
|
9740
10347
|
let results = await this.runBatch(board?.id, tasks, "work", emit, signal);
|
|
@@ -9792,7 +10399,7 @@ ${review.summary}`,
|
|
|
9792
10399
|
}
|
|
9793
10400
|
}
|
|
9794
10401
|
async runBatch(runId, tasks, phase, emit, signal) {
|
|
9795
|
-
const scheduled = tasks.map((task) => ({ id:
|
|
10402
|
+
const scheduled = tasks.map((task) => ({ id: randomUUID14(), task }));
|
|
9796
10403
|
for (const item of scheduled) {
|
|
9797
10404
|
await emit?.({ type: "agent_queued", id: item.id, profile: item.task.profile, task: item.task.task, phase });
|
|
9798
10405
|
}
|
|
@@ -9901,12 +10508,12 @@ Start the response with exactly VERDICT: ACCEPT when the evidence is sufficient,
|
|
|
9901
10508
|
});
|
|
9902
10509
|
}
|
|
9903
10510
|
async peerMessage(runId, from, to, content, emit) {
|
|
9904
|
-
const id =
|
|
10511
|
+
const id = randomUUID14();
|
|
9905
10512
|
if (runId) await this.teamStore?.recordMessage(runId, { id, from, to, content });
|
|
9906
10513
|
await emit?.({ type: "agent_message", id, from, to, content });
|
|
9907
10514
|
}
|
|
9908
10515
|
async runOne(task, phase, emit, signal, retryOf, scheduledId) {
|
|
9909
|
-
const id = scheduledId ??
|
|
10516
|
+
const id = scheduledId ?? randomUUID14();
|
|
9910
10517
|
const profile = this.options.profiles.get(task.profile);
|
|
9911
10518
|
const configuredRoute = this.team.routes?.[task.profile];
|
|
9912
10519
|
const budgetMode = configuredRoute?.budgetMode ?? this.team.budgetMode ?? "observe";
|
|
@@ -10818,11 +11425,11 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
10818
11425
|
`);
|
|
10819
11426
|
}
|
|
10820
11427
|
printText(event) {
|
|
10821
|
-
const { quiet, compact } = this.options;
|
|
11428
|
+
const { quiet, compact: compact2 } = this.options;
|
|
10822
11429
|
if (quiet) return;
|
|
10823
11430
|
switch (event.type) {
|
|
10824
11431
|
case "thinking":
|
|
10825
|
-
if (!
|
|
11432
|
+
if (!compact2) process.stderr.write(this.paint.dim(`${this.glyphs.meta} reasoning ${this.glyphs.separator} turn ${event.turn}
|
|
10826
11433
|
`));
|
|
10827
11434
|
break;
|
|
10828
11435
|
case "context":
|
|
@@ -10832,7 +11439,7 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
10832
11439
|
));
|
|
10833
11440
|
break;
|
|
10834
11441
|
case "prompt":
|
|
10835
|
-
if (!
|
|
11442
|
+
if (!compact2) {
|
|
10836
11443
|
process.stderr.write(this.paint.dim(
|
|
10837
11444
|
`${this.glyphs.meta} prompt ${this.glyphs.separator} ${event.intent} ${this.glyphs.separator} ${event.sections.join(" + ")} ${this.glyphs.separator} ~${event.estimatedTokens} tokens
|
|
10838
11445
|
`
|
|
@@ -10867,12 +11474,21 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
10867
11474
|
}
|
|
10868
11475
|
break;
|
|
10869
11476
|
case "tasks":
|
|
10870
|
-
if (!
|
|
11477
|
+
if (!compact2) {
|
|
10871
11478
|
const completed = event.tasks.filter((task) => task.status === "completed").length;
|
|
10872
11479
|
process.stderr.write(this.paint.dim(`${this.glyphs.meta} plan ${this.glyphs.separator} ${completed}/${event.tasks.length} complete
|
|
10873
11480
|
`));
|
|
10874
11481
|
}
|
|
10875
11482
|
break;
|
|
11483
|
+
case "contract": {
|
|
11484
|
+
const required = event.contract.acceptanceCriteria.filter((item) => item.required);
|
|
11485
|
+
const satisfied = required.filter((item) => item.status === "satisfied").length;
|
|
11486
|
+
process.stderr.write(this.paint.dim(
|
|
11487
|
+
`${this.glyphs.meta} contract ${this.glyphs.separator} ${event.contract.state} ${this.glyphs.separator} ${satisfied}/${required.length} accepted
|
|
11488
|
+
`
|
|
11489
|
+
));
|
|
11490
|
+
break;
|
|
11491
|
+
}
|
|
10876
11492
|
case "writer_lane":
|
|
10877
11493
|
process.stderr.write(
|
|
10878
11494
|
`${event.status === "ready" || event.status === "integrated" ? this.paint.green(this.glyphs.success) : this.paint.red(this.glyphs.error)} writer ${event.id.slice(0, 8)} ${this.glyphs.separator} ${event.status} ${this.glyphs.separator} ${event.detail}
|
|
@@ -10954,6 +11570,7 @@ function sessionSummary(session) {
|
|
|
10954
11570
|
createdAt: session.createdAt,
|
|
10955
11571
|
updatedAt: session.updatedAt,
|
|
10956
11572
|
tasks: session.tasks,
|
|
11573
|
+
...session.taskContract ? { taskContract: session.taskContract } : {},
|
|
10957
11574
|
changedFiles: session.changedFiles,
|
|
10958
11575
|
...session.lastRun ? { lastRun: session.lastRun } : {},
|
|
10959
11576
|
usage: session.usage
|
|
@@ -11644,7 +12261,7 @@ function ToolGlyph({ state, glyphs }) {
|
|
|
11644
12261
|
if (state === "cancelled") return /* @__PURE__ */ jsx(Text, { color: theme.warning, children: glyphs.warning });
|
|
11645
12262
|
return /* @__PURE__ */ jsx(Text, { color: theme.error, children: glyphs.error });
|
|
11646
12263
|
}
|
|
11647
|
-
function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = false, expandedToolId, compact = false }) {
|
|
12264
|
+
function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = false, expandedToolId, compact: compact2 = false }) {
|
|
11648
12265
|
const theme = useTheme();
|
|
11649
12266
|
const glyphs = resolveGlyphs(glyphMode);
|
|
11650
12267
|
if (!items.length) {
|
|
@@ -11652,13 +12269,13 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
11652
12269
|
}
|
|
11653
12270
|
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: items.map((item, index) => {
|
|
11654
12271
|
if (item.kind === "user") {
|
|
11655
|
-
return /* @__PURE__ */ jsxs(Box, { marginBottom:
|
|
12272
|
+
return /* @__PURE__ */ jsxs(Box, { marginBottom: compact2 || item.clipped ? 0 : 1, children: [
|
|
11656
12273
|
/* @__PURE__ */ jsx(Box, { width: 2, children: /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: glyphs.prompt }) }),
|
|
11657
12274
|
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.textStrong, wrap: "wrap", children: sanitizeTerminalText(item.text) })
|
|
11658
12275
|
] }, item.id);
|
|
11659
12276
|
}
|
|
11660
12277
|
if (item.kind === "assistant") {
|
|
11661
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom:
|
|
12278
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: compact2 || item.clipped ? 0 : 1, children: [
|
|
11662
12279
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: theme.accent, children: [
|
|
11663
12280
|
glyphs.brand,
|
|
11664
12281
|
" ",
|
|
@@ -11674,7 +12291,7 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
11674
12291
|
}
|
|
11675
12292
|
if (item.kind === "context") {
|
|
11676
12293
|
const spans = item.spans ?? [];
|
|
11677
|
-
const spanLimit =
|
|
12294
|
+
const spanLimit = compact2 ? 2 : 3;
|
|
11678
12295
|
const innerWidth = Math.max(1, safeWidth(width) - 2);
|
|
11679
12296
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
11680
12297
|
/* @__PURE__ */ jsx(
|
|
@@ -11725,7 +12342,7 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
11725
12342
|
const duration = item.durationMs !== void 0 ? formatDuration(item.durationMs) : "";
|
|
11726
12343
|
const detailText = [detail, duration].filter(Boolean).join(" ");
|
|
11727
12344
|
const expanded = Boolean(item.output) && (showToolOutput || expandedToolId === item.id);
|
|
11728
|
-
const verbose = expanded && item.output ? limitTerminalText(item.output,
|
|
12345
|
+
const verbose = expanded && item.output ? limitTerminalText(item.output, compact2 ? 24 : 80) : void 0;
|
|
11729
12346
|
const disclosure = item.output ? expanded ? glyphs.expanded : glyphs.collapsed : "";
|
|
11730
12347
|
const disclosureWidth = disclosure ? displayWidth(disclosure) + 1 : 0;
|
|
11731
12348
|
const nameLimit = Math.max(1, Math.min(rowWidth - 2 - disclosureWidth, rowWidth < 64 ? rowWidth - 2 - disclosureWidth : 28));
|
|
@@ -11859,7 +12476,7 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
11859
12476
|
}
|
|
11860
12477
|
if (item.kind === "list") return /* @__PURE__ */ jsx(ListPanel, { title: item.title, entries: item.entries, width, glyphMode }, item.id);
|
|
11861
12478
|
if (item.kind === "context-inspector") {
|
|
11862
|
-
return /* @__PURE__ */ jsx(ContextInspector, { status: item.status, working: item.working, summary: item.summary, width, compact, glyphMode }, item.id);
|
|
12479
|
+
return /* @__PURE__ */ jsx(ContextInspector, { status: item.status, working: item.working, summary: item.summary, width, compact: compact2, glyphMode }, item.id);
|
|
11863
12480
|
}
|
|
11864
12481
|
if (item.kind === "theme") return /* @__PURE__ */ jsx(ThemePreview, { name: item.name, width, glyphs }, item.id);
|
|
11865
12482
|
if (item.kind === "banner") {
|
|
@@ -12069,7 +12686,7 @@ function TaskRail({ tasks, width = 80, glyphMode = "auto", maxItems }) {
|
|
|
12069
12686
|
] }) : null
|
|
12070
12687
|
] });
|
|
12071
12688
|
}
|
|
12072
|
-
function PermissionCard({ call, category, width = 80, glyphMode = "auto", workspace, compact = false }) {
|
|
12689
|
+
function PermissionCard({ call, category, width = 80, glyphMode = "auto", workspace, compact: compact2 = false }) {
|
|
12073
12690
|
const theme = useTheme();
|
|
12074
12691
|
const glyphs = resolveGlyphs(glyphMode);
|
|
12075
12692
|
const summary = permissionSummary(call);
|
|
@@ -12106,7 +12723,7 @@ function PermissionCard({ call, category, width = 80, glyphMode = "auto", worksp
|
|
|
12106
12723
|
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: [
|
|
12107
12724
|
/* @__PURE__ */ jsx(InlineRow, { parts: shortcuts.slice(0, 2), width: innerWidth, separator: " ", separatorColor: theme.border }),
|
|
12108
12725
|
/* @__PURE__ */ jsx(InlineRow, { parts: shortcuts.slice(2), width: innerWidth, separator: " ", separatorColor: theme.border })
|
|
12109
|
-
] }) :
|
|
12726
|
+
] }) : compact2 ? /* @__PURE__ */ jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [
|
|
12110
12727
|
/* @__PURE__ */ jsx(InlineRow, { parts: compactNarrowShortcuts.slice(0, 2), width: innerWidth, separator: " ", separatorColor: theme.border }),
|
|
12111
12728
|
/* @__PURE__ */ jsx(InlineRow, { parts: compactNarrowShortcuts.slice(2), width: innerWidth, separator: " ", separatorColor: theme.border })
|
|
12112
12729
|
] }) : /* @__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)) })
|
|
@@ -12367,7 +12984,7 @@ function MeterBar({ segments, total, width, glyphs }) {
|
|
|
12367
12984
|
remainder ? /* @__PURE__ */ jsx(Text, { color: theme.border, children: glyphs.meterEmpty.repeat(remainder) }) : null
|
|
12368
12985
|
] });
|
|
12369
12986
|
}
|
|
12370
|
-
function ContextInspector({ status, working, summary, width, memory, connections, sources: sources2, compact = false, minimal = false, glyphMode = "auto" }) {
|
|
12987
|
+
function ContextInspector({ status, working, summary, width, memory, connections, sources: sources2, compact: compact2 = false, minimal = false, glyphMode = "auto" }) {
|
|
12371
12988
|
const theme = useTheme();
|
|
12372
12989
|
const glyphs = resolveGlyphs(glyphMode);
|
|
12373
12990
|
if (minimal) {
|
|
@@ -12387,10 +13004,10 @@ function ContextInspector({ status, working, summary, width, memory, connections
|
|
|
12387
13004
|
{ label: "summary", detail: summary ? `~${formatTokens(status.summaryTokens)} tokens ${glyphs.separator} ${status.compactedMessages} compacted` : "not created" },
|
|
12388
13005
|
{ label: "long-term", detail: memory ?? `retrieved by relevance ${glyphs.separator} untrusted context` }
|
|
12389
13006
|
];
|
|
12390
|
-
if (!
|
|
12391
|
-
if (!
|
|
12392
|
-
if (!
|
|
12393
|
-
if (!
|
|
13007
|
+
if (!compact2 && working?.constraints.length) entries.push({ label: `constraints ${working.constraints.length}`, detail: working.constraints.slice(0, 2).join(` ${glyphs.separator} `) });
|
|
13008
|
+
if (!compact2 && working?.decisions.length) entries.push({ label: `decisions ${working.decisions.length}`, detail: working.decisions.slice(0, 2).join(` ${glyphs.separator} `) });
|
|
13009
|
+
if (!compact2 && working?.openQuestions.length) entries.push({ label: `open ${working.openQuestions.length}`, detail: working.openQuestions.slice(0, 2).join(` ${glyphs.separator} `), tone: "warning" });
|
|
13010
|
+
if (!compact2 && working?.relevantFiles.length) entries.push({ label: "relevant files", detail: working.relevantFiles.map((file) => compactDisplayPath(sanitizeInlineTerminalText(file), 28)).join(` ${glyphs.separator} `) });
|
|
12394
13011
|
if (sources2?.length) {
|
|
12395
13012
|
const pinned = sources2.filter((source) => source.state === "pinned");
|
|
12396
13013
|
const muted = sources2.filter((source) => source.state === "muted");
|
|
@@ -12511,8 +13128,8 @@ function Banner({ model, engine, workspace, version, width, glyphs }) {
|
|
|
12511
13128
|
function UpdateNotice({ current, latest, command: command2, highlights, width, glyphs }) {
|
|
12512
13129
|
const theme = useTheme();
|
|
12513
13130
|
const availableWidth = safeWidth(width);
|
|
12514
|
-
const
|
|
12515
|
-
const parts =
|
|
13131
|
+
const compact2 = availableWidth < 48;
|
|
13132
|
+
const parts = compact2 ? [
|
|
12516
13133
|
{ text: `${glyphs.up} `, color: theme.accent, bold: true },
|
|
12517
13134
|
{ text: `v${current}`, color: theme.dim, bold: false },
|
|
12518
13135
|
{ text: ` ${glyphs.arrow} `, color: theme.muted, bold: false },
|
|
@@ -12533,7 +13150,7 @@ function UpdateNotice({ current, latest, command: command2, highlights, width, g
|
|
|
12533
13150
|
const bullets = bulletWidth > 0 ? (highlights ?? []).map((line) => truncateDisplay(sanitizeInlineTerminalText(line), bulletWidth)) : [];
|
|
12534
13151
|
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [
|
|
12535
13152
|
/* @__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)) }),
|
|
12536
|
-
|
|
13153
|
+
compact2 ? /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(` run ${command2}`, availableWidth) }) : null,
|
|
12537
13154
|
bullets.map((line, index) => /* @__PURE__ */ jsx(Text, { color: theme.dim, children: `${bulletPrefix}${line}` }, index))
|
|
12538
13155
|
] });
|
|
12539
13156
|
}
|
|
@@ -13441,9 +14058,9 @@ function tailText(value, width, maxRows) {
|
|
|
13441
14058
|
return `${marker}
|
|
13442
14059
|
${selected.join("\n")}`;
|
|
13443
14060
|
}
|
|
13444
|
-
function estimateTimelineItemRows(item, { width, compact = false, showToolOutput = false, expandedToolId }) {
|
|
14061
|
+
function estimateTimelineItemRows(item, { width, compact: compact2 = false, showToolOutput = false, expandedToolId }) {
|
|
13445
14062
|
const rowWidth = Math.max(1, Math.floor(width));
|
|
13446
|
-
const gap =
|
|
14063
|
+
const gap = compact2 ? 0 : 1;
|
|
13447
14064
|
if (item.kind === "user") return wrappedRows(item.text, Math.max(1, rowWidth - 2)) + (item.clipped ? 0 : gap);
|
|
13448
14065
|
if (item.kind === "assistant") {
|
|
13449
14066
|
return 1 + richTextRows(item.text, Math.max(1, rowWidth - 2)) + (item.clipped ? 0 : gap);
|
|
@@ -13455,7 +14072,7 @@ function estimateTimelineItemRows(item, { width, compact = false, showToolOutput
|
|
|
13455
14072
|
const detail = item.errorDetail || item.detail;
|
|
13456
14073
|
const detailRows = narrow && detail ? 1 : 0;
|
|
13457
14074
|
const metaRows = item.meta ? 1 : 0;
|
|
13458
|
-
const outputRows = (showToolOutput || item.id === expandedToolId) && item.output ? Math.min(
|
|
14075
|
+
const outputRows = (showToolOutput || item.id === expandedToolId) && item.output ? Math.min(compact2 ? 25 : 81, richTextRows(item.output, Math.max(1, rowWidth - 2))) : 0;
|
|
13459
14076
|
return 1 + detailRows + metaRows + outputRows;
|
|
13460
14077
|
}
|
|
13461
14078
|
if (item.kind === "list") {
|
|
@@ -13469,7 +14086,7 @@ function estimateTimelineItemRows(item, { width, compact = false, showToolOutput
|
|
|
13469
14086
|
if (item.kind === "theme") return 3;
|
|
13470
14087
|
if (item.kind === "context") {
|
|
13471
14088
|
const metaRows = rowWidth < 64 ? 2 : 1;
|
|
13472
|
-
const spanLimit =
|
|
14089
|
+
const spanLimit = compact2 ? 2 : 3;
|
|
13473
14090
|
const spanCount = Math.min(item.spans?.length ?? 0, spanLimit);
|
|
13474
14091
|
const moreRow = (item.spans?.length ?? 0) > spanLimit ? 1 : 0;
|
|
13475
14092
|
const degradationRows = item.degradation ? metaRows : 0;
|
|
@@ -13567,6 +14184,21 @@ function finalizeAssistant(items, id, content) {
|
|
|
13567
14184
|
function endStreamingAssistants(items) {
|
|
13568
14185
|
return items.map((item) => item.kind === "assistant" && item.streaming ? { ...item, streaming: false } : item);
|
|
13569
14186
|
}
|
|
14187
|
+
function updateContractProgress(items, contract, separator = " | ") {
|
|
14188
|
+
const id = "task-contract-progress";
|
|
14189
|
+
if (contract.state === "satisfied") return items.filter((item) => item.id !== id);
|
|
14190
|
+
const required = contract.acceptanceCriteria.filter((item) => item.required);
|
|
14191
|
+
const satisfied = required.filter((item) => item.status === "satisfied").length;
|
|
14192
|
+
const next = {
|
|
14193
|
+
id,
|
|
14194
|
+
kind: "notice",
|
|
14195
|
+
tone: contract.state === "blocked" ? "error" : "info",
|
|
14196
|
+
text: `Contract ${contract.state}${separator}${satisfied}/${required.length} accepted`
|
|
14197
|
+
};
|
|
14198
|
+
const index = items.findIndex((item) => item.id === id);
|
|
14199
|
+
if (index < 0) return [...items, next].slice(-500);
|
|
14200
|
+
return items.map((item, itemIndex) => itemIndex === index ? next : item);
|
|
14201
|
+
}
|
|
13570
14202
|
function updateAgent(items, event) {
|
|
13571
14203
|
const found = items.some((item) => item.kind === "agent" && item.id === event.id);
|
|
13572
14204
|
if (!found) {
|
|
@@ -13659,7 +14291,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
13659
14291
|
const colorEnabled = config.ui.color && !process.env.NO_COLOR;
|
|
13660
14292
|
const [theme, setTheme] = useState2(() => resolveThemeWithColor(config.ui.theme, colorEnabled));
|
|
13661
14293
|
const [themeCatalogRevision, setThemeCatalogRevision] = useState2(0);
|
|
13662
|
-
const [
|
|
14294
|
+
const [compact2, setCompact] = useState2(config.ui.compact);
|
|
13663
14295
|
const [interactionMode, setInteractionMode] = useState2(planMode ? "plan" : askMode ? "ask" : "build");
|
|
13664
14296
|
const [input2, setInput] = useState2("");
|
|
13665
14297
|
const [busy, setBusy] = useState2(false);
|
|
@@ -13859,6 +14491,11 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
13859
14491
|
case "tasks":
|
|
13860
14492
|
setTasks(event.tasks.map((task) => ({ ...task })));
|
|
13861
14493
|
break;
|
|
14494
|
+
case "contract": {
|
|
14495
|
+
setTimeline((items) => updateContractProgress(items, event.contract, separator));
|
|
14496
|
+
refreshSession();
|
|
14497
|
+
break;
|
|
14498
|
+
}
|
|
13862
14499
|
case "skill":
|
|
13863
14500
|
append({ id: nextId(), kind: "skill", name: event.name, description: event.description });
|
|
13864
14501
|
break;
|
|
@@ -14351,7 +14988,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
14351
14988
|
}
|
|
14352
14989
|
if (command2 === "density") {
|
|
14353
14990
|
const normalized = argument.toLocaleLowerCase();
|
|
14354
|
-
const next = normalized === "compact" ? true : normalized === "comfortable" || normalized === "normal" ? false : normalized === "" || normalized === "toggle" ? !
|
|
14991
|
+
const next = normalized === "compact" ? true : normalized === "comfortable" || normalized === "normal" ? false : normalized === "" || normalized === "toggle" ? !compact2 : void 0;
|
|
14355
14992
|
if (next === void 0) throw new Error("Usage: /density [compact|comfortable]");
|
|
14356
14993
|
setCompact(next);
|
|
14357
14994
|
await saveUiPreference({ compact: next });
|
|
@@ -14444,7 +15081,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
14444
15081
|
})));
|
|
14445
15082
|
return true;
|
|
14446
15083
|
}
|
|
14447
|
-
}, [append, appendList,
|
|
15084
|
+
}, [append, appendList, compact2, config, ellipsis, exit, extensions, interactionMode, refreshSession, requestPermission, runner, separator, showToolOutput, tasks, theme, workflows]);
|
|
14448
15085
|
const submit = useCallback(async (raw, mode = "normal") => {
|
|
14449
15086
|
const trimmed = raw.trim();
|
|
14450
15087
|
if (!trimmed) return;
|
|
@@ -14808,7 +15445,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
14808
15445
|
const tokenTotal = session.usage.inputTokens + session.usage.outputTokens;
|
|
14809
15446
|
const contextStatus = runner.getContextStatus();
|
|
14810
15447
|
const frame = spinnerFrames()[frameIndex % spinnerFrames().length];
|
|
14811
|
-
const compactUi =
|
|
15448
|
+
const compactUi = compact2 || terminalHeight < 28;
|
|
14812
15449
|
const constrainedHeight = terminalHeight < 18;
|
|
14813
15450
|
const compactComposer = terminalHeight < 18;
|
|
14814
15451
|
const minimalInspector = terminalHeight < 22;
|
|
@@ -15014,6 +15651,16 @@ function initialTimeline(session, banner, setupProblem) {
|
|
|
15014
15651
|
if (!items.length) {
|
|
15015
15652
|
items.push({ id: nextId(), kind: "banner", model: banner.model, engine: banner.engine, workspace: banner.workspace, version: banner.version });
|
|
15016
15653
|
}
|
|
15654
|
+
if (session.taskContract && session.taskContract.state !== "satisfied") {
|
|
15655
|
+
const required = session.taskContract.acceptanceCriteria.filter((item) => item.required);
|
|
15656
|
+
const satisfied = required.filter((item) => item.status === "satisfied").length;
|
|
15657
|
+
items.push({
|
|
15658
|
+
id: "task-contract-progress",
|
|
15659
|
+
kind: "notice",
|
|
15660
|
+
tone: session.taskContract.state === "blocked" ? "error" : "info",
|
|
15661
|
+
text: `Contract ${session.taskContract.state} | ${satisfied}/${required.length} accepted`
|
|
15662
|
+
});
|
|
15663
|
+
}
|
|
15017
15664
|
if (setupProblem && items.length <= 1) items.push({ id: nextId(), kind: "notice", tone: "error", text: setupProblem });
|
|
15018
15665
|
return items;
|
|
15019
15666
|
}
|
|
@@ -15105,17 +15752,17 @@ function composerAttachments(value) {
|
|
|
15105
15752
|
const paths = [...value.matchAll(/(?:^|\s)@([^\s]+)/g)].map((match) => match[1]).filter((path) => Boolean(path));
|
|
15106
15753
|
return [...new Set(paths)].slice(-3);
|
|
15107
15754
|
}
|
|
15108
|
-
function permissionRows(width, hasCwd,
|
|
15755
|
+
function permissionRows(width, hasCwd, compact2) {
|
|
15109
15756
|
const content = 3 + (hasCwd ? 1 : 0);
|
|
15110
15757
|
if (width >= 64) return content + 2;
|
|
15111
15758
|
if (width >= 28) return content + 3;
|
|
15112
|
-
if (
|
|
15759
|
+
if (compact2) return content + 3;
|
|
15113
15760
|
return content + 5;
|
|
15114
15761
|
}
|
|
15115
|
-
function contextInspectorRows(session,
|
|
15762
|
+
function contextInspectorRows(session, compact2, width, minimal) {
|
|
15116
15763
|
if (minimal) return 2;
|
|
15117
15764
|
const working = session.workingMemory;
|
|
15118
|
-
const entries = 5 + (
|
|
15765
|
+
const entries = 5 + (compact2 ? 0 : (working?.constraints.length ? 1 : 0) + (working?.decisions.length ? 1 : 0) + (working?.openQuestions.length ? 1 : 0) + (working?.relevantFiles.length ? 1 : 0));
|
|
15119
15766
|
return 2 + entries * (width < 52 ? 2 : 1);
|
|
15120
15767
|
}
|
|
15121
15768
|
function contextInspectorStatus(status) {
|
|
@@ -15167,14 +15814,14 @@ function WorkspacePreparationView({
|
|
|
15167
15814
|
const theme = useTheme();
|
|
15168
15815
|
const safeWidth2 = Math.max(1, Math.floor(width));
|
|
15169
15816
|
const innerWidth = Math.max(1, safeWidth2 - (safeWidth2 >= 24 ? 4 : 0));
|
|
15170
|
-
const
|
|
15817
|
+
const compact2 = safeWidth2 < 48;
|
|
15171
15818
|
const constrained = height < 14;
|
|
15172
15819
|
const ascii = process.env.SKEIN_GLYPHS === "ascii" || process.env.MOSAIC_GLYPHS === "ascii";
|
|
15173
15820
|
const spinner = (ascii ? [".", "o", "O", "o"] : ["\u25DC", "\u25E0", "\u25DD", "\u25DE", "\u25E1", "\u25DF"])[frame % (ascii ? 4 : 6)];
|
|
15174
15821
|
const separator = ascii ? "|" : "\xB7";
|
|
15175
15822
|
const brand = ascii ? "*" : PRODUCT_MARK;
|
|
15176
15823
|
const phase = readiness ? "ready" : error ? "error" : progress.phase;
|
|
15177
|
-
const phaseLabel = preparationLabel(phase, progress, readiness,
|
|
15824
|
+
const phaseLabel = preparationLabel(phase, progress, readiness, compact2);
|
|
15178
15825
|
const detail = preparationDetail(phase, progress, readiness, error);
|
|
15179
15826
|
const modelLine = `model ${sanitizeTerminalText(model)}`;
|
|
15180
15827
|
const workspaceLine = `workspace ${compactDisplayPath(sanitizeTerminalText(workspace), Math.max(1, innerWidth - 10))}`;
|
|
@@ -15186,14 +15833,14 @@ function WorkspacePreparationView({
|
|
|
15186
15833
|
" ",
|
|
15187
15834
|
PRODUCT_NAME.toUpperCase()
|
|
15188
15835
|
] }),
|
|
15189
|
-
!
|
|
15836
|
+
!compact2 && !constrained ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
|
|
15190
15837
|
" ",
|
|
15191
15838
|
separator,
|
|
15192
15839
|
" LOCAL CONTEXT"
|
|
15193
15840
|
] }) : null
|
|
15194
15841
|
] }),
|
|
15195
15842
|
!constrained ? /* @__PURE__ */ jsx4(Text4, { color: theme.muted, children: truncateDisplay("Ground the workspace before the first request.", innerWidth) }) : null,
|
|
15196
|
-
!constrained && !
|
|
15843
|
+
!constrained && !compact2 ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: truncateDisplay(`${modelLine} ${separator} ${workspaceLine}`, innerWidth) }) : !constrained ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
15197
15844
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: truncateDisplay(modelLine, innerWidth) }),
|
|
15198
15845
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: truncateDisplay(workspaceLine, innerWidth) })
|
|
15199
15846
|
] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: truncateDisplay(workspaceLine, innerWidth) }),
|
|
@@ -15207,10 +15854,10 @@ function WorkspacePreparationView({
|
|
|
15207
15854
|
step2.glyph,
|
|
15208
15855
|
" "
|
|
15209
15856
|
] }),
|
|
15210
|
-
/* @__PURE__ */ jsx4(Text4, { bold: step2.state === "active", color: step2.state === "pending" ? theme.dim : theme.textStrong, children: truncateDisplay(step2.label,
|
|
15857
|
+
/* @__PURE__ */ jsx4(Text4, { bold: step2.state === "active", color: step2.state === "pending" ? theme.dim : theme.textStrong, children: truncateDisplay(step2.label, compact2 ? 8 : 10) }),
|
|
15211
15858
|
/* @__PURE__ */ jsxs4(Text4, { color: step2.state === "active" ? theme.muted : theme.dim, children: [
|
|
15212
15859
|
" ",
|
|
15213
|
-
truncateDisplay(step2.detail, Math.max(1, innerWidth - displayWidth(step2.glyph) - (
|
|
15860
|
+
truncateDisplay(step2.detail, Math.max(1, innerWidth - displayWidth(step2.glyph) - (compact2 ? 12 : 14)))
|
|
15214
15861
|
] })
|
|
15215
15862
|
] }, step2.id)) }),
|
|
15216
15863
|
/* @__PURE__ */ jsxs4(Box3, { marginTop: 1, children: [
|
|
@@ -15351,9 +15998,9 @@ async function runWorkspacePreparation(engine, config, options = {}) {
|
|
|
15351
15998
|
await instance.waitUntilExit();
|
|
15352
15999
|
return result ?? { status: "cancelled" };
|
|
15353
16000
|
}
|
|
15354
|
-
function preparationLabel(phase, progress, readiness,
|
|
16001
|
+
function preparationLabel(phase, progress, readiness, compact2 = false) {
|
|
15355
16002
|
if (phase === "ready") {
|
|
15356
|
-
if (
|
|
16003
|
+
if (compact2) return "Index verified";
|
|
15357
16004
|
return readiness?.rebuilt ? "Workspace index created and verified" : "Workspace index verified";
|
|
15358
16005
|
}
|
|
15359
16006
|
if (phase === "error") return "Workspace preparation failed";
|
|
@@ -15757,7 +16404,7 @@ function OnboardingFlow({ initialConfig, saveConfig, onFinish }) {
|
|
|
15757
16404
|
}, [finish, saveConfig, state]);
|
|
15758
16405
|
return /* @__PURE__ */ jsx5(OnboardingScreen, { state, dispatch, width, compact: compactHeight });
|
|
15759
16406
|
}
|
|
15760
|
-
function OnboardingScreen({ state, dispatch, width, compact = false }) {
|
|
16407
|
+
function OnboardingScreen({ state, dispatch, width, compact: compact2 = false }) {
|
|
15761
16408
|
const theme = useTheme();
|
|
15762
16409
|
const ascii = process.env.SKEIN_GLYPHS === "ascii" || process.env.MOSAIC_GLYPHS === "ascii";
|
|
15763
16410
|
const marker = ascii ? ">" : "\u203A";
|
|
@@ -15774,14 +16421,14 @@ function OnboardingScreen({ state, dispatch, width, compact = false }) {
|
|
|
15774
16421
|
] }),
|
|
15775
16422
|
/* @__PURE__ */ jsx5(Text6, { color: theme.border, children: truncateDisplay(stageDivider(ascii, headerWidth), headerWidth) }),
|
|
15776
16423
|
/* @__PURE__ */ jsx5(Text6, { color: theme.dim, children: truncateDisplay(stage.name, headerWidth) }),
|
|
15777
|
-
!
|
|
16424
|
+
!compact2 ? /* @__PURE__ */ jsx5(Box4, { height: 1 }) : null,
|
|
15778
16425
|
/* @__PURE__ */ jsx5(Text6, { color: theme.textStrong, bold: true, children: truncateDisplay(titleForStep(state.step), headerWidth) }),
|
|
15779
|
-
!
|
|
16426
|
+
!compact2 ? /* @__PURE__ */ jsx5(Text6, { color: theme.muted, wrap: "wrap", children: descriptionForStep(state) }) : null,
|
|
15780
16427
|
summary ? /* @__PURE__ */ jsx5(Text6, { color: theme.dim, children: truncateDisplay(summary, headerWidth) }) : null,
|
|
15781
|
-
!
|
|
15782
|
-
state.step === "method" ? /* @__PURE__ */ jsx5(OptionList, { options: methods, selected: state.selected, marker, width: headerWidth, compact }) : null,
|
|
15783
|
-
state.step === "official-provider" ? /* @__PURE__ */ jsx5(OptionList, { options: officialProviders, selected: state.selected, marker, width: headerWidth, compact }) : null,
|
|
15784
|
-
state.step === "relay-protocol" ? /* @__PURE__ */ jsx5(OptionList, { options: relayProtocols, selected: state.selected, marker, width: headerWidth, compact }) : null,
|
|
16428
|
+
!compact2 ? /* @__PURE__ */ jsx5(Box4, { height: 1 }) : null,
|
|
16429
|
+
state.step === "method" ? /* @__PURE__ */ jsx5(OptionList, { options: methods, selected: state.selected, marker, width: headerWidth, compact: compact2 }) : null,
|
|
16430
|
+
state.step === "official-provider" ? /* @__PURE__ */ jsx5(OptionList, { options: officialProviders, selected: state.selected, marker, width: headerWidth, compact: compact2 }) : null,
|
|
16431
|
+
state.step === "relay-protocol" ? /* @__PURE__ */ jsx5(OptionList, { options: relayProtocols, selected: state.selected, marker, width: headerWidth, compact: compact2 }) : null,
|
|
15785
16432
|
inputField ? /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", children: [
|
|
15786
16433
|
/* @__PURE__ */ jsxs5(Box4, { children: [
|
|
15787
16434
|
/* @__PURE__ */ jsx5(Text6, { color: theme.textStrong, bold: true, children: inputField.label }),
|
|
@@ -15809,18 +16456,18 @@ function OnboardingScreen({ state, dispatch, width, compact = false }) {
|
|
|
15809
16456
|
"! ",
|
|
15810
16457
|
truncateDisplay(state.error, Math.max(1, headerWidth - 2))
|
|
15811
16458
|
] }) : null,
|
|
15812
|
-
!
|
|
16459
|
+
!compact2 ? /* @__PURE__ */ jsx5(Box4, { height: 1 }) : null,
|
|
15813
16460
|
/* @__PURE__ */ jsx5(Text6, { color: theme.dim, children: footerForStep(state, headerWidth) })
|
|
15814
16461
|
] });
|
|
15815
16462
|
}
|
|
15816
|
-
function OptionList({ options, selected, marker, width, compact }) {
|
|
16463
|
+
function OptionList({ options, selected, marker, width, compact: compact2 }) {
|
|
15817
16464
|
const theme = useTheme();
|
|
15818
16465
|
return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", children: options.map((option, index) => {
|
|
15819
16466
|
const active = index === selected;
|
|
15820
16467
|
const prefix = active ? `${marker} ` : " ";
|
|
15821
16468
|
const available = Math.max(1, width - displayWidth(prefix) - (active ? 2 : 0));
|
|
15822
16469
|
const label = `${prefix}${truncateDisplay(option.label, available)}`;
|
|
15823
|
-
return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom:
|
|
16470
|
+
return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", marginBottom: compact2 || index === options.length - 1 ? 0 : 1, children: [
|
|
15824
16471
|
/* @__PURE__ */ jsx5(
|
|
15825
16472
|
Text6,
|
|
15826
16473
|
{
|
|
@@ -15830,7 +16477,7 @@ function OptionList({ options, selected, marker, width, compact }) {
|
|
|
15830
16477
|
children: active ? padDisplay(label, width) : label
|
|
15831
16478
|
}
|
|
15832
16479
|
),
|
|
15833
|
-
!
|
|
16480
|
+
!compact2 && 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: option.detail }) }) : null
|
|
15834
16481
|
] }, option.label);
|
|
15835
16482
|
}) });
|
|
15836
16483
|
}
|
|
@@ -15953,7 +16600,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
15953
16600
|
import stripAnsi4 from "strip-ansi";
|
|
15954
16601
|
|
|
15955
16602
|
// src/mcp/tool.ts
|
|
15956
|
-
import { createHash as
|
|
16603
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
15957
16604
|
import stripAnsi3 from "strip-ansi";
|
|
15958
16605
|
var MAX_ARGUMENT_BYTES = 256e3;
|
|
15959
16606
|
var MAX_DESCRIPTION_LENGTH = 4e3;
|
|
@@ -15961,7 +16608,7 @@ var MAX_RESULT_LENGTH = 8e4;
|
|
|
15961
16608
|
var MAX_SCHEMA_BYTES = 1e5;
|
|
15962
16609
|
function createMcpToolAdapter(options) {
|
|
15963
16610
|
const { remoteTool } = options;
|
|
15964
|
-
const
|
|
16611
|
+
const inputSchema11 = copyInputSchema(remoteTool.inputSchema);
|
|
15965
16612
|
return {
|
|
15966
16613
|
definition: {
|
|
15967
16614
|
name: options.exposedName,
|
|
@@ -15969,7 +16616,7 @@ function createMcpToolAdapter(options) {
|
|
|
15969
16616
|
// MCP servers are an external trust boundary. Read-only annotations are
|
|
15970
16617
|
// hints from that server and must not lower the local permission level.
|
|
15971
16618
|
category: "network",
|
|
15972
|
-
inputSchema:
|
|
16619
|
+
inputSchema: inputSchema11
|
|
15973
16620
|
},
|
|
15974
16621
|
permissionCategories: () => ["network"],
|
|
15975
16622
|
async execute(arguments_, context) {
|
|
@@ -16106,7 +16753,7 @@ function fitToolName(name, identity) {
|
|
|
16106
16753
|
return `${name.slice(0, 64 - suffix.length).replace(/_+$/g, "")}${suffix}`;
|
|
16107
16754
|
}
|
|
16108
16755
|
function shortHash(value) {
|
|
16109
|
-
return
|
|
16756
|
+
return createHash13("sha256").update(value).digest("hex").slice(0, 8);
|
|
16110
16757
|
}
|
|
16111
16758
|
function truncateResult(content) {
|
|
16112
16759
|
if (content.length <= MAX_RESULT_LENGTH) return content;
|
|
@@ -16729,9 +17376,9 @@ async function closeTransportQuietly(transport) {
|
|
|
16729
17376
|
}
|
|
16730
17377
|
|
|
16731
17378
|
// src/memory/tools.ts
|
|
16732
|
-
import { z as
|
|
16733
|
-
var scopeSchema =
|
|
16734
|
-
var kindSchema =
|
|
17379
|
+
import { z as z17 } from "zod";
|
|
17380
|
+
var scopeSchema = z17.enum(["user", "workspace", "session", "agent"]);
|
|
17381
|
+
var kindSchema = z17.enum(["semantic", "episodic", "procedural"]);
|
|
16735
17382
|
function createMemoryTools(store) {
|
|
16736
17383
|
return [
|
|
16737
17384
|
{
|
|
@@ -16746,10 +17393,10 @@ function createMemoryTools(store) {
|
|
|
16746
17393
|
}, ["query"])
|
|
16747
17394
|
},
|
|
16748
17395
|
async execute(arguments_, context) {
|
|
16749
|
-
const input2 =
|
|
16750
|
-
query:
|
|
17396
|
+
const input2 = z17.object({
|
|
17397
|
+
query: z17.string().max(4e3),
|
|
16751
17398
|
scope: scopeSchema.optional(),
|
|
16752
|
-
limit:
|
|
17399
|
+
limit: z17.number().int().min(1).max(20).optional()
|
|
16753
17400
|
}).parse(arguments_);
|
|
16754
17401
|
const scopes = input2.scope ? [{ scope: input2.scope, scopeKey: scopeKey(input2.scope, context) }] : availableScopes(context);
|
|
16755
17402
|
const records = store.search(input2.query, { scopes, limit: input2.limit ?? 8 });
|
|
@@ -16781,17 +17428,17 @@ ${record.content}`
|
|
|
16781
17428
|
}, ["content", "rationale"])
|
|
16782
17429
|
},
|
|
16783
17430
|
async execute(arguments_, context) {
|
|
16784
|
-
const input2 =
|
|
16785
|
-
content:
|
|
16786
|
-
rationale:
|
|
17431
|
+
const input2 = z17.object({
|
|
17432
|
+
content: z17.string().min(1).max(12e3),
|
|
17433
|
+
rationale: z17.string().min(1).max(1e3),
|
|
16787
17434
|
scope: scopeSchema.optional(),
|
|
16788
17435
|
kind: kindSchema.optional(),
|
|
16789
|
-
tags:
|
|
16790
|
-
importance:
|
|
16791
|
-
confidence:
|
|
16792
|
-
agent:
|
|
16793
|
-
revision:
|
|
16794
|
-
conflictKey:
|
|
17436
|
+
tags: z17.array(z17.string()).max(24).optional(),
|
|
17437
|
+
importance: z17.number().min(0).max(1).optional(),
|
|
17438
|
+
confidence: z17.number().min(0).max(1).optional(),
|
|
17439
|
+
agent: z17.string().max(64).optional(),
|
|
17440
|
+
revision: z17.string().max(240).optional(),
|
|
17441
|
+
conflictKey: z17.string().max(240).optional()
|
|
16795
17442
|
}).strict().parse(arguments_);
|
|
16796
17443
|
const scope = input2.scope ?? "workspace";
|
|
16797
17444
|
const candidate = store.propose({
|
|
@@ -16829,9 +17476,9 @@ ${record.content}`
|
|
|
16829
17476
|
}, ["id"])
|
|
16830
17477
|
},
|
|
16831
17478
|
async execute(arguments_) {
|
|
16832
|
-
const input2 =
|
|
16833
|
-
id:
|
|
16834
|
-
permanent:
|
|
17479
|
+
const input2 = z17.object({
|
|
17480
|
+
id: z17.string().uuid(),
|
|
17481
|
+
permanent: z17.boolean().optional()
|
|
16835
17482
|
}).parse(arguments_);
|
|
16836
17483
|
const changed = input2.permanent ? store.remove(input2.id) : store.archive(input2.id);
|
|
16837
17484
|
return {
|
|
@@ -17035,7 +17682,7 @@ function escapeAttribute6(value) {
|
|
|
17035
17682
|
}
|
|
17036
17683
|
|
|
17037
17684
|
// src/workflows/catalog.ts
|
|
17038
|
-
import { z as
|
|
17685
|
+
import { z as z18 } from "zod";
|
|
17039
17686
|
var builtInWorkflows = [
|
|
17040
17687
|
{
|
|
17041
17688
|
name: "implement",
|
|
@@ -17126,7 +17773,7 @@ function createWorkflowTool(catalog) {
|
|
|
17126
17773
|
}, ["name", "task"])
|
|
17127
17774
|
},
|
|
17128
17775
|
async execute(arguments_) {
|
|
17129
|
-
const input2 =
|
|
17776
|
+
const input2 = z18.object({ name: z18.string(), task: z18.string().min(1).max(2e4) }).parse(arguments_);
|
|
17130
17777
|
const workflow = catalog.get(input2.name);
|
|
17131
17778
|
if (!workflow) return { ok: false, content: `Unknown workflow: ${input2.name}` };
|
|
17132
17779
|
return {
|