@abelxiaoxing/cadence 1.6.0 → 1.8.0

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 (45) hide show
  1. package/README.md +25 -4
  2. package/config/.env.example +6 -0
  3. package/config/plan-draft.example.json +3 -0
  4. package/config/plan-draft.multiple-tasks.example.json +30 -0
  5. package/package.json +1 -1
  6. package/prompts/abel-design.md +33 -3
  7. package/prompts/abel-implement.md +36 -4
  8. package/src/apply-transaction.ts +28 -8
  9. package/src/build-windows-job.mjs +95 -0
  10. package/src/candidate-workspace.ts +6 -0
  11. package/src/change-verification.ts +764 -67
  12. package/src/contracts.ts +8 -0
  13. package/src/delivery-compiler.ts +452 -9
  14. package/src/design-control.ts +2 -2
  15. package/src/design-diagnostics.ts +6 -4
  16. package/src/durable-contracts.ts +21 -3
  17. package/src/durable-workflow.ts +55 -40
  18. package/src/execution-profile.ts +28 -2
  19. package/src/execution-retention.ts +77 -0
  20. package/src/implement-continuation.ts +559 -0
  21. package/src/implement-graph.ts +114 -0
  22. package/src/implement-plan.ts +2 -0
  23. package/src/index.ts +151 -1
  24. package/src/isolation-backend.ts +2 -1
  25. package/src/operator-cli.mjs +664 -104
  26. package/src/operator-tools.ts +7 -1
  27. package/src/package-state.ts +11 -0
  28. package/src/package-verification.ts +66 -18
  29. package/src/package-workflow.ts +6 -0
  30. package/src/phase-execution.ts +18 -7
  31. package/src/plan-draft-summary.ts +385 -7
  32. package/src/plan-draft.ts +9 -0
  33. package/src/remove-path.ts +32 -0
  34. package/src/subagent-activity.ts +3 -1
  35. package/src/verification-capability.ts +18 -6
  36. package/src/verification-environment-io.ts +123 -14
  37. package/src/verification-environment.ts +50 -3
  38. package/src/windows-job-backend.ts +419 -0
  39. package/src/windows-job.c +149 -0
  40. package/src/workflow-policy.ts +296 -5
  41. package/src/workflow-recovery-policy.ts +2 -0
  42. package/src/workflow-state-machine.ts +314 -80
  43. package/src/workflow-status.ts +251 -59
  44. package/src/workspace-io-entry.mjs +2 -2
  45. package/src/workspace-io-worker.mjs +207 -110
package/src/index.ts CHANGED
@@ -48,6 +48,11 @@ import {
48
48
  DesignFinalizationError,
49
49
  validateDesignControlRequest,
50
50
  } from "./design-control.ts";
51
+ import {
52
+ ImplementContinuationDriver,
53
+ implementChangeFromInvocation,
54
+ implementPromptBindsChange,
55
+ } from "./implement-continuation.ts";
51
56
  import { canonicalJson } from "./implement-graph.ts";
52
57
  import { PACKET_ACTIONS, PacketRuntime } from "./packet-runtime.ts";
53
58
 
@@ -707,7 +712,9 @@ export function registerWorkflowControl(
707
712
  const engines = new Map<string, Promise<WorkflowControlEngine>>();
708
713
  const designOperations = new Set<Promise<void>>();
709
714
  const designFailures = new Map<string, SafeDesignFailure>();
715
+ const implementContinuation = new ImplementContinuationDriver();
710
716
  let pendingPrompt: EligiblePrompt | undefined;
717
+ let pendingImplementChange: string | undefined;
711
718
  let pendingInit = false;
712
719
  let exitingStage = false;
713
720
  let activePrompt: EligiblePrompt | undefined;
@@ -797,6 +804,7 @@ export function registerWorkflowControl(
797
804
  };
798
805
  const deactivateStage = async () => {
799
806
  activePrompt = undefined;
807
+ implementContinuation.deactivate();
800
808
  await packetRuntime.drain();
801
809
  deactivate();
802
810
  };
@@ -1349,6 +1357,13 @@ export function registerWorkflowControl(
1349
1357
  registerDispatchTool("packet");
1350
1358
 
1351
1359
  pi.on("tool_result", (event) => {
1360
+ implementContinuation.noteToolResult({
1361
+ toolName: event.toolName,
1362
+ input: event.input,
1363
+ content: event.content,
1364
+ details: event.details,
1365
+ isError: event.isError,
1366
+ });
1352
1367
  if (
1353
1368
  event.toolName !== DISPATCH_TOOL ||
1354
1369
  !event.isError ||
@@ -1385,7 +1400,9 @@ export function registerWorkflowControl(
1385
1400
  };
1386
1401
  });
1387
1402
  pi.on("input", (event) => {
1403
+ implementContinuation.noteInput();
1388
1404
  pendingPrompt = undefined;
1405
+ pendingImplementChange = undefined;
1389
1406
  pendingInit = false;
1390
1407
  const prompt = invokedPrompt(event.text);
1391
1408
  const init = /^\/abel-init(?:\s|$)/u.test(event.text);
@@ -1395,6 +1412,10 @@ export function registerWorkflowControl(
1395
1412
  return { action: prompt || init ? "handled" : "continue" };
1396
1413
  }
1397
1414
  pendingPrompt = prompt;
1415
+ pendingImplementChange =
1416
+ prompt === "abel-implement"
1417
+ ? implementChangeFromInvocation(event.text)
1418
+ : undefined;
1398
1419
  pendingInit = init && hasPackageProvenance(pi, "abel-init");
1399
1420
  if (
1400
1421
  activePrompt &&
@@ -1408,11 +1429,13 @@ export function registerWorkflowControl(
1408
1429
  }
1409
1430
  return { action: "continue" };
1410
1431
  });
1411
- pi.on("before_agent_start", (event) => {
1432
+ pi.on("before_agent_start", (event, ctx) => {
1412
1433
  const prompt = pendingPrompt;
1434
+ const implementChange = pendingImplementChange;
1413
1435
  const init =
1414
1436
  pendingInit && hasExpandedPromptMarker(event.prompt, "abel-init");
1415
1437
  pendingPrompt = undefined;
1438
+ pendingImplementChange = undefined;
1416
1439
  pendingInit = false;
1417
1440
  const verified =
1418
1441
  prompt && isVerifiedStageInvocation(pi, activation, prompt, event.prompt);
@@ -1422,10 +1445,30 @@ export function registerWorkflowControl(
1422
1445
  }
1423
1446
  registerDispatchTool(prompt === "abel-implement" ? "command" : "packet");
1424
1447
  activePrompt = prompt;
1448
+ if (prompt === "abel-implement") {
1449
+ if (
1450
+ implementChange &&
1451
+ implementPromptBindsChange(event.prompt, implementChange) &&
1452
+ typeof ctx.cwd === "string"
1453
+ )
1454
+ implementContinuation.activate({
1455
+ cwd: ctx.cwd,
1456
+ change: implementChange,
1457
+ });
1458
+ else implementContinuation.deactivate();
1459
+ } else {
1460
+ implementContinuation.deactivate();
1461
+ }
1425
1462
  }
1426
1463
  if (prompt) activateDispatcher(pi, activation, prompt, event.prompt);
1427
1464
  if (activation.isActive() && activePrompt === "abel-design")
1428
1465
  enforceDesignTools();
1466
+ if (
1467
+ activation.isActive() &&
1468
+ activePrompt === "abel-implement" &&
1469
+ typeof ctx.cwd === "string"
1470
+ )
1471
+ implementContinuation.beginParentTurn(ctx.cwd);
1429
1472
  const boundary = activePrompt
1430
1473
  ? `Abel stage ${activePrompt} is active only for the invoked task and its direct follow-ups. If the user ends it or requests an unrelated task, first call abel_dispatch with {"action":"finish"}, then handle that task normally with the restored tools. A successful finish ends stage authority immediately, including within this turn. Do not extend Gates or workflow rules to that task. A direct Gate answer or same-task continuation stays in this stage. Never invoke another Abel stage automatically.`
1431
1474
  : init
@@ -1445,10 +1488,113 @@ export function registerWorkflowControl(
1445
1488
  }
1446
1489
  return { ...event.payload, parallel_tool_calls: true };
1447
1490
  });
1491
+ pi.on("agent_end", async (event, ctx) => {
1492
+ // The host may replace its current signal after abort/settlement. Retain
1493
+ // this run's signal so a late status result cannot revive an aborted turn.
1494
+ const runSignal = ctx.signal;
1495
+ const aborted = () => runSignal?.aborted || ctx.signal?.aborted;
1496
+ if (
1497
+ aborted() ||
1498
+ exitingStage ||
1499
+ !activation.isActive() ||
1500
+ activePrompt !== "abel-implement"
1501
+ ) {
1502
+ return;
1503
+ }
1504
+ if (
1505
+ typeof ctx.hasPendingMessages === "function" &&
1506
+ ctx.hasPendingMessages()
1507
+ ) {
1508
+ return;
1509
+ }
1510
+ const probe = implementContinuation.prepareSettlement({
1511
+ cwd: ctx.cwd,
1512
+ messages: event.messages,
1513
+ });
1514
+ if (!probe) return;
1515
+
1516
+ let status: Record<string, unknown>;
1517
+ try {
1518
+ const engine = await engineFor(ctx);
1519
+ if (
1520
+ aborted() ||
1521
+ exitingStage ||
1522
+ !activation.isActive() ||
1523
+ activePrompt !== "abel-implement"
1524
+ ) {
1525
+ return;
1526
+ }
1527
+ status = await engine.execute(
1528
+ {
1529
+ command: "status",
1530
+ stage: "abel-implement",
1531
+ change: probe.change,
1532
+ },
1533
+ ctx,
1534
+ runSignal,
1535
+ );
1536
+ if (
1537
+ aborted() ||
1538
+ (typeof ctx.hasPendingMessages === "function" &&
1539
+ ctx.hasPendingMessages())
1540
+ ) {
1541
+ return;
1542
+ }
1543
+ } catch {
1544
+ // A host reminder cannot replace a trustworthy local status read.
1545
+ return;
1546
+ }
1547
+
1548
+ const decision = implementContinuation.finishSettlement(probe, status);
1549
+ if (decision?.kind === "terminal") {
1550
+ await deactivateStage();
1551
+ return;
1552
+ }
1553
+ if (!decision) return;
1554
+ if (
1555
+ aborted() ||
1556
+ (typeof ctx.hasPendingMessages === "function" && ctx.hasPendingMessages())
1557
+ ) {
1558
+ return;
1559
+ }
1560
+
1561
+ if (decision.kind === "stalled") {
1562
+ pi.sendMessage(
1563
+ {
1564
+ customType: "abel-implement-continuation-stalled",
1565
+ content: [{ type: "text", text: decision.message }],
1566
+ display: true,
1567
+ details: {
1568
+ code: decision.code,
1569
+ change: decision.change,
1570
+ state: decision.state,
1571
+ statusFingerprint: decision.statusFingerprint,
1572
+ },
1573
+ },
1574
+ { triggerTurn: false },
1575
+ );
1576
+ return;
1577
+ }
1578
+
1579
+ pi.sendMessage(
1580
+ {
1581
+ customType: "abel-implement-continuation",
1582
+ content: [{ type: "text", text: decision.message }],
1583
+ display: false,
1584
+ details: {
1585
+ change: decision.change,
1586
+ statusFingerprint: decision.statusFingerprint,
1587
+ },
1588
+ },
1589
+ { triggerTurn: true, deliverAs: "followUp" },
1590
+ );
1591
+ });
1448
1592
  pi.on("session_start", async (_event, ctx) => {
1449
1593
  pendingPrompt = undefined;
1594
+ pendingImplementChange = undefined;
1450
1595
  pendingInit = false;
1451
1596
  activePrompt = undefined;
1597
+ implementContinuation.deactivate();
1452
1598
  designFailures.clear();
1453
1599
  activity.detach();
1454
1600
  await packetRuntime.drain();
@@ -1457,7 +1603,11 @@ export function registerWorkflowControl(
1457
1603
  if (ctx.mode === "tui") activity.attach(ctx.ui);
1458
1604
  });
1459
1605
  pi.on("session_shutdown", async () => {
1606
+ pendingPrompt = undefined;
1607
+ pendingImplementChange = undefined;
1608
+ pendingInit = false;
1460
1609
  activePrompt = undefined;
1610
+ implementContinuation.deactivate();
1461
1611
  designFailures.clear();
1462
1612
  activity.detach();
1463
1613
  await packetRuntime.drain();
@@ -37,6 +37,7 @@ export type IsolationRunResult =
37
37
  code:
38
38
  | "isolation-backend-unavailable"
39
39
  | "isolation-backend-launch-failed"
40
+ | "isolation-termination-unconfirmed"
40
41
  | "isolation-execution-timeout";
41
42
  }
42
43
  | { ok: false; state: "cancelled"; code: "cancelled" };
@@ -72,7 +73,7 @@ function validEnvironmentName(name: string): boolean {
72
73
  }
73
74
 
74
75
  /** Retains independent head/tail copies; discarded chunks never stay referenced. */
75
- class BoundedLog {
76
+ export class BoundedLog {
76
77
  bytes = 0;
77
78
  #head = Buffer.alloc(0);
78
79
  #tail = Buffer.alloc(0);