@abelxiaoxing/cadence 1.0.13 → 1.1.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.
- package/README.md +29 -13
- package/agents/contract-reviewer.md +2 -2
- package/agents/design-explorer.md +1 -1
- package/agents/diagnosis-worker.md +6 -3
- package/agents/implementation-worker.md +14 -9
- package/config/.env.example +5 -17
- package/config/routes.example.json +42 -0
- package/package.json +3 -3
- package/prompts/abel-design.md +150 -228
- package/prompts/abel-diagnose.md +37 -18
- package/prompts/abel-implement.md +111 -178
- package/prompts/abel-init.md +36 -33
- package/skills/abel-workflow/SKILL.md +156 -168
- package/src/apply-transaction.ts +1427 -0
- package/src/artifact-store.ts +204 -0
- package/src/child-session.ts +136 -17
- package/src/contracts.ts +105 -696
- package/src/control-contracts.ts +259 -0
- package/src/delivery-compiler.ts +1088 -0
- package/src/file-snapshot.ts +5 -0
- package/src/implement-graph.ts +11 -4
- package/src/index.ts +1465 -133
- package/src/isolation-backend.ts +269 -0
- package/src/packet-runtime.ts +533 -0
- package/src/parent-provider.ts +23 -4
- package/src/route-policy.ts +483 -0
- package/src/run-state.ts +393 -0
- package/src/run-store.ts +918 -0
- package/src/safe-path.ts +11 -0
- package/src/scoped-tools.ts +36 -0
- package/src/state-root.ts +215 -0
- package/src/subagent-activity.ts +526 -27
- package/src/submit-tool.ts +274 -0
- package/src/task-ledger.ts +1380 -0
- package/src/verification-capability.ts +529 -42
- package/src/worker-broker.ts +703 -0
- package/src/workflow-engine.ts +7391 -0
- package/src/workspace-store.ts +544 -0
- package/src/candidate-preflight.ts +0 -1618
- package/src/drain.ts +0 -20
- package/src/patch.ts +0 -505
- package/src/result-store.ts +0 -312
- package/src/runtime.ts +0 -2436
- package/src/scheduler.ts +0 -384
- package/src/subagent-endpoint.ts +0 -158
- package/src/worker.ts +0 -204
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// Caller-facing commands for the private Abel workflow control plane.
|
|
2
|
+
// Mechanical graph, snapshot, task, and apply identities are intentionally not
|
|
3
|
+
// part of this surface: the control plane derives them from approved delivery.
|
|
4
|
+
|
|
5
|
+
export const CONTROL_PROTOCOL_VERSION = 2 as const;
|
|
6
|
+
|
|
7
|
+
export const CONTROL_COMMANDS = [
|
|
8
|
+
"start",
|
|
9
|
+
"status",
|
|
10
|
+
"resume",
|
|
11
|
+
"rebind",
|
|
12
|
+
"cancel",
|
|
13
|
+
"discard",
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
export const CONTROL_STAGES = ["abel-design", "abel-implement"] as const;
|
|
17
|
+
|
|
18
|
+
export type ControlCommandName = (typeof CONTROL_COMMANDS)[number];
|
|
19
|
+
export type ControlStage = (typeof CONTROL_STAGES)[number];
|
|
20
|
+
|
|
21
|
+
const IDENTIFIER = /^[a-z0-9](?:[a-z0-9._:-]{0,126}[a-z0-9])?$/iu;
|
|
22
|
+
const CHANGE_NAME = /^[a-z0-9](?:[a-z0-9-]{0,126}[a-z0-9])?$/u;
|
|
23
|
+
const SHA256 = /^[a-f0-9]{64}$/u;
|
|
24
|
+
|
|
25
|
+
interface ControlBase {
|
|
26
|
+
version: typeof CONTROL_PROTOCOL_VERSION;
|
|
27
|
+
command: ControlCommandName;
|
|
28
|
+
stage: ControlStage;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface NamedControlBase extends ControlBase {
|
|
32
|
+
change: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface NamedStartControlCommand extends NamedControlBase {
|
|
36
|
+
command: "start";
|
|
37
|
+
operationId: string;
|
|
38
|
+
provisionalKey?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ProvisionalDesignStartControlCommand extends ControlBase {
|
|
42
|
+
command: "start";
|
|
43
|
+
stage: "abel-design";
|
|
44
|
+
provisionalKey: string;
|
|
45
|
+
operationId: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type StartControlCommand =
|
|
49
|
+
| NamedStartControlCommand
|
|
50
|
+
| ProvisionalDesignStartControlCommand;
|
|
51
|
+
|
|
52
|
+
export interface StatusControlCommand extends NamedControlBase {
|
|
53
|
+
command: "status";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ResumeControlCommand extends NamedControlBase {
|
|
57
|
+
command: "resume";
|
|
58
|
+
operationId: string;
|
|
59
|
+
deliveryRevision?: number;
|
|
60
|
+
receiptHash?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface RebindControlCommand extends NamedControlBase {
|
|
64
|
+
command: "rebind";
|
|
65
|
+
operationId: string;
|
|
66
|
+
routeId: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CancelControlCommand extends NamedControlBase {
|
|
70
|
+
command: "cancel";
|
|
71
|
+
operationId: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface DiscardControlCommand extends NamedControlBase {
|
|
75
|
+
command: "discard";
|
|
76
|
+
operationId: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type ControlCommand =
|
|
80
|
+
| StartControlCommand
|
|
81
|
+
| StatusControlCommand
|
|
82
|
+
| ResumeControlCommand
|
|
83
|
+
| RebindControlCommand
|
|
84
|
+
| CancelControlCommand
|
|
85
|
+
| DiscardControlCommand;
|
|
86
|
+
|
|
87
|
+
export type ControlCommandValidation =
|
|
88
|
+
| { ok: true; value: ControlCommand }
|
|
89
|
+
| {
|
|
90
|
+
ok: false;
|
|
91
|
+
code: "invalid-control-command" | "unsupported-control-version";
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
95
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasExactKeys(
|
|
99
|
+
value: Record<string, unknown>,
|
|
100
|
+
required: readonly string[],
|
|
101
|
+
optional: readonly string[] = [],
|
|
102
|
+
): boolean {
|
|
103
|
+
const keys = Object.keys(value);
|
|
104
|
+
const allowed = new Set([...required, ...optional]);
|
|
105
|
+
return (
|
|
106
|
+
required.every((key) => Object.hasOwn(value, key)) &&
|
|
107
|
+
keys.every((key) => allowed.has(key))
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function validCommon(value: Record<string, unknown>): boolean {
|
|
112
|
+
return (
|
|
113
|
+
value.version === CONTROL_PROTOCOL_VERSION &&
|
|
114
|
+
(CONTROL_COMMANDS as readonly unknown[]).includes(value.command) &&
|
|
115
|
+
(CONTROL_STAGES as readonly unknown[]).includes(value.stage)
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function validChange(value: unknown): value is string {
|
|
120
|
+
return typeof value === "string" && CHANGE_NAME.test(value);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function validOperationId(value: unknown): value is string {
|
|
124
|
+
return typeof value === "string" && IDENTIFIER.test(value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function validateControlCommand(
|
|
128
|
+
value: unknown,
|
|
129
|
+
): ControlCommandValidation {
|
|
130
|
+
if (!isRecord(value)) return { ok: false, code: "invalid-control-command" };
|
|
131
|
+
if (
|
|
132
|
+
Object.hasOwn(value, "version") &&
|
|
133
|
+
value.version !== CONTROL_PROTOCOL_VERSION
|
|
134
|
+
) {
|
|
135
|
+
return { ok: false, code: "unsupported-control-version" };
|
|
136
|
+
}
|
|
137
|
+
if (!validCommon(value)) {
|
|
138
|
+
return { ok: false, code: "invalid-control-command" };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const common = ["version", "command", "stage"] as const;
|
|
142
|
+
const named = [...common, "change"] as const;
|
|
143
|
+
switch (value.command) {
|
|
144
|
+
case "status":
|
|
145
|
+
if (!hasExactKeys(value, named) || !validChange(value.change)) break;
|
|
146
|
+
return {
|
|
147
|
+
ok: true,
|
|
148
|
+
value: structuredClone(value) as unknown as StatusControlCommand,
|
|
149
|
+
};
|
|
150
|
+
case "cancel":
|
|
151
|
+
case "discard":
|
|
152
|
+
if (
|
|
153
|
+
hasExactKeys(value, [...named, "operationId"]) &&
|
|
154
|
+
validChange(value.change) &&
|
|
155
|
+
validOperationId(value.operationId)
|
|
156
|
+
) {
|
|
157
|
+
return {
|
|
158
|
+
ok: true,
|
|
159
|
+
value: structuredClone(value) as unknown as ControlCommand,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
case "start":
|
|
164
|
+
if (
|
|
165
|
+
hasExactKeys(value, [...named, "operationId"], ["provisionalKey"]) &&
|
|
166
|
+
validChange(value.change) &&
|
|
167
|
+
validOperationId(value.operationId) &&
|
|
168
|
+
(value.provisionalKey === undefined ||
|
|
169
|
+
(value.stage === "abel-design" &&
|
|
170
|
+
typeof value.provisionalKey === "string" &&
|
|
171
|
+
SHA256.test(value.provisionalKey)))
|
|
172
|
+
) {
|
|
173
|
+
return {
|
|
174
|
+
ok: true,
|
|
175
|
+
value: structuredClone(value) as unknown as NamedStartControlCommand,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (
|
|
179
|
+
hasExactKeys(value, [...common, "provisionalKey", "operationId"]) &&
|
|
180
|
+
value.stage === "abel-design" &&
|
|
181
|
+
typeof value.provisionalKey === "string" &&
|
|
182
|
+
SHA256.test(value.provisionalKey) &&
|
|
183
|
+
validOperationId(value.operationId)
|
|
184
|
+
) {
|
|
185
|
+
return {
|
|
186
|
+
ok: true,
|
|
187
|
+
value: structuredClone(
|
|
188
|
+
value,
|
|
189
|
+
) as unknown as ProvisionalDesignStartControlCommand,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
case "rebind":
|
|
194
|
+
if (
|
|
195
|
+
hasExactKeys(value, [...named, "operationId", "routeId"]) &&
|
|
196
|
+
validChange(value.change) &&
|
|
197
|
+
validOperationId(value.operationId) &&
|
|
198
|
+
typeof value.routeId === "string" &&
|
|
199
|
+
IDENTIFIER.test(value.routeId)
|
|
200
|
+
) {
|
|
201
|
+
return {
|
|
202
|
+
ok: true,
|
|
203
|
+
value: structuredClone(value) as unknown as RebindControlCommand,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
case "resume": {
|
|
208
|
+
if (
|
|
209
|
+
!hasExactKeys(
|
|
210
|
+
value,
|
|
211
|
+
[...named, "operationId"],
|
|
212
|
+
["deliveryRevision", "receiptHash"],
|
|
213
|
+
) ||
|
|
214
|
+
!validChange(value.change) ||
|
|
215
|
+
!validOperationId(value.operationId)
|
|
216
|
+
) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
const hasRevision = value.deliveryRevision !== undefined;
|
|
220
|
+
const hasReceipt = value.receiptHash !== undefined;
|
|
221
|
+
if (hasRevision !== hasReceipt) break;
|
|
222
|
+
if (
|
|
223
|
+
hasRevision &&
|
|
224
|
+
(!Number.isSafeInteger(value.deliveryRevision) ||
|
|
225
|
+
(value.deliveryRevision as number) < 1 ||
|
|
226
|
+
typeof value.receiptHash !== "string" ||
|
|
227
|
+
!SHA256.test(value.receiptHash))
|
|
228
|
+
) {
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
ok: true,
|
|
233
|
+
value: structuredClone(value) as unknown as ResumeControlCommand,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { ok: false, code: "invalid-control-command" };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function assertControlCommand(value: unknown): ControlCommand {
|
|
241
|
+
const validation = validateControlCommand(value);
|
|
242
|
+
if (!validation.ok) {
|
|
243
|
+
const error = new Error(validation.code);
|
|
244
|
+
error.name = "ControlCommandError";
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
return validation.value;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function controlRunKey(
|
|
251
|
+
canonicalRootHash: string,
|
|
252
|
+
stage: ControlStage,
|
|
253
|
+
change: string,
|
|
254
|
+
): string {
|
|
255
|
+
if (!SHA256.test(canonicalRootHash) || !CHANGE_NAME.test(change)) {
|
|
256
|
+
throw new Error("invalid-control-run-key");
|
|
257
|
+
}
|
|
258
|
+
return `${canonicalRootHash}:${stage}:${change}`;
|
|
259
|
+
}
|