@selesai/code 0.3.8 → 0.3.9
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.
|
@@ -175,6 +175,8 @@ interface WorkflowController {
|
|
|
175
175
|
// Ceiling: not persisted across session restart — a restart re-runs the
|
|
176
176
|
// loop from round 0. Acceptable for a prototype workflow.
|
|
177
177
|
loopState?: LoopState;
|
|
178
|
+
seenToolCallIds: Set<string>;
|
|
179
|
+
lastBlockedKey?: string;
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
// ponytail: Plan 3 — loop orchestration state. The parent model does not
|
|
@@ -338,6 +340,30 @@ function applyEffect(
|
|
|
338
340
|
}
|
|
339
341
|
}
|
|
340
342
|
|
|
343
|
+
function blockedKeyFor(eff: WorkflowEffect): string | undefined {
|
|
344
|
+
if (eff.kind !== "blocked") return undefined;
|
|
345
|
+
return `${eff.phase}:${eff.missing}:${eff.reason ?? ""}`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function applyControllerEffect(
|
|
349
|
+
controller: WorkflowController,
|
|
350
|
+
ctx: ExtensionContext,
|
|
351
|
+
eff: WorkflowEffect,
|
|
352
|
+
): void {
|
|
353
|
+
const blockedKey = blockedKeyFor(eff);
|
|
354
|
+
if (blockedKey) {
|
|
355
|
+
if (controller.lastBlockedKey === blockedKey) return;
|
|
356
|
+
controller.lastBlockedKey = blockedKey;
|
|
357
|
+
} else if (eff.kind !== "noOp") {
|
|
358
|
+
controller.lastBlockedKey = undefined;
|
|
359
|
+
}
|
|
360
|
+
applyEffect(controller.pi, ctx, controller.config, eff);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function isSubagentManagementAction(input: Record<string, unknown> | undefined): boolean {
|
|
364
|
+
return typeof input?.action === "string";
|
|
365
|
+
}
|
|
366
|
+
|
|
341
367
|
function registerSharedArtifactWriter(pi: ExtensionAPI): void {
|
|
342
368
|
if (registry.writerRegisteredFor.has(pi)) return;
|
|
343
369
|
registry.writerRegisteredFor.add(pi);
|
|
@@ -374,7 +400,7 @@ function registerSharedArtifactWriter(pi: ExtensionAPI): void {
|
|
|
374
400
|
await mkdir(snap.artifactDir, { recursive: true });
|
|
375
401
|
await writeFile(path, params.content, "utf8");
|
|
376
402
|
const eff = await controller.sm.onArtifactMaybe(controller.deps);
|
|
377
|
-
|
|
403
|
+
applyControllerEffect(controller, ctx, eff);
|
|
378
404
|
if (eff.kind === "blocked" && eff.reason) {
|
|
379
405
|
return {
|
|
380
406
|
content: [{ type: "text", text: `Wrote ${path}, but it is not approved: ${eff.reason}. Re-write it via write_workflow_artifact to add the required marker.` }],
|
|
@@ -410,7 +436,7 @@ export function createWorkflowExtension(
|
|
|
410
436
|
},
|
|
411
437
|
];
|
|
412
438
|
const sm = new WorkflowStateMachine({ ...config, skipRules });
|
|
413
|
-
const controller: WorkflowController = { pi, config, sm, deps };
|
|
439
|
+
const controller: WorkflowController = { pi, config, sm, deps, seenToolCallIds: new Set() };
|
|
414
440
|
registry.controllers.push(controller);
|
|
415
441
|
registerSharedArtifactWriter(pi);
|
|
416
442
|
const { start, end } = options.toolNames;
|
|
@@ -440,7 +466,11 @@ export function createWorkflowExtension(
|
|
|
440
466
|
};
|
|
441
467
|
}
|
|
442
468
|
const eff = await sm.start(params.goal, deps);
|
|
443
|
-
|
|
469
|
+
if (eff.kind === "started") {
|
|
470
|
+
controller.seenToolCallIds.clear();
|
|
471
|
+
controller.lastBlockedKey = undefined;
|
|
472
|
+
}
|
|
473
|
+
applyControllerEffect(controller, ctx, eff);
|
|
444
474
|
if (eff.kind === "alreadyActive") {
|
|
445
475
|
return {
|
|
446
476
|
content: [
|
|
@@ -480,7 +510,11 @@ export function createWorkflowExtension(
|
|
|
480
510
|
parameters: Type.Object({}),
|
|
481
511
|
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
482
512
|
const eff = await sm.end(deps);
|
|
483
|
-
|
|
513
|
+
applyControllerEffect(controller, ctx, eff);
|
|
514
|
+
if (eff.kind === "closed") {
|
|
515
|
+
controller.seenToolCallIds.clear();
|
|
516
|
+
controller.lastBlockedKey = undefined;
|
|
517
|
+
}
|
|
484
518
|
switch (eff.kind) {
|
|
485
519
|
case "closed":
|
|
486
520
|
return {
|
|
@@ -575,7 +609,7 @@ export function createWorkflowExtension(
|
|
|
575
609
|
if (tool !== "subagent" && tool !== "write" && tool !== "edit") return;
|
|
576
610
|
const snap = sm.snapshot;
|
|
577
611
|
if (!snap.active) return;
|
|
578
|
-
if (tool === "subagent" &&
|
|
612
|
+
if (tool === "subagent" && isSubagentManagementAction(event.input)) return;
|
|
579
613
|
const file = config.phaseArtifacts[snap.phase];
|
|
580
614
|
if (tool === "write" || tool === "edit") {
|
|
581
615
|
return {
|
|
@@ -615,13 +649,18 @@ export function createWorkflowExtension(
|
|
|
615
649
|
// ── tool_result: auto-advance after subagent/bash artifacts. ──
|
|
616
650
|
pi.on("tool_result", async (event: any, ctx: ExtensionContext) => {
|
|
617
651
|
if (event.toolName !== "bash" && event.toolName !== "subagent") return;
|
|
652
|
+
if (sm.snapshot.active && typeof event.toolCallId === "string") {
|
|
653
|
+
if (controller.seenToolCallIds.has(event.toolCallId)) return;
|
|
654
|
+
controller.seenToolCallIds.add(event.toolCallId);
|
|
655
|
+
}
|
|
618
656
|
// ponytail: a failed phase-owner tool used to leave the workflow quiet
|
|
619
657
|
// until the user manually typed /prototype or /quick continue. Re-check
|
|
620
658
|
// first (in case the tool wrote the artifact before failing), then if
|
|
621
659
|
// nothing advanced, re-queue the current phase prompt automatically.
|
|
622
660
|
if (event.isError && sm.snapshot.active) {
|
|
661
|
+
if (event.toolName === "subagent" && isSubagentManagementAction(event.input)) return;
|
|
623
662
|
const eff = await sm.onArtifactMaybe(deps);
|
|
624
|
-
|
|
663
|
+
applyControllerEffect(controller, ctx, eff);
|
|
625
664
|
if (eff.kind === "noOp") {
|
|
626
665
|
const retry = sm.continueCurrent();
|
|
627
666
|
continueAgent(
|
|
@@ -648,12 +687,15 @@ export function createWorkflowExtension(
|
|
|
648
687
|
// on agent-listing output (the `subagent list` result is plain
|
|
649
688
|
// text). Execution calls set `agent`/`chain`/`tasks`; management
|
|
650
689
|
// calls set `action`. Only fall back for execution calls.
|
|
651
|
-
if (
|
|
690
|
+
if (!isSubagentManagementAction(event.input)) {
|
|
652
691
|
const expectedPath = resolve(sm.snapshot.artifactDir, file);
|
|
653
692
|
const text = textFromToolResultContent(event.content);
|
|
654
693
|
if (text) {
|
|
655
|
-
|
|
656
|
-
|
|
694
|
+
const validator = validatorForPhaseArtifact(config, phase);
|
|
695
|
+
let shouldWrite = false;
|
|
696
|
+
if (!(await realFileExists(expectedPath))) {
|
|
697
|
+
shouldWrite = !validator || validator(text).ok;
|
|
698
|
+
} else {
|
|
657
699
|
try {
|
|
658
700
|
const current = await readFile(expectedPath, "utf8");
|
|
659
701
|
shouldWrite = shouldReplaceInvalidArtifact(config, phase, current, text);
|
|
@@ -726,7 +768,7 @@ or
|
|
|
726
768
|
// onArtifactMaybe (a no-op unless loop-complete.md exists).
|
|
727
769
|
}
|
|
728
770
|
const eff = await sm.onArtifactMaybe(deps);
|
|
729
|
-
|
|
771
|
+
applyControllerEffect(controller, ctx, eff);
|
|
730
772
|
// Clear loop state once we have left the loop phase (advanced to audit).
|
|
731
773
|
if (sm.snapshot.phase !== "loop") {
|
|
732
774
|
controller.loopState = undefined;
|
|
@@ -759,7 +801,7 @@ or
|
|
|
759
801
|
if (choice === undefined) return;
|
|
760
802
|
if (choice.startsWith("Continue")) {
|
|
761
803
|
const eff = sm.continueCurrent();
|
|
762
|
-
|
|
804
|
+
applyControllerEffect(controller, ctx, eff);
|
|
763
805
|
if (eff.kind === "advanced" && eff.prompt) {
|
|
764
806
|
// continueCurrent already applied footer+entry; send the prompt.
|
|
765
807
|
continueAgent(pi, ctx, eff.prompt);
|
|
@@ -796,7 +838,11 @@ or
|
|
|
796
838
|
return;
|
|
797
839
|
}
|
|
798
840
|
const eff = await sm.start(args, deps);
|
|
799
|
-
|
|
841
|
+
if (eff.kind === "started") {
|
|
842
|
+
controller.seenToolCallIds.clear();
|
|
843
|
+
controller.lastBlockedKey = undefined;
|
|
844
|
+
}
|
|
845
|
+
applyControllerEffect(controller, ctx, eff);
|
|
800
846
|
},
|
|
801
847
|
});
|
|
802
848
|
};
|