@abelxiaoxing/cadence 1.0.14 → 1.1.1

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.
Files changed (48) hide show
  1. package/README.md +39 -13
  2. package/agents/contract-reviewer.md +2 -2
  3. package/agents/design-explorer.md +1 -1
  4. package/agents/diagnosis-worker.md +6 -3
  5. package/agents/implementation-worker.md +14 -9
  6. package/config/.env.example +5 -17
  7. package/config/routes.example.json +42 -0
  8. package/package.json +3 -3
  9. package/prompts/abel-design.md +206 -228
  10. package/prompts/abel-diagnose.md +39 -18
  11. package/prompts/abel-implement.md +122 -178
  12. package/prompts/abel-init.md +36 -33
  13. package/skills/abel-workflow/SKILL.md +190 -168
  14. package/src/apply-transaction.ts +1427 -0
  15. package/src/artifact-store.ts +204 -0
  16. package/src/child-session.ts +136 -17
  17. package/src/contracts.ts +121 -696
  18. package/src/control-contracts.ts +246 -0
  19. package/src/delivery-compiler.ts +1118 -0
  20. package/src/design-control.ts +846 -0
  21. package/src/design-journal.ts +1569 -0
  22. package/src/file-snapshot.ts +5 -0
  23. package/src/implement-graph.ts +11 -4
  24. package/src/index.ts +1833 -118
  25. package/src/isolation-backend.ts +269 -0
  26. package/src/packet-runtime.ts +533 -0
  27. package/src/parent-provider.ts +23 -4
  28. package/src/route-policy.ts +483 -0
  29. package/src/run-state.ts +430 -0
  30. package/src/run-store.ts +990 -0
  31. package/src/safe-path.ts +11 -0
  32. package/src/scoped-tools.ts +36 -0
  33. package/src/state-root.ts +215 -0
  34. package/src/subagent-activity.ts +526 -27
  35. package/src/submit-tool.ts +274 -0
  36. package/src/task-ledger.ts +1380 -0
  37. package/src/verification-capability.ts +560 -45
  38. package/src/worker-broker.ts +703 -0
  39. package/src/workflow-engine.ts +7706 -0
  40. package/src/workspace-store.ts +544 -0
  41. package/src/candidate-preflight.ts +0 -1618
  42. package/src/drain.ts +0 -20
  43. package/src/patch.ts +0 -505
  44. package/src/result-store.ts +0 -312
  45. package/src/runtime.ts +0 -2436
  46. package/src/scheduler.ts +0 -384
  47. package/src/subagent-endpoint.ts +0 -158
  48. package/src/worker.ts +0 -204
package/src/drain.ts DELETED
@@ -1,20 +0,0 @@
1
- // Unified idempotent drain (P-006). One drain step closes admission, erases
2
- // retained diffs and Worker metadata, and removes only the dispatcher
3
- // activation. No private files are created; child usage is returned exactly
4
- // once by the child-session dispose path.
5
- import type { Activation } from "./activation.ts";
6
- import type { ResultStore } from "./result-store.ts";
7
- import type { WorkerRegistry } from "./worker.ts";
8
-
9
- export interface DrainTargets {
10
- results: ResultStore;
11
- registry: WorkerRegistry;
12
- activation: Activation;
13
- }
14
-
15
- /** Close admission, erase retained diffs and Worker metadata, deactivate. */
16
- export function drainStage(targets: DrainTargets): void {
17
- targets.results.clear();
18
- targets.registry.clear();
19
- targets.activation.drain();
20
- }
package/src/patch.ts DELETED
@@ -1,505 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import {
3
- copyFileSync,
4
- lstatSync,
5
- mkdirSync,
6
- mkdtempSync,
7
- readFileSync,
8
- rmSync,
9
- } from "node:fs";
10
- import { tmpdir } from "node:os";
11
- import path from "node:path";
12
- import { preflightCandidate } from "./candidate-preflight.ts";
13
- import {
14
- type AgentsCheckpointRequest,
15
- type ApplyCandidateResult,
16
- type CandidateFailure,
17
- diffWritePaths,
18
- validateAgentsCheckpointRequest,
19
- } from "./contracts.ts";
20
- import { type Bound, isCurrent } from "./file-snapshot.ts";
21
- import type { ResultStore } from "./result-store.ts";
22
-
23
- interface CommandResult {
24
- code: number;
25
- stdout: Buffer;
26
- stderr: Buffer;
27
- cancelled: boolean;
28
- }
29
-
30
- async function git(
31
- root: string,
32
- args: string[],
33
- input: Buffer,
34
- signal?: AbortSignal,
35
- ): Promise<CommandResult> {
36
- return new Promise((resolve, reject) => {
37
- const child = spawn("git", args, {
38
- cwd: root,
39
- shell: false,
40
- stdio: ["pipe", "pipe", "pipe"],
41
- ...(signal ? { signal } : {}),
42
- });
43
- const stdout: Buffer[] = [];
44
- const stderr: Buffer[] = [];
45
- let settled = false;
46
- const finish = (result: CommandResult) => {
47
- if (settled) return;
48
- settled = true;
49
- resolve(result);
50
- };
51
- const fail = (error: Error) => {
52
- if (settled) return;
53
- settled = true;
54
- reject(error);
55
- };
56
- child.stdout.on("data", (b: Buffer) => stdout.push(b));
57
- child.stderr.on("data", (b: Buffer) => stderr.push(b));
58
- child.on("error", (error) => {
59
- if (signal?.aborted || error.name === "AbortError") {
60
- finish({
61
- code: 1,
62
- stdout: Buffer.concat(stdout),
63
- stderr: Buffer.concat(stderr),
64
- cancelled: true,
65
- });
66
- } else {
67
- fail(error);
68
- }
69
- });
70
- child.on("close", (code) =>
71
- finish({
72
- code: code ?? 1,
73
- stdout: Buffer.concat(stdout),
74
- stderr: Buffer.concat(stderr),
75
- cancelled: signal?.aborted ?? false,
76
- }),
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
- });
83
- child.stdin.end(input);
84
- });
85
- }
86
-
87
- function failure(value: CandidateFailure): {
88
- ok: false;
89
- failure: CandidateFailure;
90
- } {
91
- return { ok: false, failure: value };
92
- }
93
-
94
- function cancellationFailure(): {
95
- ok: false;
96
- failure: CandidateFailure;
97
- } {
98
- return failure({ kind: "cancelled", code: "cancelled" });
99
- }
100
-
101
- type AgentsCheckpointResult =
102
- | {
103
- ok: true;
104
- target: string;
105
- agentsImpact: AgentsCheckpointRequest["agentsImpact"];
106
- checkExitCode: 0;
107
- applyExitCode: 0;
108
- }
109
- | {
110
- ok: false;
111
- failure: CandidateFailure;
112
- };
113
-
114
- const AGENTS_START = "<!-- ABEL:AGENTS-INDEX:START -->";
115
- const AGENTS_END = "<!-- ABEL:AGENTS-INDEX:END -->";
116
- const NONREGULAR_AGENTS_TARGET = Symbol("nonregular AGENTS target");
117
-
118
- function managedParts(text: string): { prefix: string; suffix: string } | null {
119
- const lines = text.split("\n");
120
- const starts: Array<{ line: number; start: number; end: number }> = [];
121
- const ends: Array<{ line: number; start: number; end: number }> = [];
122
- let offset = 0;
123
- for (const [line, value] of lines.entries()) {
124
- const range = { line, start: offset, end: offset + value.length };
125
- if (value === AGENTS_START) starts.push(range);
126
- if (value === AGENTS_END) ends.push(range);
127
- offset = range.end + 1;
128
- }
129
- if (
130
- starts.length !== 1 ||
131
- ends.length !== 1 ||
132
- starts[0].line >= ends[0].line
133
- ) {
134
- return null;
135
- }
136
- return {
137
- prefix: text.slice(0, starts[0].start),
138
- suffix: text.slice(ends[0].end),
139
- };
140
- }
141
-
142
- function checkpointContentIsScoped(
143
- impact: AgentsCheckpointRequest["agentsImpact"],
144
- before: string | null,
145
- after: string | null,
146
- ): boolean {
147
- const beforeParts = before === null ? null : managedParts(before);
148
- const afterParts = after === null ? null : managedParts(after);
149
- if (impact === "update-existing") {
150
- return (
151
- beforeParts !== null &&
152
- afterParts !== null &&
153
- beforeParts.prefix === afterParts.prefix &&
154
- beforeParts.suffix === afterParts.suffix
155
- );
156
- }
157
- if (impact === "create-index") {
158
- return (
159
- before === null &&
160
- afterParts !== null &&
161
- afterParts.prefix.trim() === "" &&
162
- afterParts.suffix.trim() === ""
163
- );
164
- }
165
- if (beforeParts === null) return false;
166
- if (after === null) {
167
- return beforeParts.prefix.trim() === "" && beforeParts.suffix.trim() === "";
168
- }
169
- return (
170
- managedParts(after) === null &&
171
- after === `${beforeParts.prefix}${beforeParts.suffix}`
172
- );
173
- }
174
-
175
- function regularText(
176
- root: string,
177
- target: string,
178
- ): string | null | typeof NONREGULAR_AGENTS_TARGET {
179
- const resolvedRoot = path.resolve(root);
180
- const absolute = path.resolve(resolvedRoot, target);
181
- if (
182
- absolute === resolvedRoot ||
183
- !absolute.startsWith(`${resolvedRoot}${path.sep}`)
184
- ) {
185
- throw new Error("AGENTS target escapes the root");
186
- }
187
- const stat = lstatSync(absolute, { throwIfNoEntry: false });
188
- if (!stat) return null;
189
- if (!stat.isFile() || stat.isSymbolicLink()) {
190
- return NONREGULAR_AGENTS_TARGET;
191
- }
192
- return readFileSync(absolute, "utf8");
193
- }
194
-
195
- export async function applyAgentsCheckpoint(
196
- root: string,
197
- value: unknown,
198
- signal?: AbortSignal,
199
- ): Promise<AgentsCheckpointResult> {
200
- const validation = validateAgentsCheckpointRequest(value);
201
- if (!validation.ok) {
202
- return failure({
203
- kind: "artifact",
204
- code: "invalid-checkpoint-contract",
205
- stage: "agents-checkpoint",
206
- });
207
- }
208
- const request = validation.value;
209
- if (signal?.aborted) {
210
- return cancellationFailure();
211
- }
212
- const before = regularText(root, request.agentsTarget);
213
- if (before === NONREGULAR_AGENTS_TARGET) {
214
- return failure({
215
- kind: "artifact",
216
- code: "nonregular-mode",
217
- stage: "agents-checkpoint",
218
- });
219
- }
220
- if (!isCurrent(root, request.snapshot as Bound)) {
221
- return failure({
222
- kind: "stale",
223
- code: "stale-snapshot",
224
- stage: "agents-checkpoint",
225
- });
226
- }
227
- let targets: string[];
228
- try {
229
- targets = diffWritePaths(request.diff).paths;
230
- } catch {
231
- return failure({
232
- kind: "artifact",
233
- code: "invalid-diff",
234
- stage: "agents-checkpoint",
235
- });
236
- }
237
- if (targets.length !== 1 || targets[0] !== request.agentsTarget) {
238
- return failure({
239
- kind: "artifact",
240
- code: "agents-target-mismatch",
241
- stage: "agents-checkpoint",
242
- });
243
- }
244
-
245
- let temp: string | undefined;
246
- try {
247
- if (
248
- (request.agentsImpact === "create-index" && before !== null) ||
249
- (request.agentsImpact !== "create-index" && before === null)
250
- ) {
251
- return failure({
252
- kind: "artifact",
253
- code: "agents-impact-mismatch",
254
- stage: "agents-checkpoint",
255
- });
256
- }
257
- temp = mkdtempSync(path.join(tmpdir(), "cadence-agents-checkpoint-"));
258
- if (before !== null) {
259
- const temporaryTarget = path.join(temp, request.agentsTarget);
260
- mkdirSync(path.dirname(temporaryTarget), { recursive: true });
261
- copyFileSync(path.join(root, request.agentsTarget), temporaryTarget);
262
- }
263
- const bytes = Buffer.from(request.diff, "utf8");
264
- const candidateCheck = await git(
265
- temp,
266
- ["apply", "--check", "--recount", "--whitespace=nowarn", "-"],
267
- bytes,
268
- signal,
269
- );
270
- if (candidateCheck.cancelled || signal?.aborted) {
271
- return cancellationFailure();
272
- }
273
- if (candidateCheck.code !== 0) {
274
- return failure({
275
- kind: "artifact",
276
- code: "git-apply-check-failed",
277
- stage: "agents-checkpoint",
278
- });
279
- }
280
- const candidateApply = await git(
281
- temp,
282
- ["apply", "--recount", "--whitespace=nowarn", "-"],
283
- bytes,
284
- signal,
285
- );
286
- if (candidateApply.cancelled || signal?.aborted) {
287
- return cancellationFailure();
288
- }
289
- if (candidateApply.code !== 0) {
290
- return failure({
291
- kind: "artifact",
292
- code: "git-apply-failed",
293
- stage: "agents-checkpoint",
294
- });
295
- }
296
- const after = regularText(temp, request.agentsTarget);
297
- if (after === NONREGULAR_AGENTS_TARGET) {
298
- return failure({
299
- kind: "artifact",
300
- code: "nonregular-mode",
301
- stage: "agents-checkpoint",
302
- });
303
- }
304
- if (!checkpointContentIsScoped(request.agentsImpact, before, after)) {
305
- return failure({
306
- kind: "artifact",
307
- code: "outside-managed-region",
308
- stage: "agents-checkpoint",
309
- });
310
- }
311
- if (!isCurrent(root, request.snapshot as Bound)) {
312
- return failure({
313
- kind: "stale",
314
- code: "stale-snapshot",
315
- stage: "agents-checkpoint",
316
- });
317
- }
318
- const check = await git(
319
- root,
320
- ["apply", "--check", "--recount", "--whitespace=nowarn", "-"],
321
- bytes,
322
- signal,
323
- );
324
- if (check.cancelled || signal?.aborted) {
325
- return cancellationFailure();
326
- }
327
- if (check.code !== 0) {
328
- return failure({
329
- kind: "stale",
330
- code: "git-apply-check-failed",
331
- stage: "agents-checkpoint",
332
- });
333
- }
334
- const apply = await git(
335
- root,
336
- ["apply", "--recount", "--whitespace=nowarn", "-"],
337
- bytes,
338
- );
339
- if (apply.code !== 0) {
340
- return failure({ kind: "environment", code: "git-apply-failed" });
341
- }
342
- return {
343
- ok: true,
344
- target: request.agentsTarget,
345
- agentsImpact: request.agentsImpact,
346
- checkExitCode: 0,
347
- applyExitCode: 0,
348
- };
349
- } finally {
350
- if (temp) rmSync(temp, { recursive: true, force: true });
351
- }
352
- }
353
-
354
- export async function applyRetainedPatch(input: {
355
- root: string;
356
- id: string;
357
- store: ResultStore;
358
- candidateOutputsAvailable?: (checkoutRoot: string) => boolean;
359
- signal?: AbortSignal;
360
- }): Promise<ApplyCandidateResult> {
361
- if (input.signal?.aborted) return cancellationFailure();
362
- const retained = input.store.resolveForApply(input.id);
363
- if (retained.root !== input.root)
364
- throw new Error("retained result root mismatch");
365
- if (!retained.verification && !isCurrent(input.root, retained.snapshot))
366
- return failure({
367
- kind: "stale",
368
- code: "stale-snapshot",
369
- stage: "candidate-retention",
370
- });
371
-
372
- let targets: string[];
373
- try {
374
- targets = diffWritePaths(retained.diff.toString("utf8")).paths;
375
- } catch {
376
- return failure({
377
- kind: "artifact",
378
- code: "invalid-diff",
379
- stage: "candidate-diff",
380
- });
381
- }
382
- if (targets.some((path) => !retained.writeSet.includes(path))) {
383
- return failure({
384
- kind: "artifact",
385
- code: "write-set-mismatch",
386
- stage: "candidate-diff",
387
- });
388
- }
389
-
390
- if (retained.verification) {
391
- if (
392
- !retained.packageManifest ||
393
- !retained.lockfile ||
394
- !retained.dependencyTarget
395
- ) {
396
- throw new Error("retained candidate preflight inputs are incomplete");
397
- }
398
- const preflight = await preflightCandidate({
399
- root: input.root,
400
- diff: retained.diff,
401
- writeSet: retained.writeSet,
402
- approvedDependencies: retained.approvedDependencies,
403
- snapshot: retained.snapshot,
404
- baseline: retained.baseline,
405
- verification: retained.verification,
406
- packageManifest: retained.packageManifest,
407
- lockfile: retained.lockfile,
408
- dependencyTarget: retained.dependencyTarget,
409
- candidateOutputsAvailable: input.candidateOutputsAvailable,
410
- signal: input.signal,
411
- });
412
- if (!preflight.ok) {
413
- switch (preflight.kind) {
414
- case "artifact":
415
- return failure({
416
- kind: preflight.kind,
417
- code: preflight.code,
418
- stage: "candidate-preflight",
419
- });
420
- case "stale":
421
- return failure({
422
- kind: preflight.kind,
423
- code: preflight.code,
424
- stage: "candidate-preflight",
425
- });
426
- case "environment":
427
- return failure({ kind: preflight.kind, code: preflight.code });
428
- case "verification-adapter":
429
- return failure({ kind: preflight.kind, code: preflight.code });
430
- case "approval-boundary":
431
- return failure({ kind: preflight.kind, code: preflight.code });
432
- case "cancelled":
433
- return failure({ kind: preflight.kind, code: preflight.code });
434
- }
435
- }
436
- if (!isCurrent(input.root, retained.snapshot)) {
437
- return failure({
438
- kind: "stale",
439
- code: "stale-snapshot",
440
- stage: "candidate-preflight",
441
- });
442
- }
443
- }
444
- if (input.signal?.aborted) return cancellationFailure();
445
-
446
- const numstat = await git(
447
- input.root,
448
- ["apply", "--numstat", "-z", "--recount", "-"],
449
- retained.diff,
450
- input.signal,
451
- );
452
- if (numstat.cancelled || input.signal?.aborted) return cancellationFailure();
453
- if (numstat.code !== 0 || numstat.stdout.includes(Buffer.from("-\t-\t"))) {
454
- return failure({
455
- kind: "artifact",
456
- code: "invalid-diff",
457
- stage: "candidate-diff",
458
- });
459
- }
460
- const summary = await git(
461
- input.root,
462
- ["apply", "--summary", "--recount", "-"],
463
- retained.diff,
464
- input.signal,
465
- );
466
- if (summary.cancelled || input.signal?.aborted) return cancellationFailure();
467
- if (
468
- summary.code !== 0 ||
469
- /rename|copy|mode change|create mode 160000/i.test(
470
- summary.stdout.toString("utf8"),
471
- )
472
- ) {
473
- return failure({
474
- kind: "artifact",
475
- code: "invalid-diff",
476
- stage: "candidate-diff",
477
- });
478
- }
479
- const check = await git(
480
- input.root,
481
- ["apply", "--check", "--recount", "--whitespace=nowarn", "-"],
482
- retained.diff,
483
- input.signal,
484
- );
485
- if (check.cancelled || input.signal?.aborted) return cancellationFailure();
486
- if (check.code !== 0)
487
- return failure({
488
- kind: "artifact",
489
- code: "git-apply-check-failed",
490
- stage: "candidate-apply",
491
- });
492
- if (input.signal?.aborted) return cancellationFailure();
493
- const apply = await git(
494
- input.root,
495
- ["apply", "--recount", "--whitespace=nowarn", "-"],
496
- retained.diff,
497
- );
498
- if (apply.code !== 0)
499
- return failure({ kind: "environment", code: "git-apply-failed" });
500
- input.store.discard(input.id);
501
- return {
502
- ok: true,
503
- result: { targets, checkExitCode: 0, applyExitCode: 0 },
504
- };
505
- }