@quintinshaw/pi-dynamic-workflows 1.9.2 → 1.9.3
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/dist/workflow-editor.d.ts +2 -0
- package/dist/workflow-editor.js +41 -4
- package/package.json +1 -1
- package/src/workflow-editor.ts +40 -4
|
@@ -70,5 +70,7 @@ export declare function buildForcedWorkflowPrompt(text: string): string;
|
|
|
70
70
|
* Install the workflows-mode editor and the submit-time forcing hook.
|
|
71
71
|
* Call once with the UI context (e.g. in `session_start`).
|
|
72
72
|
*/
|
|
73
|
+
/** The exact name of the workflow tool that workflows mode forces. */
|
|
74
|
+
export declare const WORKFLOW_TOOL_NAME = "workflow";
|
|
73
75
|
export declare function installWorkflowEditor(pi: ExtensionAPI, ui: ExtensionUIContext): WorkflowModeState;
|
|
74
76
|
export {};
|
package/dist/workflow-editor.js
CHANGED
|
@@ -205,24 +205,61 @@ export function buildForcedWorkflowPrompt(text) {
|
|
|
205
205
|
return [
|
|
206
206
|
text,
|
|
207
207
|
"",
|
|
208
|
-
"
|
|
209
|
-
"
|
|
210
|
-
"
|
|
208
|
+
"---",
|
|
209
|
+
"[workflows mode is ON for this message]",
|
|
210
|
+
"You MUST handle this request by calling the tool named exactly `workflow` (Pi's",
|
|
211
|
+
"deterministic JavaScript workflow-orchestration tool from pi-dynamic-workflows).",
|
|
212
|
+
"Write a workflow script that fans the task out across subagents via",
|
|
213
|
+
"agent()/parallel()/pipeline().",
|
|
214
|
+
"",
|
|
215
|
+
"The ONLY acceptable action is a `workflow` tool call. Do NOT instead:",
|
|
216
|
+
"- answer directly or in prose,",
|
|
217
|
+
"- call the `subagent` tool yourself,",
|
|
218
|
+
"- use any skill or command (e.g. pi-subagents, /code-review, deep-research),",
|
|
219
|
+
'- or interpret the word "workflow/workflows" loosely as some other parallel/audit approach.',
|
|
220
|
+
"Even for a small task, wrap it in a minimal `workflow` call with at least one agent().",
|
|
211
221
|
].join("\n");
|
|
212
222
|
}
|
|
213
223
|
/**
|
|
214
224
|
* Install the workflows-mode editor and the submit-time forcing hook.
|
|
215
225
|
* Call once with the UI context (e.g. in `session_start`).
|
|
216
226
|
*/
|
|
227
|
+
/** The exact name of the workflow tool that workflows mode forces. */
|
|
228
|
+
export const WORKFLOW_TOOL_NAME = "workflow";
|
|
217
229
|
export function installWorkflowEditor(pi, ui) {
|
|
218
230
|
const state = { active: false };
|
|
219
231
|
ui.setEditorComponent((tui, theme, keybindings) => new WorkflowEditor(tui, theme, keybindings, state));
|
|
220
|
-
//
|
|
232
|
+
// Active tools saved while a turn is restricted to `workflow`; restored on turn_end.
|
|
233
|
+
let savedTools;
|
|
234
|
+
// When armed at submit time, rewrite the user's message to force a workflow AND
|
|
235
|
+
// restrict this turn's tools to just `workflow`, so the model can't fall back to
|
|
236
|
+
// the subagent tool, a skill, or a direct answer. Restored at turn_end.
|
|
221
237
|
pi.on("input", (event) => {
|
|
222
238
|
if (event.source !== "interactive" || !state.active || !event.text)
|
|
223
239
|
return { action: "continue" };
|
|
224
240
|
state.active = false; // consume the arm for this submission
|
|
241
|
+
try {
|
|
242
|
+
if (savedTools === undefined)
|
|
243
|
+
savedTools = pi.getActiveTools?.();
|
|
244
|
+
pi.setActiveTools?.([WORKFLOW_TOOL_NAME]);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Tool restriction is best-effort; the directive still forces the workflow.
|
|
248
|
+
}
|
|
225
249
|
return { action: "transform", text: buildForcedWorkflowPrompt(event.text) };
|
|
226
250
|
});
|
|
251
|
+
// Restore the user's full tool set once the forced turn completes.
|
|
252
|
+
pi.on("turn_end", () => {
|
|
253
|
+
if (savedTools === undefined)
|
|
254
|
+
return;
|
|
255
|
+
const restore = savedTools;
|
|
256
|
+
savedTools = undefined;
|
|
257
|
+
try {
|
|
258
|
+
pi.setActiveTools?.(restore);
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
// ignore — nothing we can do if the host rejects the restore
|
|
262
|
+
}
|
|
263
|
+
});
|
|
227
264
|
return state;
|
|
228
265
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quintinshaw/pi-dynamic-workflows",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"description": "Claude-Code-style dynamic workflows for Pi — fan a task out across 100s of subagents with real model routing, token/cost accounting, resume, git-worktree isolation, an interactive /workflows TUI, and a real /deep-research.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/src/workflow-editor.ts
CHANGED
|
@@ -226,9 +226,19 @@ export function buildForcedWorkflowPrompt(text: string): string {
|
|
|
226
226
|
return [
|
|
227
227
|
text,
|
|
228
228
|
"",
|
|
229
|
-
"
|
|
230
|
-
"
|
|
231
|
-
"
|
|
229
|
+
"---",
|
|
230
|
+
"[workflows mode is ON for this message]",
|
|
231
|
+
"You MUST handle this request by calling the tool named exactly `workflow` (Pi's",
|
|
232
|
+
"deterministic JavaScript workflow-orchestration tool from pi-dynamic-workflows).",
|
|
233
|
+
"Write a workflow script that fans the task out across subagents via",
|
|
234
|
+
"agent()/parallel()/pipeline().",
|
|
235
|
+
"",
|
|
236
|
+
"The ONLY acceptable action is a `workflow` tool call. Do NOT instead:",
|
|
237
|
+
"- answer directly or in prose,",
|
|
238
|
+
"- call the `subagent` tool yourself,",
|
|
239
|
+
"- use any skill or command (e.g. pi-subagents, /code-review, deep-research),",
|
|
240
|
+
'- or interpret the word "workflow/workflows" loosely as some other parallel/audit approach.',
|
|
241
|
+
"Even for a small task, wrap it in a minimal `workflow` call with at least one agent().",
|
|
232
242
|
].join("\n");
|
|
233
243
|
}
|
|
234
244
|
|
|
@@ -236,17 +246,43 @@ export function buildForcedWorkflowPrompt(text: string): string {
|
|
|
236
246
|
* Install the workflows-mode editor and the submit-time forcing hook.
|
|
237
247
|
* Call once with the UI context (e.g. in `session_start`).
|
|
238
248
|
*/
|
|
249
|
+
/** The exact name of the workflow tool that workflows mode forces. */
|
|
250
|
+
export const WORKFLOW_TOOL_NAME = "workflow";
|
|
251
|
+
|
|
239
252
|
export function installWorkflowEditor(pi: ExtensionAPI, ui: ExtensionUIContext): WorkflowModeState {
|
|
240
253
|
const state: WorkflowModeState = { active: false };
|
|
241
254
|
|
|
242
255
|
ui.setEditorComponent((tui, theme, keybindings) => new WorkflowEditor(tui, theme, keybindings, state));
|
|
243
256
|
|
|
244
|
-
//
|
|
257
|
+
// Active tools saved while a turn is restricted to `workflow`; restored on turn_end.
|
|
258
|
+
let savedTools: string[] | undefined;
|
|
259
|
+
|
|
260
|
+
// When armed at submit time, rewrite the user's message to force a workflow AND
|
|
261
|
+
// restrict this turn's tools to just `workflow`, so the model can't fall back to
|
|
262
|
+
// the subagent tool, a skill, or a direct answer. Restored at turn_end.
|
|
245
263
|
pi.on("input", (event: { source?: string; text?: string }) => {
|
|
246
264
|
if (event.source !== "interactive" || !state.active || !event.text) return { action: "continue" } as const;
|
|
247
265
|
state.active = false; // consume the arm for this submission
|
|
266
|
+
try {
|
|
267
|
+
if (savedTools === undefined) savedTools = pi.getActiveTools?.();
|
|
268
|
+
pi.setActiveTools?.([WORKFLOW_TOOL_NAME]);
|
|
269
|
+
} catch {
|
|
270
|
+
// Tool restriction is best-effort; the directive still forces the workflow.
|
|
271
|
+
}
|
|
248
272
|
return { action: "transform", text: buildForcedWorkflowPrompt(event.text) } as const;
|
|
249
273
|
});
|
|
250
274
|
|
|
275
|
+
// Restore the user's full tool set once the forced turn completes.
|
|
276
|
+
pi.on("turn_end", () => {
|
|
277
|
+
if (savedTools === undefined) return;
|
|
278
|
+
const restore = savedTools;
|
|
279
|
+
savedTools = undefined;
|
|
280
|
+
try {
|
|
281
|
+
pi.setActiveTools?.(restore);
|
|
282
|
+
} catch {
|
|
283
|
+
// ignore — nothing we can do if the host rejects the restore
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
251
287
|
return state;
|
|
252
288
|
}
|