@signalridge/pi-workflows 1.7.1 → 1.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.7.3
4
+ ### Patch Changes
5
+
6
+ - e04f729: Remove repeated copies of the workflow arming reminder while preserving other input hooks.
7
+
8
+ ## 1.7.2
9
+ ### Patch Changes
10
+
11
+ - 919655f: Yield before live agent dispatch so synchronous managed-spawn setup cannot be interrupted by the script bootstrap watchdog before promise tracking is attached. Preserve lexical call identity and reservations, check cancellation after yielding, and retain the 1000ms script watchdog. Add real Node process regressions for slow setup, runaway scripts, and pending-spawn cleanup.
12
+
3
13
  ## 1.7.1
4
14
  ### Patch Changes
5
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalridge/pi-workflows",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "Deterministic JavaScript orchestration workflows backed by pi-subagents managed spawning.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -2,7 +2,7 @@
2
2
  name: workflow-authoring
3
3
  description: Design, write, review, and debug Pi JavaScript workflows that coordinate multiple subagents. Use when a request needs named dependencies, bounded fan-out, per-item pipelines, quality gates, saved or nested workflows, pause/resume, or workflow progress; do not use it merely to run an existing named workflow.
4
4
  metadata:
5
- version: "1.7.1"
5
+ version: "1.7.3"
6
6
  ---
7
7
 
8
8
  # Workflow authoring
@@ -2,7 +2,7 @@
2
2
  # Workflow capability index
3
3
 
4
4
  Contract format: `2.0.0`<br>
5
- Contract content / skill / extension: `1.7.1`
5
+ Contract content / skill / extension: `1.7.3`
6
6
 
7
7
  This compact generated index covers supported runtime globals and workflow-tool inputs. For constraints, compatibility behavior, internal boundaries, and dynamic-reference ownership, follow the [exhaustive generated facts](capability-details.md).
8
8
 
@@ -2,7 +2,7 @@
2
2
  # Exhaustive workflow capability facts
3
3
 
4
4
  Contract format: `2.0.0`<br>
5
- Contract content / skill / extension: `1.7.1`
5
+ Contract content / skill / extension: `1.7.3`
6
6
 
7
7
  Every exact fact below is projected from the installed extension's capability contract. Explanatory judgment belongs in the hand-written references next to this file.
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: workflow-patterns
3
3
  description: Choose and run one of Pi's built-in multi-agent workflows for research, adversarial review, code review, multi-perspective analysis, or codebase auditing. Use when the request matches a named pattern; use workflow-authoring instead for a new script or custom topology.
4
4
  metadata:
5
- version: "1.7.1"
5
+ version: "1.7.3"
6
6
  ---
7
7
 
8
8
  # Built-in workflow patterns
@@ -2,7 +2,7 @@
2
2
  name: workflow-review
3
3
  description: Audit an existing Pi JavaScript workflow for topology, prompt/data flow, limits, failure handling, cancellation, resume safety, and publication compatibility. Use when reviewing or debugging a workflow script or saved run; do not use to run a standard named workflow.
4
4
  metadata:
5
- version: "1.7.1"
5
+ version: "1.7.3"
6
6
  ---
7
7
 
8
8
  # Workflow review
package/src/arming.ts CHANGED
@@ -29,6 +29,16 @@ export const DEFAULT_TRIGGER_WORD = "workflow";
29
29
  export const WORKFLOW_ARMED_DIRECTIVE =
30
30
  "<system-reminder>You typed the workflow trigger word, which counts as an explicit opt-in to multi-agent orchestration: the `workflow` tool is authorized for this turn. It runs in the background by default — the turn ends and the result is delivered back into the conversation when it finishes, which is expected rather than a stall, so you need not stay and block. Pass background:false only when the user is waiting for the result inline. This is permission, not an instruction: if the request is conversational, trivial, or better answered directly, answer it directly and do not run a workflow.</system-reminder>";
31
31
 
32
+ /** Keep one copy when an earlier input transform already added this directive. */
33
+ export function deduplicateWorkflowDirective(text: string): string {
34
+ let seen = false;
35
+ return text.replaceAll(WORKFLOW_ARMED_DIRECTIVE, (directive) => {
36
+ if (seen) return "";
37
+ seen = true;
38
+ return directive;
39
+ });
40
+ }
41
+
32
42
  function escapeRegExp(text: string): string {
33
43
  return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
34
44
  }
package/src/index.ts CHANGED
@@ -3,7 +3,12 @@ import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@e
3
3
  import { defineTool } from "@earendil-works/pi-coding-agent";
4
4
  import { Text } from "@earendil-works/pi-tui";
5
5
  import { Type } from "@sinclair/typebox";
6
- import { DEFAULT_TRIGGER_WORD, hasTriggerWord, WORKFLOW_ARMED_DIRECTIVE } from "./arming.js";
6
+ import {
7
+ DEFAULT_TRIGGER_WORD,
8
+ deduplicateWorkflowDirective,
9
+ hasTriggerWord,
10
+ WORKFLOW_ARMED_DIRECTIVE,
11
+ } from "./arming.js";
7
12
  import { BUILTIN_WORKFLOWS, validateBuiltinArgs } from "./builtins.js";
8
13
  import { type CommandResult, resolveCodeReviewScope } from "./code-review-scope.js";
9
14
  import { type ScriptStartOptions, type ScriptStartResult, WorkflowEngine, WorkflowWaitAbortedError } from "./engine.js";
@@ -952,6 +957,14 @@ export default function piWorkflows(pi: ExtensionAPI): void {
952
957
  // question. Nothing is swallowed and no UI opens.
953
958
  pi.on("input", (event) => {
954
959
  if (event.source === "extension") return { action: "continue" as const };
960
+ const deduplicatedText = deduplicateWorkflowDirective(event.text);
961
+ if (deduplicatedText !== event.text) {
962
+ return {
963
+ action: "transform" as const,
964
+ text: deduplicatedText,
965
+ ...(event.images ? { images: event.images } : {}),
966
+ };
967
+ }
955
968
  const explicitTrigger = keywordTriggerEnabled && hasTriggerWord(event.text, triggerKeyword);
956
969
  const substantive = effortLevel !== "off" && event.text.trim().length >= 16 && !event.text.trim().startsWith("/");
957
970
  if (!explicitTrigger && !substantive) return { action: "continue" as const };
@@ -969,6 +982,29 @@ export default function piWorkflows(pi: ExtensionAPI): void {
969
982
  };
970
983
  });
971
984
 
985
+ // Input transforms can be chained with handlers loaded after this package.
986
+ // Normalize only our own repeated marker again at the final context boundary;
987
+ // unrelated plugin content and hooks remain untouched.
988
+ pi.on("context", (event) => {
989
+ type UserMessage = Extract<(typeof event.messages)[number], { role: "user" }>;
990
+ let lastUserIndex = -1;
991
+ let lastUserMessage: UserMessage | undefined;
992
+ for (let index = 0; index < event.messages.length; index += 1) {
993
+ const message = event.messages[index];
994
+ if (message && "role" in message && message.role === "user") {
995
+ lastUserIndex = index;
996
+ lastUserMessage = message;
997
+ }
998
+ }
999
+ const message = lastUserMessage;
1000
+ if (lastUserIndex < 0 || !message || !("content" in message) || typeof message.content !== "string") return;
1001
+ const deduplicatedText = deduplicateWorkflowDirective(message.content);
1002
+ if (deduplicatedText === message.content) return;
1003
+ const messages = event.messages.slice();
1004
+ messages[lastUserIndex] = { ...message, content: deduplicatedText };
1005
+ return { messages };
1006
+ });
1007
+
972
1008
  // Standing effort mode mirrors the reference: it auto-arms substantive
973
1009
  // interactive messages but remains guidance, not an inferred spend ceiling.
974
1010
  pi.registerCommand("effort", {
package/src/runtime.ts CHANGED
@@ -1606,6 +1606,16 @@ export async function runWorkflow<T = unknown>(
1606
1606
  shared.physicalAgentCount++;
1607
1607
 
1608
1608
  return limiter(async () => {
1609
+ // Keep lexical identity, scope, thread ownership and cap reservations above
1610
+ // synchronous, but leave the timed VM bootstrap before invoking host code.
1611
+ // Even an immediately available limiter slot must yield: synchronous spawn
1612
+ // setup can outlast the script watchdog and interrupt RPC promise creation
1613
+ // before its callers attach rejection/cleanup handlers. The returned call
1614
+ // is now tracked before dispatch, and a bootstrap timeout cancels it here.
1615
+ await Promise.resolve();
1616
+ throwIfAborted();
1617
+ if (batch?.cancelled) throw agentLimitError();
1618
+
1609
1619
  const timeout = agentOptions.timeoutMs !== undefined ? agentOptions.timeoutMs : agentTimeoutMs;
1610
1620
  const retryAttempts = normalizeAgentRetries(agentOptions.retries ?? options.agentRetries ?? 0);
1611
1621
  const maxAttempts = retryAttempts + 1;