@signalridge/pi-workflows 1.11.0 → 1.11.1
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 +14 -0
- package/README.md +8 -0
- package/package.json +3 -2
- package/skills/workflow-authoring/SKILL.md +1 -1
- package/skills/workflow-authoring/references/capabilities.md +1 -1
- package/skills/workflow-authoring/references/capability-details.md +1 -1
- package/skills/workflow-patterns/SKILL.md +1 -1
- package/skills/workflow-review/SKILL.md +1 -1
- package/src/arming.ts +7 -1
- package/src/context-overflow.ts +98 -0
- package/src/index.ts +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.11.1
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
- da9ed0f: - Reject rename hardlink aliases and retargeted queue identities before writes; support all LSP newline styles and identify possibly partially modified targets on write failure.
|
|
7
|
+
- Fence pending physical Plan model mutations at cancellable navigation/replacement gates, reconcile handoffs by native selection chronology, and scope thinking-only restoration to its originating model.
|
|
8
|
+
- Compose Goal guidance through an owned structured prompt section. Resolve overflow usage against physical response models and invalidate stale size evidence after persisted context edits in each independent continuation helper.
|
|
9
|
+
- Persist queued Rules reminders/offers only at authenticated native admission, match file URL paths, and provide usable rule-file references when read_rule is unavailable.
|
|
10
|
+
- Inherit nested workers' thinking from their immediate spawning session while preserving explicit policy precedence.
|
|
11
|
+
- Refresh skill discovery for custom Analytics runs without consuming pending user attribution, and finalize successful Codex diagnostics only after UI cleanup and the final owned result fence.
|
|
12
|
+
- da9ed0f: Restore workflow keyword and standing `/effort high|ultra` authorization on Pi 1.2 and later. Those hosts report the session as busy while its own run starts, so the hidden guidance that authorizes the `workflow` tool was silently dropped for every idle prompt. The idle check now applies only on hosts that do not identify the starting run, and runs started by a custom message never consume an armed prompt.
|
|
13
|
+
- da9ed0f: Stop settled workflow results from waking a session whose context overflowed the model window. While the latest response on the branch is a context overflow with no compaction after it, results are still delivered and acknowledged as context but no wake is sent, and one warning per overflow episode asks to run `/compact` (or `/codex-compact` when installed) or start a new session. A compaction or a later successful response restores normal wakes. Previously each wake resent the same over-window context and failed again. The package now declares `@earendil-works/pi-ai` as a host peer dependency.
|
|
14
|
+
- Updated dependencies [da9ed0f]
|
|
15
|
+
- @signalridge/pi-ui@1.4.1
|
|
16
|
+
|
|
3
17
|
## 1.11.0
|
|
4
18
|
### Minor Changes
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -306,6 +306,14 @@ awaits while navigation or shutdown retires the owner, the obsolete
|
|
|
306
306
|
extension-sourced wake is handled without starting a turn. Interactive/RPC
|
|
307
307
|
input and other extensions' messages are not consumed by this guard.
|
|
308
308
|
|
|
309
|
+
No wake is sent while the session's latest meaningful response overflowed the model's
|
|
310
|
+
context window and no compaction followed it: that turn would resend the same
|
|
311
|
+
over-window context and fail again. Results are still delivered and acknowledged
|
|
312
|
+
as context, and one warning per overflow episode asks to run `/compact` (or
|
|
313
|
+
`/codex-compact` when installed) or start a new session. A compaction or a later
|
|
314
|
+
successful response restores normal wakes. Aborted responses and transient errors
|
|
315
|
+
(including HTTP 503 and rate limits) do not prove context recovery.
|
|
316
|
+
|
|
309
317
|
This is a handoff check, not a navigation lock: public hooks cannot fence an
|
|
310
318
|
arbitrary later async input or preparation handler that navigates after our
|
|
311
319
|
input hook has accepted the wake. Earlier transforms must preserve the private
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signalridge/pi-workflows",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "Deterministic JavaScript orchestration workflows backed by pi-subagents managed spawning.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"lifecycle": "stable"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
+
"@earendil-works/pi-ai": "*",
|
|
18
19
|
"@earendil-works/pi-coding-agent": "*",
|
|
19
20
|
"@earendil-works/pi-tui": "*",
|
|
20
21
|
"@signalridge/pi-subagents": ">=1.9.0",
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
24
25
|
"@signalridge/pi-subagents-protocol": "^1.6.0",
|
|
25
|
-
"@signalridge/pi-ui": "^1.4.
|
|
26
|
+
"@signalridge/pi-ui": "^1.4.1",
|
|
26
27
|
"acorn": "^8.16.0"
|
|
27
28
|
},
|
|
28
29
|
"pi": {
|
|
@@ -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.11.
|
|
5
|
+
version: "1.11.1"
|
|
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.11.
|
|
5
|
+
Contract content / skill / extension: `1.11.1`
|
|
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.11.
|
|
5
|
+
Contract content / skill / extension: `1.11.1`
|
|
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.11.
|
|
5
|
+
version: "1.11.1"
|
|
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.11.
|
|
5
|
+
version: "1.11.1"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Workflow review
|
package/src/arming.ts
CHANGED
|
@@ -235,15 +235,21 @@ export class WorkflowTurnArming {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
consume(event: BeforeAgentStartEvent, ctx: ExtensionContext): void {
|
|
238
|
+
// Pi 1.2+ also announces idle custom-message runs; only a user prompt can
|
|
239
|
+
// carry the armed input, and earlier hosts never reached here for them.
|
|
240
|
+
if (event.messageType === "custom") return;
|
|
238
241
|
const pending = this.pending;
|
|
239
242
|
const ambiguous = this.ambiguous;
|
|
240
243
|
this.pending = undefined; // one-use, also for mismatched/consumed prompts
|
|
241
244
|
this.current = undefined;
|
|
242
245
|
try {
|
|
246
|
+
// Pi 1.2+ (which reports messageType) counts the session busy while its
|
|
247
|
+
// own run prepares and queues concurrent sends into it. Earlier hosts were
|
|
248
|
+
// still idle here, so a busy session meant another run had started.
|
|
243
249
|
if (
|
|
244
250
|
ambiguous ||
|
|
245
251
|
!pending?.content ||
|
|
246
|
-
!ctx.isIdle() ||
|
|
252
|
+
(event.messageType === undefined && !ctx.isIdle()) ||
|
|
247
253
|
event.prompt !== pending.text ||
|
|
248
254
|
inputKey("", event.images) !== pending.images ||
|
|
249
255
|
ctx.sessionManager.getSessionId() !== pending.sessionId ||
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overflow-aware waking. After a response overflowed the model's context window and no
|
|
3
|
+
* compaction followed, a turn this extension starts resends the same over-window context and
|
|
4
|
+
* fails again, and each failure is still a billed request. Wakes are held until a compaction
|
|
5
|
+
* or a later successful response ends the episode.
|
|
6
|
+
*/
|
|
7
|
+
import { isContextOverflow } from "@earendil-works/pi-ai";
|
|
8
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
type EpisodeContext = Pick<ExtensionContext, "sessionManager"> & {
|
|
11
|
+
modelRegistry?: { find(provider: string, model: string): { contextWindow?: number } | undefined };
|
|
12
|
+
};
|
|
13
|
+
export type OverflowContext = EpisodeContext & Pick<ExtensionContext, "hasUI" | "ui">;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The first overflowed response of the current episode: the latest meaningful response on the
|
|
17
|
+
* branch overflowed and neither compaction nor a successful response followed. Aborted and
|
|
18
|
+
* transient error responses say nothing about the context size and are skipped.
|
|
19
|
+
*/
|
|
20
|
+
export function overflowEpisode(ctx: EpisodeContext): string | undefined {
|
|
21
|
+
try {
|
|
22
|
+
const branch = ctx.sessionManager.getBranch();
|
|
23
|
+
const projection = ctx.sessionManager.buildSessionProjection?.();
|
|
24
|
+
const projected = projection
|
|
25
|
+
? new Set(
|
|
26
|
+
projection.entries
|
|
27
|
+
.filter((entry) => entry.messages.some((message) => message.role === "assistant"))
|
|
28
|
+
.map((entry) => entry.sourceEntry.id),
|
|
29
|
+
)
|
|
30
|
+
: undefined;
|
|
31
|
+
let contextEdited = false;
|
|
32
|
+
let episode: string | undefined;
|
|
33
|
+
for (let index = branch.length - 1; index >= 0; index--) {
|
|
34
|
+
const entry = branch[index];
|
|
35
|
+
if (entry.type === "compaction") break;
|
|
36
|
+
if (entry.type === "context_edit") {
|
|
37
|
+
contextEdited = true;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (entry.type !== "message" || entry.message.role !== "assistant" || entry.message.stopReason === "aborted")
|
|
41
|
+
continue;
|
|
42
|
+
const explicit = entry.message.stopReason === "error" && isContextOverflow(entry.message);
|
|
43
|
+
if (entry.message.stopReason === "error" && !explicit) continue;
|
|
44
|
+
// Pi can omit a failed recovery attempt before a cancelled compaction.
|
|
45
|
+
// That alone does not repair its oversized input; invalidate usage only.
|
|
46
|
+
if (!explicit && projected && !projected.has(entry.id)) break;
|
|
47
|
+
const contextWindow = ctx.modelRegistry?.find(entry.message.provider, entry.message.model)?.contextWindow;
|
|
48
|
+
if (!explicit && (contextEdited || !isContextOverflow(entry.message, contextWindow))) {
|
|
49
|
+
if (entry.message.stopReason === "error") continue;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
episode = entry.id;
|
|
53
|
+
}
|
|
54
|
+
return episode;
|
|
55
|
+
} catch {
|
|
56
|
+
// A replaced session's context is stale; never hold a wake on a guess.
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function overflowNotice(held: string, codexCompact: boolean): string {
|
|
62
|
+
return (
|
|
63
|
+
`Context exceeds the model's window; ${held} without starting a turn. ` +
|
|
64
|
+
`Run ${codexCompact ? "/compact or /codex-compact" : "/compact"}, or start a new session.`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** One guard per extension instance, so the user sees one notice per overflow episode. */
|
|
69
|
+
export class OverflowWakeGuard {
|
|
70
|
+
private noticed: string | undefined;
|
|
71
|
+
constructor(
|
|
72
|
+
private readonly pi: Pick<ExtensionAPI, "getCommands">,
|
|
73
|
+
private readonly held: string,
|
|
74
|
+
) {}
|
|
75
|
+
|
|
76
|
+
/** True while a turn must not start; the first hold of an episode tells the user why. */
|
|
77
|
+
holds(ctx: OverflowContext | undefined): boolean {
|
|
78
|
+
const episode = ctx ? overflowEpisode(ctx) : undefined;
|
|
79
|
+
if (!ctx || episode === undefined) return false;
|
|
80
|
+
if (episode !== this.noticed) {
|
|
81
|
+
this.noticed = episode;
|
|
82
|
+
try {
|
|
83
|
+
if (ctx.hasUI) ctx.ui.notify(overflowNotice(this.held, this.hasCommand("codex-compact")), "warning");
|
|
84
|
+
} catch {
|
|
85
|
+
// A disposed UI must not block the delivery that follows.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private hasCommand(name: string): boolean {
|
|
92
|
+
try {
|
|
93
|
+
return this.pi.getCommands().some((command) => command.name === name);
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
8
8
|
import { DEFAULT_TRIGGER_WORD, hasTriggerWord, WORKFLOW_ARMING_MESSAGE_TYPE, WorkflowTurnArming } from "./arming.js";
|
|
9
9
|
import { BUILTIN_WORKFLOWS, validateBuiltinArgs } from "./builtins.js";
|
|
10
10
|
import { type CommandResult, resolveCodeReviewScope } from "./code-review-scope.js";
|
|
11
|
+
import { OverflowWakeGuard } from "./context-overflow.js";
|
|
11
12
|
import { type ScriptStartOptions, type ScriptStartResult, WorkflowEngine, WorkflowWaitAbortedError } from "./engine.js";
|
|
12
13
|
import { JOURNAL_ENTRY_TYPE, type SessionEntryLike } from "./journal.js";
|
|
13
14
|
import {
|
|
@@ -361,6 +362,7 @@ export default function piWorkflows(pi: ExtensionAPI): void {
|
|
|
361
362
|
let keywordTriggerEnabled = workflowSettings.keywordTriggerEnabled;
|
|
362
363
|
/** Public sends are void: acceptance is checked against the owning branch. */
|
|
363
364
|
let deliveryContext: ExtensionContext | undefined;
|
|
365
|
+
const overflowWakes = new OverflowWakeGuard(pi, "workflow results are added to the conversation");
|
|
364
366
|
let deliveryTimer: ReturnType<typeof setTimeout> | undefined;
|
|
365
367
|
let inputPreparing = false;
|
|
366
368
|
const turnArming = new WorkflowTurnArming();
|
|
@@ -1808,7 +1810,8 @@ export default function piWorkflows(pi: ExtensionAPI): void {
|
|
|
1808
1810
|
try {
|
|
1809
1811
|
if (!isCurrent()) return;
|
|
1810
1812
|
// Never use a custom-message triggerTurn: that skips input preparation
|
|
1811
|
-
// and before_agent_start. The void API is NOT an
|
|
1813
|
+
// (and, before Pi 1.2, before_agent_start). The void API is NOT an
|
|
1814
|
+
// acceptance promise.
|
|
1812
1815
|
const missing = batch.filter(([runId]) => !persisted(runId));
|
|
1813
1816
|
if (missing.length > 0)
|
|
1814
1817
|
pi.sendMessage(
|
|
@@ -1842,7 +1845,9 @@ export default function piWorkflows(pi: ExtensionAPI): void {
|
|
|
1842
1845
|
}
|
|
1843
1846
|
// An earlier input hook can await while the same manager navigates. Keep
|
|
1844
1847
|
// exact owner evidence until our input hook consumes this private wake.
|
|
1845
|
-
|
|
1848
|
+
// An overflowed parent would resend the same over-window context: the
|
|
1849
|
+
// acknowledged results above wait for the next run instead.
|
|
1850
|
+
if (isCurrent() && engine && !overflowWakes.holds(ctx)) {
|
|
1846
1851
|
const wake = `${DELIVERY_WAKE_PREFIX}${randomUUID()}] ${DELIVERY_WAKE_TEXT}`;
|
|
1847
1852
|
wakeAdmissions.set(wake, { generation, context: ctx, engine, anchor: deliveryBranchAnchor });
|
|
1848
1853
|
while (wakeAdmissions.size > MAX_WAKE_ADMISSIONS) {
|