@tranhoangnguyen0310/pi-flow-external 1.2.0-external.0 → 1.3.0-external.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/AGENTS.md +1 -1
- package/CHANGELOG.md +12 -0
- package/CONTEXT.md +10 -1
- package/package.json +1 -1
- package/src/core/agy.ts +14 -0
- package/src/core/spawn.ts +24 -3
- package/src/types.ts +4 -0
package/AGENTS.md
CHANGED
|
@@ -29,7 +29,7 @@ This fork changes the original pi-flow contract: `Agent` is not a generic Pi sub
|
|
|
29
29
|
- A backend failure with a complete local record is different from an incomplete or damaged record; preserve that distinction in reports.
|
|
30
30
|
- Normal runs write private best-effort evidence under `~/.pi/agent/pi-flow-external/runs/` or `PI_FLOW_EXTERNAL_RUNS_DIR`. Records may still contain sensitive prompts, excerpts, and tool output despite redaction.
|
|
31
31
|
- Structured nested-agent activity may extend the wall-clock deadline once, by one fresh base timeout, capped at twice the original deadline.
|
|
32
|
-
- Do not automatically retry failed or aborted external runs. Preserve the receipt and retry only when the user asks.
|
|
32
|
+
- Do not automatically retry failed or aborted external runs. Preserve the receipt and retry only when the user asks. Exception: the agy backend retries once on infrastructure-classified failures (auth, eligibility, network); the retry is disclosed in the receipt details (`retries`, `retryOf`) and never applies to agent-level failures, aborts, or timeouts.
|
|
33
33
|
|
|
34
34
|
## Workflow contract
|
|
35
35
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to pi-flow external are documented here.
|
|
4
4
|
|
|
5
|
+
## [1.3.0-external.0] - 2026-09-06
|
|
6
|
+
|
|
7
|
+
Adds a bounded retry for the Antigravity backend and documents the standardized external profile role matrix.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Single automatic retry for agy infrastructure failures (authentication, eligibility, network timeouts), disclosed in receipt details as `retries` and `retryOf`. Agent-level failures, aborts, and timeouts are never retried.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- AGENTS.md records the agy retry exception to the no-auto-retry policy; CONTEXT.md documents the profile role-matrix design stance (guardrails define lanes, not methods) and the known one-file-per-backend format inelegance.
|
|
16
|
+
|
|
5
17
|
## [1.2.0-external.0] - 2026-08-29
|
|
6
18
|
|
|
7
19
|
Implements the orchestrator-decided tiers design ([#11](https://github.com/tranhoangnguyen03/pi-flow-external/issues/11)), live-verified against Claude Code 2.1.239, codex-cli 0.150.1, and agy 1.1.22.
|
package/CONTEXT.md
CHANGED
|
@@ -28,6 +28,15 @@ The split is global and intentional to avoid tool ambiguity across projects. The
|
|
|
28
28
|
|
|
29
29
|
User operations are namespaced under `/external`: status, Doctor, settings, profiles, profile creation, workflows, runs, and help. Extension-owned concurrency and timeout defaults live in `$PI_CODING_AGENT_DIR/pi-flow-external/settings.json`; profiles remain authoritative for downstream backend/model/thinking metadata.
|
|
30
30
|
|
|
31
|
+
## Design stance
|
|
32
|
+
|
|
33
|
+
Guardrails exist to keep lanes from bleeding into each other (a reviewer that edits, a debugger that fixes), not to constrain how a model works. Role bodies stay short: define the job, state the boundary, then get out of the way and trust the model's judgment on approach, depth, and method. The generic `worker` role covers non-coding tasks with no method constraints at all.
|
|
34
|
+
|
|
35
|
+
## Known inelegance
|
|
36
|
+
|
|
37
|
+
<!-- ponytail: one-backend-per-file profile format forces role x backend file duplication; extend src/profiles.ts with multi-backend profiles (e.g. backends: [claude, codex, agy] plus per-backend model map) if maintaining N copies of identical role bodies ever hurts -->
|
|
38
|
+
A standardized role roster needs one file per role per backend (e.g. `claude-qa`, `codex-qa`, `agy-qa` with identical bodies) because the profile format binds `backend:` and `model:` to a single file. The duplication is accepted for now; a future format extension could let one role file cover all backends. The same applies to `permission:` tiers: one tier per profile, but backends interpret tiers differently (Claude denies Bash at anything below `danger`), so command-running roles must declare `danger` even where a finer tier exists.
|
|
39
|
+
|
|
31
40
|
## Evidence boundary
|
|
32
41
|
|
|
33
|
-
Normal external runs write best-effort local records under `~/.pi/agent/pi-flow-external/runs/` unless `PI_FLOW_EXTERNAL_RUNS_DIR` overrides it. The TUI shows short evidence IDs by default and exposes record/journal paths only through expanded output. Redaction is best-effort, records are not rotated automatically, and failed/aborted runs are not retried automatically.
|
|
42
|
+
Normal external runs write best-effort local records under `~/.pi/agent/pi-flow-external/runs/` unless `PI_FLOW_EXTERNAL_RUNS_DIR` overrides it. The TUI shows short evidence IDs by default and exposes record/journal paths only through expanded output. Redaction is best-effort, records are not rotated automatically, and failed/aborted runs are not retried automatically, except one agy retry for infrastructure-classified failures (disclosed via receipt `retries`/`retryOf`).
|
package/package.json
CHANGED
package/src/core/agy.ts
CHANGED
|
@@ -237,6 +237,20 @@ function abortChild(child: ChildProcess): void {
|
|
|
237
237
|
}, FORCE_KILL_DELAY_MS).unref();
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
/** Infra-classified agy failures worth one bounded retry (auth/eligibility/network).
|
|
241
|
+
* Agent-level failures (task errors, protocol errors, aborts, timeouts) must not match. */
|
|
242
|
+
const TRANSIENT_AGY_FAILURE_PATTERNS: RegExp[] = [
|
|
243
|
+
/authentication failed/i,
|
|
244
|
+
/eligibility check failed/i,
|
|
245
|
+
/operation timed out/i,
|
|
246
|
+
/\b(ETIMEDOUT|ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/,
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
export function isTransientAgyFailure(error: unknown): boolean {
|
|
250
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
251
|
+
return TRANSIENT_AGY_FAILURE_PATTERNS.some((pattern) => pattern.test(message));
|
|
252
|
+
}
|
|
253
|
+
|
|
240
254
|
export async function spawnAgySubagent(params: {
|
|
241
255
|
toolCallId: string;
|
|
242
256
|
description: string;
|
package/src/core/spawn.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "./progress.ts";
|
|
20
20
|
import { spawnClaudeSubagent } from "./claude.ts";
|
|
21
21
|
import { spawnCodexSubagent } from "./codex.ts";
|
|
22
|
-
import { spawnAgySubagent } from "./agy.ts";
|
|
22
|
+
import { spawnAgySubagent, isTransientAgyFailure } from "./agy.ts";
|
|
23
23
|
import type {
|
|
24
24
|
PermissionTier,
|
|
25
25
|
SubagentBackend,
|
|
@@ -281,6 +281,8 @@ export async function spawnSubagent(params: SpawnSubagentParams): Promise<AgentT
|
|
|
281
281
|
? { budgetEnforceable: false }
|
|
282
282
|
: {}),
|
|
283
283
|
...(details.sessionId ? { sessionId: details.sessionId } : {}),
|
|
284
|
+
...(details.retries !== undefined ? { retries: details.retries } : {}),
|
|
285
|
+
...(details.retryOf ? { retryOf: details.retryOf } : {}),
|
|
284
286
|
...(resumeSession ? { resumedFrom: resumeSession.runId } : {}),
|
|
285
287
|
});
|
|
286
288
|
attachRunRecord(
|
|
@@ -341,7 +343,8 @@ async function spawnSubagentRuntime(params: SpawnSubagentRuntimeParams): Promise
|
|
|
341
343
|
});
|
|
342
344
|
}
|
|
343
345
|
if (params.profile.backend === "agy") {
|
|
344
|
-
|
|
346
|
+
// ponytail: one immediate retry, agy-only, infra-classified failures only; per-attempt backoff if flakes persist
|
|
347
|
+
const agyParams = {
|
|
345
348
|
toolCallId: params.toolCallId,
|
|
346
349
|
description: params.description,
|
|
347
350
|
prompt: params.prompt,
|
|
@@ -357,7 +360,25 @@ async function spawnSubagentRuntime(params: SpawnSubagentRuntimeParams): Promise
|
|
|
357
360
|
outputSchema: params.outputSchema,
|
|
358
361
|
permission: params.permission,
|
|
359
362
|
resumeConversationId: params.resumeSessionId,
|
|
360
|
-
}
|
|
363
|
+
};
|
|
364
|
+
let retryOf: string | undefined;
|
|
365
|
+
for (let attempt = 1; ; attempt++) {
|
|
366
|
+
const result = await spawnAgySubagent(agyParams);
|
|
367
|
+
const details = result.details as SubagentToolDetails;
|
|
368
|
+
const transientFailure =
|
|
369
|
+
details.status === "error" && !params.signal?.aborted && isTransientAgyFailure(details.error);
|
|
370
|
+
if (transientFailure && attempt === 1) {
|
|
371
|
+
retryOf = details.error ?? "agy transient failure";
|
|
372
|
+
// Resume the failed conversation so a mid-turn network failure does not
|
|
373
|
+
// replay already-executed tool calls in a fresh conversation.
|
|
374
|
+
agyParams.resumeConversationId = details.sessionId ?? agyParams.resumeConversationId;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (retryOf) {
|
|
378
|
+
return { ...result, details: { ...details, retries: attempt - 1, retryOf } };
|
|
379
|
+
}
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
361
382
|
}
|
|
362
383
|
if (params.profile.backend === "claude") {
|
|
363
384
|
return spawnClaudeSubagent({
|
package/src/types.ts
CHANGED
|
@@ -191,6 +191,10 @@ export interface SubagentToolDetails {
|
|
|
191
191
|
recordingError?: string;
|
|
192
192
|
/** Number of currently running subagents, used to choose rich vs compact live rendering. */
|
|
193
193
|
activeCount?: number;
|
|
194
|
+
/** Attempts retried for this run (agy infra failures retry once). */
|
|
195
|
+
retries?: number;
|
|
196
|
+
/** Error message of the retried-away first attempt, for transparency. */
|
|
197
|
+
retryOf?: string;
|
|
194
198
|
frame?: number;
|
|
195
199
|
/** Resolved permission tier for this run. */
|
|
196
200
|
permission?: PermissionTier;
|