@skein-code/cli 0.3.15 → 0.3.17
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 +6 -4
- package/dist/cli.js +406 -144
- package/dist/cli.js.map +1 -1
- package/docs/ARCHITECTURE.md +13 -1
- package/docs/NEXT_STEPS.md +21 -9
- package/package.json +8 -1
package/dist/cli.js
CHANGED
|
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
|
|
|
220
220
|
// package.json
|
|
221
221
|
var package_default = {
|
|
222
222
|
name: "@skein-code/cli",
|
|
223
|
-
version: "0.3.
|
|
223
|
+
version: "0.3.17",
|
|
224
224
|
description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
|
|
225
225
|
type: "module",
|
|
226
226
|
license: "MIT",
|
|
@@ -236,6 +236,12 @@ var package_default = {
|
|
|
236
236
|
},
|
|
237
237
|
skein: {
|
|
238
238
|
releaseNotes: [
|
|
239
|
+
"Calibrated Type-1/2 duplication matches now block completion until reuse or exact audited suppression",
|
|
240
|
+
"Type-3 duplication remains warning-only and Type-4 semantic equivalence remains explicitly unsupported",
|
|
241
|
+
"Duplication benchmark fixtures report deterministic recall, precision, false-positive rate, and latency",
|
|
242
|
+
"Duplication warnings now aggregate into the single completion receipt without changing verified status",
|
|
243
|
+
"Exact match ids support bounded, reason-coded suppression with audit and credential/code-block safeguards",
|
|
244
|
+
"Duplication audit tools are disclosed only when active warnings exist and repaired paths clear stale matches",
|
|
239
245
|
"Warning-only TS/JS duplication audit compares new functions with the pre-write index generation",
|
|
240
246
|
"Deterministic Type-1/2 and Type-3 receipts retain only hashes, locations, scores, and bounded counts",
|
|
241
247
|
"Ordinary edits, renames, moves, deletions, small functions, tests, generated paths, and failed writes stay quiet",
|
|
@@ -271,6 +277,7 @@ var package_default = {
|
|
|
271
277
|
"pretest:pty": "npm run build",
|
|
272
278
|
"test:pty": "sh test/pty/run-visual.sh",
|
|
273
279
|
"benchmark:context": "tsx scripts/benchmark-local-index.ts",
|
|
280
|
+
"benchmark:duplication": "tsx scripts/benchmark-duplication.ts",
|
|
274
281
|
"verify:package": "node scripts/verify-package.mjs",
|
|
275
282
|
"release:verify": "npm run check && npm run verify:package --",
|
|
276
283
|
typecheck: "tsc --noEmit",
|
|
@@ -5671,25 +5678,35 @@ var reuseReceiptSchema = z5.object({
|
|
|
5671
5678
|
indexGeneration: z5.string().optional(),
|
|
5672
5679
|
changeSequence: z5.number().int().nonnegative(),
|
|
5673
5680
|
status: z5.enum(["warning", "skipped", "unresolved"]),
|
|
5674
|
-
warningOnly: z5.
|
|
5681
|
+
warningOnly: z5.boolean()
|
|
5682
|
+
}).strict();
|
|
5683
|
+
var duplicationMatchSchema = z5.object({
|
|
5684
|
+
matchId: z5.string().regex(/^[a-f0-9]{24}$/u).optional(),
|
|
5685
|
+
changedPath: z5.string(),
|
|
5686
|
+
changedSymbol: z5.string(),
|
|
5687
|
+
candidatePath: z5.string(),
|
|
5688
|
+
candidateSymbol: z5.string(),
|
|
5689
|
+
kind: z5.enum(["type-1-or-2", "type-3"]),
|
|
5690
|
+
similarity: z5.number().min(0).max(1)
|
|
5675
5691
|
}).strict();
|
|
5676
5692
|
var duplicationAuditSchema = z5.object({
|
|
5677
5693
|
baselineGeneration: z5.string().min(1),
|
|
5678
5694
|
changeSequence: z5.number().int().nonnegative(),
|
|
5679
5695
|
status: z5.enum(["clear", "warning", "unresolved"]),
|
|
5680
|
-
warningOnly: z5.
|
|
5696
|
+
warningOnly: z5.boolean(),
|
|
5697
|
+
enforcement: z5.enum(["warning", "blocking"]).optional(),
|
|
5681
5698
|
checkedFunctions: z5.number().int().nonnegative(),
|
|
5682
5699
|
skippedSmallFunctions: z5.number().int().nonnegative(),
|
|
5683
|
-
matches: z5.array(
|
|
5684
|
-
changedPath: z5.string(),
|
|
5685
|
-
changedSymbol: z5.string(),
|
|
5686
|
-
candidatePath: z5.string(),
|
|
5687
|
-
candidateSymbol: z5.string(),
|
|
5688
|
-
kind: z5.enum(["type-1-or-2", "type-3"]),
|
|
5689
|
-
similarity: z5.number().min(0).max(1)
|
|
5690
|
-
}).strict()).max(8),
|
|
5700
|
+
matches: z5.array(duplicationMatchSchema).max(8),
|
|
5691
5701
|
rationale: z5.string().max(500)
|
|
5692
5702
|
}).strict();
|
|
5703
|
+
var duplicationSuppressionSchema = z5.object({
|
|
5704
|
+
matchId: z5.string().regex(/^[a-f0-9]{24}$/u),
|
|
5705
|
+
reasonCode: z5.enum(["separate-boundary", "protocol-required", "generated-contract", "false-positive", "other"]),
|
|
5706
|
+
reason: z5.string().min(12).max(240),
|
|
5707
|
+
createdAt: z5.string().datetime(),
|
|
5708
|
+
toolCallId: z5.string().min(1).max(512)
|
|
5709
|
+
}).strict();
|
|
5693
5710
|
var auditMetadataSchema = z5.record(z5.string(), z5.unknown()).superRefine((metadata, ctx) => {
|
|
5694
5711
|
const receipt = metadata.reuseReceipt;
|
|
5695
5712
|
if (receipt !== void 0 && !reuseReceiptSchema.safeParse(receipt).success) {
|
|
@@ -5699,6 +5716,14 @@ var auditMetadataSchema = z5.record(z5.string(), z5.unknown()).superRefine((meta
|
|
|
5699
5716
|
if (duplication !== void 0 && !duplicationAuditSchema.safeParse(duplication).success) {
|
|
5700
5717
|
ctx.addIssue({ code: "custom", message: "Invalid duplication audit receipt" });
|
|
5701
5718
|
}
|
|
5719
|
+
const suppression = metadata.duplicationSuppression;
|
|
5720
|
+
if (suppression !== void 0 && !duplicationSuppressionSchema.safeParse(suppression).success) {
|
|
5721
|
+
ctx.addIssue({ code: "custom", message: "Invalid duplication suppression receipt" });
|
|
5722
|
+
}
|
|
5723
|
+
const activeMatches = metadata.activeDuplicationMatches;
|
|
5724
|
+
if (activeMatches !== void 0 && !z5.array(z5.string().regex(/^[a-f0-9]{24}$/u)).max(8).safeParse(activeMatches).success) {
|
|
5725
|
+
ctx.addIssue({ code: "custom", message: "Invalid active duplication matches" });
|
|
5726
|
+
}
|
|
5702
5727
|
});
|
|
5703
5728
|
var auditSchema = z5.object({
|
|
5704
5729
|
id: z5.string(),
|
|
@@ -5738,6 +5763,16 @@ var lastRunSchema = z5.object({
|
|
|
5738
5763
|
checks: z5.array(verificationEvidenceSchema),
|
|
5739
5764
|
detail: z5.string(),
|
|
5740
5765
|
mutationTracking: z5.enum(["complete", "unknown"]).optional(),
|
|
5766
|
+
duplication: z5.object({
|
|
5767
|
+
enforcement: z5.enum(["warning", "blocking"]),
|
|
5768
|
+
status: z5.enum(["clear", "warning", "unresolved", "suppressed"]),
|
|
5769
|
+
warningCount: z5.number().int().nonnegative(),
|
|
5770
|
+
unresolvedCount: z5.number().int().nonnegative(),
|
|
5771
|
+
suppressedCount: z5.number().int().nonnegative(),
|
|
5772
|
+
matches: z5.array(duplicationMatchSchema.extend({
|
|
5773
|
+
matchId: z5.string().regex(/^[a-f0-9]{24}$/u)
|
|
5774
|
+
})).max(8)
|
|
5775
|
+
}).strict().optional(),
|
|
5741
5776
|
acceptance: z5.object({
|
|
5742
5777
|
state: z5.enum(["draft", "active", "satisfied", "blocked"]),
|
|
5743
5778
|
total: z5.number().int().nonnegative(),
|
|
@@ -5803,6 +5838,7 @@ var sessionSchema = z5.object({
|
|
|
5803
5838
|
contextSources: z5.array(contextSourceSchema).max(64).optional(),
|
|
5804
5839
|
toolArtifacts: z5.array(toolArtifactSchema).max(200).optional(),
|
|
5805
5840
|
taskContract: taskContractSchema.optional(),
|
|
5841
|
+
duplicationSuppressions: z5.array(duplicationSuppressionSchema).max(64).optional(),
|
|
5806
5842
|
lastRun: lastRunSchema.optional(),
|
|
5807
5843
|
usage: z5.object({
|
|
5808
5844
|
inputTokens: z5.number().nonnegative(),
|
|
@@ -8332,7 +8368,7 @@ function captureVerification(call, result, changeSequence, configuredCommands) {
|
|
|
8332
8368
|
commandKey: createHash11("sha256").update(normalized).digest("hex")
|
|
8333
8369
|
};
|
|
8334
8370
|
}
|
|
8335
|
-
function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = []) {
|
|
8371
|
+
function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = [], duplication) {
|
|
8336
8372
|
const files = [...new Set(changedFiles)];
|
|
8337
8373
|
const acceptance = taskContract ? buildAcceptance(taskContract, audit, evidence, currentChangeSequence) : void 0;
|
|
8338
8374
|
if (mutationTracking === "unknown") {
|
|
@@ -8342,6 +8378,7 @@ function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutat
|
|
|
8342
8378
|
checks: [],
|
|
8343
8379
|
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.",
|
|
8344
8380
|
mutationTracking,
|
|
8381
|
+
...duplication ? { duplication } : {},
|
|
8345
8382
|
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "unverified") } : {}
|
|
8346
8383
|
};
|
|
8347
8384
|
}
|
|
@@ -8352,7 +8389,8 @@ function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutat
|
|
|
8352
8389
|
changedFiles: [],
|
|
8353
8390
|
checks: [],
|
|
8354
8391
|
detail: acceptanceDetail(acceptance),
|
|
8355
|
-
acceptance: acceptanceForCompletion(acceptance, "unverified")
|
|
8392
|
+
acceptance: acceptanceForCompletion(acceptance, "unverified"),
|
|
8393
|
+
...duplication ? { duplication } : {}
|
|
8356
8394
|
};
|
|
8357
8395
|
}
|
|
8358
8396
|
return {
|
|
@@ -8360,7 +8398,18 @@ function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutat
|
|
|
8360
8398
|
changedFiles: [],
|
|
8361
8399
|
checks: [],
|
|
8362
8400
|
detail: "No workspace files changed in this run.",
|
|
8363
|
-
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "no_changes") } : {}
|
|
8401
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "no_changes") } : {},
|
|
8402
|
+
...duplication ? { duplication } : {}
|
|
8403
|
+
};
|
|
8404
|
+
}
|
|
8405
|
+
if (duplication?.enforcement === "blocking" && duplication.warningCount > 0) {
|
|
8406
|
+
return {
|
|
8407
|
+
status: "unverified",
|
|
8408
|
+
changedFiles: files,
|
|
8409
|
+
checks: [],
|
|
8410
|
+
detail: "A high-confidence duplicate implementation requires reuse, extension, or an exact audited suppression before completion.",
|
|
8411
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "unverified") } : {},
|
|
8412
|
+
duplication
|
|
8364
8413
|
};
|
|
8365
8414
|
}
|
|
8366
8415
|
const latestByCommand = /* @__PURE__ */ new Map();
|
|
@@ -8376,7 +8425,8 @@ function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutat
|
|
|
8376
8425
|
changedFiles: files,
|
|
8377
8426
|
checks,
|
|
8378
8427
|
detail: `No successful verification was recorded after the last change to ${fileCount(files.length)}.`,
|
|
8379
|
-
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "unverified") } : {}
|
|
8428
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "unverified") } : {},
|
|
8429
|
+
...duplication ? { duplication } : {}
|
|
8380
8430
|
};
|
|
8381
8431
|
}
|
|
8382
8432
|
const failures = checks.filter((check) => !check.ok);
|
|
@@ -8386,7 +8436,8 @@ function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutat
|
|
|
8386
8436
|
changedFiles: files,
|
|
8387
8437
|
checks,
|
|
8388
8438
|
detail: `${failures.length} of ${checks.length} current verification ${checks.length === 1 ? "check" : "checks"} failed.`,
|
|
8389
|
-
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "verification_failed") } : {}
|
|
8439
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "verification_failed") } : {},
|
|
8440
|
+
...duplication ? { duplication } : {}
|
|
8390
8441
|
};
|
|
8391
8442
|
}
|
|
8392
8443
|
if (acceptance && acceptanceUnresolved(acceptance)) {
|
|
@@ -8395,7 +8446,8 @@ function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutat
|
|
|
8395
8446
|
changedFiles: files,
|
|
8396
8447
|
checks,
|
|
8397
8448
|
detail: acceptanceDetail(acceptance),
|
|
8398
|
-
acceptance: acceptanceForCompletion(acceptance, "unverified")
|
|
8449
|
+
acceptance: acceptanceForCompletion(acceptance, "unverified"),
|
|
8450
|
+
...duplication ? { duplication } : {}
|
|
8399
8451
|
};
|
|
8400
8452
|
}
|
|
8401
8453
|
return {
|
|
@@ -8403,10 +8455,18 @@ function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutat
|
|
|
8403
8455
|
changedFiles: files,
|
|
8404
8456
|
checks,
|
|
8405
8457
|
detail: `${checks.length} current verification ${checks.length === 1 ? "check" : "checks"} passed for ${fileCount(files.length)}.`,
|
|
8406
|
-
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "verified") } : {}
|
|
8458
|
+
...acceptance ? { acceptance: acceptanceForCompletion(acceptance, "verified") } : {},
|
|
8459
|
+
...duplication ? { duplication } : {}
|
|
8407
8460
|
};
|
|
8408
8461
|
}
|
|
8409
8462
|
function completionRecoveryDirective(completion) {
|
|
8463
|
+
if (completion.duplication?.enforcement === "blocking" && completion.duplication.warningCount > 0) {
|
|
8464
|
+
const matches = completion.duplication.matches.filter((match) => match.kind === "type-1-or-2").map((match) => `- ${match.matchId}: ${match.changedPath} ${match.changedSymbol} duplicates ${match.candidatePath} ${match.candidateSymbol}`).join("\n");
|
|
8465
|
+
return `<runtime-completion-gate status="duplication_blocking" authorization="none">
|
|
8466
|
+
High-confidence Type-1/2 duplication was detected. Reuse or extend the existing implementation, remove the duplicate, or call the read-only duplication_audit tool to suppress an exact match with an audited reason. Suppression never waives verification, safety, accessibility, error handling, concurrency, or correctness requirements.
|
|
8467
|
+
${matches || "- Active duplicate match details are unavailable."}
|
|
8468
|
+
</runtime-completion-gate>`;
|
|
8469
|
+
}
|
|
8410
8470
|
if (completion.acceptance && acceptanceUnresolved(completion.acceptance)) {
|
|
8411
8471
|
const unresolved2 = completion.acceptance.unresolved.map((item) => `- [${item.status}] ${item.id}: ${item.description}`).join("\n");
|
|
8412
8472
|
const failed = completion.checks.filter((check) => !check.ok).map((check) => `- ${check.command} (tool call ${check.toolCallId})`).join("\n");
|
|
@@ -8815,6 +8875,140 @@ function render(memory) {
|
|
|
8815
8875
|
].join("\n");
|
|
8816
8876
|
}
|
|
8817
8877
|
|
|
8878
|
+
// src/tools/duplication.ts
|
|
8879
|
+
import { z as z17 } from "zod";
|
|
8880
|
+
|
|
8881
|
+
// src/agent/duplication-state.ts
|
|
8882
|
+
function buildDuplicationCompletion(audit, suppressions = [], runStartedAt) {
|
|
8883
|
+
const active = /* @__PURE__ */ new Map();
|
|
8884
|
+
const unresolved2 = /* @__PURE__ */ new Map();
|
|
8885
|
+
let sawCurrentReceipt = false;
|
|
8886
|
+
for (const event of audit) {
|
|
8887
|
+
if (runStartedAt && event.createdAt < runStartedAt) continue;
|
|
8888
|
+
const receipt = duplicationReceipt(event.metadata?.duplicationAudit);
|
|
8889
|
+
if (!receipt) continue;
|
|
8890
|
+
if (receipt.status === "clear" || receipt.status === "unresolved" || receipt.matches.some((match) => typeof match.matchId === "string")) sawCurrentReceipt = true;
|
|
8891
|
+
const changedPaths2 = changedPathsFor(event, receipt);
|
|
8892
|
+
for (const path of changedPaths2) {
|
|
8893
|
+
for (const [id, match] of active) if (match.changedPath === path) active.delete(id);
|
|
8894
|
+
unresolved2.delete(path);
|
|
8895
|
+
}
|
|
8896
|
+
if (receipt.status === "unresolved") {
|
|
8897
|
+
for (const path of changedPaths2) unresolved2.set(path, receipt.changeSequence);
|
|
8898
|
+
}
|
|
8899
|
+
for (const match of receipt.matches) {
|
|
8900
|
+
if (typeof match.matchId === "string") {
|
|
8901
|
+
active.set(match.matchId, match);
|
|
8902
|
+
}
|
|
8903
|
+
}
|
|
8904
|
+
}
|
|
8905
|
+
if (!active.size && !unresolved2.size && !sawCurrentReceipt) return void 0;
|
|
8906
|
+
const activeIds = new Set(active.keys());
|
|
8907
|
+
const suppressedIds = new Set(suppressions.filter((item) => activeIds.has(item.matchId)).map((item) => item.matchId));
|
|
8908
|
+
const warnings = [...active.values()].filter((match) => !suppressedIds.has(match.matchId));
|
|
8909
|
+
const suppressedCount = active.size - warnings.length;
|
|
8910
|
+
const enforcement = warnings.some((match) => match.kind === "type-1-or-2") ? "blocking" : "warning";
|
|
8911
|
+
return {
|
|
8912
|
+
enforcement,
|
|
8913
|
+
status: unresolved2.size ? "unresolved" : warnings.length ? "warning" : suppressedCount ? "suppressed" : "clear",
|
|
8914
|
+
warningCount: warnings.length,
|
|
8915
|
+
unresolvedCount: unresolved2.size,
|
|
8916
|
+
suppressedCount,
|
|
8917
|
+
matches: warnings.slice(0, 8)
|
|
8918
|
+
};
|
|
8919
|
+
}
|
|
8920
|
+
function activeDuplicationMatchIds(audit, suppressions = []) {
|
|
8921
|
+
const completion = buildDuplicationCompletion(audit, suppressions);
|
|
8922
|
+
return new Set(completion?.matches.map((match) => match.matchId) ?? []);
|
|
8923
|
+
}
|
|
8924
|
+
function pruneDuplicationSuppressions(audit, suppressions = []) {
|
|
8925
|
+
if (!suppressions.length) return [];
|
|
8926
|
+
const active = new Set(buildDuplicationCompletion(audit, [])?.matches.map((match) => match.matchId) ?? []);
|
|
8927
|
+
return suppressions.filter((item) => active.has(item.matchId)).slice(-64);
|
|
8928
|
+
}
|
|
8929
|
+
function findActiveDuplicationMatches(audit, suppressions = []) {
|
|
8930
|
+
return buildDuplicationCompletion(audit, suppressions)?.matches ?? [];
|
|
8931
|
+
}
|
|
8932
|
+
function hasDuplicationActivity(audit, runStartedAt) {
|
|
8933
|
+
return audit.some(
|
|
8934
|
+
(event) => (!runStartedAt || event.createdAt >= runStartedAt) && (duplicationReceipt(event.metadata?.duplicationAudit) !== void 0 || event.metadata?.duplicationSuppression !== void 0 || event.metadata?.activeDuplicationMatches !== void 0)
|
|
8935
|
+
);
|
|
8936
|
+
}
|
|
8937
|
+
function duplicationReceipt(value) {
|
|
8938
|
+
if (!value || typeof value !== "object") return void 0;
|
|
8939
|
+
const receipt = value;
|
|
8940
|
+
if (!Array.isArray(receipt.matches) || typeof receipt.changeSequence !== "number" || !["clear", "warning", "unresolved"].includes(String(receipt.status))) return void 0;
|
|
8941
|
+
return receipt;
|
|
8942
|
+
}
|
|
8943
|
+
function changedPathsFor(event, receipt) {
|
|
8944
|
+
const metadataPaths = event.metadata?.changedFiles;
|
|
8945
|
+
const fromMetadata = Array.isArray(metadataPaths) ? metadataPaths.filter((path) => typeof path === "string") : [];
|
|
8946
|
+
return [.../* @__PURE__ */ new Set([...fromMetadata, ...receipt.matches.map((match) => match.changedPath)])];
|
|
8947
|
+
}
|
|
8948
|
+
|
|
8949
|
+
// src/tools/duplication.ts
|
|
8950
|
+
var inputSchema12 = z17.discriminatedUnion("action", [
|
|
8951
|
+
z17.object({ action: z17.literal("show") }).strict(),
|
|
8952
|
+
z17.object({
|
|
8953
|
+
action: z17.literal("suppress"),
|
|
8954
|
+
match_id: z17.string().regex(/^[a-f0-9]{24}$/u),
|
|
8955
|
+
reason_code: z17.enum(["separate-boundary", "protocol-required", "generated-contract", "false-positive", "other"]),
|
|
8956
|
+
reason: z17.string().trim().min(12).max(240)
|
|
8957
|
+
}).strict()
|
|
8958
|
+
]);
|
|
8959
|
+
var duplicationTool = {
|
|
8960
|
+
definition: {
|
|
8961
|
+
name: "duplication_audit",
|
|
8962
|
+
description: "Inspect active duplicate-function warnings or suppress one exact match with an auditable reason. Suppression does not bypass correctness, security, accessibility, concurrency, or verification requirements.",
|
|
8963
|
+
category: "read",
|
|
8964
|
+
inputSchema: jsonSchema({
|
|
8965
|
+
action: { type: "string", enum: ["show", "suppress"] },
|
|
8966
|
+
match_id: { type: "string", description: "Exact 24-character match id from the current audit." },
|
|
8967
|
+
reason_code: { type: "string", enum: ["separate-boundary", "protocol-required", "generated-contract", "false-positive", "other"] },
|
|
8968
|
+
reason: { type: "string", description: "Specific 12-240 character explanation; code blocks and credentials are rejected." }
|
|
8969
|
+
}, ["action"])
|
|
8970
|
+
},
|
|
8971
|
+
async execute(arguments_, context) {
|
|
8972
|
+
const input2 = inputSchema12.parse(arguments_);
|
|
8973
|
+
const suppressions = pruneDuplicationSuppressions(
|
|
8974
|
+
context.session.audit ?? [],
|
|
8975
|
+
context.session.duplicationSuppressions ?? []
|
|
8976
|
+
);
|
|
8977
|
+
context.session.duplicationSuppressions = suppressions;
|
|
8978
|
+
const active = activeDuplicationMatchIds(context.session.audit ?? [], suppressions);
|
|
8979
|
+
if (input2.action === "suppress") {
|
|
8980
|
+
if (!active.has(input2.match_id)) {
|
|
8981
|
+
throw new Error(`Unknown, stale, or already suppressed duplication match: ${input2.match_id}`);
|
|
8982
|
+
}
|
|
8983
|
+
suppressions.push({
|
|
8984
|
+
matchId: input2.match_id,
|
|
8985
|
+
reasonCode: input2.reason_code,
|
|
8986
|
+
reason: cleanReason(input2.reason),
|
|
8987
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8988
|
+
toolCallId: context.toolCallId ?? "unavailable"
|
|
8989
|
+
});
|
|
8990
|
+
context.session.duplicationSuppressions = suppressions.slice(-64);
|
|
8991
|
+
return {
|
|
8992
|
+
content: `Suppressed duplication match ${input2.match_id}. This does not waive verification or safety requirements.`,
|
|
8993
|
+
metadata: { duplicationSuppression: context.session.duplicationSuppressions.at(-1) }
|
|
8994
|
+
};
|
|
8995
|
+
}
|
|
8996
|
+
const matches = findActiveDuplicationMatches(context.session.audit ?? [], suppressions);
|
|
8997
|
+
return {
|
|
8998
|
+
content: matches.length ? matches.map(
|
|
8999
|
+
(match) => `- ${match.matchId}: ${match.changedPath}#${match.changedSymbol} -> ${match.candidatePath}#${match.candidateSymbol} (${match.kind}, ${match.similarity.toFixed(3)})`
|
|
9000
|
+
).join("\n") : "No unsuppressed duplication matches.",
|
|
9001
|
+
metadata: { activeDuplicationMatches: matches.map((match) => match.matchId) }
|
|
9002
|
+
};
|
|
9003
|
+
}
|
|
9004
|
+
};
|
|
9005
|
+
function cleanReason(value) {
|
|
9006
|
+
if (/```|~~~|\b(?:api[_-]?key|access[_-]?token|authorization|cookie|password|secret)\s*[:=]/iu.test(value) || /\b(?:sk-[A-Za-z0-9_-]{12,}|AIza[0-9A-Za-z_-]{20,})\b/u.test(value)) {
|
|
9007
|
+
throw new Error("Suppression reasons cannot contain code blocks or credentials.");
|
|
9008
|
+
}
|
|
9009
|
+
return value.replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ").replace(/\s+/gu, " ").trim();
|
|
9010
|
+
}
|
|
9011
|
+
|
|
8818
9012
|
// src/tools/index.ts
|
|
8819
9013
|
function createDefaultToolRegistry(_options = {}) {
|
|
8820
9014
|
return new ToolRegistry([
|
|
@@ -8828,6 +9022,7 @@ function createDefaultToolRegistry(_options = {}) {
|
|
|
8828
9022
|
gitTool,
|
|
8829
9023
|
taskTool,
|
|
8830
9024
|
taskContractTool,
|
|
9025
|
+
duplicationTool,
|
|
8831
9026
|
workingMemoryTool
|
|
8832
9027
|
]);
|
|
8833
9028
|
}
|
|
@@ -9633,10 +9828,15 @@ function roundScore(value) {
|
|
|
9633
9828
|
}
|
|
9634
9829
|
|
|
9635
9830
|
// src/agent/duplication-audit.ts
|
|
9831
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
9636
9832
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
9637
|
-
var
|
|
9833
|
+
var DEFAULT_NEAR_CLONE_THRESHOLD = 0.55;
|
|
9638
9834
|
var MAX_MATCHES = 8;
|
|
9639
9835
|
async function auditChangedFunctions(input2) {
|
|
9836
|
+
const nearCloneThreshold = input2.nearCloneThreshold ?? DEFAULT_NEAR_CLONE_THRESHOLD;
|
|
9837
|
+
if (!Number.isFinite(nearCloneThreshold) || nearCloneThreshold < 0 || nearCloneThreshold > 1) {
|
|
9838
|
+
throw new Error("nearCloneThreshold must be a finite number between 0 and 1.");
|
|
9839
|
+
}
|
|
9640
9840
|
const auditableFiles = input2.changedFiles.filter(supportsFunctionFingerprintPath);
|
|
9641
9841
|
if (!auditableFiles.length) return void 0;
|
|
9642
9842
|
const reports = [];
|
|
@@ -9658,13 +9858,16 @@ async function auditChangedFunctions(input2) {
|
|
|
9658
9858
|
return unresolved(input2.changeSequence, input2.baseline?.generation);
|
|
9659
9859
|
}
|
|
9660
9860
|
const currentFunctions = reports.flatMap((report) => report.functions.map(withoutTokens));
|
|
9661
|
-
if (!currentFunctions.length)
|
|
9861
|
+
if (!currentFunctions.length) {
|
|
9862
|
+
return auditableFiles.some((path) => input2.recheckPaths?.has(path)) && input2.baseline ? clear(input2.baseline.generation, input2.changeSequence, skippedSmallFunctions) : void 0;
|
|
9863
|
+
}
|
|
9662
9864
|
if (!input2.baseline) return unresolved(input2.changeSequence);
|
|
9663
9865
|
const lookup = createBaselineLookup(input2.baseline.functions);
|
|
9664
|
-
const previousByCurrent = matchCurrentFunctions(input2.baseline.functions, currentFunctions);
|
|
9866
|
+
const previousByCurrent = matchCurrentFunctions(input2.baseline.functions, currentFunctions, nearCloneThreshold);
|
|
9665
9867
|
const added = currentFunctions.flatMap((current) => {
|
|
9666
9868
|
const previous = previousByCurrent.get(locationIdentity(current));
|
|
9667
|
-
|
|
9869
|
+
const recheck = input2.recheckFunctions?.has(identity(current)) || previous !== void 0 && input2.recheckFunctions?.has(identity(previous));
|
|
9870
|
+
return !previous || current.tokenCount >= previous.tokenCount * 1.5 || recheck ? [{ current, previous }] : [];
|
|
9668
9871
|
});
|
|
9669
9872
|
if (!added.length) return void 0;
|
|
9670
9873
|
const matches = [];
|
|
@@ -9673,11 +9876,12 @@ async function auditChangedFunctions(input2) {
|
|
|
9673
9876
|
for (const candidate of findCandidateFunctions(lookup, item)) {
|
|
9674
9877
|
if (previous && locationIdentity(candidate) === locationIdentity(previous)) continue;
|
|
9675
9878
|
const similarity = fingerprintSimilarity(item, candidate);
|
|
9676
|
-
if (similarity <
|
|
9879
|
+
if (similarity < nearCloneThreshold || best && best.similarity >= similarity) continue;
|
|
9677
9880
|
best = { candidate, similarity };
|
|
9678
9881
|
}
|
|
9679
9882
|
if (!best) continue;
|
|
9680
9883
|
matches.push({
|
|
9884
|
+
matchId: matchId(input2.baseline.generation, input2.changeSequence, item, best.candidate, best.similarity),
|
|
9681
9885
|
changedPath: item.path,
|
|
9682
9886
|
changedSymbol: item.symbol,
|
|
9683
9887
|
candidatePath: best.candidate.path,
|
|
@@ -9688,17 +9892,32 @@ async function auditChangedFunctions(input2) {
|
|
|
9688
9892
|
}
|
|
9689
9893
|
matches.sort((left, right) => right.similarity - left.similarity || left.changedPath.localeCompare(right.changedPath));
|
|
9690
9894
|
const bounded = matches.slice(0, MAX_MATCHES);
|
|
9895
|
+
const enforcement = bounded.some((match) => match.kind === "type-1-or-2") ? "blocking" : "warning";
|
|
9691
9896
|
return {
|
|
9692
9897
|
baselineGeneration: input2.baseline.generation,
|
|
9693
9898
|
changeSequence: input2.changeSequence,
|
|
9694
9899
|
status: bounded.length ? "warning" : "clear",
|
|
9695
|
-
warningOnly:
|
|
9900
|
+
warningOnly: enforcement === "warning",
|
|
9901
|
+
enforcement,
|
|
9696
9902
|
checkedFunctions: added.length,
|
|
9697
9903
|
skippedSmallFunctions,
|
|
9698
9904
|
matches: bounded,
|
|
9699
9905
|
rationale: bounded.length ? `${bounded.length} deterministic duplicate candidate${bounded.length === 1 ? "" : "s"} found; review for reuse.` : "No deterministic duplicate candidate exceeded the warning threshold."
|
|
9700
9906
|
};
|
|
9701
9907
|
}
|
|
9908
|
+
function clear(baselineGeneration, changeSequence, skippedSmallFunctions) {
|
|
9909
|
+
return {
|
|
9910
|
+
baselineGeneration,
|
|
9911
|
+
changeSequence,
|
|
9912
|
+
status: "clear",
|
|
9913
|
+
warningOnly: true,
|
|
9914
|
+
enforcement: "warning",
|
|
9915
|
+
checkedFunctions: 0,
|
|
9916
|
+
skippedSmallFunctions,
|
|
9917
|
+
matches: [],
|
|
9918
|
+
rationale: "No active deterministic duplicate candidate remains on the rechecked path."
|
|
9919
|
+
};
|
|
9920
|
+
}
|
|
9702
9921
|
function withoutTokens(value) {
|
|
9703
9922
|
const { normalizedTokens: _tokens, ...fingerprint } = value;
|
|
9704
9923
|
return fingerprint;
|
|
@@ -9742,15 +9961,15 @@ function findCandidateFunctions(lookup, current) {
|
|
|
9742
9961
|
}
|
|
9743
9962
|
return [...candidates.values()];
|
|
9744
9963
|
}
|
|
9745
|
-
function matchCurrentFunctions(baseline, current) {
|
|
9964
|
+
function matchCurrentFunctions(baseline, current, nearCloneThreshold) {
|
|
9746
9965
|
const matches = /* @__PURE__ */ new Map();
|
|
9747
9966
|
const available = new Map(baseline.map((item) => [locationIdentity(item), item]));
|
|
9748
|
-
assignMatches(current, matches, available, (item) => [...available.values()].filter((candidate) => identity(candidate) === identity(item) && linesOverlap(candidate, item)), false);
|
|
9749
|
-
assignMatches(current, matches, available, (item) => [...available.values()].filter((candidate) => identity(candidate) === identity(item)), true);
|
|
9750
|
-
assignMatches(current, matches, available, (item) => [...available.values()].filter((candidate) => candidate.path === item.path && linesOverlap(candidate, item)), true);
|
|
9967
|
+
assignMatches(current, matches, available, (item) => [...available.values()].filter((candidate) => identity(candidate) === identity(item) && linesOverlap(candidate, item)), false, nearCloneThreshold);
|
|
9968
|
+
assignMatches(current, matches, available, (item) => [...available.values()].filter((candidate) => identity(candidate) === identity(item)), true, nearCloneThreshold);
|
|
9969
|
+
assignMatches(current, matches, available, (item) => [...available.values()].filter((candidate) => candidate.path === item.path && linesOverlap(candidate, item)), true, nearCloneThreshold);
|
|
9751
9970
|
return matches;
|
|
9752
9971
|
}
|
|
9753
|
-
function assignMatches(current, matches, available, candidatesFor, requireSimilarity) {
|
|
9972
|
+
function assignMatches(current, matches, available, candidatesFor, requireSimilarity, nearCloneThreshold) {
|
|
9754
9973
|
for (const item of current) {
|
|
9755
9974
|
const currentId = locationIdentity(item);
|
|
9756
9975
|
if (matches.has(currentId)) continue;
|
|
@@ -9759,7 +9978,7 @@ function assignMatches(current, matches, available, candidatesFor, requireSimila
|
|
|
9759
9978
|
const similarity = fingerprintSimilarity(candidate, item);
|
|
9760
9979
|
if (!best || similarity > best.similarity) best = { candidate, similarity };
|
|
9761
9980
|
}
|
|
9762
|
-
if (!best || requireSimilarity && best.similarity <
|
|
9981
|
+
if (!best || requireSimilarity && best.similarity < nearCloneThreshold) continue;
|
|
9763
9982
|
matches.set(currentId, best.candidate);
|
|
9764
9983
|
available.delete(locationIdentity(best.candidate));
|
|
9765
9984
|
}
|
|
@@ -9773,6 +9992,7 @@ function unresolved(changeSequence, generation = "unavailable") {
|
|
|
9773
9992
|
changeSequence,
|
|
9774
9993
|
status: "unresolved",
|
|
9775
9994
|
warningOnly: true,
|
|
9995
|
+
enforcement: "warning",
|
|
9776
9996
|
checkedFunctions: 0,
|
|
9777
9997
|
skippedSmallFunctions: 0,
|
|
9778
9998
|
matches: [],
|
|
@@ -9782,6 +10002,17 @@ function unresolved(changeSequence, generation = "unavailable") {
|
|
|
9782
10002
|
function round(value) {
|
|
9783
10003
|
return Math.round(value * 1e3) / 1e3;
|
|
9784
10004
|
}
|
|
10005
|
+
function matchId(generation, changeSequence, changed, candidate, similarity) {
|
|
10006
|
+
return createHash14("sha256").update([
|
|
10007
|
+
generation,
|
|
10008
|
+
String(changeSequence),
|
|
10009
|
+
changed.path,
|
|
10010
|
+
changed.symbol,
|
|
10011
|
+
candidate.path,
|
|
10012
|
+
candidate.symbol,
|
|
10013
|
+
similarity === 1 ? "type-1-or-2" : "type-3"
|
|
10014
|
+
].join("\0")).digest("hex").slice(0, 24);
|
|
10015
|
+
}
|
|
9785
10016
|
|
|
9786
10017
|
// src/agent/runner.ts
|
|
9787
10018
|
var AgentRunner = class {
|
|
@@ -9853,6 +10084,7 @@ var AgentRunner = class {
|
|
|
9853
10084
|
await options.onEvent?.(event);
|
|
9854
10085
|
};
|
|
9855
10086
|
const changeSequenceAtStart = this.changeSequence;
|
|
10087
|
+
const runStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9856
10088
|
const runChangedFiles = /* @__PURE__ */ new Set();
|
|
9857
10089
|
const verificationEvidence = [];
|
|
9858
10090
|
const toolRecovery = new ToolRecoveryController();
|
|
@@ -9882,7 +10114,11 @@ var AgentRunner = class {
|
|
|
9882
10114
|
this.changeSequence,
|
|
9883
10115
|
mutationTracking,
|
|
9884
10116
|
activeRunContract,
|
|
9885
|
-
this.session.audit ?? []
|
|
10117
|
+
this.session.audit ?? [],
|
|
10118
|
+
hasDuplicationActivity(this.session.audit ?? [], runStartedAt) ? buildDuplicationCompletion(
|
|
10119
|
+
this.session.audit ?? [],
|
|
10120
|
+
this.session.duplicationSuppressions ?? []
|
|
10121
|
+
) : void 0
|
|
9886
10122
|
);
|
|
9887
10123
|
if (completion.acceptance && activeRunContract && activeRunContract.state !== "draft") {
|
|
9888
10124
|
activeRunContract.state = completion.acceptance.state;
|
|
@@ -10008,7 +10244,11 @@ var AgentRunner = class {
|
|
|
10008
10244
|
this.tools,
|
|
10009
10245
|
options.askMode === true,
|
|
10010
10246
|
contractEnabled,
|
|
10011
|
-
this.hasReadableToolArtifact()
|
|
10247
|
+
this.hasReadableToolArtifact(),
|
|
10248
|
+
activeDuplicationMatchIds(
|
|
10249
|
+
this.session.audit ?? [],
|
|
10250
|
+
this.session.duplicationSuppressions ?? []
|
|
10251
|
+
).size > 0
|
|
10012
10252
|
);
|
|
10013
10253
|
const estimatedInputTokens = estimateMessages2(messages) + estimateToolDefinitions(visibleTools);
|
|
10014
10254
|
if (availableTokens <= 0 || estimatedInputTokens >= availableTokens) {
|
|
@@ -10293,7 +10533,8 @@ ${input2}`
|
|
|
10293
10533
|
contextEngine: this.contextEngine,
|
|
10294
10534
|
toolArtifactStore: this.toolArtifactStore,
|
|
10295
10535
|
emit,
|
|
10296
|
-
...options.signal ? { signal: options.signal } : {}
|
|
10536
|
+
...options.signal ? { signal: options.signal } : {},
|
|
10537
|
+
toolCallId: call.id
|
|
10297
10538
|
};
|
|
10298
10539
|
try {
|
|
10299
10540
|
let reuseReceipt;
|
|
@@ -10343,10 +10584,21 @@ ${input2}`
|
|
|
10343
10584
|
throwIfAborted(options.signal);
|
|
10344
10585
|
const execution = await tool.execute(call.arguments, toolExecutionContext);
|
|
10345
10586
|
const changedFiles = await this.acceptChangedFiles(execution.changedFiles ?? []);
|
|
10587
|
+
const activeDuplicateFunctions = new Set(
|
|
10588
|
+
buildDuplicationCompletion(
|
|
10589
|
+
this.session.audit ?? [],
|
|
10590
|
+
[]
|
|
10591
|
+
)?.matches.map((match) => `${match.changedPath}\0${match.changedSymbol}`) ?? []
|
|
10592
|
+
);
|
|
10593
|
+
const activeDuplicatePaths = new Set(
|
|
10594
|
+
buildDuplicationCompletion(this.session.audit ?? [], [])?.matches.map((match) => match.changedPath) ?? []
|
|
10595
|
+
);
|
|
10346
10596
|
const duplicationAudit = duplicationAuditEnabled ? await auditChangedFunctions({
|
|
10347
10597
|
...duplicationBaseline ? { baseline: duplicationBaseline } : {},
|
|
10348
10598
|
changedFiles,
|
|
10349
|
-
changeSequence: this.changeSequence
|
|
10599
|
+
changeSequence: this.changeSequence,
|
|
10600
|
+
recheckFunctions: activeDuplicateFunctions,
|
|
10601
|
+
recheckPaths: activeDuplicatePaths
|
|
10350
10602
|
}) : void 0;
|
|
10351
10603
|
const tasksBefore = JSON.stringify(this.session.tasks);
|
|
10352
10604
|
let afterHookError;
|
|
@@ -10367,7 +10619,8 @@ Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : executio
|
|
|
10367
10619
|
|
|
10368
10620
|
${completeContent}`;
|
|
10369
10621
|
if (duplicationAudit?.status === "warning" || duplicationAudit?.status === "unresolved") {
|
|
10370
|
-
|
|
10622
|
+
const enforcement = duplicationAudit.enforcement === "blocking" ? "completion-blocking Type-1/2" : "warning-only";
|
|
10623
|
+
completeContent = `Duplication audit (${enforcement}): ${duplicationAudit.rationale}
|
|
10371
10624
|
|
|
10372
10625
|
${completeContent}`;
|
|
10373
10626
|
}
|
|
@@ -10826,9 +11079,9 @@ ${content}` : content,
|
|
|
10826
11079
|
...failure ? { metadata: { failure } } : {}
|
|
10827
11080
|
};
|
|
10828
11081
|
}
|
|
10829
|
-
function visibleToolDefinitions(tools, askMode, contractEnabled, artifactReadAvailable) {
|
|
11082
|
+
function visibleToolDefinitions(tools, askMode, contractEnabled, artifactReadAvailable, duplicationAvailable) {
|
|
10830
11083
|
return tools.definitions().filter(
|
|
10831
|
-
(tool) => (!askMode || tool.category === "read") && (contractEnabled || tool.name !== "task_contract") && (artifactReadAvailable || tool.name !== "read_tool_artifact")
|
|
11084
|
+
(tool) => (!askMode || tool.category === "read") && (contractEnabled || tool.name !== "task_contract") && (artifactReadAvailable || tool.name !== "read_tool_artifact") && (duplicationAvailable || tool.name !== "duplication_audit")
|
|
10832
11085
|
);
|
|
10833
11086
|
}
|
|
10834
11087
|
function formatToolError(error) {
|
|
@@ -11020,7 +11273,7 @@ function integer(value, fallback, min, max) {
|
|
|
11020
11273
|
// src/agent/delegation.ts
|
|
11021
11274
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
11022
11275
|
import { join as join18 } from "node:path";
|
|
11023
|
-
import { z as
|
|
11276
|
+
import { z as z19 } from "zod";
|
|
11024
11277
|
|
|
11025
11278
|
// src/agent/external-runtime.ts
|
|
11026
11279
|
async function runExternalAgent(request) {
|
|
@@ -11147,86 +11400,86 @@ function numeric(...values) {
|
|
|
11147
11400
|
}
|
|
11148
11401
|
|
|
11149
11402
|
// src/agent/team-store.ts
|
|
11150
|
-
import { createHash as
|
|
11403
|
+
import { createHash as createHash15, randomUUID as randomUUID13 } from "node:crypto";
|
|
11151
11404
|
import { lstat as lstat18, readFile as readFile17, readdir as readdir7, rm as rm3 } from "node:fs/promises";
|
|
11152
11405
|
import { join as join16, resolve as resolve16 } from "node:path";
|
|
11153
|
-
import { z as
|
|
11154
|
-
var runIdSchema =
|
|
11155
|
-
var hashSchema =
|
|
11156
|
-
var artifactSchema2 =
|
|
11406
|
+
import { z as z18 } from "zod";
|
|
11407
|
+
var runIdSchema = z18.string().uuid();
|
|
11408
|
+
var hashSchema = z18.string().regex(/^[a-f0-9]{64}$/u);
|
|
11409
|
+
var artifactSchema2 = z18.object({
|
|
11157
11410
|
sha256: hashSchema,
|
|
11158
|
-
bytes:
|
|
11411
|
+
bytes: z18.number().int().nonnegative().max(5e5)
|
|
11159
11412
|
}).strict();
|
|
11160
|
-
var phaseSchema =
|
|
11161
|
-
var agentRecordSchema =
|
|
11162
|
-
id:
|
|
11163
|
-
profile:
|
|
11164
|
-
provider:
|
|
11165
|
-
model:
|
|
11413
|
+
var phaseSchema = z18.enum(["work", "review", "revision", "write"]);
|
|
11414
|
+
var agentRecordSchema = z18.object({
|
|
11415
|
+
id: z18.string().uuid(),
|
|
11416
|
+
profile: z18.string(),
|
|
11417
|
+
provider: z18.string(),
|
|
11418
|
+
model: z18.string(),
|
|
11166
11419
|
phase: phaseSchema,
|
|
11167
|
-
ok:
|
|
11168
|
-
createdAt:
|
|
11169
|
-
startedAt:
|
|
11170
|
-
endedAt:
|
|
11171
|
-
durationMs:
|
|
11172
|
-
toolCalls:
|
|
11173
|
-
usage:
|
|
11174
|
-
inputTokens:
|
|
11175
|
-
outputTokens:
|
|
11420
|
+
ok: z18.boolean(),
|
|
11421
|
+
createdAt: z18.string(),
|
|
11422
|
+
startedAt: z18.string().optional(),
|
|
11423
|
+
endedAt: z18.string().optional(),
|
|
11424
|
+
durationMs: z18.number().int().nonnegative().optional(),
|
|
11425
|
+
toolCalls: z18.number().int().nonnegative().optional(),
|
|
11426
|
+
usage: z18.object({
|
|
11427
|
+
inputTokens: z18.number().int().nonnegative(),
|
|
11428
|
+
outputTokens: z18.number().int().nonnegative()
|
|
11176
11429
|
}).strict().optional(),
|
|
11177
11430
|
report: artifactSchema2
|
|
11178
11431
|
}).strict();
|
|
11179
|
-
var messageRecordSchema =
|
|
11180
|
-
id:
|
|
11181
|
-
from:
|
|
11182
|
-
to:
|
|
11183
|
-
createdAt:
|
|
11432
|
+
var messageRecordSchema = z18.object({
|
|
11433
|
+
id: z18.string().uuid(),
|
|
11434
|
+
from: z18.string(),
|
|
11435
|
+
to: z18.string(),
|
|
11436
|
+
createdAt: z18.string(),
|
|
11184
11437
|
content: artifactSchema2
|
|
11185
11438
|
}).strict();
|
|
11186
|
-
var writerIntegrationSchema =
|
|
11187
|
-
status:
|
|
11188
|
-
checkedAt:
|
|
11189
|
-
detail:
|
|
11190
|
-
checkpoint:
|
|
11191
|
-
sessionId:
|
|
11192
|
-
checkpointId:
|
|
11439
|
+
var writerIntegrationSchema = z18.object({
|
|
11440
|
+
status: z18.enum(["ready", "conflict", "integrated"]),
|
|
11441
|
+
checkedAt: z18.string(),
|
|
11442
|
+
detail: z18.string().max(2e4),
|
|
11443
|
+
checkpoint: z18.object({
|
|
11444
|
+
sessionId: z18.string(),
|
|
11445
|
+
checkpointId: z18.string()
|
|
11193
11446
|
}).strict().optional(),
|
|
11194
|
-
integratedAt:
|
|
11447
|
+
integratedAt: z18.string().optional()
|
|
11195
11448
|
}).strict();
|
|
11196
|
-
var writerLaneSchema =
|
|
11197
|
-
profile:
|
|
11198
|
-
reviewer:
|
|
11199
|
-
baseCommit:
|
|
11200
|
-
outcome:
|
|
11449
|
+
var writerLaneSchema = z18.object({
|
|
11450
|
+
profile: z18.string(),
|
|
11451
|
+
reviewer: z18.string(),
|
|
11452
|
+
baseCommit: z18.string().regex(/^[a-f0-9]{40,64}$/u),
|
|
11453
|
+
outcome: z18.enum(["accepted", "rejected", "failed", "cancelled"]),
|
|
11201
11454
|
patch: artifactSchema2,
|
|
11202
|
-
files:
|
|
11203
|
-
worktreeCleaned:
|
|
11455
|
+
files: z18.array(z18.string().min(1).max(4e3)).max(2e3),
|
|
11456
|
+
worktreeCleaned: z18.boolean(),
|
|
11204
11457
|
review: artifactSchema2.optional(),
|
|
11205
11458
|
integration: writerIntegrationSchema.optional()
|
|
11206
11459
|
}).strict();
|
|
11207
11460
|
var manifestFields = {
|
|
11208
11461
|
id: runIdSchema,
|
|
11209
|
-
workspace:
|
|
11210
|
-
objective:
|
|
11211
|
-
reviewer:
|
|
11212
|
-
createdAt:
|
|
11213
|
-
updatedAt:
|
|
11214
|
-
status:
|
|
11215
|
-
maxReviewRounds:
|
|
11216
|
-
reviewRounds:
|
|
11217
|
-
agents:
|
|
11218
|
-
messages:
|
|
11462
|
+
workspace: z18.string(),
|
|
11463
|
+
objective: z18.string().max(3e4),
|
|
11464
|
+
reviewer: z18.string(),
|
|
11465
|
+
createdAt: z18.string(),
|
|
11466
|
+
updatedAt: z18.string(),
|
|
11467
|
+
status: z18.enum(["running", "accepted", "rejected", "failed"]),
|
|
11468
|
+
maxReviewRounds: z18.number().int().min(0).max(3),
|
|
11469
|
+
reviewRounds: z18.number().int().min(0).max(3),
|
|
11470
|
+
agents: z18.array(agentRecordSchema).max(256),
|
|
11471
|
+
messages: z18.array(messageRecordSchema).max(512)
|
|
11219
11472
|
};
|
|
11220
|
-
var manifestV1Schema =
|
|
11221
|
-
version:
|
|
11473
|
+
var manifestV1Schema = z18.object({
|
|
11474
|
+
version: z18.literal(1),
|
|
11222
11475
|
...manifestFields
|
|
11223
11476
|
}).strict();
|
|
11224
|
-
var manifestV2Schema =
|
|
11225
|
-
version:
|
|
11477
|
+
var manifestV2Schema = z18.object({
|
|
11478
|
+
version: z18.literal(2),
|
|
11226
11479
|
...manifestFields,
|
|
11227
11480
|
writer: writerLaneSchema.optional()
|
|
11228
11481
|
}).strict();
|
|
11229
|
-
var manifestSchema2 =
|
|
11482
|
+
var manifestSchema2 = z18.discriminatedUnion("version", [manifestV1Schema, manifestV2Schema]);
|
|
11230
11483
|
var TeamRunStore = class {
|
|
11231
11484
|
workspace;
|
|
11232
11485
|
directory;
|
|
@@ -11405,7 +11658,7 @@ var TeamRunStore = class {
|
|
|
11405
11658
|
async writeArtifact(runId, content, truncate = true) {
|
|
11406
11659
|
const data = boundedArtifactText(content, 5e5, truncate);
|
|
11407
11660
|
const bytes = Buffer.byteLength(data);
|
|
11408
|
-
const sha256 =
|
|
11661
|
+
const sha256 = createHash15("sha256").update(data).digest("hex");
|
|
11409
11662
|
const directory = join16(this.runDirectory(runId), "blobs");
|
|
11410
11663
|
await ensureWorkspaceStorageDirectory(this.workspace, directory, {
|
|
11411
11664
|
requireActiveNamespace: this.managedDirectory
|
|
@@ -11414,7 +11667,7 @@ var TeamRunStore = class {
|
|
|
11414
11667
|
try {
|
|
11415
11668
|
await this.assertRegularFile(path);
|
|
11416
11669
|
const existing = await readFile17(path);
|
|
11417
|
-
if (
|
|
11670
|
+
if (createHash15("sha256").update(existing).digest("hex") !== sha256) {
|
|
11418
11671
|
throw new Error(`Team artifact hash collision or corruption: ${sha256}`);
|
|
11419
11672
|
}
|
|
11420
11673
|
} catch (error) {
|
|
@@ -11435,7 +11688,7 @@ var TeamRunStore = class {
|
|
|
11435
11688
|
const path = this.artifactPath(runId, artifact.sha256);
|
|
11436
11689
|
await this.assertRegularFile(path);
|
|
11437
11690
|
const data = await readFile17(path);
|
|
11438
|
-
const hash4 =
|
|
11691
|
+
const hash4 = createHash15("sha256").update(data).digest("hex");
|
|
11439
11692
|
if (hash4 !== artifact.sha256 || data.byteLength !== artifact.bytes) {
|
|
11440
11693
|
throw new Error(`Team artifact integrity check failed: ${artifact.sha256}`);
|
|
11441
11694
|
}
|
|
@@ -11498,7 +11751,7 @@ function resolveAgentModelRoute(team, parent, profile) {
|
|
|
11498
11751
|
}
|
|
11499
11752
|
|
|
11500
11753
|
// src/agent/writer-lane.ts
|
|
11501
|
-
import { createHash as
|
|
11754
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
11502
11755
|
import { lstat as lstat19, mkdtemp, realpath as realpath7, rm as rm4 } from "node:fs/promises";
|
|
11503
11756
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
11504
11757
|
import { isAbsolute as isAbsolute6, join as join17, resolve as resolve17 } from "node:path";
|
|
@@ -11596,7 +11849,7 @@ var WriterLane = class {
|
|
|
11596
11849
|
return {
|
|
11597
11850
|
baseCommit,
|
|
11598
11851
|
patch,
|
|
11599
|
-
patchSha256:
|
|
11852
|
+
patchSha256: createHash16("sha256").update(patch).digest("hex"),
|
|
11600
11853
|
files,
|
|
11601
11854
|
worktreeCleaned,
|
|
11602
11855
|
value
|
|
@@ -11826,14 +12079,14 @@ async function pathExists2(path) {
|
|
|
11826
12079
|
}
|
|
11827
12080
|
|
|
11828
12081
|
// src/agent/delegation.ts
|
|
11829
|
-
var writerRunInputSchema =
|
|
11830
|
-
task:
|
|
11831
|
-
profile:
|
|
11832
|
-
reviewer:
|
|
12082
|
+
var writerRunInputSchema = z19.object({
|
|
12083
|
+
task: z19.string().min(1).max(2e4),
|
|
12084
|
+
profile: z19.string().max(64).optional(),
|
|
12085
|
+
reviewer: z19.string().max(64).optional()
|
|
11833
12086
|
}).strict();
|
|
11834
|
-
var writerIntegrateInputSchema =
|
|
11835
|
-
run_id:
|
|
11836
|
-
patch_sha256:
|
|
12087
|
+
var writerIntegrateInputSchema = z19.object({
|
|
12088
|
+
run_id: z19.string().uuid(),
|
|
12089
|
+
patch_sha256: z19.string().regex(/^[a-f0-9]{64}$/u)
|
|
11837
12090
|
}).strict();
|
|
11838
12091
|
var DelegationManager = class {
|
|
11839
12092
|
constructor(options) {
|
|
@@ -11894,10 +12147,10 @@ var DelegationManager = class {
|
|
|
11894
12147
|
},
|
|
11895
12148
|
async execute(arguments_, context) {
|
|
11896
12149
|
if (!manager.team.enabled) return { ok: false, content: "Multi-agent delegation is disabled." };
|
|
11897
|
-
const input2 =
|
|
11898
|
-
tasks:
|
|
11899
|
-
profile:
|
|
11900
|
-
task:
|
|
12150
|
+
const input2 = z19.object({
|
|
12151
|
+
tasks: z19.array(z19.object({
|
|
12152
|
+
profile: z19.string().max(64).optional(),
|
|
12153
|
+
task: z19.string().min(1).max(2e4)
|
|
11901
12154
|
})).min(1).max(manager.team.maxDelegations)
|
|
11902
12155
|
}).parse(arguments_);
|
|
11903
12156
|
const tasks = input2.tasks.map((task) => ({
|
|
@@ -11943,13 +12196,13 @@ var DelegationManager = class {
|
|
|
11943
12196
|
},
|
|
11944
12197
|
async execute(arguments_, context) {
|
|
11945
12198
|
if (!manager.team.enabled) return { ok: false, content: "Multi-agent teams are disabled." };
|
|
11946
|
-
const input2 =
|
|
11947
|
-
objective:
|
|
11948
|
-
tasks:
|
|
11949
|
-
profile:
|
|
11950
|
-
task:
|
|
12199
|
+
const input2 = z19.object({
|
|
12200
|
+
objective: z19.string().min(1).max(3e4),
|
|
12201
|
+
tasks: z19.array(z19.object({
|
|
12202
|
+
profile: z19.string().max(64).optional(),
|
|
12203
|
+
task: z19.string().min(1).max(2e4)
|
|
11951
12204
|
})).min(1).max(manager.team.maxDelegations),
|
|
11952
|
-
reviewer:
|
|
12205
|
+
reviewer: z19.string().max(64).optional()
|
|
11953
12206
|
}).parse(arguments_);
|
|
11954
12207
|
const tasks = input2.tasks.map((task) => ({
|
|
11955
12208
|
profile: task.profile ?? manager.team.defaultProfile,
|
|
@@ -13597,22 +13850,24 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
13597
13850
|
if (!completion || completion.status === "no_changes") return;
|
|
13598
13851
|
const checks = completion.checks.map((check) => check.command).join(", ");
|
|
13599
13852
|
const suffix = checks ? ` ${this.glyphs.separator} ${checks}` : "";
|
|
13853
|
+
const duplication = completion.duplication;
|
|
13854
|
+
const duplicateSuffix = duplication ? ` ${this.glyphs.separator} duplication ${duplication.status} (${duplication.warningCount} warning, ${duplication.unresolvedCount} incomplete, ${duplication.suppressedCount} suppressed)` : "";
|
|
13600
13855
|
if (completion.status === "verified") {
|
|
13601
13856
|
process.stderr.write(this.paint.green(
|
|
13602
|
-
`${this.glyphs.success} verified ${this.glyphs.separator} ${completion.detail}${suffix}
|
|
13857
|
+
`${this.glyphs.success} verified ${this.glyphs.separator} ${completion.detail}${suffix}${duplicateSuffix}
|
|
13603
13858
|
`
|
|
13604
13859
|
));
|
|
13605
13860
|
return;
|
|
13606
13861
|
}
|
|
13607
13862
|
if (completion.status === "verification_failed") {
|
|
13608
13863
|
process.stderr.write(this.paint.red(
|
|
13609
|
-
`${this.glyphs.error} verification failed ${this.glyphs.separator} ${completion.detail}${suffix}
|
|
13864
|
+
`${this.glyphs.error} verification failed ${this.glyphs.separator} ${completion.detail}${suffix}${duplicateSuffix}
|
|
13610
13865
|
`
|
|
13611
13866
|
));
|
|
13612
13867
|
return;
|
|
13613
13868
|
}
|
|
13614
13869
|
process.stderr.write(this.paint.yellow(
|
|
13615
|
-
`${this.glyphs.warning} unverified ${this.glyphs.separator} ${completion.detail}
|
|
13870
|
+
`${this.glyphs.warning} unverified ${this.glyphs.separator} ${completion.detail}${duplicateSuffix}
|
|
13616
13871
|
`
|
|
13617
13872
|
));
|
|
13618
13873
|
}
|
|
@@ -16231,6 +16486,11 @@ function toolMetaSummary(metadata) {
|
|
|
16231
16486
|
if (status === "warning") parts.push(`duplicates ${matches} (warning)`);
|
|
16232
16487
|
else if (status === "unresolved") parts.push("duplicates incomplete");
|
|
16233
16488
|
}
|
|
16489
|
+
const suppression = metadata.duplicationSuppression;
|
|
16490
|
+
if (suppression && typeof suppression === "object") {
|
|
16491
|
+
const matchId2 = suppression.matchId;
|
|
16492
|
+
if (typeof matchId2 === "string") parts.push(`duplicate ${matchId2.slice(0, 8)} suppressed`);
|
|
16493
|
+
}
|
|
16234
16494
|
const hooks = metadata.hooks;
|
|
16235
16495
|
if (hooks && typeof hooks === "object") {
|
|
16236
16496
|
const before = Number(hooks.before ?? 0);
|
|
@@ -16690,12 +16950,14 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
16690
16950
|
refreshSession();
|
|
16691
16951
|
if (event.completion && event.completion.status !== "no_changes") {
|
|
16692
16952
|
const checks = event.completion.checks.map((check) => check.command).join(` ${separator} `);
|
|
16953
|
+
const duplication = event.completion.duplication;
|
|
16954
|
+
const duplicateDetail = duplication ? `${separator} duplication ${duplication.status} (${duplication.warningCount} warning, ${duplication.unresolvedCount} incomplete, ${duplication.suppressedCount} suppressed)` : "";
|
|
16693
16955
|
append({
|
|
16694
16956
|
id: nextId(),
|
|
16695
16957
|
kind: "notice",
|
|
16696
16958
|
wrapWidth: contentWidth,
|
|
16697
16959
|
tone: event.completion.status === "verified" ? "success" : event.completion.status === "unverified" ? "warning" : "error",
|
|
16698
|
-
text: event.completion.status === "verified" ? `Verified${separator}${event.completion.detail}${checks ? `${separator}${checks}` : ""}` : event.completion.status === "verification_failed" ? `Verification failed${separator}${event.completion.detail}${checks ? `${separator}${checks}` : ""}` : `Unverified${separator}${event.completion.detail}`
|
|
16960
|
+
text: event.completion.status === "verified" ? `Verified${separator}${event.completion.detail}${checks ? `${separator}${checks}` : ""}${duplicateDetail}` : event.completion.status === "verification_failed" ? `Verification failed${separator}${event.completion.detail}${checks ? `${separator}${checks}` : ""}${duplicateDetail}` : `Unverified${separator}${event.completion.detail}${duplicateDetail}`
|
|
16699
16961
|
});
|
|
16700
16962
|
}
|
|
16701
16963
|
if (event.reason !== "completed" && event.reason !== "unverified" && event.reason !== "verification_failed") {
|
|
@@ -18717,7 +18979,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
18717
18979
|
import stripAnsi5 from "strip-ansi";
|
|
18718
18980
|
|
|
18719
18981
|
// src/mcp/tool.ts
|
|
18720
|
-
import { createHash as
|
|
18982
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
18721
18983
|
import stripAnsi4 from "strip-ansi";
|
|
18722
18984
|
var MAX_ARGUMENT_BYTES = 256e3;
|
|
18723
18985
|
var MAX_DESCRIPTION_LENGTH = 4e3;
|
|
@@ -18725,7 +18987,7 @@ var MAX_RESULT_BYTES = 5 * 1024 * 1024;
|
|
|
18725
18987
|
var MAX_SCHEMA_BYTES = 1e5;
|
|
18726
18988
|
function createMcpToolAdapter(options) {
|
|
18727
18989
|
const { remoteTool } = options;
|
|
18728
|
-
const
|
|
18990
|
+
const inputSchema13 = copyInputSchema(remoteTool.inputSchema);
|
|
18729
18991
|
return {
|
|
18730
18992
|
definition: {
|
|
18731
18993
|
name: options.exposedName,
|
|
@@ -18733,7 +18995,7 @@ function createMcpToolAdapter(options) {
|
|
|
18733
18995
|
// MCP servers are an external trust boundary. Read-only annotations are
|
|
18734
18996
|
// hints from that server and must not lower the local permission level.
|
|
18735
18997
|
category: "network",
|
|
18736
|
-
inputSchema:
|
|
18998
|
+
inputSchema: inputSchema13
|
|
18737
18999
|
},
|
|
18738
19000
|
permissionCategories: () => ["network"],
|
|
18739
19001
|
async execute(arguments_, context) {
|
|
@@ -18887,7 +19149,7 @@ function fitToolName(name, identity2) {
|
|
|
18887
19149
|
return `${name.slice(0, 64 - suffix.length).replace(/_+$/g, "")}${suffix}`;
|
|
18888
19150
|
}
|
|
18889
19151
|
function shortHash(value) {
|
|
18890
|
-
return
|
|
19152
|
+
return createHash17("sha256").update(value).digest("hex").slice(0, 8);
|
|
18891
19153
|
}
|
|
18892
19154
|
function sanitizeOutputText(value) {
|
|
18893
19155
|
return stripAnsi4(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
|
|
@@ -19517,9 +19779,9 @@ async function closeTransportQuietly(transport) {
|
|
|
19517
19779
|
}
|
|
19518
19780
|
|
|
19519
19781
|
// src/memory/tools.ts
|
|
19520
|
-
import { z as
|
|
19521
|
-
var scopeSchema =
|
|
19522
|
-
var kindSchema =
|
|
19782
|
+
import { z as z20 } from "zod";
|
|
19783
|
+
var scopeSchema = z20.enum(["user", "workspace", "session", "agent"]);
|
|
19784
|
+
var kindSchema = z20.enum(["semantic", "episodic", "procedural"]);
|
|
19523
19785
|
function createMemoryTools(store) {
|
|
19524
19786
|
return [
|
|
19525
19787
|
{
|
|
@@ -19534,10 +19796,10 @@ function createMemoryTools(store) {
|
|
|
19534
19796
|
}, ["query"])
|
|
19535
19797
|
},
|
|
19536
19798
|
async execute(arguments_, context) {
|
|
19537
|
-
const input2 =
|
|
19538
|
-
query:
|
|
19799
|
+
const input2 = z20.object({
|
|
19800
|
+
query: z20.string().max(4e3),
|
|
19539
19801
|
scope: scopeSchema.optional(),
|
|
19540
|
-
limit:
|
|
19802
|
+
limit: z20.number().int().min(1).max(20).optional()
|
|
19541
19803
|
}).parse(arguments_);
|
|
19542
19804
|
const scopes = input2.scope ? [{ scope: input2.scope, scopeKey: scopeKey(input2.scope, context) }] : availableScopes(context);
|
|
19543
19805
|
const records = store.search(input2.query, { scopes, limit: input2.limit ?? 8 });
|
|
@@ -19569,17 +19831,17 @@ ${record.content}`
|
|
|
19569
19831
|
}, ["content", "rationale"])
|
|
19570
19832
|
},
|
|
19571
19833
|
async execute(arguments_, context) {
|
|
19572
|
-
const input2 =
|
|
19573
|
-
content:
|
|
19574
|
-
rationale:
|
|
19834
|
+
const input2 = z20.object({
|
|
19835
|
+
content: z20.string().min(1).max(12e3),
|
|
19836
|
+
rationale: z20.string().min(1).max(1e3),
|
|
19575
19837
|
scope: scopeSchema.optional(),
|
|
19576
19838
|
kind: kindSchema.optional(),
|
|
19577
|
-
tags:
|
|
19578
|
-
importance:
|
|
19579
|
-
confidence:
|
|
19580
|
-
agent:
|
|
19581
|
-
revision:
|
|
19582
|
-
conflictKey:
|
|
19839
|
+
tags: z20.array(z20.string()).max(24).optional(),
|
|
19840
|
+
importance: z20.number().min(0).max(1).optional(),
|
|
19841
|
+
confidence: z20.number().min(0).max(1).optional(),
|
|
19842
|
+
agent: z20.string().max(64).optional(),
|
|
19843
|
+
revision: z20.string().max(240).optional(),
|
|
19844
|
+
conflictKey: z20.string().max(240).optional()
|
|
19583
19845
|
}).strict().parse(arguments_);
|
|
19584
19846
|
const scope = input2.scope ?? "workspace";
|
|
19585
19847
|
const candidate = store.propose({
|
|
@@ -19617,9 +19879,9 @@ ${record.content}`
|
|
|
19617
19879
|
}, ["id"])
|
|
19618
19880
|
},
|
|
19619
19881
|
async execute(arguments_) {
|
|
19620
|
-
const input2 =
|
|
19621
|
-
id:
|
|
19622
|
-
permanent:
|
|
19882
|
+
const input2 = z20.object({
|
|
19883
|
+
id: z20.string().uuid(),
|
|
19884
|
+
permanent: z20.boolean().optional()
|
|
19623
19885
|
}).parse(arguments_);
|
|
19624
19886
|
const changed = input2.permanent ? store.remove(input2.id) : store.archive(input2.id);
|
|
19625
19887
|
return {
|
|
@@ -19823,7 +20085,7 @@ function escapeAttribute6(value) {
|
|
|
19823
20085
|
}
|
|
19824
20086
|
|
|
19825
20087
|
// src/workflows/catalog.ts
|
|
19826
|
-
import { z as
|
|
20088
|
+
import { z as z21 } from "zod";
|
|
19827
20089
|
var builtInWorkflows = [
|
|
19828
20090
|
{
|
|
19829
20091
|
name: "implement",
|
|
@@ -19914,7 +20176,7 @@ function createWorkflowTool(catalog) {
|
|
|
19914
20176
|
}, ["name", "task"])
|
|
19915
20177
|
},
|
|
19916
20178
|
async execute(arguments_) {
|
|
19917
|
-
const input2 =
|
|
20179
|
+
const input2 = z21.object({ name: z21.string(), task: z21.string().min(1).max(2e4) }).parse(arguments_);
|
|
19918
20180
|
const workflow = catalog.get(input2.name);
|
|
19919
20181
|
if (!workflow) return { ok: false, content: `Unknown workflow: ${input2.name}` };
|
|
19920
20182
|
return {
|