@abelxiaoxing/cadence 1.1.0 → 1.2.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 +19 -3
- package/agents/implementation-worker.md +17 -11
- package/config/routes.example.json +0 -2
- package/package.json +2 -2
- package/prompts/abel-design.md +77 -17
- package/prompts/abel-diagnose.md +2 -0
- package/prompts/abel-implement.md +23 -11
- package/skills/abel-workflow/SKILL.md +42 -5
- package/src/agent-registry.ts +0 -1
- package/src/candidate-patch.ts +250 -0
- package/src/child-session.ts +27 -24
- package/src/contracts.ts +19 -10
- package/src/control-contracts.ts +15 -62
- package/src/delivery-compiler.ts +47 -17
- package/src/design-control.ts +1053 -0
- package/src/design-journal.ts +1616 -0
- package/src/index.ts +626 -196
- package/src/packet-runtime.ts +6 -6
- package/src/route-policy.ts +45 -17
- package/src/run-state.ts +43 -4
- package/src/run-store.ts +103 -15
- package/src/subagent-activity.ts +1 -1
- package/src/submit-tool.ts +149 -49
- package/src/verification-capability.ts +43 -15
- package/src/worker-broker.ts +0 -1
- package/src/workflow-engine.ts +416 -250
- package/agents/contract-reviewer.md +0 -11
package/src/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ import { loadAgentDefinitions } from "./agent-registry.ts";
|
|
|
31
31
|
import { runChildSession } from "./child-session.ts";
|
|
32
32
|
import {
|
|
33
33
|
type AtomicVerificationContract,
|
|
34
|
+
type DesignEvidenceResult,
|
|
34
35
|
isValidRelativePath,
|
|
35
36
|
LIMITS,
|
|
36
37
|
type StructuredVerificationContract,
|
|
@@ -41,11 +42,16 @@ import {
|
|
|
41
42
|
assessDeliveryTraceability,
|
|
42
43
|
compileImplementPlan,
|
|
43
44
|
DeliveryValidationError,
|
|
45
|
+
type GateApprovalProof,
|
|
44
46
|
IMPLEMENT_PLAN_SCHEMA_VERSION,
|
|
45
47
|
parseGateAReceipt,
|
|
46
48
|
parseImplementPlan,
|
|
47
49
|
parseReadyReceipt,
|
|
48
50
|
} from "./delivery-compiler.ts";
|
|
51
|
+
import {
|
|
52
|
+
DesignController,
|
|
53
|
+
validateDesignControlRequest,
|
|
54
|
+
} from "./design-control.ts";
|
|
49
55
|
import { canonicalJson, hashCanonicalValue } from "./implement-graph.ts";
|
|
50
56
|
import { BubblewrapIsolationBackend } from "./isolation-backend.ts";
|
|
51
57
|
import { PACKET_ACTIONS, PacketRuntime } from "./packet-runtime.ts";
|
|
@@ -75,10 +81,12 @@ import {
|
|
|
75
81
|
} from "./verification-capability.ts";
|
|
76
82
|
import {
|
|
77
83
|
openDurableWorkflowEngine,
|
|
84
|
+
type WorkflowAvailableDelivery,
|
|
78
85
|
type WorkflowDeliverySource,
|
|
79
86
|
} from "./workflow-engine.ts";
|
|
80
87
|
|
|
81
88
|
export const DISPATCH_TOOL = "abel_dispatch";
|
|
89
|
+
const DESIGN_PARENT_READ_TOOLS = new Set(["read", "grep", "find", "ls"]);
|
|
82
90
|
|
|
83
91
|
const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
84
92
|
const ELIGIBLE_PROMPTS = [
|
|
@@ -109,6 +117,10 @@ const DESIGN_REQUEST_SCHEMA = {
|
|
|
109
117
|
enum: ["design-explorer"],
|
|
110
118
|
description: "Exact package-owned read-only Agent role.",
|
|
111
119
|
},
|
|
120
|
+
runId: {
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "Durable Design run identity returned by Design start.",
|
|
123
|
+
},
|
|
112
124
|
id: {
|
|
113
125
|
type: "string",
|
|
114
126
|
description:
|
|
@@ -182,6 +194,7 @@ const DESIGN_REQUEST_SCHEMA = {
|
|
|
182
194
|
required: [
|
|
183
195
|
"stage",
|
|
184
196
|
"role",
|
|
197
|
+
"runId",
|
|
185
198
|
"id",
|
|
186
199
|
"phase",
|
|
187
200
|
"objective",
|
|
@@ -193,6 +206,145 @@ const DESIGN_REQUEST_SCHEMA = {
|
|
|
193
206
|
additionalProperties: false,
|
|
194
207
|
} as const;
|
|
195
208
|
|
|
209
|
+
const DESIGN_CONTROL_REQUEST_SCHEMA = {
|
|
210
|
+
description:
|
|
211
|
+
"One closed durable Design control operation. The operation body is strict and idempotent by operationId.",
|
|
212
|
+
oneOf: [
|
|
213
|
+
...(["requirement", "change"] as const).map((field) => ({
|
|
214
|
+
type: "object" as const,
|
|
215
|
+
properties: {
|
|
216
|
+
operation: { type: "string" as const, enum: ["start"] },
|
|
217
|
+
operationId: { type: "string" as const },
|
|
218
|
+
[field]: {
|
|
219
|
+
type: "string" as const,
|
|
220
|
+
description:
|
|
221
|
+
field === "requirement"
|
|
222
|
+
? "Raw Design requirement; code derives and stores only its stable hash."
|
|
223
|
+
: "Existing canonical change name to resume or revise.",
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
required: ["operation", "operationId", field],
|
|
227
|
+
additionalProperties: false,
|
|
228
|
+
})),
|
|
229
|
+
{
|
|
230
|
+
type: "object",
|
|
231
|
+
properties: {
|
|
232
|
+
operation: { type: "string", enum: ["status"] },
|
|
233
|
+
runId: { type: "string" },
|
|
234
|
+
},
|
|
235
|
+
required: ["operation", "runId"],
|
|
236
|
+
additionalProperties: false,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
type: "object",
|
|
240
|
+
properties: {
|
|
241
|
+
operation: { type: "string", enum: ["bind-change"] },
|
|
242
|
+
runId: { type: "string" },
|
|
243
|
+
operationId: { type: "string" },
|
|
244
|
+
change: { type: "string" },
|
|
245
|
+
},
|
|
246
|
+
required: ["operation", "runId", "operationId", "change"],
|
|
247
|
+
additionalProperties: false,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
operation: { type: "string", enum: ["record-decision"] },
|
|
253
|
+
runId: { type: "string" },
|
|
254
|
+
operationId: { type: "string" },
|
|
255
|
+
decisionId: { type: "string" },
|
|
256
|
+
category: { type: "string", enum: ["behavior", "technical"] },
|
|
257
|
+
contract: {
|
|
258
|
+
type: "string",
|
|
259
|
+
description:
|
|
260
|
+
"Normalized decision contract; code hashes it and never persists the text.",
|
|
261
|
+
},
|
|
262
|
+
refs: { type: "array", items: { type: "string" } },
|
|
263
|
+
},
|
|
264
|
+
required: [
|
|
265
|
+
"operation",
|
|
266
|
+
"runId",
|
|
267
|
+
"operationId",
|
|
268
|
+
"decisionId",
|
|
269
|
+
"category",
|
|
270
|
+
"contract",
|
|
271
|
+
"refs",
|
|
272
|
+
],
|
|
273
|
+
additionalProperties: false,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
type: "object",
|
|
277
|
+
properties: {
|
|
278
|
+
operation: { type: "string", enum: ["approve-gate"] },
|
|
279
|
+
runId: { type: "string" },
|
|
280
|
+
operationId: { type: "string" },
|
|
281
|
+
gate: { type: "string", enum: ["gate-a"] },
|
|
282
|
+
contract: {
|
|
283
|
+
type: "string",
|
|
284
|
+
description:
|
|
285
|
+
"Complete approved WHAT contract; code hashes it and never persists the text.",
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
required: ["operation", "runId", "operationId", "gate", "contract"],
|
|
289
|
+
additionalProperties: false,
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
operation: { type: "string", enum: ["approve-gate"] },
|
|
295
|
+
runId: { type: "string" },
|
|
296
|
+
operationId: { type: "string" },
|
|
297
|
+
gate: { type: "string", enum: ["gate-b"] },
|
|
298
|
+
},
|
|
299
|
+
required: ["operation", "runId", "operationId", "gate"],
|
|
300
|
+
additionalProperties: false,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
operation: { type: "string", enum: ["write-artifact"] },
|
|
306
|
+
runId: { type: "string" },
|
|
307
|
+
operationId: { type: "string" },
|
|
308
|
+
path: {
|
|
309
|
+
type: "string",
|
|
310
|
+
description: "Allowed path relative to this Design change root.",
|
|
311
|
+
},
|
|
312
|
+
content: {
|
|
313
|
+
type: "string",
|
|
314
|
+
maxLength: 16 * 1024 * 1024,
|
|
315
|
+
description: "Exact bounded UTF-8 artifact content.",
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
required: ["operation", "runId", "operationId", "path", "content"],
|
|
319
|
+
additionalProperties: false,
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
type: "object",
|
|
323
|
+
properties: {
|
|
324
|
+
operation: { type: "string", enum: ["delete-artifact"] },
|
|
325
|
+
runId: { type: "string" },
|
|
326
|
+
operationId: { type: "string" },
|
|
327
|
+
path: {
|
|
328
|
+
type: "string",
|
|
329
|
+
description: "Allowed path relative to this Design change root.",
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
required: ["operation", "runId", "operationId", "path"],
|
|
333
|
+
additionalProperties: false,
|
|
334
|
+
},
|
|
335
|
+
...(["compile-plan", "finalize-delivery"] as const).map((operation) => ({
|
|
336
|
+
type: "object" as const,
|
|
337
|
+
properties: {
|
|
338
|
+
operation: { type: "string" as const, enum: [operation] },
|
|
339
|
+
runId: { type: "string" as const },
|
|
340
|
+
operationId: { type: "string" as const },
|
|
341
|
+
},
|
|
342
|
+
required: ["operation", "runId", "operationId"],
|
|
343
|
+
additionalProperties: false,
|
|
344
|
+
})),
|
|
345
|
+
],
|
|
346
|
+
} as const;
|
|
347
|
+
|
|
196
348
|
function invokedPrompt(text: string): EligiblePrompt | undefined {
|
|
197
349
|
const name = text.match(/^\/([^\s]+)(?:\s|$)/)?.[1];
|
|
198
350
|
return ELIGIBLE_PROMPTS.find((candidate) => candidate === name);
|
|
@@ -273,6 +425,12 @@ export interface WorkflowControlEngine {
|
|
|
273
425
|
signal?: AbortSignal,
|
|
274
426
|
onActivity?: (event: WorkflowActivityUpdate) => void,
|
|
275
427
|
): Promise<Record<string, unknown>>;
|
|
428
|
+
executeDesign?(request: unknown): Promise<Record<string, unknown>>;
|
|
429
|
+
assertDesignRun?(runId: string): void;
|
|
430
|
+
recordDesignEvidence?(input: {
|
|
431
|
+
runId: string;
|
|
432
|
+
evidence: DesignEvidenceResult;
|
|
433
|
+
}): unknown;
|
|
276
434
|
close(): void | Promise<void>;
|
|
277
435
|
}
|
|
278
436
|
|
|
@@ -297,12 +455,40 @@ export interface PackageDeliverySourceOptions {
|
|
|
297
455
|
consumerRoot: string,
|
|
298
456
|
change: string,
|
|
299
457
|
) => Promise<OpenSpecDeliveryInspection>;
|
|
458
|
+
verifyGateProof?: (input: {
|
|
459
|
+
change: string;
|
|
460
|
+
gate: "gate-a" | "gate-b";
|
|
461
|
+
proof: GateApprovalProof;
|
|
462
|
+
}) => boolean;
|
|
463
|
+
verifyFinalizedDelivery?: (input: {
|
|
464
|
+
change: string;
|
|
465
|
+
deliveryRevision: number;
|
|
466
|
+
receiptHash: string;
|
|
467
|
+
gateA: GateApprovalProof;
|
|
468
|
+
gateB: GateApprovalProof;
|
|
469
|
+
planCanonicalHash: string;
|
|
470
|
+
}) => boolean;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export interface PackageWorkflowDeliverySource extends WorkflowDeliverySource {
|
|
474
|
+
discoverLatest(input: {
|
|
475
|
+
stage: "abel-implement";
|
|
476
|
+
change: string;
|
|
477
|
+
}): Promise<WorkflowAvailableDelivery | undefined>;
|
|
300
478
|
}
|
|
301
479
|
|
|
302
480
|
function sha256(value: Uint8Array | string): string {
|
|
303
481
|
return createHash("sha256").update(value).digest("hex");
|
|
304
482
|
}
|
|
305
483
|
|
|
484
|
+
function proofVerified(verify: () => boolean): boolean {
|
|
485
|
+
try {
|
|
486
|
+
return verify() === true;
|
|
487
|
+
} catch {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
306
492
|
function normalizedTrackingArtifact(
|
|
307
493
|
bytes: Uint8Array,
|
|
308
494
|
taskIds: readonly string[],
|
|
@@ -449,9 +635,80 @@ export async function inspectOpenSpecDelivery(
|
|
|
449
635
|
export function packageDeliverySource(
|
|
450
636
|
consumerRoot: string,
|
|
451
637
|
options: PackageDeliverySourceOptions = {},
|
|
452
|
-
):
|
|
638
|
+
): PackageWorkflowDeliverySource {
|
|
453
639
|
const inspect = options.inspectOpenSpec ?? inspectOpenSpecDelivery;
|
|
640
|
+
const verifyGateProof = options.verifyGateProof;
|
|
641
|
+
const verifyFinalizedDelivery = options.verifyFinalizedDelivery;
|
|
454
642
|
return {
|
|
643
|
+
async discoverLatest(input) {
|
|
644
|
+
if (
|
|
645
|
+
input.stage !== "abel-implement" ||
|
|
646
|
+
!verifyGateProof ||
|
|
647
|
+
!verifyFinalizedDelivery
|
|
648
|
+
) {
|
|
649
|
+
return undefined;
|
|
650
|
+
}
|
|
651
|
+
try {
|
|
652
|
+
const changeRoot = `openspec/changes/${input.change}`;
|
|
653
|
+
const receiptBytes = readPackageDeliveryFile(
|
|
654
|
+
consumerRoot,
|
|
655
|
+
`${changeRoot}/ready.yaml`,
|
|
656
|
+
4 * 1024 * 1024,
|
|
657
|
+
);
|
|
658
|
+
const receipt = parseReadyReceipt(receiptBytes);
|
|
659
|
+
if (receipt.change !== input.change) return undefined;
|
|
660
|
+
const gateABytes = readPackageDeliveryFile(
|
|
661
|
+
consumerRoot,
|
|
662
|
+
`${changeRoot}/${receipt.approvals.gateA.path}`,
|
|
663
|
+
4 * 1024 * 1024,
|
|
664
|
+
);
|
|
665
|
+
if (sha256(gateABytes) !== receipt.approvals.gateA.rawSha256) {
|
|
666
|
+
return undefined;
|
|
667
|
+
}
|
|
668
|
+
const gateA = parseGateAReceipt(gateABytes);
|
|
669
|
+
if (
|
|
670
|
+
gateA.change !== receipt.change ||
|
|
671
|
+
gateA.schema !== receipt.schema ||
|
|
672
|
+
!proofVerified(() =>
|
|
673
|
+
verifyGateProof({
|
|
674
|
+
change: input.change,
|
|
675
|
+
gate: "gate-a",
|
|
676
|
+
proof: gateA.approval,
|
|
677
|
+
}),
|
|
678
|
+
) ||
|
|
679
|
+
!proofVerified(() =>
|
|
680
|
+
verifyGateProof({
|
|
681
|
+
change: input.change,
|
|
682
|
+
gate: "gate-b",
|
|
683
|
+
proof: receipt.approvals.gateB,
|
|
684
|
+
}),
|
|
685
|
+
)
|
|
686
|
+
) {
|
|
687
|
+
return undefined;
|
|
688
|
+
}
|
|
689
|
+
const receiptHash = sha256(receiptBytes);
|
|
690
|
+
if (
|
|
691
|
+
!proofVerified(() =>
|
|
692
|
+
verifyFinalizedDelivery({
|
|
693
|
+
change: input.change,
|
|
694
|
+
deliveryRevision: receipt.deliveryRevision,
|
|
695
|
+
receiptHash,
|
|
696
|
+
gateA: gateA.approval,
|
|
697
|
+
gateB: receipt.approvals.gateB,
|
|
698
|
+
planCanonicalHash: receipt.plan.canonicalHash,
|
|
699
|
+
}),
|
|
700
|
+
)
|
|
701
|
+
) {
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
return {
|
|
705
|
+
deliveryRevision: receipt.deliveryRevision,
|
|
706
|
+
receiptHash,
|
|
707
|
+
};
|
|
708
|
+
} catch {
|
|
709
|
+
return undefined;
|
|
710
|
+
}
|
|
711
|
+
},
|
|
455
712
|
async load(input) {
|
|
456
713
|
if (input.stage !== "abel-implement") {
|
|
457
714
|
throw new DeliveryValidationError(["delivery-stage-invalid"]);
|
|
@@ -567,6 +824,19 @@ export function packageDeliverySource(
|
|
|
567
824
|
}
|
|
568
825
|
}
|
|
569
826
|
if (gateA) {
|
|
827
|
+
if (!verifyGateProof) {
|
|
828
|
+
diagnostics.add("delivery-gate-proof-verifier-unavailable");
|
|
829
|
+
} else if (
|
|
830
|
+
!proofVerified(() =>
|
|
831
|
+
verifyGateProof({
|
|
832
|
+
change: input.change,
|
|
833
|
+
gate: "gate-a",
|
|
834
|
+
proof: gateA.approval,
|
|
835
|
+
}),
|
|
836
|
+
)
|
|
837
|
+
) {
|
|
838
|
+
diagnostics.add("delivery-gate-a-proof-invalid");
|
|
839
|
+
}
|
|
570
840
|
for (const artifact of gateA.artifacts) {
|
|
571
841
|
if (artifactHashes.get(artifact.path) !== artifact.rawSha256) {
|
|
572
842
|
diagnostics.add(
|
|
@@ -575,6 +845,36 @@ export function packageDeliverySource(
|
|
|
575
845
|
}
|
|
576
846
|
}
|
|
577
847
|
}
|
|
848
|
+
if (
|
|
849
|
+
verifyGateProof &&
|
|
850
|
+
!proofVerified(() =>
|
|
851
|
+
verifyGateProof({
|
|
852
|
+
change: input.change,
|
|
853
|
+
gate: "gate-b",
|
|
854
|
+
proof: receipt.approvals.gateB,
|
|
855
|
+
}),
|
|
856
|
+
)
|
|
857
|
+
) {
|
|
858
|
+
diagnostics.add("delivery-gate-b-proof-invalid");
|
|
859
|
+
}
|
|
860
|
+
if (gateA) {
|
|
861
|
+
if (!verifyFinalizedDelivery) {
|
|
862
|
+
diagnostics.add("delivery-finalization-verifier-unavailable");
|
|
863
|
+
} else if (
|
|
864
|
+
!proofVerified(() =>
|
|
865
|
+
verifyFinalizedDelivery({
|
|
866
|
+
change: input.change,
|
|
867
|
+
deliveryRevision: receipt.deliveryRevision,
|
|
868
|
+
receiptHash,
|
|
869
|
+
gateA: gateA.approval,
|
|
870
|
+
gateB: receipt.approvals.gateB,
|
|
871
|
+
planCanonicalHash: receipt.plan.canonicalHash,
|
|
872
|
+
}),
|
|
873
|
+
)
|
|
874
|
+
) {
|
|
875
|
+
diagnostics.add("delivery-finalization-proof-invalid");
|
|
876
|
+
}
|
|
877
|
+
}
|
|
578
878
|
|
|
579
879
|
let plan: ReturnType<typeof parseImplementPlan> | undefined;
|
|
580
880
|
let planBytes: Buffer | undefined;
|
|
@@ -675,7 +975,7 @@ export function packageDeliverySource(
|
|
|
675
975
|
}
|
|
676
976
|
}
|
|
677
977
|
}
|
|
678
|
-
if (!plan || diagnostics.size > 0) {
|
|
978
|
+
if (!plan || !gateA || diagnostics.size > 0) {
|
|
679
979
|
throw new DeliveryValidationError([...diagnostics]);
|
|
680
980
|
}
|
|
681
981
|
return {
|
|
@@ -684,6 +984,10 @@ export function packageDeliverySource(
|
|
|
684
984
|
revision: receipt.deliveryRevision,
|
|
685
985
|
receiptHash,
|
|
686
986
|
plan,
|
|
987
|
+
approvalProofs: {
|
|
988
|
+
gateA: structuredClone(gateA.approval),
|
|
989
|
+
gateB: structuredClone(receipt.approvals.gateB),
|
|
990
|
+
},
|
|
687
991
|
};
|
|
688
992
|
},
|
|
689
993
|
};
|
|
@@ -1169,12 +1473,18 @@ export function openPackageWorkflowControlEngine(
|
|
|
1169
1473
|
const routeResolution = loadRoutePolicy({
|
|
1170
1474
|
cwd: consumerRoot,
|
|
1171
1475
|
home: homedir(),
|
|
1476
|
+
...(initialContext.model ? { parentModel: initialContext.model } : {}),
|
|
1172
1477
|
});
|
|
1173
1478
|
const stateRoot = resolveStateRoot({
|
|
1174
1479
|
consumerRoot,
|
|
1175
1480
|
xdgStateHome: process.env.XDG_STATE_HOME,
|
|
1176
1481
|
});
|
|
1177
1482
|
const contexts = new AsyncLocalStorage<ExtensionContext>();
|
|
1483
|
+
const design = DesignController.open({
|
|
1484
|
+
consumerRoot,
|
|
1485
|
+
stateRoot,
|
|
1486
|
+
inspectOpenSpec: inspectOpenSpecDelivery,
|
|
1487
|
+
});
|
|
1178
1488
|
const implementationAgent = loadAgentDefinitions().find(
|
|
1179
1489
|
(agent) => agent.role === "implementation-worker",
|
|
1180
1490
|
);
|
|
@@ -1184,7 +1494,10 @@ export function openPackageWorkflowControlEngine(
|
|
|
1184
1494
|
const engine = openDurableWorkflowEngine({
|
|
1185
1495
|
consumerRoot,
|
|
1186
1496
|
stateRoot,
|
|
1187
|
-
deliverySource: packageDeliverySource(consumerRoot
|
|
1497
|
+
deliverySource: packageDeliverySource(consumerRoot, {
|
|
1498
|
+
verifyGateProof: (input) => design.verifyGateProof(input),
|
|
1499
|
+
verifyFinalizedDelivery: (input) => design.verifyFinalizedDelivery(input),
|
|
1500
|
+
}),
|
|
1188
1501
|
routePolicy: routeResolution.ok
|
|
1189
1502
|
? routeResolution.policy
|
|
1190
1503
|
: unavailableRoutePolicy(),
|
|
@@ -1357,6 +1670,7 @@ export function openPackageWorkflowControlEngine(
|
|
|
1357
1670
|
const operationRouteResolution = loadRoutePolicy({
|
|
1358
1671
|
cwd: consumerRoot,
|
|
1359
1672
|
home: homedir(),
|
|
1673
|
+
...(context.model ? { parentModel: context.model } : {}),
|
|
1360
1674
|
});
|
|
1361
1675
|
engine.updateRoutePolicy(
|
|
1362
1676
|
operationRouteResolution.ok
|
|
@@ -1382,9 +1696,19 @@ export function openPackageWorkflowControlEngine(
|
|
|
1382
1696
|
return { ...outcome, routePolicy };
|
|
1383
1697
|
});
|
|
1384
1698
|
},
|
|
1699
|
+
executeDesign(request: unknown) {
|
|
1700
|
+
return design.execute(request);
|
|
1701
|
+
},
|
|
1702
|
+
assertDesignRun(runId: string) {
|
|
1703
|
+
design.assertDesignRun(runId);
|
|
1704
|
+
},
|
|
1705
|
+
recordDesignEvidence(input) {
|
|
1706
|
+
return design.recordEvidence(input);
|
|
1707
|
+
},
|
|
1385
1708
|
async close() {
|
|
1386
1709
|
contexts.disable();
|
|
1387
1710
|
await engine.close();
|
|
1711
|
+
design.close();
|
|
1388
1712
|
},
|
|
1389
1713
|
};
|
|
1390
1714
|
}
|
|
@@ -1410,6 +1734,7 @@ export function registerWorkflowControl(
|
|
|
1410
1734
|
const engines = new Map<string, Promise<WorkflowControlEngine>>();
|
|
1411
1735
|
let pendingPrompt: EligiblePrompt | undefined;
|
|
1412
1736
|
let activePrompt: EligiblePrompt | undefined;
|
|
1737
|
+
let designToolSnapshot: string[] | undefined;
|
|
1413
1738
|
const activation = new (class implements Activation {
|
|
1414
1739
|
state: Activation["state"] = "inactive";
|
|
1415
1740
|
|
|
@@ -1460,222 +1785,322 @@ export function registerWorkflowControl(
|
|
|
1460
1785
|
),
|
|
1461
1786
|
);
|
|
1462
1787
|
};
|
|
1788
|
+
const restoreDesignTools = () => {
|
|
1789
|
+
if (!designToolSnapshot) return;
|
|
1790
|
+
const snapshot = designToolSnapshot;
|
|
1791
|
+
designToolSnapshot = undefined;
|
|
1792
|
+
pi.setActiveTools([...snapshot]);
|
|
1793
|
+
};
|
|
1794
|
+
const enforceDesignTools = () => {
|
|
1795
|
+
if (!designToolSnapshot) {
|
|
1796
|
+
designToolSnapshot = pi
|
|
1797
|
+
.getActiveTools()
|
|
1798
|
+
.filter((name) => name !== DISPATCH_TOOL);
|
|
1799
|
+
}
|
|
1800
|
+
pi.setActiveTools(
|
|
1801
|
+
activateTool(
|
|
1802
|
+
designToolSnapshot.filter((name) => DESIGN_PARENT_READ_TOOLS.has(name)),
|
|
1803
|
+
DISPATCH_TOOL,
|
|
1804
|
+
),
|
|
1805
|
+
);
|
|
1806
|
+
};
|
|
1463
1807
|
const deactivate = () => {
|
|
1464
1808
|
activation.drain();
|
|
1809
|
+
if (designToolSnapshot) {
|
|
1810
|
+
restoreDesignTools();
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1465
1813
|
const active = pi.getActiveTools();
|
|
1466
1814
|
if (active.includes(DISPATCH_TOOL)) {
|
|
1467
1815
|
pi.setActiveTools(deactivateTool(active, DISPATCH_TOOL));
|
|
1468
1816
|
}
|
|
1469
1817
|
};
|
|
1818
|
+
const deactivateStage = async () => {
|
|
1819
|
+
activePrompt = undefined;
|
|
1820
|
+
await packetRuntime.drain();
|
|
1821
|
+
deactivate();
|
|
1822
|
+
parentPayloadBridge.clear();
|
|
1823
|
+
};
|
|
1470
1824
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
properties: {
|
|
1480
|
-
version: { type: "integer", enum: [2] },
|
|
1481
|
-
command: {
|
|
1482
|
-
type: "string",
|
|
1483
|
-
enum: ["start", "status", "resume", "rebind", "cancel", "discard"],
|
|
1484
|
-
},
|
|
1485
|
-
stage: {
|
|
1486
|
-
type: "string",
|
|
1487
|
-
enum: ["abel-design", "abel-implement"],
|
|
1488
|
-
},
|
|
1489
|
-
change: { type: "string" },
|
|
1490
|
-
provisionalKey: { type: "string" },
|
|
1491
|
-
operationId: { type: "string" },
|
|
1492
|
-
deliveryRevision: { type: "integer", minimum: 1 },
|
|
1493
|
-
receiptHash: { type: "string" },
|
|
1494
|
-
routeId: { type: "string" },
|
|
1495
|
-
action: { type: "string", enum: [...PACKET_ACTIONS] },
|
|
1496
|
-
request: {
|
|
1497
|
-
anyOf: [DESIGN_REQUEST_SCHEMA, GENERIC_REQUEST_SCHEMA],
|
|
1498
|
-
},
|
|
1825
|
+
// Pi validates tool arguments before execute(), so expose only the schema
|
|
1826
|
+
// legal for the active stage instead of one ambiguous command/packet union.
|
|
1827
|
+
const CONTROL_COMMAND_PARAMETERS = {
|
|
1828
|
+
type: "object",
|
|
1829
|
+
properties: {
|
|
1830
|
+
command: {
|
|
1831
|
+
type: "string",
|
|
1832
|
+
enum: ["start", "status", "resume", "rebind", "cancel", "discard"],
|
|
1499
1833
|
},
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
required: ["action"],
|
|
1510
|
-
not: {
|
|
1511
|
-
anyOf: [
|
|
1512
|
-
{ required: ["version"] },
|
|
1513
|
-
{ required: ["command"] },
|
|
1514
|
-
{ required: ["stage"] },
|
|
1515
|
-
{ required: ["change"] },
|
|
1516
|
-
{ required: ["provisionalKey"] },
|
|
1517
|
-
{ required: ["operationId"] },
|
|
1518
|
-
{ required: ["deliveryRevision"] },
|
|
1519
|
-
{ required: ["receiptHash"] },
|
|
1520
|
-
{ required: ["routeId"] },
|
|
1521
|
-
],
|
|
1522
|
-
},
|
|
1523
|
-
},
|
|
1524
|
-
],
|
|
1834
|
+
stage: {
|
|
1835
|
+
type: "string",
|
|
1836
|
+
enum: ["abel-implement"],
|
|
1837
|
+
},
|
|
1838
|
+
change: { type: "string" },
|
|
1839
|
+
operationId: { type: "string" },
|
|
1840
|
+
deliveryRevision: { type: "integer", minimum: 1 },
|
|
1841
|
+
receiptHash: { type: "string" },
|
|
1842
|
+
routeId: { type: "string" },
|
|
1525
1843
|
},
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1844
|
+
required: ["command", "stage"],
|
|
1845
|
+
additionalProperties: false,
|
|
1846
|
+
} as const;
|
|
1847
|
+
const PACKET_PARAMETERS = {
|
|
1848
|
+
type: "object",
|
|
1849
|
+
properties: {
|
|
1850
|
+
action: { type: "string", enum: [...PACKET_ACTIONS, "design"] },
|
|
1851
|
+
request: {
|
|
1852
|
+
anyOf: [
|
|
1853
|
+
DESIGN_REQUEST_SCHEMA,
|
|
1854
|
+
DESIGN_CONTROL_REQUEST_SCHEMA,
|
|
1855
|
+
GENERIC_REQUEST_SCHEMA,
|
|
1856
|
+
],
|
|
1857
|
+
},
|
|
1858
|
+
},
|
|
1859
|
+
required: ["action"],
|
|
1860
|
+
additionalProperties: false,
|
|
1861
|
+
} as const;
|
|
1862
|
+
let registeredParameterKind: "command" | "packet" | undefined;
|
|
1863
|
+
|
|
1864
|
+
const registerDispatchTool = (kind: "command" | "packet") => {
|
|
1865
|
+
if (registeredParameterKind === kind) return;
|
|
1866
|
+
registeredParameterKind = kind;
|
|
1867
|
+
pi.registerTool({
|
|
1868
|
+
name: DISPATCH_TOOL,
|
|
1869
|
+
label: "Abel Control",
|
|
1870
|
+
description:
|
|
1871
|
+
kind === "command"
|
|
1872
|
+
? "Private stage-bound Abel workflow control. Accepts durable Implement change commands."
|
|
1873
|
+
: "Private stage-bound Abel packet control. Accepts bounded Design and Diagnose packet operations.",
|
|
1874
|
+
executionMode: "parallel",
|
|
1875
|
+
parameters:
|
|
1876
|
+
kind === "command" ? CONTROL_COMMAND_PARAMETERS : PACKET_PARAMETERS,
|
|
1877
|
+
async execute(
|
|
1878
|
+
toolCallId: string,
|
|
1879
|
+
params: unknown,
|
|
1880
|
+
signal: AbortSignal | undefined,
|
|
1881
|
+
onUpdate: AgentToolUpdateCallback<unknown> | undefined,
|
|
1882
|
+
ctx: ExtensionContext,
|
|
1883
|
+
) {
|
|
1884
|
+
const record =
|
|
1885
|
+
params && typeof params === "object" && !Array.isArray(params)
|
|
1886
|
+
? (params as Record<string, unknown>)
|
|
1887
|
+
: undefined;
|
|
1888
|
+
if (record && typeof record.action === "string") {
|
|
1889
|
+
if (
|
|
1890
|
+
Object.keys(record).some(
|
|
1891
|
+
(key) => key !== "action" && key !== "request",
|
|
1892
|
+
)
|
|
1893
|
+
) {
|
|
1894
|
+
throw new Error("control-envelope-ambiguous");
|
|
1895
|
+
}
|
|
1896
|
+
if (
|
|
1897
|
+
activePrompt !== "abel-design" &&
|
|
1898
|
+
activePrompt !== "abel-diagnose"
|
|
1899
|
+
) {
|
|
1900
|
+
throw new Error("stage-control-mismatch");
|
|
1901
|
+
}
|
|
1902
|
+
if (record.action === "design") {
|
|
1903
|
+
if (activePrompt !== "abel-design") {
|
|
1904
|
+
throw new Error("stage-control-mismatch");
|
|
1905
|
+
}
|
|
1906
|
+
const designRequest = validateDesignControlRequest(record.request);
|
|
1907
|
+
if (!designRequest.ok) throw new Error(designRequest.code);
|
|
1908
|
+
const engine = await engineFor(ctx);
|
|
1909
|
+
if (!engine.executeDesign)
|
|
1910
|
+
throw new Error("design-control-unavailable");
|
|
1911
|
+
const payload = await engine.executeDesign(designRequest.value);
|
|
1912
|
+
if (
|
|
1913
|
+
designRequest.value.operation === "finalize-delivery" &&
|
|
1914
|
+
payload.state === "completed"
|
|
1915
|
+
) {
|
|
1916
|
+
await deactivateStage();
|
|
1917
|
+
}
|
|
1918
|
+
return {
|
|
1919
|
+
content: [
|
|
1920
|
+
{ type: "text" as const, text: JSON.stringify(payload) },
|
|
1921
|
+
],
|
|
1922
|
+
details: payload,
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
let designEngine: WorkflowControlEngine | undefined;
|
|
1926
|
+
if (record.action === "run") {
|
|
1927
|
+
const packet = packetRuntime.validateRequest(record.request);
|
|
1928
|
+
if (!packet.ok || packet.value.stage !== activePrompt) {
|
|
1929
|
+
throw new Error(
|
|
1930
|
+
packet.ok ? "stage-control-mismatch" : packet.reason,
|
|
1931
|
+
);
|
|
1932
|
+
}
|
|
1933
|
+
if (packet.value.stage === "abel-design") {
|
|
1934
|
+
designEngine = await engineFor(ctx);
|
|
1935
|
+
if (!designEngine.assertDesignRun || !packet.value.runId) {
|
|
1936
|
+
throw new Error("design-control-unavailable");
|
|
1937
|
+
}
|
|
1938
|
+
designEngine.assertDesignRun(packet.value.runId);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
const operation = {
|
|
1942
|
+
request: record.request,
|
|
1943
|
+
};
|
|
1944
|
+
const tuiRun = ctx.mode === "tui" && record.action === "run";
|
|
1945
|
+
const packetPayload = tuiRun
|
|
1946
|
+
? await packetRuntime.execute(
|
|
1947
|
+
record.action,
|
|
1948
|
+
operation,
|
|
1949
|
+
ctx,
|
|
1950
|
+
signal,
|
|
1951
|
+
activity.observe(
|
|
1952
|
+
toolCallId,
|
|
1953
|
+
onUpdate as ((result: unknown) => void) | undefined,
|
|
1954
|
+
),
|
|
1955
|
+
)
|
|
1956
|
+
: await packetRuntime.execute(
|
|
1957
|
+
record.action,
|
|
1958
|
+
operation,
|
|
1959
|
+
ctx,
|
|
1960
|
+
signal,
|
|
1961
|
+
);
|
|
1962
|
+
let payload:
|
|
1963
|
+
| typeof packetPayload
|
|
1964
|
+
| (typeof packetPayload & {
|
|
1965
|
+
recordedEvidence: unknown;
|
|
1966
|
+
}) = packetPayload;
|
|
1967
|
+
if (
|
|
1968
|
+
packetPayload.ok &&
|
|
1969
|
+
record.action === "run" &&
|
|
1970
|
+
activePrompt === "abel-design"
|
|
1971
|
+
) {
|
|
1972
|
+
const packet = packetRuntime.validateRequest(record.request);
|
|
1973
|
+
if (
|
|
1974
|
+
!packet.ok ||
|
|
1975
|
+
!packet.value.runId ||
|
|
1976
|
+
!designEngine?.recordDesignEvidence
|
|
1977
|
+
) {
|
|
1978
|
+
throw new Error("design-control-unavailable");
|
|
1979
|
+
}
|
|
1980
|
+
const recordedEvidence = designEngine.recordDesignEvidence({
|
|
1981
|
+
runId: packet.value.runId,
|
|
1982
|
+
evidence: packetPayload.result as DesignEvidenceResult,
|
|
1983
|
+
});
|
|
1984
|
+
payload = { ...packetPayload, recordedEvidence };
|
|
1985
|
+
}
|
|
1986
|
+
if (record.action === "finish" && packetPayload.ok) {
|
|
1987
|
+
await deactivateStage();
|
|
1988
|
+
}
|
|
1989
|
+
const display = tuiRun
|
|
1990
|
+
? activity.finalize(toolCallId, payload)
|
|
1991
|
+
: undefined;
|
|
1992
|
+
const { payload: publicPayload, usage } = splitUsage(payload);
|
|
1993
|
+
const details = display
|
|
1994
|
+
? {
|
|
1995
|
+
...(publicPayload as Record<string, unknown>),
|
|
1996
|
+
[ACTIVITY_DETAILS_KEY]: display,
|
|
1997
|
+
}
|
|
1998
|
+
: publicPayload;
|
|
1999
|
+
return {
|
|
2000
|
+
content: [
|
|
2001
|
+
{ type: "text" as const, text: JSON.stringify(publicPayload) },
|
|
2002
|
+
],
|
|
2003
|
+
details,
|
|
2004
|
+
...(usage === undefined ? {} : { usage }),
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
const validation = validateControlCommand(params);
|
|
2008
|
+
if (!validation.ok) {
|
|
2009
|
+
const error = new Error(validation.code);
|
|
2010
|
+
error.name = "ControlCommandError";
|
|
2011
|
+
throw error;
|
|
1544
2012
|
}
|
|
1545
2013
|
if (
|
|
1546
|
-
activePrompt !==
|
|
1547
|
-
activePrompt
|
|
2014
|
+
activePrompt !== undefined &&
|
|
2015
|
+
(activePrompt === "abel-diagnose" ||
|
|
2016
|
+
validation.value.stage !== activePrompt)
|
|
1548
2017
|
) {
|
|
1549
2018
|
throw new Error("stage-control-mismatch");
|
|
1550
2019
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
2020
|
+
const engine = await engineFor(ctx);
|
|
2021
|
+
const tuiControl = ctx.mode === "tui";
|
|
2022
|
+
if (tuiControl) {
|
|
2023
|
+
try {
|
|
2024
|
+
activity.beginWorkflow(
|
|
2025
|
+
toolCallId,
|
|
2026
|
+
validation.value,
|
|
2027
|
+
onUpdate as ((result: unknown) => void) | undefined,
|
|
1556
2028
|
);
|
|
2029
|
+
} catch {
|
|
2030
|
+
// Presentation state must never alter workflow execution.
|
|
1557
2031
|
}
|
|
1558
2032
|
}
|
|
1559
|
-
|
|
1560
|
-
request: record.request,
|
|
1561
|
-
};
|
|
1562
|
-
const tuiRun = ctx.mode === "tui" && record.action === "run";
|
|
1563
|
-
const payload = tuiRun
|
|
1564
|
-
? await packetRuntime.execute(
|
|
1565
|
-
record.action,
|
|
1566
|
-
operation,
|
|
1567
|
-
ctx,
|
|
1568
|
-
signal,
|
|
1569
|
-
activity.observe(
|
|
1570
|
-
toolCallId,
|
|
1571
|
-
onUpdate as ((result: unknown) => void) | undefined,
|
|
1572
|
-
),
|
|
1573
|
-
)
|
|
1574
|
-
: await packetRuntime.execute(record.action, operation, ctx, signal);
|
|
1575
|
-
const display = tuiRun
|
|
1576
|
-
? activity.finalize(toolCallId, payload)
|
|
1577
|
-
: undefined;
|
|
1578
|
-
const { payload: publicPayload, usage } = splitUsage(payload);
|
|
1579
|
-
const details = display
|
|
1580
|
-
? {
|
|
1581
|
-
...(publicPayload as Record<string, unknown>),
|
|
1582
|
-
[ACTIVITY_DETAILS_KEY]: display,
|
|
1583
|
-
}
|
|
1584
|
-
: publicPayload;
|
|
1585
|
-
return {
|
|
1586
|
-
content: [
|
|
1587
|
-
{ type: "text" as const, text: JSON.stringify(publicPayload) },
|
|
1588
|
-
],
|
|
1589
|
-
details,
|
|
1590
|
-
...(usage === undefined ? {} : { usage }),
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
|
-
const validation = validateControlCommand(params);
|
|
1594
|
-
if (!validation.ok) {
|
|
1595
|
-
const error = new Error(validation.code);
|
|
1596
|
-
error.name = "ControlCommandError";
|
|
1597
|
-
throw error;
|
|
1598
|
-
}
|
|
1599
|
-
if (
|
|
1600
|
-
activePrompt !== undefined &&
|
|
1601
|
-
(activePrompt === "abel-diagnose" ||
|
|
1602
|
-
validation.value.stage !== activePrompt)
|
|
1603
|
-
) {
|
|
1604
|
-
throw new Error("stage-control-mismatch");
|
|
1605
|
-
}
|
|
1606
|
-
const engine = await engineFor(ctx);
|
|
1607
|
-
const tuiControl = ctx.mode === "tui";
|
|
1608
|
-
if (tuiControl) {
|
|
2033
|
+
let payload: Record<string, unknown>;
|
|
1609
2034
|
try {
|
|
1610
|
-
|
|
1611
|
-
toolCallId,
|
|
2035
|
+
payload = await engine.execute(
|
|
1612
2036
|
validation.value,
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
? (event) => {
|
|
1627
|
-
try {
|
|
1628
|
-
activity.updateWorkflow(toolCallId, validation.value, event);
|
|
1629
|
-
} catch {
|
|
1630
|
-
// Presentation state must never alter workflow execution.
|
|
2037
|
+
ctx,
|
|
2038
|
+
signal,
|
|
2039
|
+
tuiControl
|
|
2040
|
+
? (event) => {
|
|
2041
|
+
try {
|
|
2042
|
+
activity.updateWorkflow(
|
|
2043
|
+
toolCallId,
|
|
2044
|
+
validation.value,
|
|
2045
|
+
event,
|
|
2046
|
+
);
|
|
2047
|
+
} catch {
|
|
2048
|
+
// Presentation state must never alter workflow execution.
|
|
2049
|
+
}
|
|
1631
2050
|
}
|
|
1632
|
-
|
|
1633
|
-
: undefined,
|
|
1634
|
-
);
|
|
1635
|
-
} catch (error) {
|
|
1636
|
-
if (tuiControl) activity.failWorkflow(toolCallId);
|
|
1637
|
-
throw error;
|
|
1638
|
-
}
|
|
1639
|
-
let display: ReturnType<typeof activity.finalizeWorkflow>;
|
|
1640
|
-
if (tuiControl) {
|
|
1641
|
-
try {
|
|
1642
|
-
display = activity.finalizeWorkflow(
|
|
1643
|
-
toolCallId,
|
|
1644
|
-
validation.value,
|
|
1645
|
-
payload,
|
|
2051
|
+
: undefined,
|
|
1646
2052
|
);
|
|
1647
|
-
} catch {
|
|
1648
|
-
activity.failWorkflow(toolCallId);
|
|
2053
|
+
} catch (error) {
|
|
2054
|
+
if (tuiControl) activity.failWorkflow(toolCallId);
|
|
2055
|
+
throw error;
|
|
1649
2056
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
2057
|
+
if (
|
|
2058
|
+
["completed", "discarded", "rejected"].includes(String(payload.state))
|
|
2059
|
+
) {
|
|
2060
|
+
await deactivateStage();
|
|
2061
|
+
}
|
|
2062
|
+
let display: ReturnType<typeof activity.finalizeWorkflow>;
|
|
2063
|
+
if (tuiControl) {
|
|
2064
|
+
try {
|
|
2065
|
+
display = activity.finalizeWorkflow(
|
|
2066
|
+
toolCallId,
|
|
2067
|
+
validation.value,
|
|
2068
|
+
payload,
|
|
2069
|
+
);
|
|
2070
|
+
} catch {
|
|
2071
|
+
activity.failWorkflow(toolCallId);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
return {
|
|
2075
|
+
content: [{ type: "text" as const, text: JSON.stringify(payload) }],
|
|
2076
|
+
details: display
|
|
2077
|
+
? { ...payload, [ACTIVITY_DETAILS_KEY]: display }
|
|
2078
|
+
: payload,
|
|
2079
|
+
};
|
|
2080
|
+
},
|
|
2081
|
+
renderCall(args: unknown, theme: Theme, context: unknown) {
|
|
2082
|
+
return renderActivityCall(
|
|
2083
|
+
args,
|
|
2084
|
+
theme,
|
|
2085
|
+
context as Parameters<typeof renderActivityCall>[2],
|
|
2086
|
+
);
|
|
2087
|
+
},
|
|
2088
|
+
renderResult(
|
|
2089
|
+
result: AgentToolResult<unknown>,
|
|
2090
|
+
options: ToolRenderResultOptions,
|
|
2091
|
+
theme: Theme,
|
|
2092
|
+
context: unknown,
|
|
2093
|
+
) {
|
|
2094
|
+
return renderActivityResult(
|
|
2095
|
+
result,
|
|
2096
|
+
options,
|
|
2097
|
+
theme,
|
|
2098
|
+
context as Parameters<typeof renderActivityResult>[3],
|
|
2099
|
+
);
|
|
2100
|
+
},
|
|
2101
|
+
} as never);
|
|
2102
|
+
};
|
|
2103
|
+
registerDispatchTool("packet");
|
|
1679
2104
|
|
|
1680
2105
|
pi.on("input", (event) => {
|
|
1681
2106
|
pendingPrompt = invokedPrompt(event.text);
|
|
@@ -1687,6 +2112,10 @@ export function registerWorkflowControl(
|
|
|
1687
2112
|
const verified =
|
|
1688
2113
|
prompt && isVerifiedStageInvocation(pi, activation, prompt, event.prompt);
|
|
1689
2114
|
if (verified) {
|
|
2115
|
+
if (activePrompt === "abel-design" && prompt !== "abel-design") {
|
|
2116
|
+
restoreDesignTools();
|
|
2117
|
+
}
|
|
2118
|
+
registerDispatchTool(prompt === "abel-implement" ? "command" : "packet");
|
|
1690
2119
|
activePrompt = prompt;
|
|
1691
2120
|
const sessionId = ctx.sessionManager?.getSessionId?.();
|
|
1692
2121
|
if (typeof sessionId === "string") {
|
|
@@ -1699,6 +2128,7 @@ export function registerWorkflowControl(
|
|
|
1699
2128
|
parentPayloadBridge.install(ctx.model, ctx.modelRegistry);
|
|
1700
2129
|
}
|
|
1701
2130
|
if (prompt) activateDispatcher(pi, activation, prompt, event.prompt);
|
|
2131
|
+
if (verified && prompt === "abel-design") enforceDesignTools();
|
|
1702
2132
|
});
|
|
1703
2133
|
pi.on("model_select", (event, ctx) => {
|
|
1704
2134
|
const sessionId = ctx.sessionManager?.getSessionId?.();
|