@abelxiaoxing/cadence 1.2.1 → 1.2.3
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 +7 -1
- package/agents/diagnosis-worker.md +6 -2
- package/agents/implementation-worker.md +9 -2
- package/config/routes.example.json +0 -1
- package/package.json +1 -1
- package/prompts/abel-design.md +15 -4
- package/skills/abel-workflow/SKILL.md +5 -3
- package/src/apply-transaction.ts +1 -5
- package/src/child-session.ts +37 -46
- package/src/contracts.ts +278 -32
- package/src/control-contracts.ts +62 -0
- package/src/delivery-compiler.ts +239 -124
- package/src/design-control.ts +72 -27
- package/src/design-journal.ts +2 -2
- package/src/file-snapshot.ts +1 -1
- package/src/index.ts +295 -58
- package/src/packet-runtime.ts +79 -21
- package/src/parent-provider.ts +63 -30
- package/src/route-policy.ts +58 -14
- package/src/run-store.ts +115 -23
- package/src/subagent-activity.ts +82 -6
- package/src/submit-tool.ts +431 -39
- package/src/task-ledger.ts +1 -2
- package/src/worker-broker.ts +72 -13
- package/src/workflow-engine.ts +782 -54
- package/src/workspace-store.ts +2 -10
package/src/submit-tool.ts
CHANGED
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
type CandidateFailure,
|
|
10
10
|
type DiffResult,
|
|
11
11
|
type EvidenceResult,
|
|
12
|
+
isAgentsPath,
|
|
12
13
|
isValidRelativePath,
|
|
14
|
+
LIMITS,
|
|
13
15
|
type SubmitFinalCategory,
|
|
14
16
|
type SubmitSchemaState,
|
|
15
17
|
validateDiffResult,
|
|
@@ -144,15 +146,57 @@ function candidateArtifactSchema(candidateId: string) {
|
|
|
144
146
|
code: Type.Union([
|
|
145
147
|
Type.Literal("approved-context-needed"),
|
|
146
148
|
Type.Literal("task-split-needed"),
|
|
147
|
-
Type.Literal("boundary-review-needed"),
|
|
148
149
|
]),
|
|
149
|
-
refs: Type.Array(
|
|
150
|
+
refs: Type.Array(
|
|
151
|
+
Type.Union([
|
|
152
|
+
Type.String(),
|
|
153
|
+
Type.Object(
|
|
154
|
+
{
|
|
155
|
+
kind: Type.Literal("requested-path"),
|
|
156
|
+
path: Type.String(),
|
|
157
|
+
access: Type.Union([
|
|
158
|
+
Type.Literal("read"),
|
|
159
|
+
Type.Literal("write"),
|
|
160
|
+
]),
|
|
161
|
+
},
|
|
162
|
+
{ additionalProperties: false },
|
|
163
|
+
),
|
|
164
|
+
Type.Object(
|
|
165
|
+
{
|
|
166
|
+
kind: Type.Literal("source-citation"),
|
|
167
|
+
path: Type.String(),
|
|
168
|
+
line: Type.Integer({ minimum: 1 }),
|
|
169
|
+
},
|
|
170
|
+
{ additionalProperties: false },
|
|
171
|
+
),
|
|
172
|
+
Type.Object(
|
|
173
|
+
{
|
|
174
|
+
kind: Type.Literal("contract-diagnostic"),
|
|
175
|
+
ref: Type.String(),
|
|
176
|
+
},
|
|
177
|
+
{ additionalProperties: false },
|
|
178
|
+
),
|
|
179
|
+
]),
|
|
180
|
+
{ minItems: 1, maxItems: 32 },
|
|
181
|
+
),
|
|
150
182
|
},
|
|
151
183
|
{ additionalProperties: false },
|
|
152
184
|
),
|
|
153
185
|
]);
|
|
154
186
|
}
|
|
155
187
|
|
|
188
|
+
export type CandidateContextRequestCode =
|
|
189
|
+
| "approved-context-needed"
|
|
190
|
+
| "task-split-needed"
|
|
191
|
+
| "boundary-review-needed";
|
|
192
|
+
|
|
193
|
+
export type CandidateContextRef =
|
|
194
|
+
| { kind: "requested-path"; path: string; access: "read" | "write" }
|
|
195
|
+
| { kind: "source-citation"; path: string; line: number }
|
|
196
|
+
| { kind: "contract-diagnostic"; ref: string };
|
|
197
|
+
|
|
198
|
+
export type CandidateContextRefInput = string | CandidateContextRef;
|
|
199
|
+
|
|
156
200
|
export type CandidateArtifactSubmissionResult =
|
|
157
201
|
| {
|
|
158
202
|
kind: "sealed-candidate";
|
|
@@ -166,11 +210,8 @@ export type CandidateArtifactSubmissionResult =
|
|
|
166
210
|
| {
|
|
167
211
|
kind: "context-request";
|
|
168
212
|
candidateId: string;
|
|
169
|
-
code:
|
|
170
|
-
|
|
171
|
-
| "task-split-needed"
|
|
172
|
-
| "boundary-review-needed";
|
|
173
|
-
refs: string[];
|
|
213
|
+
code: CandidateContextRequestCode;
|
|
214
|
+
refs: CandidateContextRef[];
|
|
174
215
|
};
|
|
175
216
|
|
|
176
217
|
export type CandidateContextRequest = Extract<
|
|
@@ -178,18 +219,114 @@ export type CandidateContextRequest = Extract<
|
|
|
178
219
|
{ kind: "context-request" }
|
|
179
220
|
>;
|
|
180
221
|
|
|
222
|
+
export interface CandidateContextBoundary {
|
|
223
|
+
phase: "red" | "green" | "refactor";
|
|
224
|
+
readPaths: readonly string[];
|
|
225
|
+
writePaths: readonly string[];
|
|
226
|
+
deletePaths?: readonly string[];
|
|
227
|
+
taskPaths: readonly string[];
|
|
228
|
+
redWritePaths: readonly string[];
|
|
229
|
+
agents: {
|
|
230
|
+
impact: "none" | "update-existing" | "create-index" | "remove-index";
|
|
231
|
+
target?: string;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
181
235
|
export interface ClassifiedCandidateContextRequest {
|
|
182
|
-
kind: "paused" | "approval-needed";
|
|
236
|
+
kind: "paused" | "retryable" | "approval-needed";
|
|
183
237
|
code:
|
|
184
238
|
| "approved-context-needed"
|
|
185
239
|
| "task-split-needed"
|
|
186
|
-
| "boundary-review-needed"
|
|
240
|
+
| "boundary-review-needed"
|
|
241
|
+
| "red-artifact-constraint"
|
|
242
|
+
| "agents-context-needed"
|
|
243
|
+
| "agents-write-parent-owned"
|
|
244
|
+
| "agents-contract-insufficient";
|
|
187
245
|
contextRequest: {
|
|
188
|
-
code:
|
|
189
|
-
refs:
|
|
246
|
+
code: CandidateContextRequestCode;
|
|
247
|
+
refs: CandidateContextRef[];
|
|
190
248
|
};
|
|
191
249
|
}
|
|
192
250
|
|
|
251
|
+
const LEGACY_CITATION = /^(.*):(\d+)$/u;
|
|
252
|
+
const LEGACY_DIAGNOSTIC =
|
|
253
|
+
/^(?:phase-contract|task-contract|sealed-contract|workflow-contract)\.[a-z0-9._-]+$/iu;
|
|
254
|
+
|
|
255
|
+
function validDiagnosticRef(value: unknown): value is string {
|
|
256
|
+
return (
|
|
257
|
+
typeof value === "string" &&
|
|
258
|
+
value.length > 0 &&
|
|
259
|
+
value.length <= 512 &&
|
|
260
|
+
value.trim() === value &&
|
|
261
|
+
!/[\r\n\0]/u.test(value)
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function normalizeCandidateContextRef(
|
|
266
|
+
input: CandidateContextRefInput,
|
|
267
|
+
): CandidateContextRef {
|
|
268
|
+
if (typeof input === "string") {
|
|
269
|
+
const citation = LEGACY_CITATION.exec(input);
|
|
270
|
+
if (citation && isValidRelativePath(citation[1])) {
|
|
271
|
+
const line = Number(citation[2]);
|
|
272
|
+
if (Number.isSafeInteger(line) && line >= 1) {
|
|
273
|
+
return { kind: "source-citation", path: citation[1], line };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (LEGACY_DIAGNOSTIC.test(input)) {
|
|
277
|
+
return { kind: "contract-diagnostic", ref: input };
|
|
278
|
+
}
|
|
279
|
+
if (!isValidRelativePath(input)) {
|
|
280
|
+
throw new Error("context request refs are invalid");
|
|
281
|
+
}
|
|
282
|
+
return { kind: "requested-path", path: input, access: "read" };
|
|
283
|
+
}
|
|
284
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
285
|
+
throw new Error("context request refs are invalid");
|
|
286
|
+
}
|
|
287
|
+
if (
|
|
288
|
+
input.kind === "requested-path" &&
|
|
289
|
+
Object.keys(input).length === 3 &&
|
|
290
|
+
isValidRelativePath(input.path) &&
|
|
291
|
+
(input.access === "read" || input.access === "write")
|
|
292
|
+
) {
|
|
293
|
+
return { ...input };
|
|
294
|
+
}
|
|
295
|
+
if (
|
|
296
|
+
input.kind === "source-citation" &&
|
|
297
|
+
Object.keys(input).length === 3 &&
|
|
298
|
+
isValidRelativePath(input.path) &&
|
|
299
|
+
Number.isSafeInteger(input.line) &&
|
|
300
|
+
input.line >= 1
|
|
301
|
+
) {
|
|
302
|
+
return { ...input };
|
|
303
|
+
}
|
|
304
|
+
if (
|
|
305
|
+
input.kind === "contract-diagnostic" &&
|
|
306
|
+
Object.keys(input).length === 2 &&
|
|
307
|
+
validDiagnosticRef(input.ref)
|
|
308
|
+
) {
|
|
309
|
+
return { ...input };
|
|
310
|
+
}
|
|
311
|
+
throw new Error("context request refs are invalid");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function normalizeCandidateContextRefs(
|
|
315
|
+
value: unknown,
|
|
316
|
+
): CandidateContextRef[] {
|
|
317
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 32) {
|
|
318
|
+
throw new Error("context request refs are invalid");
|
|
319
|
+
}
|
|
320
|
+
const refs = value.map((entry) =>
|
|
321
|
+
normalizeCandidateContextRef(entry as CandidateContextRefInput),
|
|
322
|
+
);
|
|
323
|
+
const identities = refs.map((ref) => JSON.stringify(ref));
|
|
324
|
+
if (new Set(identities).size !== refs.length) {
|
|
325
|
+
throw new Error("context request refs are invalid");
|
|
326
|
+
}
|
|
327
|
+
return refs;
|
|
328
|
+
}
|
|
329
|
+
|
|
193
330
|
function contextRefWithinBoundary(relative: string, approved: string): boolean {
|
|
194
331
|
return (
|
|
195
332
|
approved === "." ||
|
|
@@ -198,23 +335,108 @@ function contextRefWithinBoundary(relative: string, approved: string): boolean {
|
|
|
198
335
|
);
|
|
199
336
|
}
|
|
200
337
|
|
|
338
|
+
function normalizedBoundary(
|
|
339
|
+
boundary: readonly string[] | CandidateContextBoundary,
|
|
340
|
+
): CandidateContextBoundary {
|
|
341
|
+
if (!Array.isArray(boundary)) return boundary as CandidateContextBoundary;
|
|
342
|
+
return {
|
|
343
|
+
phase: "green",
|
|
344
|
+
readPaths: boundary,
|
|
345
|
+
writePaths: [],
|
|
346
|
+
deletePaths: [],
|
|
347
|
+
taskPaths: boundary,
|
|
348
|
+
redWritePaths: [],
|
|
349
|
+
agents: { impact: "none" },
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function pathWithinAny(relative: string, approved: readonly string[]): boolean {
|
|
354
|
+
return approved.some((path) => contextRefWithinBoundary(relative, path));
|
|
355
|
+
}
|
|
356
|
+
|
|
201
357
|
export function classifyCandidateContextRequest(
|
|
202
|
-
request:
|
|
203
|
-
|
|
358
|
+
request: {
|
|
359
|
+
kind: "context-request";
|
|
360
|
+
candidateId: string;
|
|
361
|
+
code: CandidateContextRequestCode;
|
|
362
|
+
refs: readonly CandidateContextRefInput[];
|
|
363
|
+
},
|
|
364
|
+
inputBoundary: readonly string[] | CandidateContextBoundary,
|
|
204
365
|
): ClassifiedCandidateContextRequest {
|
|
205
|
-
const refs =
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
366
|
+
const refs = normalizeCandidateContextRefs(request.refs);
|
|
367
|
+
const boundary = normalizedBoundary(inputBoundary);
|
|
368
|
+
const phasePaths = [
|
|
369
|
+
...boundary.readPaths,
|
|
370
|
+
...boundary.writePaths,
|
|
371
|
+
...(boundary.deletePaths ?? []),
|
|
372
|
+
];
|
|
373
|
+
const requested = refs.filter(
|
|
374
|
+
(ref): ref is Extract<CandidateContextRef, { kind: "requested-path" }> =>
|
|
375
|
+
ref.kind === "requested-path",
|
|
376
|
+
);
|
|
377
|
+
const ordinaryRequested = requested.filter((ref) => !isAgentsPath(ref.path));
|
|
378
|
+
const outsideOrdinary = ordinaryRequested.filter(
|
|
379
|
+
(ref) =>
|
|
380
|
+
!pathWithinAny(
|
|
381
|
+
ref.path,
|
|
382
|
+
ref.access === "write"
|
|
383
|
+
? [...boundary.writePaths, ...(boundary.deletePaths ?? [])]
|
|
384
|
+
: phasePaths,
|
|
211
385
|
),
|
|
386
|
+
);
|
|
387
|
+
const agentsRequested = requested.filter((ref) => isAgentsPath(ref.path));
|
|
388
|
+
const agentsWrites = agentsRequested.filter((ref) => ref.access === "write");
|
|
389
|
+
const citedRedArtifacts = refs.filter(
|
|
390
|
+
(ref): ref is Extract<CandidateContextRef, { kind: "source-citation" }> =>
|
|
391
|
+
ref.kind === "source-citation" &&
|
|
392
|
+
pathWithinAny(ref.path, boundary.redWritePaths) &&
|
|
393
|
+
!pathWithinAny(ref.path, [
|
|
394
|
+
...boundary.writePaths,
|
|
395
|
+
...(boundary.deletePaths ?? []),
|
|
396
|
+
]),
|
|
397
|
+
);
|
|
398
|
+
const requestedRedArtifacts = outsideOrdinary.filter(
|
|
399
|
+
(ref) =>
|
|
400
|
+
pathWithinAny(ref.path, boundary.redWritePaths) &&
|
|
401
|
+
pathWithinAny(ref.path, boundary.taskPaths),
|
|
402
|
+
);
|
|
403
|
+
const redArtifactConstraint =
|
|
404
|
+
boundary.phase === "green" &&
|
|
405
|
+
(citedRedArtifacts.length > 0 || requestedRedArtifacts.length > 0) &&
|
|
406
|
+
outsideOrdinary.every((ref) => requestedRedArtifacts.includes(ref)) &&
|
|
407
|
+
agentsRequested.every((ref) => ref.access === "read");
|
|
408
|
+
|
|
409
|
+
let classification: Pick<ClassifiedCandidateContextRequest, "kind" | "code">;
|
|
410
|
+
if (outsideOrdinary.length > 0 && !redArtifactConstraint) {
|
|
411
|
+
classification = {
|
|
412
|
+
kind: "approval-needed",
|
|
413
|
+
code: "boundary-review-needed",
|
|
414
|
+
};
|
|
415
|
+
} else if (agentsWrites.length > 0) {
|
|
416
|
+
const targetMismatch = agentsWrites.some(
|
|
417
|
+
(ref) =>
|
|
418
|
+
boundary.agents.impact === "none" ||
|
|
419
|
+
boundary.agents.target === undefined ||
|
|
420
|
+
ref.path !== boundary.agents.target,
|
|
212
421
|
);
|
|
213
|
-
|
|
214
|
-
|
|
422
|
+
classification = targetMismatch
|
|
423
|
+
? { kind: "approval-needed", code: "agents-contract-insufficient" }
|
|
424
|
+
: { kind: "paused", code: "agents-write-parent-owned" };
|
|
425
|
+
} else if (redArtifactConstraint) {
|
|
426
|
+
classification = { kind: "retryable", code: "red-artifact-constraint" };
|
|
427
|
+
} else if (agentsRequested.length > 0) {
|
|
428
|
+
classification = { kind: "paused", code: "agents-context-needed" };
|
|
429
|
+
} else {
|
|
430
|
+
classification = {
|
|
431
|
+
kind: request.code === "approved-context-needed" ? "retryable" : "paused",
|
|
432
|
+
code:
|
|
433
|
+
request.code === "task-split-needed"
|
|
434
|
+
? "task-split-needed"
|
|
435
|
+
: "approved-context-needed",
|
|
436
|
+
};
|
|
437
|
+
}
|
|
215
438
|
return {
|
|
216
|
-
|
|
217
|
-
code: inBoundary ? request.code : "boundary-review-needed",
|
|
439
|
+
...classification,
|
|
218
440
|
contextRequest: {
|
|
219
441
|
code: request.code,
|
|
220
442
|
refs,
|
|
@@ -256,10 +478,16 @@ export function createCandidateArtifactTool(
|
|
|
256
478
|
executionMode: "sequential",
|
|
257
479
|
parameters,
|
|
258
480
|
async execute(_toolCallId, params) {
|
|
481
|
+
attempts++;
|
|
259
482
|
if (result !== undefined) {
|
|
483
|
+
schema = "invalid";
|
|
484
|
+
failure = {
|
|
485
|
+
kind: "artifact",
|
|
486
|
+
code: "invalid-structural-result",
|
|
487
|
+
stage: "structural-submit",
|
|
488
|
+
};
|
|
260
489
|
throw new Error("candidate terminal result already submitted");
|
|
261
490
|
}
|
|
262
|
-
attempts++;
|
|
263
491
|
if (attempts > 2) {
|
|
264
492
|
schema = "invalid";
|
|
265
493
|
failure = {
|
|
@@ -391,18 +619,17 @@ export function createCandidateArtifactTool(
|
|
|
391
619
|
terminate: true,
|
|
392
620
|
};
|
|
393
621
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
refs
|
|
397
|
-
|
|
398
|
-
) {
|
|
622
|
+
let refs: CandidateContextRef[];
|
|
623
|
+
try {
|
|
624
|
+
refs = normalizeCandidateContextRefs(value.refs);
|
|
625
|
+
} catch (error) {
|
|
399
626
|
schema = "invalid";
|
|
400
627
|
failure = {
|
|
401
628
|
kind: "artifact",
|
|
402
629
|
code: "invalid-structural-result",
|
|
403
630
|
stage: "structural-submit",
|
|
404
631
|
};
|
|
405
|
-
throw
|
|
632
|
+
throw error;
|
|
406
633
|
}
|
|
407
634
|
result = {
|
|
408
635
|
kind: "context-request",
|
|
@@ -411,7 +638,7 @@ export function createCandidateArtifactTool(
|
|
|
411
638
|
CandidateArtifactSubmissionResult,
|
|
412
639
|
{ kind: "context-request" }
|
|
413
640
|
>["code"],
|
|
414
|
-
refs
|
|
641
|
+
refs,
|
|
415
642
|
};
|
|
416
643
|
return {
|
|
417
644
|
content: [
|
|
@@ -434,6 +661,170 @@ export function createCandidateArtifactTool(
|
|
|
434
661
|
};
|
|
435
662
|
}
|
|
436
663
|
|
|
664
|
+
export function createStructuredPatchTool(input: {
|
|
665
|
+
requestId: string;
|
|
666
|
+
taskId?: string;
|
|
667
|
+
role: string;
|
|
668
|
+
phase: string;
|
|
669
|
+
workspaceRoot: string;
|
|
670
|
+
writePaths: string[];
|
|
671
|
+
deletePaths: string[];
|
|
672
|
+
}) {
|
|
673
|
+
let submitted: DiffResult | CandidateContextRequest | undefined;
|
|
674
|
+
let failure: CandidateFailure | undefined;
|
|
675
|
+
let attempts = 0;
|
|
676
|
+
let schema: SubmitSchemaState = "not-submitted";
|
|
677
|
+
let identity: IdentityOutcome = {
|
|
678
|
+
request: true,
|
|
679
|
+
role: true,
|
|
680
|
+
task: true,
|
|
681
|
+
phase: true,
|
|
682
|
+
};
|
|
683
|
+
const candidateId = input.requestId;
|
|
684
|
+
const tool = defineTool({
|
|
685
|
+
name: "abel_submit_result",
|
|
686
|
+
label: "Submit Abel Structured Patch",
|
|
687
|
+
description:
|
|
688
|
+
"Submit one complete structured patch. Unified-diff headers and hunks are generated by the trusted control plane.",
|
|
689
|
+
executionMode: "sequential",
|
|
690
|
+
parameters: candidateArtifactSchema(candidateId),
|
|
691
|
+
async execute(_toolCallId, params) {
|
|
692
|
+
attempts += 1;
|
|
693
|
+
if (submitted !== undefined) {
|
|
694
|
+
schema = "invalid";
|
|
695
|
+
failure = {
|
|
696
|
+
kind: "artifact",
|
|
697
|
+
code: "invalid-structural-result",
|
|
698
|
+
stage: "structural-submit",
|
|
699
|
+
};
|
|
700
|
+
throw new Error("structured patch terminal result already submitted");
|
|
701
|
+
}
|
|
702
|
+
if (attempts > 2) {
|
|
703
|
+
schema = "invalid";
|
|
704
|
+
failure = {
|
|
705
|
+
kind: "artifact",
|
|
706
|
+
code: "invalid-structural-result",
|
|
707
|
+
stage: "structural-submit",
|
|
708
|
+
};
|
|
709
|
+
throw new Error("structural submission correction limit exceeded");
|
|
710
|
+
}
|
|
711
|
+
identity = {
|
|
712
|
+
request: true,
|
|
713
|
+
role: true,
|
|
714
|
+
task: true,
|
|
715
|
+
phase: true,
|
|
716
|
+
};
|
|
717
|
+
failure = undefined;
|
|
718
|
+
const value = params as unknown as Record<string, unknown>;
|
|
719
|
+
if (value.candidateId !== candidateId) {
|
|
720
|
+
identity.request = false;
|
|
721
|
+
schema = "invalid";
|
|
722
|
+
failure = {
|
|
723
|
+
kind: "artifact",
|
|
724
|
+
code: "structural-identity-mismatch",
|
|
725
|
+
stage: "structural-submit",
|
|
726
|
+
};
|
|
727
|
+
throw new Error("diagnosis candidate identity mismatch");
|
|
728
|
+
}
|
|
729
|
+
if (value.kind === "context-request") {
|
|
730
|
+
let refs: CandidateContextRef[];
|
|
731
|
+
try {
|
|
732
|
+
refs = normalizeCandidateContextRefs(value.refs);
|
|
733
|
+
} catch (error) {
|
|
734
|
+
schema = "invalid";
|
|
735
|
+
failure = {
|
|
736
|
+
kind: "artifact",
|
|
737
|
+
code: "invalid-structural-result",
|
|
738
|
+
stage: "structural-submit",
|
|
739
|
+
};
|
|
740
|
+
throw error;
|
|
741
|
+
}
|
|
742
|
+
submitted = {
|
|
743
|
+
kind: "context-request",
|
|
744
|
+
candidateId,
|
|
745
|
+
code: value.code as CandidateContextRequest["code"],
|
|
746
|
+
refs,
|
|
747
|
+
};
|
|
748
|
+
schema = "valid";
|
|
749
|
+
return {
|
|
750
|
+
content: [
|
|
751
|
+
{ type: "text" as const, text: "Context request accepted." },
|
|
752
|
+
],
|
|
753
|
+
details: { accepted: true },
|
|
754
|
+
terminate: true,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
if (value.kind !== "candidate-patch") {
|
|
758
|
+
schema = "invalid";
|
|
759
|
+
failure = {
|
|
760
|
+
kind: "artifact",
|
|
761
|
+
code: "invalid-structural-result",
|
|
762
|
+
stage: "structural-submit",
|
|
763
|
+
};
|
|
764
|
+
throw new Error("diagnosis result must be a patch or context request");
|
|
765
|
+
}
|
|
766
|
+
let compiled: ReturnType<typeof compileCandidatePatch>;
|
|
767
|
+
try {
|
|
768
|
+
compiled = compileCandidatePatch({
|
|
769
|
+
root: input.workspaceRoot,
|
|
770
|
+
operations: value.operations as CandidatePatchOperation[],
|
|
771
|
+
writePaths: input.writePaths,
|
|
772
|
+
deletePaths: input.deletePaths,
|
|
773
|
+
maxBytes: LIMITS.maxCompleteResultBytes,
|
|
774
|
+
});
|
|
775
|
+
} catch (error) {
|
|
776
|
+
schema = "invalid";
|
|
777
|
+
failure = {
|
|
778
|
+
kind: "artifact",
|
|
779
|
+
code: "invalid-diff",
|
|
780
|
+
stage: "candidate-diff",
|
|
781
|
+
};
|
|
782
|
+
throw error;
|
|
783
|
+
}
|
|
784
|
+
const result: DiffResult = {
|
|
785
|
+
id: input.requestId,
|
|
786
|
+
role: input.role,
|
|
787
|
+
kind: "diff",
|
|
788
|
+
taskId: input.taskId ?? input.requestId,
|
|
789
|
+
phase: input.phase,
|
|
790
|
+
summary:
|
|
791
|
+
"Trusted control plane generated this diff from structured operations.",
|
|
792
|
+
diff: compiled,
|
|
793
|
+
expectedVerification: "parent-owned verification contract",
|
|
794
|
+
risks: [],
|
|
795
|
+
contractCompliant: true,
|
|
796
|
+
};
|
|
797
|
+
const validation = validateDiffResult(result);
|
|
798
|
+
if (!validation.ok) {
|
|
799
|
+
schema = "invalid";
|
|
800
|
+
failure = validation.failure ?? {
|
|
801
|
+
kind: "artifact",
|
|
802
|
+
code: "invalid-structural-result",
|
|
803
|
+
stage: "structural-submit",
|
|
804
|
+
};
|
|
805
|
+
throw new Error(validation.reason ?? "structured patch is invalid");
|
|
806
|
+
}
|
|
807
|
+
submitted = result;
|
|
808
|
+
schema = "valid";
|
|
809
|
+
return {
|
|
810
|
+
content: [{ type: "text" as const, text: "Abel patch accepted." }],
|
|
811
|
+
details: { accepted: true },
|
|
812
|
+
terminate: true,
|
|
813
|
+
};
|
|
814
|
+
},
|
|
815
|
+
});
|
|
816
|
+
return {
|
|
817
|
+
tool,
|
|
818
|
+
getResult: () =>
|
|
819
|
+
submitted === undefined ? undefined : structuredClone(submitted),
|
|
820
|
+
getAttempts: () => attempts,
|
|
821
|
+
getSchema: () => schema,
|
|
822
|
+
getIdentity: () => ({ ...identity }),
|
|
823
|
+
getFailure: () =>
|
|
824
|
+
failure === undefined ? undefined : structuredClone(failure),
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
|
|
437
828
|
export type FinalCategory = SubmitFinalCategory;
|
|
438
829
|
|
|
439
830
|
export interface IdentityOutcome {
|
|
@@ -482,6 +873,15 @@ export function createSubmitTool(input: {
|
|
|
482
873
|
: diffSchema,
|
|
483
874
|
async execute(_toolCallId, params) {
|
|
484
875
|
attempts++;
|
|
876
|
+
if (submitted !== undefined) {
|
|
877
|
+
schema = "invalid";
|
|
878
|
+
failure = {
|
|
879
|
+
kind: "artifact",
|
|
880
|
+
code: "invalid-structural-result",
|
|
881
|
+
stage: "structural-submit",
|
|
882
|
+
};
|
|
883
|
+
throw new Error("duplicate structural submission");
|
|
884
|
+
}
|
|
485
885
|
if (attempts > 2) {
|
|
486
886
|
schema = "invalid";
|
|
487
887
|
failure = {
|
|
@@ -548,14 +948,6 @@ export function createSubmitTool(input: {
|
|
|
548
948
|
validation.reason ?? "submitted result identity does not match",
|
|
549
949
|
);
|
|
550
950
|
}
|
|
551
|
-
if (submitted) {
|
|
552
|
-
failure = {
|
|
553
|
-
kind: "artifact",
|
|
554
|
-
code: "invalid-structural-result",
|
|
555
|
-
stage: "structural-submit",
|
|
556
|
-
};
|
|
557
|
-
throw new Error("duplicate structural submission");
|
|
558
|
-
}
|
|
559
951
|
submitted = structuredClone(value) as unknown as
|
|
560
952
|
| EvidenceResult
|
|
561
953
|
| DiffResult;
|
package/src/task-ledger.ts
CHANGED
|
@@ -230,7 +230,7 @@ function hashEvent(
|
|
|
230
230
|
eventJson: string,
|
|
231
231
|
): string {
|
|
232
232
|
return createHash("sha256")
|
|
233
|
-
.update("cadence-task-ledger-event
|
|
233
|
+
.update("cadence-task-ledger-event\0")
|
|
234
234
|
.update(priorHash)
|
|
235
235
|
.update("\0")
|
|
236
236
|
.update(String(ordinal))
|
|
@@ -1354,7 +1354,6 @@ export class TaskLedger {
|
|
|
1354
1354
|
return { ordinal: row.ordinal, ...fact };
|
|
1355
1355
|
});
|
|
1356
1356
|
const projection = {
|
|
1357
|
-
version: 2,
|
|
1358
1357
|
runId: input.runId,
|
|
1359
1358
|
taskId: input.taskId,
|
|
1360
1359
|
deliveryRevision: task.delivery_revision,
|