pi-cohort 7.6.0 → 7.6.2
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 +12 -0
- package/package.json +1 -1
- package/src/execution-backend/child-host-controller.ts +1 -1
- package/src/execution-backend/child-host-protocol.ts +5 -2
- package/src/execution-backend/child-host-runtime.ts +8 -1
- package/src/execution-backend/registry.ts +4 -0
- package/src/execution-backend/types.ts +6 -0
- package/src/runs/background/async-execution.ts +4 -0
- package/src/runs/background/subagent-runner.ts +7 -0
- package/src/runs/foreground/chain-execution.ts +3 -0
- package/src/runs/foreground/execution.ts +1 -0
- package/src/runs/foreground/external-single-attempt.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +4 -1
- package/src/runs/shared/pi-args.ts +14 -0
- package/src/shared/session-identity.ts +10 -0
- package/src/shared/types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [7.6.2] - 2026-10-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- 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)
|
|
7
|
+
|
|
8
|
+
## [7.6.1] - 2026-10-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Execution backends can declare `surfaceEnvironmentPrefixes`; each child attempt gets the launched surface's own values for matching keys instead of the parent's, and inherits none of the parent's matching keys. Before, a child started in a terminal-mux pane carried the parent pane's identity (for example `CMUX_SURFACE_ID`), so mux-aware tools in the child acted on the parent's pane.
|
|
12
|
+
|
|
3
13
|
## [7.6.0] - 2026-10-26
|
|
4
14
|
|
|
5
15
|
### Breaking
|
package/README.md
CHANGED
|
@@ -79,6 +79,8 @@ The descriptor has exactly four fields. `packageJsonUrl` must be an absolute `fi
|
|
|
79
79
|
|
|
80
80
|
Cohort serializes only backend names and validated reload descriptors into its detached coordinator - never live backend objects or an environment capsule. The coordinator reloads registrations in their original order before selecting a backend, and every child launch receives its exact resolved `cwd`. Selecting `native` bypasses reload entirely. An explicit backend selection reports that backend's reload failure; `auto` reports any registered reload failure rather than silently changing policy. Once reload succeeds, normal auto-detection still falls back to native when no registered backend is available. Backends registered without `reload` remain usable in the foreground but cannot be reconstructed for a background run.
|
|
81
81
|
|
|
82
|
+
A backend whose surface sets its own identity variables declares them with `surfaceEnvironmentPrefixes` (for example `["CMUX_"]`). Each child attempt then drops the parent's keys with those prefixes and takes the launched surface's values instead, so mux-aware tools in the child address the child's surface. Prefixes must be nonblank strings.
|
|
83
|
+
|
|
82
84
|
## Mental model
|
|
83
85
|
|
|
84
86
|
Pi is the parent session. A subagent is a focused child Pi session with its own job. When you ask for a subagent, Pi starts the child, gives it the task, and brings the result back. Foreground runs stream in the conversation; background runs keep working and can be checked later.
|
|
@@ -165,6 +167,16 @@ Review and delivery workflows live in pi-gauntlet; pi-cohort ships delegation pr
|
|
|
165
167
|
|
|
166
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).
|
|
167
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
|
+
|
|
168
180
|
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).
|
|
169
181
|
|
|
170
182
|
## 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.2",
|
|
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",
|
|
@@ -55,7 +55,7 @@ export async function createChildHostController(options: ChildHostControllerOpti
|
|
|
55
55
|
let descriptor: number | undefined;
|
|
56
56
|
try {
|
|
57
57
|
descriptor = fileSystem.openSync(configPath, "wx", 0o600);
|
|
58
|
-
fileSystem.writeFileSync(descriptor, JSON.stringify({ protocolVersion: 1, socketPath, runId: options.runId, childId: options.childId }));
|
|
58
|
+
fileSystem.writeFileSync(descriptor, JSON.stringify({ protocolVersion: 1, socketPath, runId: options.runId, childId: options.childId, surfaceEnvironmentPrefixes: options.backend.surfaceEnvironmentPrefixes ?? [] }));
|
|
59
59
|
} finally { if (descriptor !== undefined) fileSystem.closeSync(descriptor); }
|
|
60
60
|
|
|
61
61
|
let lease: ExecutionBackendLease | undefined;
|
|
@@ -6,6 +6,7 @@ export interface ChildHostConfig {
|
|
|
6
6
|
readonly socketPath: string;
|
|
7
7
|
readonly runId: string;
|
|
8
8
|
readonly childId: string;
|
|
9
|
+
readonly surfaceEnvironmentPrefixes: readonly string[];
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
type Correlation = { readonly protocolVersion: 1; readonly runId: string; readonly childId: string };
|
|
@@ -35,9 +36,11 @@ function correlation(input: Record<string, unknown>): Correlation {
|
|
|
35
36
|
|
|
36
37
|
export function validateChildHostConfig(value: unknown): ChildHostConfig {
|
|
37
38
|
const input = record(value, "child host config");
|
|
38
|
-
exact(input, ["protocolVersion", "socketPath", "runId", "childId"]);
|
|
39
|
+
exact(input, ["protocolVersion", "socketPath", "runId", "childId", "surfaceEnvironmentPrefixes"]);
|
|
39
40
|
if (input.protocolVersion !== CHILD_HOST_PROTOCOL_VERSION) throw new Error("incompatible child host protocol");
|
|
40
|
-
|
|
41
|
+
const prefixes = input.surfaceEnvironmentPrefixes;
|
|
42
|
+
if (!Array.isArray(prefixes)) throw new Error("surfaceEnvironmentPrefixes must be an array");
|
|
43
|
+
return { ...correlation(input), socketPath: text(input.socketPath, "socketPath"), surfaceEnvironmentPrefixes: prefixes.map(prefix => text(prefix, "surfaceEnvironmentPrefixes entry")) };
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export function validateChildHostMessage(value: unknown): ChildHostMessage {
|
|
@@ -17,6 +17,13 @@ export function loadChildHostConfig(configPath = process.env[PI_COHORT_CHILD_HOS
|
|
|
17
17
|
} finally { if (descriptor !== undefined) fs.closeSync(descriptor); }
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function attemptEnvironment(parent: Readonly<Record<string, string>>, surfacePrefixes: readonly string[]): Record<string, string> {
|
|
21
|
+
const surfaceOwned = (key: string) => surfacePrefixes.some(prefix => key.startsWith(prefix));
|
|
22
|
+
const environment = Object.fromEntries(Object.entries(parent).filter(([key]) => !surfaceOwned(key)));
|
|
23
|
+
for (const [key, value] of Object.entries(process.env)) if (value !== undefined && surfaceOwned(key)) environment[key] = value;
|
|
24
|
+
return environment;
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
export function runChildHost(config = loadChildHostConfig()): void {
|
|
21
28
|
const correlation = { protocolVersion: 1 as const, runId: config.runId, childId: config.childId };
|
|
22
29
|
const socket = net.createConnection(config.socketPath);
|
|
@@ -36,7 +43,7 @@ export function runChildHost(config = loadChildHostConfig()): void {
|
|
|
36
43
|
}
|
|
37
44
|
if (message.kind !== "start_attempt" || active || stopping) { socket.destroy(); return; }
|
|
38
45
|
let child: ChildProcess;
|
|
39
|
-
try { child = spawn(message.command, [...message.args], { cwd: message.cwd, env: message.environment, shell: false, stdio: "inherit", windowsHide: true }); }
|
|
46
|
+
try { child = spawn(message.command, [...message.args], { cwd: message.cwd, env: attemptEnvironment(message.environment, config.surfaceEnvironmentPrefixes), shell: false, stdio: "inherit", windowsHide: true }); }
|
|
40
47
|
catch { send({ ...correlation, kind: "attempt_spawn_error", attemptId: message.attemptId }); continue; }
|
|
41
48
|
active = { attemptId: message.attemptId, process: child };
|
|
42
49
|
send({ ...correlation, kind: "attempt_started", attemptId: message.attemptId });
|
|
@@ -54,6 +54,10 @@ function assertBackend(backend: ExecutionBackend): void {
|
|
|
54
54
|
for (const method of ["detect", "launch", "reattach", "close"] as const) {
|
|
55
55
|
if (typeof backend[method] !== "function") throw new Error(`Execution backend ${method} must be a function`);
|
|
56
56
|
}
|
|
57
|
+
const prefixes = backend.surfaceEnvironmentPrefixes;
|
|
58
|
+
if (prefixes !== undefined && (!Array.isArray(prefixes) || prefixes.some(prefix => typeof prefix !== "string" || prefix.trim() === ""))) {
|
|
59
|
+
throw new Error("Execution backend surfaceEnvironmentPrefixes must be nonblank strings");
|
|
60
|
+
}
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
export function registerExecutionBackend(
|
|
@@ -77,6 +77,12 @@ export type ExecutionBackendCloseReason = "delivered" | "explicit_cleanup";
|
|
|
77
77
|
export interface ExecutionBackend {
|
|
78
78
|
readonly name: string;
|
|
79
79
|
readonly protocolVersion: typeof EXECUTION_BACKEND_PROTOCOL_VERSION;
|
|
80
|
+
/**
|
|
81
|
+
* Environment-variable prefixes the surface owns, such as a terminal's own
|
|
82
|
+
* surface identity. Each attempt gets the launched surface's values for
|
|
83
|
+
* matching keys instead of the parent's.
|
|
84
|
+
*/
|
|
85
|
+
readonly surfaceEnvironmentPrefixes?: readonly string[];
|
|
80
86
|
detect(): Promise<ExecutionBackendDetection>;
|
|
81
87
|
launch(request: ExecutionSurfaceRequest): Promise<ExecutionBackendLease>;
|
|
82
88
|
reattach(handle: ExecutionSurfaceHandle): Promise<ReattachResult>;
|
|
@@ -57,6 +57,8 @@ 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;
|
|
60
62
|
executionBackend?: string;
|
|
61
63
|
}
|
|
62
64
|
|
|
@@ -632,6 +634,7 @@ export function executeAsyncChain(
|
|
|
632
634
|
sessionDir: path.join(sessionRoot ?? path.join(RUN_STATE_ROOT_DIR, "sessions"), `async-${id}`),
|
|
633
635
|
asyncDir,
|
|
634
636
|
sessionId: ctx.currentSessionId,
|
|
637
|
+
parentSessionLabel: ctx.parentSessionLabel,
|
|
635
638
|
runtime: ctx.runtime,
|
|
636
639
|
forwardedFlags,
|
|
637
640
|
worktreeSetupHook,
|
|
@@ -846,6 +849,7 @@ export function executeAsyncSingle(
|
|
|
846
849
|
sessionDir: path.join(sessionRoot ?? path.join(RUN_STATE_ROOT_DIR, "sessions"), `async-${id}`),
|
|
847
850
|
asyncDir,
|
|
848
851
|
sessionId: ctx.currentSessionId,
|
|
852
|
+
parentSessionLabel: ctx.parentSessionLabel,
|
|
849
853
|
runtime: ctx.runtime,
|
|
850
854
|
forwardedFlags,
|
|
851
855
|
worktreeSetupHook,
|
|
@@ -117,6 +117,7 @@ interface SubagentRunConfig {
|
|
|
117
117
|
sessionDir?: string;
|
|
118
118
|
asyncDir: string;
|
|
119
119
|
sessionId?: string | null;
|
|
120
|
+
parentSessionLabel?: string;
|
|
120
121
|
runtime: PiRuntimeIdentity;
|
|
121
122
|
forwardedFlags?: string[];
|
|
122
123
|
worktreeSetupHook?: string;
|
|
@@ -625,6 +626,7 @@ export interface SingleStepContext {
|
|
|
625
626
|
outputFile: string;
|
|
626
627
|
runtime: PiRuntimeIdentity;
|
|
627
628
|
forwardedFlags?: string[];
|
|
629
|
+
parentSessionLabel?: string;
|
|
628
630
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void;
|
|
629
631
|
nestedRoute?: NestedRouteInfo;
|
|
630
632
|
onAttemptStart?: (attempt: { model?: string; thinking?: string }) => void;
|
|
@@ -790,6 +792,7 @@ export async function runSingleStep(
|
|
|
790
792
|
runId: ctx.id,
|
|
791
793
|
childAgentName: step.agent,
|
|
792
794
|
childIndex: ctx.flatIndex,
|
|
795
|
+
parentSessionLabel: ctx.parentSessionLabel,
|
|
793
796
|
parentEventSink: ctx.nestedRoute?.eventSink,
|
|
794
797
|
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
795
798
|
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
@@ -936,6 +939,7 @@ export async function runSingleStep(
|
|
|
936
939
|
runId: ctx.id,
|
|
937
940
|
childAgentName: step.agent,
|
|
938
941
|
childIndex: ctx.flatIndex,
|
|
942
|
+
parentSessionLabel: ctx.parentSessionLabel,
|
|
939
943
|
parentEventSink: ctx.nestedRoute?.eventSink,
|
|
940
944
|
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
941
945
|
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
@@ -1978,6 +1982,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1978
1982
|
runtime: config.runtime,
|
|
1979
1983
|
controlConfig: config.controlConfig,
|
|
1980
1984
|
forwardedFlags: config.forwardedFlags,
|
|
1985
|
+
parentSessionLabel: config.parentSessionLabel,
|
|
1981
1986
|
executionBackendCoordinator,
|
|
1982
1987
|
executionBackendConfig,
|
|
1983
1988
|
nestedRoute: config.nestedRoute,
|
|
@@ -2268,6 +2273,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2268
2273
|
runtime: config.runtime,
|
|
2269
2274
|
controlConfig: config.controlConfig,
|
|
2270
2275
|
forwardedFlags: config.forwardedFlags,
|
|
2276
|
+
parentSessionLabel: config.parentSessionLabel,
|
|
2271
2277
|
executionBackendCoordinator,
|
|
2272
2278
|
executionBackendConfig,
|
|
2273
2279
|
nestedRoute: config.nestedRoute,
|
|
@@ -2448,6 +2454,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2448
2454
|
runtime: config.runtime,
|
|
2449
2455
|
controlConfig: config.controlConfig,
|
|
2450
2456
|
forwardedFlags: config.forwardedFlags,
|
|
2457
|
+
parentSessionLabel: config.parentSessionLabel,
|
|
2451
2458
|
executionBackendCoordinator,
|
|
2452
2459
|
executionBackendConfig,
|
|
2453
2460
|
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,7 @@ 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),
|
|
321
323
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
322
324
|
structuredOutput: structuredRuntime,
|
|
323
325
|
acceptance: task.acceptance,
|
|
@@ -1161,6 +1163,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1161
1163
|
preferredModelProvider: ctx.model?.provider,
|
|
1162
1164
|
parentModel: parentModelFullId(ctx.model),
|
|
1163
1165
|
parentThinking: ctx.thinkingLevel,
|
|
1166
|
+
parentSessionLabel: resolveSessionLabel(ctx.sessionManager),
|
|
1164
1167
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
1165
1168
|
structuredOutput: structuredRuntime,
|
|
1166
1169
|
acceptance: seqStep.acceptance,
|
|
@@ -158,6 +158,7 @@ async function runSingleAttempt(
|
|
|
158
158
|
runId: options.runId,
|
|
159
159
|
childAgentName: agent.name,
|
|
160
160
|
childIndex: options.index ?? 0,
|
|
161
|
+
parentSessionLabel: options.parentSessionLabel,
|
|
161
162
|
parentEventSink: options.nestedRoute?.eventSink,
|
|
162
163
|
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
163
164
|
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
@@ -92,6 +92,7 @@ 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,
|
|
95
96
|
parentEventSink: options.nestedRoute?.eventSink,
|
|
96
97
|
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
97
98
|
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,7 @@ 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),
|
|
228
229
|
executionBackend: deps.config.executionBackend,
|
|
229
230
|
};
|
|
230
231
|
}
|
|
@@ -1503,6 +1504,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
1503
1504
|
preferredModelProvider: input.ctx.model?.provider,
|
|
1504
1505
|
parentModel: parentModelFullId(input.ctx.model),
|
|
1505
1506
|
parentThinking: input.ctx.thinkingLevel,
|
|
1507
|
+
parentSessionLabel: resolveSessionLabel(input.ctx.sessionManager),
|
|
1506
1508
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
1507
1509
|
acceptance: task.acceptance,
|
|
1508
1510
|
acceptanceContext: { mode: "parallel" },
|
|
@@ -2064,6 +2066,7 @@ async function runSinglePath(data: ExecutionContextData, deps: CallExecutorDeps)
|
|
|
2064
2066
|
preferredModelProvider: currentProvider,
|
|
2065
2067
|
parentModel: parentModelFullId(ctx.model),
|
|
2066
2068
|
parentThinking: ctx.thinkingLevel,
|
|
2069
|
+
parentSessionLabel: resolveSessionLabel(ctx.sessionManager),
|
|
2067
2070
|
skills: effectiveSkills,
|
|
2068
2071
|
acceptance: params.acceptance,
|
|
2069
2072
|
acceptanceContext: { mode: "single" },
|
|
@@ -45,6 +45,8 @@ interface BuildPiArgsInput {
|
|
|
45
45
|
runId?: string;
|
|
46
46
|
childAgentName?: string;
|
|
47
47
|
childIndex?: number;
|
|
48
|
+
/** Parent session display name, or its id when unnamed; names the child session. */
|
|
49
|
+
parentSessionLabel?: string;
|
|
48
50
|
parentEventSink?: string;
|
|
49
51
|
parentControlInbox?: string;
|
|
50
52
|
parentRootRunId?: string;
|
|
@@ -79,6 +81,13 @@ export function applyThinkingSuffix(model: string | undefined, thinking: string
|
|
|
79
81
|
return `${model}:${thinking}`;
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
function childSessionName(input: BuildPiArgsInput): string | undefined {
|
|
85
|
+
if (!input.childAgentName || !input.runId) return undefined;
|
|
86
|
+
const index = input.childIndex ? `/${input.childIndex}` : "";
|
|
87
|
+
const parent = input.parentSessionLabel ? ` from ${input.parentSessionLabel}` : "";
|
|
88
|
+
return `${input.childAgentName} ${input.runId.slice(0, 8)}${index}${parent}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
export function buildPiArgs(input: BuildPiArgsInput, fileSystem: BuildPiArgsFileSystem = fs): BuildPiArgsResult {
|
|
83
92
|
const args = [...input.baseArgs];
|
|
84
93
|
// Forwarded flags are already name-deduped by deriveForwardedFlags; append only
|
|
@@ -100,6 +109,11 @@ export function buildPiArgs(input: BuildPiArgsInput, fileSystem: BuildPiArgsFile
|
|
|
100
109
|
}
|
|
101
110
|
}
|
|
102
111
|
|
|
112
|
+
const sessionName = childSessionName(input);
|
|
113
|
+
if (sessionName) {
|
|
114
|
+
args.push("--name", sessionName);
|
|
115
|
+
}
|
|
116
|
+
|
|
103
117
|
const modelArg = applyThinkingSuffix(input.model, input.thinking);
|
|
104
118
|
if (modelArg) {
|
|
105
119
|
args.push("--model", modelArg);
|
|
@@ -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,8 @@ 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;
|
|
833
835
|
/** Skills to inject (overrides agent default if provided) */
|
|
834
836
|
skills?: string[];
|
|
835
837
|
structuredOutput?: {
|