pi-cohort 7.6.1 → 7.6.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 +10 -0
- package/README.md +19 -0
- package/package.json +1 -1
- package/src/runs/background/async-execution.ts +8 -0
- package/src/runs/background/subagent-runner.ts +14 -0
- package/src/runs/foreground/chain-execution.ts +5 -0
- package/src/runs/foreground/execution.ts +2 -0
- package/src/runs/foreground/external-single-attempt.ts +2 -0
- package/src/runs/foreground/subagent-executor.ts +7 -1
- package/src/runs/shared/pi-args.ts +18 -0
- package/src/shared/session-identity.ts +10 -0
- package/src/shared/types.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [7.6.3] - 2026-10-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Every child process gets `PI_SUBAGENT_PARENT_SESSION_ID`, the pi session id of the session that launched it, so a session roster can link each child to its parent without parsing the session name. A nested child gets its immediate parent's id. The README now lists `PI_SUBAGENT_CHILD`, `PI_SUBAGENT_CHILD_AGENT`, `PI_SUBAGENT_RUN_ID`, and `PI_SUBAGENT_PARENT_SESSION_ID` as stable inputs for other extensions. (#34)
|
|
7
|
+
|
|
8
|
+
## [7.6.2] - 2026-10-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Each child session is named `<agent> <run id prefix>[/<index>] from <parent>`, where `<parent>` is the parent session's name or, when unnamed, its id. `/resume`, terminal titles, and live-session rosters now tell children apart instead of listing unnamed sessions. (#32)
|
|
12
|
+
|
|
3
13
|
## [7.6.1] - 2026-10-26
|
|
4
14
|
|
|
5
15
|
### Added
|
package/README.md
CHANGED
|
@@ -167,6 +167,25 @@ Review and delivery workflows live in pi-gauntlet; pi-cohort ships delegation pr
|
|
|
167
167
|
|
|
168
168
|
`context: "fork"` starts a child from a real branched session instead of a fresh one, when it needs the parent's history. Cost across the whole subtree - main loop plus every foreground/background/nested child - rolls up into one `Σ$` total in the footer; see [doc/observability.md](doc/observability.md).
|
|
169
169
|
|
|
170
|
+
Each child session is named `<agent> <run id prefix>[/<index>] from <parent>`, so `/resume`, terminal titles, and live-session rosters can tell children apart:
|
|
171
|
+
|
|
172
|
+
| Child | Session name |
|
|
173
|
+
|---|---|
|
|
174
|
+
| Single `scout` from a session named `billing reports` | `scout 3f9a2c1e from billing reports` |
|
|
175
|
+
| Third task (index 2) of the same parallel run | `scout 3f9a2c1e/2 from billing reports` |
|
|
176
|
+
| `scout` from an unnamed session | `scout 3f9a2c1e from <parent session id>` |
|
|
177
|
+
|
|
178
|
+
The run id prefix works as the `id` for `subagent` status, interrupt, and resume; the index is the child `index` for resume. A nested child names its parent child: `worker 7b41d0aa from scout 3f9a2c1e from billing reports`.
|
|
179
|
+
|
|
180
|
+
Every child process also gets these environment variables, stable inputs for other extensions (for example, a session roster linking each child to its parent):
|
|
181
|
+
|
|
182
|
+
| Variable | Value | Set |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| `PI_SUBAGENT_CHILD` | `1` | on every child |
|
|
185
|
+
| `PI_SUBAGENT_CHILD_AGENT` | the child's agent name | when the agent is known |
|
|
186
|
+
| `PI_SUBAGENT_RUN_ID` | the full run id; its prefix appears in the session name | when the run id is known |
|
|
187
|
+
| `PI_SUBAGENT_PARENT_SESSION_ID` | the pi session id of the session that launched the child | on every child; a nested child gets its immediate parent's id, never an inherited ancestor's |
|
|
188
|
+
|
|
170
189
|
Full reference: agent/chain authoring in [doc/agents-and-chains.md](doc/agents-and-chains.md), exact command syntax in [doc/commands.md](doc/commands.md), the raw tool API in [doc/programmatic-api.md](doc/programmatic-api.md).
|
|
171
190
|
|
|
172
191
|
## Key concepts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cohort",
|
|
3
|
-
"version": "7.6.
|
|
3
|
+
"version": "7.6.3",
|
|
4
4
|
"description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,6 +57,10 @@ interface AsyncExecutionContext {
|
|
|
57
57
|
parentModel?: string;
|
|
58
58
|
/** Parent session thinking level, captured at dispatch */
|
|
59
59
|
parentThinking?: string;
|
|
60
|
+
/** Parent session display name, or its id when unnamed, captured at dispatch */
|
|
61
|
+
parentSessionLabel?: string;
|
|
62
|
+
/** Parent session id, captured at dispatch */
|
|
63
|
+
parentSessionId?: string;
|
|
60
64
|
executionBackend?: string;
|
|
61
65
|
}
|
|
62
66
|
|
|
@@ -632,6 +636,8 @@ export function executeAsyncChain(
|
|
|
632
636
|
sessionDir: path.join(sessionRoot ?? path.join(RUN_STATE_ROOT_DIR, "sessions"), `async-${id}`),
|
|
633
637
|
asyncDir,
|
|
634
638
|
sessionId: ctx.currentSessionId,
|
|
639
|
+
parentSessionLabel: ctx.parentSessionLabel,
|
|
640
|
+
parentSessionId: ctx.parentSessionId,
|
|
635
641
|
runtime: ctx.runtime,
|
|
636
642
|
forwardedFlags,
|
|
637
643
|
worktreeSetupHook,
|
|
@@ -846,6 +852,8 @@ export function executeAsyncSingle(
|
|
|
846
852
|
sessionDir: path.join(sessionRoot ?? path.join(RUN_STATE_ROOT_DIR, "sessions"), `async-${id}`),
|
|
847
853
|
asyncDir,
|
|
848
854
|
sessionId: ctx.currentSessionId,
|
|
855
|
+
parentSessionLabel: ctx.parentSessionLabel,
|
|
856
|
+
parentSessionId: ctx.parentSessionId,
|
|
849
857
|
runtime: ctx.runtime,
|
|
850
858
|
forwardedFlags,
|
|
851
859
|
worktreeSetupHook,
|
|
@@ -117,6 +117,8 @@ interface SubagentRunConfig {
|
|
|
117
117
|
sessionDir?: string;
|
|
118
118
|
asyncDir: string;
|
|
119
119
|
sessionId?: string | null;
|
|
120
|
+
parentSessionLabel?: string;
|
|
121
|
+
parentSessionId?: string;
|
|
120
122
|
runtime: PiRuntimeIdentity;
|
|
121
123
|
forwardedFlags?: string[];
|
|
122
124
|
worktreeSetupHook?: string;
|
|
@@ -625,6 +627,8 @@ export interface SingleStepContext {
|
|
|
625
627
|
outputFile: string;
|
|
626
628
|
runtime: PiRuntimeIdentity;
|
|
627
629
|
forwardedFlags?: string[];
|
|
630
|
+
parentSessionLabel?: string;
|
|
631
|
+
parentSessionId?: string;
|
|
628
632
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void;
|
|
629
633
|
nestedRoute?: NestedRouteInfo;
|
|
630
634
|
onAttemptStart?: (attempt: { model?: string; thinking?: string }) => void;
|
|
@@ -790,6 +794,8 @@ export async function runSingleStep(
|
|
|
790
794
|
runId: ctx.id,
|
|
791
795
|
childAgentName: step.agent,
|
|
792
796
|
childIndex: ctx.flatIndex,
|
|
797
|
+
parentSessionLabel: ctx.parentSessionLabel,
|
|
798
|
+
parentSessionId: ctx.parentSessionId,
|
|
793
799
|
parentEventSink: ctx.nestedRoute?.eventSink,
|
|
794
800
|
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
795
801
|
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
@@ -936,6 +942,8 @@ export async function runSingleStep(
|
|
|
936
942
|
runId: ctx.id,
|
|
937
943
|
childAgentName: step.agent,
|
|
938
944
|
childIndex: ctx.flatIndex,
|
|
945
|
+
parentSessionLabel: ctx.parentSessionLabel,
|
|
946
|
+
parentSessionId: ctx.parentSessionId,
|
|
939
947
|
parentEventSink: ctx.nestedRoute?.eventSink,
|
|
940
948
|
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
941
949
|
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
@@ -1978,6 +1986,8 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1978
1986
|
runtime: config.runtime,
|
|
1979
1987
|
controlConfig: config.controlConfig,
|
|
1980
1988
|
forwardedFlags: config.forwardedFlags,
|
|
1989
|
+
parentSessionLabel: config.parentSessionLabel,
|
|
1990
|
+
parentSessionId: config.parentSessionId,
|
|
1981
1991
|
executionBackendCoordinator,
|
|
1982
1992
|
executionBackendConfig,
|
|
1983
1993
|
nestedRoute: config.nestedRoute,
|
|
@@ -2268,6 +2278,8 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2268
2278
|
runtime: config.runtime,
|
|
2269
2279
|
controlConfig: config.controlConfig,
|
|
2270
2280
|
forwardedFlags: config.forwardedFlags,
|
|
2281
|
+
parentSessionLabel: config.parentSessionLabel,
|
|
2282
|
+
parentSessionId: config.parentSessionId,
|
|
2271
2283
|
executionBackendCoordinator,
|
|
2272
2284
|
executionBackendConfig,
|
|
2273
2285
|
nestedRoute: config.nestedRoute,
|
|
@@ -2448,6 +2460,8 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2448
2460
|
runtime: config.runtime,
|
|
2449
2461
|
controlConfig: config.controlConfig,
|
|
2450
2462
|
forwardedFlags: config.forwardedFlags,
|
|
2463
|
+
parentSessionLabel: config.parentSessionLabel,
|
|
2464
|
+
parentSessionId: config.parentSessionId,
|
|
2451
2465
|
executionBackendCoordinator,
|
|
2452
2466
|
executionBackendConfig,
|
|
2453
2467
|
nestedRoute: config.nestedRoute,
|
|
@@ -9,6 +9,7 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
9
9
|
import type { AgentConfig } from "../../agents/agents.ts";
|
|
10
10
|
import { ChainClarifyComponent, type ChainClarifyResult, type BehaviorOverride } from "./chain-clarify.ts";
|
|
11
11
|
import { parentModelFullId, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
12
|
+
import { resolveSessionLabel } from "../../shared/session-identity.ts";
|
|
12
13
|
import {
|
|
13
14
|
resolveChainTemplates,
|
|
14
15
|
createChainDir,
|
|
@@ -318,6 +319,8 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
318
319
|
preferredModelProvider: input.ctx.model?.provider,
|
|
319
320
|
parentModel: parentModelFullId(input.ctx.model),
|
|
320
321
|
parentThinking: input.ctx.thinkingLevel,
|
|
322
|
+
parentSessionLabel: resolveSessionLabel(input.ctx.sessionManager),
|
|
323
|
+
parentSessionId: input.ctx.sessionManager.getSessionId(),
|
|
321
324
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
322
325
|
structuredOutput: structuredRuntime,
|
|
323
326
|
acceptance: task.acceptance,
|
|
@@ -1161,6 +1164,8 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1161
1164
|
preferredModelProvider: ctx.model?.provider,
|
|
1162
1165
|
parentModel: parentModelFullId(ctx.model),
|
|
1163
1166
|
parentThinking: ctx.thinkingLevel,
|
|
1167
|
+
parentSessionLabel: resolveSessionLabel(ctx.sessionManager),
|
|
1168
|
+
parentSessionId: ctx.sessionManager.getSessionId(),
|
|
1164
1169
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
1165
1170
|
structuredOutput: structuredRuntime,
|
|
1166
1171
|
acceptance: seqStep.acceptance,
|
|
@@ -158,6 +158,8 @@ async function runSingleAttempt(
|
|
|
158
158
|
runId: options.runId,
|
|
159
159
|
childAgentName: agent.name,
|
|
160
160
|
childIndex: options.index ?? 0,
|
|
161
|
+
parentSessionLabel: options.parentSessionLabel,
|
|
162
|
+
parentSessionId: options.parentSessionId,
|
|
161
163
|
parentEventSink: options.nestedRoute?.eventSink,
|
|
162
164
|
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
163
165
|
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
@@ -92,6 +92,8 @@ export async function runExternalSingleAttempt(input: RunExternalSingleAttemptIn
|
|
|
92
92
|
runId: options.runId,
|
|
93
93
|
childAgentName: agent.name,
|
|
94
94
|
childIndex: options.index ?? 0,
|
|
95
|
+
parentSessionLabel: options.parentSessionLabel,
|
|
96
|
+
parentSessionId: options.parentSessionId,
|
|
95
97
|
parentEventSink: options.nestedRoute?.eventSink,
|
|
96
98
|
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
97
99
|
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
38
38
|
import { executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable } from "../background/async-execution.ts";
|
|
39
39
|
import { createForkContextResolver } from "../../shared/fork-context.ts";
|
|
40
|
-
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
40
|
+
import { resolveCurrentSessionId, resolveSessionLabel } from "../../shared/session-identity.ts";
|
|
41
41
|
import { formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
|
|
42
42
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeTopLevelOutput, resolveParallelTaskOutputPath, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
43
43
|
import { blockedLine } from "../shared/completion-guard.ts";
|
|
@@ -225,6 +225,8 @@ function buildAsyncContext(ctx: ExtensionContext, deps: CallExecutorDeps, cwd: s
|
|
|
225
225
|
currentModelProvider: ctx.model?.provider,
|
|
226
226
|
parentModel: parentModelFullId(ctx.model),
|
|
227
227
|
parentThinking: ctx.thinkingLevel,
|
|
228
|
+
parentSessionLabel: resolveSessionLabel(ctx.sessionManager),
|
|
229
|
+
parentSessionId: ctx.sessionManager.getSessionId(),
|
|
228
230
|
executionBackend: deps.config.executionBackend,
|
|
229
231
|
};
|
|
230
232
|
}
|
|
@@ -1503,6 +1505,8 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
1503
1505
|
preferredModelProvider: input.ctx.model?.provider,
|
|
1504
1506
|
parentModel: parentModelFullId(input.ctx.model),
|
|
1505
1507
|
parentThinking: input.ctx.thinkingLevel,
|
|
1508
|
+
parentSessionLabel: resolveSessionLabel(input.ctx.sessionManager),
|
|
1509
|
+
parentSessionId: input.ctx.sessionManager.getSessionId(),
|
|
1506
1510
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
1507
1511
|
acceptance: task.acceptance,
|
|
1508
1512
|
acceptanceContext: { mode: "parallel" },
|
|
@@ -2064,6 +2068,8 @@ async function runSinglePath(data: ExecutionContextData, deps: CallExecutorDeps)
|
|
|
2064
2068
|
preferredModelProvider: currentProvider,
|
|
2065
2069
|
parentModel: parentModelFullId(ctx.model),
|
|
2066
2070
|
parentThinking: ctx.thinkingLevel,
|
|
2071
|
+
parentSessionLabel: resolveSessionLabel(ctx.sessionManager),
|
|
2072
|
+
parentSessionId: ctx.sessionManager.getSessionId(),
|
|
2067
2073
|
skills: effectiveSkills,
|
|
2068
2074
|
acceptance: params.acceptance,
|
|
2069
2075
|
acceptanceContext: { mode: "single" },
|
|
@@ -24,6 +24,7 @@ export const SUBAGENT_PARENT_CHILD_INDEX_ENV = "PI_SUBAGENT_PARENT_CHILD_INDEX";
|
|
|
24
24
|
export const SUBAGENT_PARENT_DEPTH_ENV = "PI_SUBAGENT_PARENT_DEPTH";
|
|
25
25
|
export const SUBAGENT_PARENT_PATH_ENV = "PI_SUBAGENT_PARENT_PATH";
|
|
26
26
|
export const SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV = "PI_SUBAGENT_PARENT_CAPABILITY_TOKEN";
|
|
27
|
+
export const SUBAGENT_PARENT_SESSION_ID_ENV = "PI_SUBAGENT_PARENT_SESSION_ID";
|
|
27
28
|
|
|
28
29
|
interface BuildPiArgsInput {
|
|
29
30
|
baseArgs: string[];
|
|
@@ -45,6 +46,9 @@ interface BuildPiArgsInput {
|
|
|
45
46
|
runId?: string;
|
|
46
47
|
childAgentName?: string;
|
|
47
48
|
childIndex?: number;
|
|
49
|
+
/** Parent session display name, or its id when unnamed; names the child session. */
|
|
50
|
+
parentSessionLabel?: string;
|
|
51
|
+
parentSessionId?: string;
|
|
48
52
|
parentEventSink?: string;
|
|
49
53
|
parentControlInbox?: string;
|
|
50
54
|
parentRootRunId?: string;
|
|
@@ -79,6 +83,13 @@ export function applyThinkingSuffix(model: string | undefined, thinking: string
|
|
|
79
83
|
return `${model}:${thinking}`;
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
function childSessionName(input: BuildPiArgsInput): string | undefined {
|
|
87
|
+
if (!input.childAgentName || !input.runId) return undefined;
|
|
88
|
+
const index = input.childIndex ? `/${input.childIndex}` : "";
|
|
89
|
+
const parent = input.parentSessionLabel ? ` from ${input.parentSessionLabel}` : "";
|
|
90
|
+
return `${input.childAgentName} ${input.runId.slice(0, 8)}${index}${parent}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
82
93
|
export function buildPiArgs(input: BuildPiArgsInput, fileSystem: BuildPiArgsFileSystem = fs): BuildPiArgsResult {
|
|
83
94
|
const args = [...input.baseArgs];
|
|
84
95
|
// Forwarded flags are already name-deduped by deriveForwardedFlags; append only
|
|
@@ -100,6 +111,11 @@ export function buildPiArgs(input: BuildPiArgsInput, fileSystem: BuildPiArgsFile
|
|
|
100
111
|
}
|
|
101
112
|
}
|
|
102
113
|
|
|
114
|
+
const sessionName = childSessionName(input);
|
|
115
|
+
if (sessionName) {
|
|
116
|
+
args.push("--name", sessionName);
|
|
117
|
+
}
|
|
118
|
+
|
|
103
119
|
const modelArg = applyThinkingSuffix(input.model, input.thinking);
|
|
104
120
|
if (modelArg) {
|
|
105
121
|
args.push("--model", modelArg);
|
|
@@ -204,6 +220,8 @@ export function buildPiArgs(input: BuildPiArgsInput, fileSystem: BuildPiArgsFile
|
|
|
204
220
|
env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] = fanoutAuthorized
|
|
205
221
|
? input.parentCapabilityToken ?? process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] ?? ""
|
|
206
222
|
: "";
|
|
223
|
+
// Always set, so a grandchild sees its immediate parent rather than an inherited ancestor.
|
|
224
|
+
env[SUBAGENT_PARENT_SESSION_ID_ENV] = input.parentSessionId ?? "";
|
|
207
225
|
env.PI_SUBAGENT_INHERIT_PROJECT_CONTEXT = input.inheritProjectContext ? "1" : "0";
|
|
208
226
|
env.PI_SUBAGENT_INHERIT_SKILLS = input.inheritSkills ? "1" : "0";
|
|
209
227
|
if (input.runId) {
|
|
@@ -3,8 +3,18 @@ interface SessionIdentityManager {
|
|
|
3
3
|
getSessionId(): string | null | undefined;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
interface SessionLabelManager {
|
|
7
|
+
getSessionName(): string | undefined;
|
|
8
|
+
getSessionId(): string;
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
export function resolveCurrentSessionId(sessionManager: SessionIdentityManager): string {
|
|
7
12
|
const sessionId = sessionManager.getSessionFile() ?? sessionManager.getSessionId();
|
|
8
13
|
if (!sessionId) throw new Error("Current session identity is unavailable.");
|
|
9
14
|
return sessionId;
|
|
10
15
|
}
|
|
16
|
+
|
|
17
|
+
/** The session's display name, or its id when unnamed. */
|
|
18
|
+
export function resolveSessionLabel(sessionManager: SessionLabelManager): string {
|
|
19
|
+
return sessionManager.getSessionName() || sessionManager.getSessionId();
|
|
20
|
+
}
|
package/src/shared/types.ts
CHANGED
|
@@ -830,6 +830,10 @@ export interface RunSyncOptions {
|
|
|
830
830
|
parentModel?: string;
|
|
831
831
|
/** Parent session thinking level; used when the agent sets no `thinking` */
|
|
832
832
|
parentThinking?: string;
|
|
833
|
+
/** Parent session display name, or its id when unnamed; names the child session */
|
|
834
|
+
parentSessionLabel?: string;
|
|
835
|
+
/** Parent session id; exported to the child as PI_SUBAGENT_PARENT_SESSION_ID */
|
|
836
|
+
parentSessionId?: string;
|
|
833
837
|
/** Skills to inject (overrides agent default if provided) */
|
|
834
838
|
skills?: string[];
|
|
835
839
|
structuredOutput?: {
|