@abelxiaoxing/cadence 1.7.0 → 1.8.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.
- package/README.md +24 -3
- package/config/.env.example +6 -0
- package/package.json +1 -1
- package/prompts/abel-design.md +6 -0
- package/prompts/abel-diagnose.md +6 -0
- package/prompts/abel-implement.md +6 -0
- package/src/apply-transaction.ts +28 -8
- package/src/build-windows-job.mjs +95 -0
- package/src/candidate-workspace.ts +6 -0
- package/src/change-verification.ts +6 -2
- package/src/durable-contracts.ts +2 -0
- package/src/durable-workflow.ts +8 -2
- package/src/execution-profile.ts +28 -2
- package/src/execution-retention.ts +77 -0
- package/src/index.ts +200 -27
- package/src/isolation-backend.ts +2 -1
- package/src/operator-cli.mjs +652 -97
- package/src/operator-tools.ts +7 -1
- package/src/package-state.ts +11 -0
- package/src/package-verification.ts +49 -17
- package/src/package-workflow.ts +3 -0
- package/src/phase-execution.ts +11 -6
- package/src/remove-path.ts +32 -0
- package/src/verification-capability.ts +1 -1
- package/src/verification-environment-io.ts +123 -14
- package/src/verification-environment.ts +31 -2
- package/src/windows-job-backend.ts +419 -0
- package/src/windows-job.c +149 -0
- package/src/workspace-io-entry.mjs +2 -2
- package/src/workspace-io-worker.mjs +207 -110
package/src/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export { executePackageVerification } from "./package-verification.ts";
|
|
|
21
21
|
// workflow routing (abel-design/implement/diagnose provenance) in the prompts
|
|
22
22
|
// integration; abel-init and ordinary prompts never activate dispatch.
|
|
23
23
|
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
24
25
|
import { dirname, join } from "node:path";
|
|
25
26
|
import { fileURLToPath } from "node:url";
|
|
26
27
|
import type {
|
|
@@ -369,6 +370,27 @@ function hasExpandedPromptMarker(
|
|
|
369
370
|
);
|
|
370
371
|
}
|
|
371
372
|
|
|
373
|
+
// A marker is never authority. Recognize only the expanded entrypoint shape
|
|
374
|
+
// here so quoted documentation and ordinary command discussions stay ordinary.
|
|
375
|
+
function expandedStagePrompt(text: string): EligiblePrompt | undefined {
|
|
376
|
+
return ELIGIBLE_PROMPTS.find(
|
|
377
|
+
(name) =>
|
|
378
|
+
text.startsWith(
|
|
379
|
+
`This procedure applies only when the user explicitly invokes \`/${name}\`.`,
|
|
380
|
+
) && hasExpandedPromptMarker(text, name),
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
type StageStartupError =
|
|
385
|
+
| "abel-stage-requires-idle"
|
|
386
|
+
| "abel-stage-provenance-invalid"
|
|
387
|
+
| "abel-stage-unverified-input"
|
|
388
|
+
| "abel-stage-tools-unavailable";
|
|
389
|
+
|
|
390
|
+
function startupErrorText(code: StageStartupError): string {
|
|
391
|
+
return `Cadence configuration error: ${code}. Do not execute this workflow or substitute shell/subagent tools for abel_dispatch. Ensure the Cadence extension and its package prompts are enabled, inspect host extension-load errors and tool filters, then submit the original slash command through session.prompt when idle. Do not pre-expand the template or queue it through steer/followUp. No new stage authority was granted by this rejected request.`;
|
|
392
|
+
}
|
|
393
|
+
|
|
372
394
|
function isVerifiedStageInvocation(
|
|
373
395
|
pi: ExtensionAPI,
|
|
374
396
|
activation: Activation,
|
|
@@ -719,6 +741,21 @@ export function registerWorkflowControl(
|
|
|
719
741
|
let exitingStage = false;
|
|
720
742
|
let activePrompt: EligiblePrompt | undefined;
|
|
721
743
|
let designToolSnapshot: string[] | undefined;
|
|
744
|
+
let startupFailure: StageStartupError | undefined;
|
|
745
|
+
let admittedPrompt: string | undefined;
|
|
746
|
+
let latestContextUser: string | undefined;
|
|
747
|
+
const admittedMessages = new Set<string>();
|
|
748
|
+
const reportStartupFailure = (code: StageStartupError) => {
|
|
749
|
+
pi.sendMessage(
|
|
750
|
+
{
|
|
751
|
+
customType: "abel-stage-startup-error",
|
|
752
|
+
content: startupErrorText(code),
|
|
753
|
+
display: true,
|
|
754
|
+
details: { code },
|
|
755
|
+
},
|
|
756
|
+
{ triggerTurn: false },
|
|
757
|
+
);
|
|
758
|
+
};
|
|
722
759
|
const activation = new (class implements Activation {
|
|
723
760
|
state: Activation["state"] = "inactive";
|
|
724
761
|
|
|
@@ -791,6 +828,18 @@ export function registerWorkflowControl(
|
|
|
791
828
|
),
|
|
792
829
|
);
|
|
793
830
|
};
|
|
831
|
+
const stageToolsAvailable = () => {
|
|
832
|
+
if (!activation.isActive()) return true;
|
|
833
|
+
const tools = pi.getActiveTools();
|
|
834
|
+
return (
|
|
835
|
+
tools.includes(DISPATCH_TOOL) &&
|
|
836
|
+
(activePrompt !== "abel-design" ||
|
|
837
|
+
tools.every(
|
|
838
|
+
(name) =>
|
|
839
|
+
name === DISPATCH_TOOL || DESIGN_PARENT_READ_TOOLS.has(name),
|
|
840
|
+
))
|
|
841
|
+
);
|
|
842
|
+
};
|
|
794
843
|
const deactivate = () => {
|
|
795
844
|
activation.drain();
|
|
796
845
|
if (designToolSnapshot) {
|
|
@@ -900,7 +949,6 @@ export function registerWorkflowControl(
|
|
|
900
949
|
|
|
901
950
|
const registerDispatchTool = (kind: "command" | "packet") => {
|
|
902
951
|
if (registeredParameterKind === kind) return;
|
|
903
|
-
registeredParameterKind = kind;
|
|
904
952
|
pi.registerTool({
|
|
905
953
|
name: DISPATCH_TOOL,
|
|
906
954
|
label: "Abel Control",
|
|
@@ -926,6 +974,7 @@ export function registerWorkflowControl(
|
|
|
926
974
|
ctx: ExtensionContext,
|
|
927
975
|
) {
|
|
928
976
|
if (
|
|
977
|
+
startupFailure ||
|
|
929
978
|
exitingStage ||
|
|
930
979
|
!activation.isActive() ||
|
|
931
980
|
activePrompt === undefined
|
|
@@ -1353,6 +1402,7 @@ export function registerWorkflowControl(
|
|
|
1353
1402
|
);
|
|
1354
1403
|
},
|
|
1355
1404
|
} as never);
|
|
1405
|
+
registeredParameterKind = kind;
|
|
1356
1406
|
};
|
|
1357
1407
|
registerDispatchTool("packet");
|
|
1358
1408
|
|
|
@@ -1411,6 +1461,17 @@ export function registerWorkflowControl(
|
|
|
1411
1461
|
// expose the complete workflow instructions to the parent model.
|
|
1412
1462
|
return { action: prompt || init ? "handled" : "continue" };
|
|
1413
1463
|
}
|
|
1464
|
+
if (
|
|
1465
|
+
prompt &&
|
|
1466
|
+
(event.streamingBehavior || !hasPackageProvenance(pi, prompt))
|
|
1467
|
+
) {
|
|
1468
|
+
reportStartupFailure(
|
|
1469
|
+
event.streamingBehavior
|
|
1470
|
+
? "abel-stage-requires-idle"
|
|
1471
|
+
: "abel-stage-provenance-invalid",
|
|
1472
|
+
);
|
|
1473
|
+
return { action: "handled" };
|
|
1474
|
+
}
|
|
1414
1475
|
pendingPrompt = prompt;
|
|
1415
1476
|
pendingImplementChange =
|
|
1416
1477
|
prompt === "abel-implement"
|
|
@@ -1430,6 +1491,9 @@ export function registerWorkflowControl(
|
|
|
1430
1491
|
return { action: "continue" };
|
|
1431
1492
|
});
|
|
1432
1493
|
pi.on("before_agent_start", (event, ctx) => {
|
|
1494
|
+
startupFailure = undefined;
|
|
1495
|
+
admittedPrompt = undefined;
|
|
1496
|
+
latestContextUser = undefined;
|
|
1433
1497
|
const prompt = pendingPrompt;
|
|
1434
1498
|
const implementChange = pendingImplementChange;
|
|
1435
1499
|
const init =
|
|
@@ -1439,43 +1503,143 @@ export function registerWorkflowControl(
|
|
|
1439
1503
|
pendingInit = false;
|
|
1440
1504
|
const verified =
|
|
1441
1505
|
prompt && isVerifiedStageInvocation(pi, activation, prompt, event.prompt);
|
|
1442
|
-
if (verified)
|
|
1443
|
-
|
|
1444
|
-
|
|
1506
|
+
if (prompt && !verified) startupFailure = "abel-stage-unverified-input";
|
|
1507
|
+
if (!prompt && expandedStagePrompt(event.prompt))
|
|
1508
|
+
startupFailure = "abel-stage-unverified-input";
|
|
1509
|
+
const toolsBefore = pi.getActiveTools();
|
|
1510
|
+
const wasActive = activation.isActive();
|
|
1511
|
+
try {
|
|
1512
|
+
if (verified) {
|
|
1513
|
+
if (activePrompt === "abel-design" && prompt !== "abel-design") {
|
|
1514
|
+
restoreDesignTools();
|
|
1515
|
+
}
|
|
1516
|
+
registerDispatchTool(
|
|
1517
|
+
prompt === "abel-implement" ? "command" : "packet",
|
|
1518
|
+
);
|
|
1519
|
+
activePrompt = prompt;
|
|
1520
|
+
if (prompt === "abel-implement") {
|
|
1521
|
+
if (
|
|
1522
|
+
implementChange &&
|
|
1523
|
+
implementPromptBindsChange(event.prompt, implementChange) &&
|
|
1524
|
+
typeof ctx.cwd === "string"
|
|
1525
|
+
)
|
|
1526
|
+
implementContinuation.activate({
|
|
1527
|
+
cwd: ctx.cwd,
|
|
1528
|
+
change: implementChange,
|
|
1529
|
+
});
|
|
1530
|
+
else implementContinuation.deactivate();
|
|
1531
|
+
} else {
|
|
1532
|
+
implementContinuation.deactivate();
|
|
1533
|
+
}
|
|
1445
1534
|
}
|
|
1446
|
-
|
|
1447
|
-
activePrompt
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1535
|
+
if (prompt) activateDispatcher(pi, activation, prompt, event.prompt);
|
|
1536
|
+
if (activation.isActive() && activePrompt === "abel-design")
|
|
1537
|
+
enforceDesignTools();
|
|
1538
|
+
if (!stageToolsAvailable())
|
|
1539
|
+
startupFailure = "abel-stage-tools-unavailable";
|
|
1540
|
+
} catch {
|
|
1541
|
+
startupFailure = "abel-stage-tools-unavailable";
|
|
1542
|
+
}
|
|
1543
|
+
if (
|
|
1544
|
+
startupFailure === "abel-stage-tools-unavailable" &&
|
|
1545
|
+
verified &&
|
|
1546
|
+
!wasActive
|
|
1547
|
+
) {
|
|
1548
|
+
// Roll back the failed startup, not a retained durable run. Never leave
|
|
1549
|
+
// partially applied Design restrictions or a falsely active stage.
|
|
1550
|
+
activePrompt = undefined;
|
|
1551
|
+
activation.drain();
|
|
1552
|
+
implementContinuation.deactivate();
|
|
1553
|
+
designToolSnapshot = undefined;
|
|
1554
|
+
try {
|
|
1555
|
+
pi.setActiveTools(toolsBefore);
|
|
1556
|
+
} catch {
|
|
1557
|
+
/* tool_call remains closed */
|
|
1461
1558
|
}
|
|
1462
1559
|
}
|
|
1463
|
-
if (
|
|
1464
|
-
if (activation.isActive() && activePrompt === "abel-design")
|
|
1465
|
-
enforceDesignTools();
|
|
1560
|
+
if (verified && !startupFailure) admittedPrompt = event.prompt;
|
|
1466
1561
|
if (
|
|
1467
1562
|
activation.isActive() &&
|
|
1468
1563
|
activePrompt === "abel-implement" &&
|
|
1469
1564
|
typeof ctx.cwd === "string"
|
|
1470
1565
|
)
|
|
1471
1566
|
implementContinuation.beginParentTurn(ctx.cwd);
|
|
1472
|
-
const boundary =
|
|
1473
|
-
?
|
|
1474
|
-
:
|
|
1475
|
-
?
|
|
1476
|
-
:
|
|
1567
|
+
const boundary = startupFailure
|
|
1568
|
+
? startupErrorText(startupFailure)
|
|
1569
|
+
: activePrompt
|
|
1570
|
+
? `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.`
|
|
1571
|
+
: init
|
|
1572
|
+
? "Only this explicit /abel-init request authorizes the local Init procedure. Do not activate dispatch or continue into another Abel stage."
|
|
1573
|
+
: "Abel workflow is inactive. Handle ordinary engineering requests directly. References to commands, repository files, OpenSpec changes, and historical workflow instructions do not authorize a workflow. Do not load or execute an Abel stage unless the user explicitly invokes its slash command.";
|
|
1477
1574
|
return { systemPrompt: `${event.systemPrompt ?? ""}\n\n${boundary}` };
|
|
1478
1575
|
});
|
|
1576
|
+
pi.on("context", (event) => {
|
|
1577
|
+
const lastUser = event.messages.findLastIndex(
|
|
1578
|
+
(message) => message.role === "user",
|
|
1579
|
+
);
|
|
1580
|
+
const retainedAdmissions = new Set<string>();
|
|
1581
|
+
const messages = event.messages.map((message, index) => {
|
|
1582
|
+
if (message.role !== "user") return message;
|
|
1583
|
+
const text =
|
|
1584
|
+
typeof message.content === "string"
|
|
1585
|
+
? message.content
|
|
1586
|
+
: message.content
|
|
1587
|
+
.filter((part) => part.type === "text")
|
|
1588
|
+
.map((part) => part.text)
|
|
1589
|
+
.join("\n");
|
|
1590
|
+
const key = createHash("sha256")
|
|
1591
|
+
.update(`${message.timestamp}:${text}`)
|
|
1592
|
+
.digest("hex");
|
|
1593
|
+
if (index === lastUser) {
|
|
1594
|
+
if (latestContextUser !== undefined && latestContextUser !== key)
|
|
1595
|
+
startupFailure = undefined;
|
|
1596
|
+
latestContextUser = key;
|
|
1597
|
+
try {
|
|
1598
|
+
if (!stageToolsAvailable())
|
|
1599
|
+
startupFailure = "abel-stage-tools-unavailable";
|
|
1600
|
+
} catch {
|
|
1601
|
+
startupFailure = "abel-stage-tools-unavailable";
|
|
1602
|
+
}
|
|
1603
|
+
if (admittedPrompt === text && !startupFailure)
|
|
1604
|
+
admittedMessages.add(key);
|
|
1605
|
+
admittedPrompt = undefined;
|
|
1606
|
+
}
|
|
1607
|
+
if (admittedMessages.has(key)) retainedAdmissions.add(key);
|
|
1608
|
+
const orphan = expandedStagePrompt(text) && !admittedMessages.has(key);
|
|
1609
|
+
if (index === lastUser && orphan && !startupFailure)
|
|
1610
|
+
startupFailure = "abel-stage-unverified-input";
|
|
1611
|
+
if (!orphan && !(index === lastUser && startupFailure)) return message;
|
|
1612
|
+
return {
|
|
1613
|
+
...message,
|
|
1614
|
+
content: [
|
|
1615
|
+
{
|
|
1616
|
+
type: "text" as const,
|
|
1617
|
+
text: startupErrorText(
|
|
1618
|
+
index === lastUser && startupFailure
|
|
1619
|
+
? startupFailure
|
|
1620
|
+
: "abel-stage-unverified-input",
|
|
1621
|
+
),
|
|
1622
|
+
},
|
|
1623
|
+
],
|
|
1624
|
+
};
|
|
1625
|
+
});
|
|
1626
|
+
// Keep only identities still present after compaction; never retain raw requirements.
|
|
1627
|
+
admittedMessages.clear();
|
|
1628
|
+
for (const key of retainedAdmissions) admittedMessages.add(key);
|
|
1629
|
+
return { messages };
|
|
1630
|
+
});
|
|
1631
|
+
pi.on("tool_call", (event) => {
|
|
1632
|
+
if (!stageToolsAvailable()) startupFailure = "abel-stage-tools-unavailable";
|
|
1633
|
+
if (startupFailure)
|
|
1634
|
+
return { block: true, reason: startupErrorText(startupFailure) };
|
|
1635
|
+
// Defense in depth against a host/extension restoring a stale tool list.
|
|
1636
|
+
if (
|
|
1637
|
+
activePrompt === "abel-design" &&
|
|
1638
|
+
event.toolName !== DISPATCH_TOOL &&
|
|
1639
|
+
!DESIGN_PARENT_READ_TOOLS.has(event.toolName)
|
|
1640
|
+
)
|
|
1641
|
+
return { block: true, reason: "abel-design-tool-boundary" };
|
|
1642
|
+
});
|
|
1479
1643
|
pi.on("before_provider_request", (event, ctx) => {
|
|
1480
1644
|
if (
|
|
1481
1645
|
activePrompt !== "abel-design" ||
|
|
@@ -1495,6 +1659,7 @@ export function registerWorkflowControl(
|
|
|
1495
1659
|
const aborted = () => runSignal?.aborted || ctx.signal?.aborted;
|
|
1496
1660
|
if (
|
|
1497
1661
|
aborted() ||
|
|
1662
|
+
startupFailure ||
|
|
1498
1663
|
exitingStage ||
|
|
1499
1664
|
!activation.isActive() ||
|
|
1500
1665
|
activePrompt !== "abel-implement"
|
|
@@ -1590,6 +1755,10 @@ export function registerWorkflowControl(
|
|
|
1590
1755
|
);
|
|
1591
1756
|
});
|
|
1592
1757
|
pi.on("session_start", async (_event, ctx) => {
|
|
1758
|
+
startupFailure = undefined;
|
|
1759
|
+
admittedPrompt = undefined;
|
|
1760
|
+
latestContextUser = undefined;
|
|
1761
|
+
admittedMessages.clear();
|
|
1593
1762
|
pendingPrompt = undefined;
|
|
1594
1763
|
pendingImplementChange = undefined;
|
|
1595
1764
|
pendingInit = false;
|
|
@@ -1603,6 +1772,10 @@ export function registerWorkflowControl(
|
|
|
1603
1772
|
if (ctx.mode === "tui") activity.attach(ctx.ui);
|
|
1604
1773
|
});
|
|
1605
1774
|
pi.on("session_shutdown", async () => {
|
|
1775
|
+
startupFailure = undefined;
|
|
1776
|
+
admittedPrompt = undefined;
|
|
1777
|
+
latestContextUser = undefined;
|
|
1778
|
+
admittedMessages.clear();
|
|
1606
1779
|
pendingPrompt = undefined;
|
|
1607
1780
|
pendingImplementChange = undefined;
|
|
1608
1781
|
pendingInit = false;
|
package/src/isolation-backend.ts
CHANGED
|
@@ -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);
|