@abelxiaoxing/cadence 1.0.10 → 1.0.12
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 +49 -1
- package/package.json +1 -1
- package/prompts/abel-design.md +14 -2
- package/prompts/abel-implement.md +10 -2
- package/skills/abel-workflow/SKILL.md +36 -0
- package/src/candidate-preflight.ts +237 -90
- package/src/child-session.ts +26 -3
- package/src/contracts.ts +482 -41
- package/src/index.ts +14 -4
- package/src/parent-payload-bridge.ts +31 -7
- package/src/patch.ts +13 -1
- package/src/result-store.ts +6 -9
- package/src/runtime.ts +64 -9
- package/src/scoped-tools.ts +278 -38
- package/src/subagent-activity.ts +80 -12
- package/src/verification-capability.ts +481 -0
|
@@ -46,6 +46,7 @@ export class ParentPayloadBridgeError extends Error {
|
|
|
46
46
|
|
|
47
47
|
export interface ParentProviderRegistry {
|
|
48
48
|
getProvider(provider: string): Provider | undefined;
|
|
49
|
+
getRegisteredNativeProvider?(provider: string): Provider | undefined;
|
|
49
50
|
registerProvider(provider: Provider): void;
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -198,9 +199,12 @@ export class ParentPayloadBridge {
|
|
|
198
199
|
model: Model<string>,
|
|
199
200
|
registry: ParentProviderRegistry,
|
|
200
201
|
): Provider | undefined {
|
|
201
|
-
const current =
|
|
202
|
+
const current = this.nativeCarrier(model.provider, registry);
|
|
202
203
|
if (!current) return undefined;
|
|
203
204
|
const delegate = this.unwrapProvider(current);
|
|
205
|
+
const wrapped = this.isWrappedProvider(current)
|
|
206
|
+
? current
|
|
207
|
+
: this.wrapProvider(delegate);
|
|
204
208
|
const state = this.state;
|
|
205
209
|
if (state?.active) {
|
|
206
210
|
const key = modelKeyId(modelKeyFor(model));
|
|
@@ -210,14 +214,18 @@ export class ParentPayloadBridge {
|
|
|
210
214
|
state.ready.delete(key);
|
|
211
215
|
}
|
|
212
216
|
}
|
|
213
|
-
|
|
214
|
-
|
|
217
|
+
if (wrapped !== current) {
|
|
218
|
+
registry.registerProvider(wrapped);
|
|
219
|
+
}
|
|
215
220
|
return wrapped;
|
|
216
221
|
}
|
|
217
222
|
|
|
218
223
|
capture(
|
|
219
224
|
modelKey: ParentModelKey,
|
|
220
|
-
registry?: Pick<
|
|
225
|
+
registry?: Pick<
|
|
226
|
+
ParentProviderRegistry,
|
|
227
|
+
"getProvider" | "getRegisteredNativeProvider"
|
|
228
|
+
>,
|
|
221
229
|
): ParentPayloadCapture | undefined {
|
|
222
230
|
const state = this.state;
|
|
223
231
|
if (!state?.active) return undefined;
|
|
@@ -226,7 +234,7 @@ export class ParentPayloadBridge {
|
|
|
226
234
|
return undefined;
|
|
227
235
|
}
|
|
228
236
|
if (registry) {
|
|
229
|
-
const current =
|
|
237
|
+
const current = this.nativeCarrier(modelKey.provider, registry);
|
|
230
238
|
if (!current || this.unwrapProvider(current) !== capture.delegate) {
|
|
231
239
|
return undefined;
|
|
232
240
|
}
|
|
@@ -236,11 +244,14 @@ export class ParentPayloadBridge {
|
|
|
236
244
|
|
|
237
245
|
diagnoseCapture(
|
|
238
246
|
modelKey: ParentModelKey,
|
|
239
|
-
registry: Pick<
|
|
247
|
+
registry: Pick<
|
|
248
|
+
ParentProviderRegistry,
|
|
249
|
+
"getProvider" | "getRegisteredNativeProvider"
|
|
250
|
+
>,
|
|
240
251
|
): ParentBridgeFailureCode | undefined {
|
|
241
252
|
const state = this.state;
|
|
242
253
|
if (!state?.active) return "parent-bridge-session-unavailable";
|
|
243
|
-
const current =
|
|
254
|
+
const current = this.nativeCarrier(modelKey.provider, registry);
|
|
244
255
|
if (!current || !this.isWrappedProvider(current)) {
|
|
245
256
|
return "parent-bridge-provider-not-installed";
|
|
246
257
|
}
|
|
@@ -468,6 +479,19 @@ export class ParentPayloadBridge {
|
|
|
468
479
|
return state;
|
|
469
480
|
}
|
|
470
481
|
|
|
482
|
+
private nativeCarrier(
|
|
483
|
+
providerId: string,
|
|
484
|
+
registry: Pick<
|
|
485
|
+
ParentProviderRegistry,
|
|
486
|
+
"getProvider" | "getRegisteredNativeProvider"
|
|
487
|
+
>,
|
|
488
|
+
): Provider | undefined {
|
|
489
|
+
const registered = registry.getRegisteredNativeProvider?.(providerId);
|
|
490
|
+
return registered && this.isWrappedProvider(registered)
|
|
491
|
+
? registered
|
|
492
|
+
: registry.getProvider(providerId);
|
|
493
|
+
}
|
|
494
|
+
|
|
471
495
|
private isWrappedProvider(provider: Provider): boolean {
|
|
472
496
|
return Reflect.get(provider, PROVIDER_WRAPPER) !== undefined;
|
|
473
497
|
}
|
package/src/patch.ts
CHANGED
|
@@ -48,6 +48,11 @@ async function git(
|
|
|
48
48
|
settled = true;
|
|
49
49
|
resolve(result);
|
|
50
50
|
};
|
|
51
|
+
const fail = (error: Error) => {
|
|
52
|
+
if (settled) return;
|
|
53
|
+
settled = true;
|
|
54
|
+
reject(error);
|
|
55
|
+
};
|
|
51
56
|
child.stdout.on("data", (b: Buffer) => stdout.push(b));
|
|
52
57
|
child.stderr.on("data", (b: Buffer) => stderr.push(b));
|
|
53
58
|
child.on("error", (error) => {
|
|
@@ -59,7 +64,7 @@ async function git(
|
|
|
59
64
|
cancelled: true,
|
|
60
65
|
});
|
|
61
66
|
} else {
|
|
62
|
-
|
|
67
|
+
fail(error);
|
|
63
68
|
}
|
|
64
69
|
});
|
|
65
70
|
child.on("close", (code) =>
|
|
@@ -70,6 +75,11 @@ async function git(
|
|
|
70
75
|
cancelled: signal?.aborted ?? false,
|
|
71
76
|
}),
|
|
72
77
|
);
|
|
78
|
+
child.stdin.on("error", (error: NodeJS.ErrnoException) => {
|
|
79
|
+
// A command may reject its input and exit before Node finishes writing it.
|
|
80
|
+
// Its exit status remains the authoritative command result.
|
|
81
|
+
if (error.code !== "EPIPE") fail(error);
|
|
82
|
+
});
|
|
73
83
|
child.stdin.end(input);
|
|
74
84
|
});
|
|
75
85
|
}
|
|
@@ -349,6 +359,8 @@ export async function applyRetainedPatch(input: {
|
|
|
349
359
|
return failure({ kind: preflight.kind, code: preflight.code });
|
|
350
360
|
case "environment":
|
|
351
361
|
return failure({ kind: preflight.kind, code: preflight.code });
|
|
362
|
+
case "verification-adapter":
|
|
363
|
+
return failure({ kind: preflight.kind, code: preflight.code });
|
|
352
364
|
case "approval-boundary":
|
|
353
365
|
return failure({ kind: preflight.kind, code: preflight.code });
|
|
354
366
|
case "cancelled":
|
package/src/result-store.ts
CHANGED
|
@@ -4,7 +4,10 @@ import type {
|
|
|
4
4
|
BaselineEntry,
|
|
5
5
|
VerificationContract,
|
|
6
6
|
} from "./candidate-preflight.ts";
|
|
7
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
cloneVerificationContract,
|
|
9
|
+
type ImplementationPhase,
|
|
10
|
+
} from "./contracts.ts";
|
|
8
11
|
import type { Bound, DirBound, FileBound } from "./file-snapshot.ts";
|
|
9
12
|
import { snapshotFiles } from "./file-snapshot.ts";
|
|
10
13
|
|
|
@@ -105,10 +108,7 @@ function cloneRetained(retained: RetainedResult): RetainedResult {
|
|
|
105
108
|
baseline: retained.baseline.map((entry) => ({ ...entry })),
|
|
106
109
|
};
|
|
107
110
|
if (retained.verification) {
|
|
108
|
-
clone.verification =
|
|
109
|
-
...retained.verification,
|
|
110
|
-
argv: [...retained.verification.argv],
|
|
111
|
-
};
|
|
111
|
+
clone.verification = cloneVerificationContract(retained.verification);
|
|
112
112
|
}
|
|
113
113
|
if (retained.packageManifest) {
|
|
114
114
|
clone.packageManifest = { ...retained.packageManifest };
|
|
@@ -147,10 +147,7 @@ export class ResultStore {
|
|
|
147
147
|
...(input.verification === undefined
|
|
148
148
|
? {}
|
|
149
149
|
: {
|
|
150
|
-
verification:
|
|
151
|
-
...input.verification,
|
|
152
|
-
argv: [...input.verification.argv],
|
|
153
|
-
},
|
|
150
|
+
verification: cloneVerificationContract(input.verification),
|
|
154
151
|
}),
|
|
155
152
|
...(input.packageManifest === undefined
|
|
156
153
|
? {}
|
package/src/runtime.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type AgentsCheckpointRequest,
|
|
13
13
|
type CandidateFailure,
|
|
14
14
|
type ChildFailure,
|
|
15
|
+
cloneVerificationContract,
|
|
15
16
|
type DiffResult,
|
|
16
17
|
type ImplementApplyOperation,
|
|
17
18
|
type ImplementationPhase,
|
|
@@ -29,6 +30,7 @@ import {
|
|
|
29
30
|
validateImplementDiscardOperation,
|
|
30
31
|
validatePhaseAttemptAgainstBoundary,
|
|
31
32
|
validateRequestEnvelope,
|
|
33
|
+
verificationInputPaths,
|
|
32
34
|
} from "./contracts.ts";
|
|
33
35
|
import { drainStage } from "./drain.ts";
|
|
34
36
|
import {
|
|
@@ -56,6 +58,7 @@ import {
|
|
|
56
58
|
resolveSubagentEndpoint,
|
|
57
59
|
type SubagentEndpoint,
|
|
58
60
|
} from "./subagent-endpoint.ts";
|
|
61
|
+
import { assessVerificationReadiness } from "./verification-capability.ts";
|
|
59
62
|
import {
|
|
60
63
|
type TaskRecord,
|
|
61
64
|
taskConflictOf,
|
|
@@ -119,10 +122,7 @@ function cloneLegacyEnvelope(request: RequestEnvelope): RequestEnvelope {
|
|
|
119
122
|
...(request.verification === undefined
|
|
120
123
|
? {}
|
|
121
124
|
: {
|
|
122
|
-
verification:
|
|
123
|
-
...request.verification,
|
|
124
|
-
argv: [...request.verification.argv],
|
|
125
|
-
},
|
|
125
|
+
verification: cloneVerificationContract(request.verification),
|
|
126
126
|
}),
|
|
127
127
|
...(request.snapshot === undefined
|
|
128
128
|
? {}
|
|
@@ -163,10 +163,7 @@ function deriveImplementEnvelope(
|
|
|
163
163
|
agentsManagedOnly: true,
|
|
164
164
|
approvedDependencies: [...boundary.approvedDependencies],
|
|
165
165
|
impactClosure: structuredClone(boundary.impactClosure),
|
|
166
|
-
verification:
|
|
167
|
-
...phase.verification,
|
|
168
|
-
argv: [...phase.verification.argv],
|
|
169
|
-
},
|
|
166
|
+
verification: cloneVerificationContract(phase.verification),
|
|
170
167
|
snapshot: structuredClone(attempt.snapshot),
|
|
171
168
|
};
|
|
172
169
|
}
|
|
@@ -318,7 +315,7 @@ function preparePreflight(
|
|
|
318
315
|
...envelope.declared.write,
|
|
319
316
|
"package.json",
|
|
320
317
|
lockPath,
|
|
321
|
-
...envelope.verification
|
|
318
|
+
...verificationInputPaths(envelope.verification),
|
|
322
319
|
]);
|
|
323
320
|
const entries = new Map<string, BaselineEntry>();
|
|
324
321
|
const observed: Bound = {};
|
|
@@ -446,6 +443,7 @@ function failureClassOf(failure: ChildFailure): FailureClass | undefined {
|
|
|
446
443
|
return "transport";
|
|
447
444
|
case "stale":
|
|
448
445
|
case "approval-boundary":
|
|
446
|
+
case "verification-adapter":
|
|
449
447
|
case "cancelled":
|
|
450
448
|
case "result-limit":
|
|
451
449
|
return undefined;
|
|
@@ -465,6 +463,7 @@ function childFailureOf(result: InternalFailureResult): ChildFailure {
|
|
|
465
463
|
case "stale":
|
|
466
464
|
case "environment":
|
|
467
465
|
case "approval-boundary":
|
|
466
|
+
case "verification-adapter":
|
|
468
467
|
case "cancelled":
|
|
469
468
|
case "result-limit":
|
|
470
469
|
case "transport":
|
|
@@ -495,6 +494,7 @@ function terminalTaskFailure(failure: ChildFailure): TaskFailure | undefined {
|
|
|
495
494
|
switch (failure.kind) {
|
|
496
495
|
case "approval-boundary":
|
|
497
496
|
case "environment":
|
|
497
|
+
case "verification-adapter":
|
|
498
498
|
case "result-limit":
|
|
499
499
|
return failure;
|
|
500
500
|
case "artifact":
|
|
@@ -515,6 +515,7 @@ function attemptFailureCause(
|
|
|
515
515
|
return failure.kind;
|
|
516
516
|
case "approval-boundary":
|
|
517
517
|
case "environment":
|
|
518
|
+
case "verification-adapter":
|
|
518
519
|
case "result-limit":
|
|
519
520
|
case "cancelled":
|
|
520
521
|
throw new Error("non-retryable failure reached the launch budget");
|
|
@@ -992,6 +993,15 @@ export class Runtime {
|
|
|
992
993
|
};
|
|
993
994
|
return blockedOutcome(identity, taskFailure);
|
|
994
995
|
}
|
|
996
|
+
case "verification-adapter": {
|
|
997
|
+
const taskFailure: TaskFailure = failure;
|
|
998
|
+
worker.state = {
|
|
999
|
+
kind: "blocked",
|
|
1000
|
+
phase,
|
|
1001
|
+
failure: structuredClone(taskFailure),
|
|
1002
|
+
};
|
|
1003
|
+
return blockedOutcome(identity, taskFailure);
|
|
1004
|
+
}
|
|
995
1005
|
case "cancelled":
|
|
996
1006
|
return cancelledOutcome(identity);
|
|
997
1007
|
case "result-limit": {
|
|
@@ -1260,6 +1270,15 @@ export class Runtime {
|
|
|
1260
1270
|
};
|
|
1261
1271
|
return blockedOutcome(outcomeIdentity, failure);
|
|
1262
1272
|
}
|
|
1273
|
+
case "verification-adapter": {
|
|
1274
|
+
this.results.discard(resultId);
|
|
1275
|
+
worker.state = {
|
|
1276
|
+
kind: "blocked",
|
|
1277
|
+
phase: identity.phase,
|
|
1278
|
+
failure: structuredClone(failure),
|
|
1279
|
+
};
|
|
1280
|
+
return blockedOutcome(outcomeIdentity, failure);
|
|
1281
|
+
}
|
|
1263
1282
|
case "stale": {
|
|
1264
1283
|
this.results.discard(resultId);
|
|
1265
1284
|
if (identity.launchIndex === 0) {
|
|
@@ -1562,6 +1581,42 @@ export class Runtime {
|
|
|
1562
1581
|
attempt.changeId,
|
|
1563
1582
|
attempt.taskId,
|
|
1564
1583
|
);
|
|
1584
|
+
if (runRequest.kind === "open-task") {
|
|
1585
|
+
const phases = runRequest.boundary.phases;
|
|
1586
|
+
const readiness = assessVerificationReadiness(
|
|
1587
|
+
workspaceRoot,
|
|
1588
|
+
[
|
|
1589
|
+
phases.red.verification,
|
|
1590
|
+
phases.green.verification,
|
|
1591
|
+
...(phases.refactor ? [phases.refactor.verification] : []),
|
|
1592
|
+
],
|
|
1593
|
+
{
|
|
1594
|
+
allowedMissingInputs: [
|
|
1595
|
+
...new Set([
|
|
1596
|
+
...phases.red.write,
|
|
1597
|
+
...phases.green.write,
|
|
1598
|
+
...(phases.refactor?.write ?? []),
|
|
1599
|
+
]),
|
|
1600
|
+
],
|
|
1601
|
+
},
|
|
1602
|
+
);
|
|
1603
|
+
const diagnostic = readiness.diagnostics[0];
|
|
1604
|
+
if (diagnostic) {
|
|
1605
|
+
if (diagnostic.kind === "design-readiness") {
|
|
1606
|
+
throw new Error(
|
|
1607
|
+
`Implement protocol error: ${diagnostic.code}: ${diagnostic.message}`,
|
|
1608
|
+
);
|
|
1609
|
+
}
|
|
1610
|
+
return {
|
|
1611
|
+
ok: false,
|
|
1612
|
+
error: `${diagnostic.kind}/${diagnostic.code}: ${diagnostic.message}`,
|
|
1613
|
+
failure: {
|
|
1614
|
+
kind: diagnostic.kind,
|
|
1615
|
+
code: diagnostic.code,
|
|
1616
|
+
},
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1565
1620
|
if (
|
|
1566
1621
|
runRequest.kind === "open-task" &&
|
|
1567
1622
|
this.registry
|