@gotgenes/pi-subagents 6.18.0 → 6.18.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 +9 -0
- package/docs/plans/0165-decompose-resolved-spawn-config.md +157 -0
- package/docs/retro/0165-decompose-resolved-spawn-config.md +40 -0
- package/package.json +1 -1
- package/src/tools/agent-tool.ts +3 -3
- package/src/tools/background-spawner.ts +14 -14
- package/src/tools/foreground-runner.ts +16 -16
- package/src/tools/spawn-config.ts +32 -20
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [6.18.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.18.0...pi-subagents-v6.18.1) (2026-05-24)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* plan decompose ResolvedSpawnConfig ([#165](https://github.com/gotgenes/pi-packages/issues/165)) ([1b14d56](https://github.com/gotgenes/pi-packages/commit/1b14d56aaada427a7344ec22adb4bbf8d7ce0bf7))
|
|
14
|
+
* **retro:** add planning stage notes for issue [#165](https://github.com/gotgenes/pi-packages/issues/165) ([8e0476a](https://github.com/gotgenes/pi-packages/commit/8e0476afa991953884455c7dd09c7ffb742cb329))
|
|
15
|
+
* **retro:** add TDD stage notes for issue [#165](https://github.com/gotgenes/pi-packages/issues/165) ([68248e5](https://github.com/gotgenes/pi-packages/commit/68248e572d38ad6e0cdb61bdd22f2b46193eaac6))
|
|
16
|
+
|
|
8
17
|
## [6.18.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.17.2...pi-subagents-v6.18.0) (2026-05-24)
|
|
9
18
|
|
|
10
19
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 165
|
|
3
|
+
issue_title: "refactor(pi-subagents): decompose ResolvedSpawnConfig (15 fields)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Decompose ResolvedSpawnConfig into domain-aligned sub-interfaces
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
`ResolvedSpawnConfig` in `tools/spawn-config.ts` has 15 fields mixing identity, execution, and presentation concerns.
|
|
11
|
+
Each consumer uses a different subset but receives the full bag — violating ISP and making the real dependencies of `foreground-runner` and `background-spawner` invisible.
|
|
12
|
+
|
|
13
|
+
## Goals
|
|
14
|
+
|
|
15
|
+
- Split `ResolvedSpawnConfig` into three focused interfaces: `SpawnIdentity`, `SpawnExecution`, `SpawnPresentation`.
|
|
16
|
+
- Each consumer declares its real dependencies explicitly.
|
|
17
|
+
- Preserve existing behavior — pure structural refactor with no behavioral changes.
|
|
18
|
+
|
|
19
|
+
## Non-Goals
|
|
20
|
+
|
|
21
|
+
- Extracting `ParentSessionInfo` from `AgentSpawnConfig` — that's #166.
|
|
22
|
+
- Changing how `resolveSpawnConfig` computes values internally.
|
|
23
|
+
- Modifying the `AgentSpawnConfig` interface passed to `AgentManager`.
|
|
24
|
+
|
|
25
|
+
## Background
|
|
26
|
+
|
|
27
|
+
`resolveSpawnConfig` is called by `agent-tool.ts` and produces a single flat config object.
|
|
28
|
+
Three consumers read from it:
|
|
29
|
+
|
|
30
|
+
1. `agent-tool.ts` — reads `inheritContext` (to build snapshot), `runInBackground` (to branch), and `detailBase` (for resume result).
|
|
31
|
+
2. `foreground-runner.ts` — reads identity fields for fallback messages, execution fields for spawn options, and `detailBase` for result formatting.
|
|
32
|
+
3. `background-spawner.ts` — reads identity fields for launch message, execution fields for spawn options, and `detailBase` for result formatting.
|
|
33
|
+
|
|
34
|
+
Two fields (`modelName`, `agentTags`) are never accessed by any external consumer — they're intermediate values used only inside `resolveSpawnConfig` to build `detailBase`.
|
|
35
|
+
They belong on `SpawnPresentation` for transparency but could also be made internal-only.
|
|
36
|
+
|
|
37
|
+
The `code-design` skill's ISP and dependency-width guidance both apply: clients should not depend on properties they don't use, and a shared bag where each consumer only touches a subset hides real dependencies.
|
|
38
|
+
|
|
39
|
+
## Design Overview
|
|
40
|
+
|
|
41
|
+
### New interfaces
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
/** Identity: who is being spawned. */
|
|
45
|
+
export interface SpawnIdentity {
|
|
46
|
+
subagentType: string;
|
|
47
|
+
rawType: SubagentType;
|
|
48
|
+
fellBack: boolean;
|
|
49
|
+
displayName: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Execution: how the agent will run. */
|
|
53
|
+
export interface SpawnExecution {
|
|
54
|
+
prompt: string;
|
|
55
|
+
description: string;
|
|
56
|
+
model: Model<any> | undefined;
|
|
57
|
+
effectiveMaxTurns: number | undefined;
|
|
58
|
+
thinking: ThinkingLevel | undefined;
|
|
59
|
+
inheritContext: boolean;
|
|
60
|
+
runInBackground: boolean;
|
|
61
|
+
isolated: boolean;
|
|
62
|
+
isolation: IsolationMode | undefined;
|
|
63
|
+
agentInvocation: AgentInvocation;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Presentation: display/UI values derived from identity + execution. */
|
|
67
|
+
export interface SpawnPresentation {
|
|
68
|
+
modelName: string | undefined;
|
|
69
|
+
agentTags: string[];
|
|
70
|
+
detailBase: Pick<AgentDetails, "displayName" | "description" | "subagentType" | "modelName" | "tags">;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Fully resolved config — now a composition of domain-aligned sub-interfaces. */
|
|
74
|
+
export interface ResolvedSpawnConfig {
|
|
75
|
+
identity: SpawnIdentity;
|
|
76
|
+
execution: SpawnExecution;
|
|
77
|
+
presentation: SpawnPresentation;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Consumer interaction pattern
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// agent-tool.ts — uses execution for routing, presentation for resume
|
|
85
|
+
const config = resolveSpawnConfig(params, registry, modelInfo, settings);
|
|
86
|
+
if ("error" in config) return textResult(config.error);
|
|
87
|
+
const snapshot = buildSnapshot(config.execution.inheritContext);
|
|
88
|
+
if (config.execution.runInBackground) { /* ... */ }
|
|
89
|
+
return buildDetails(config.presentation.detailBase, record);
|
|
90
|
+
|
|
91
|
+
// foreground-runner.ts — destructures what it needs
|
|
92
|
+
const { identity, execution, presentation } = params.config;
|
|
93
|
+
record = await manager.spawnAndWait(snapshot, identity.subagentType, execution.prompt, { ... });
|
|
94
|
+
const fallbackNote = identity.fellBack ? `Note: Unknown agent type "${identity.rawType}"...` : "";
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This follows Tell-Don't-Ask — callers pick the sub-object relevant to their concern rather than reaching through a flat bag.
|
|
98
|
+
The one-level nesting (`config.execution.inheritContext`) is acceptable because it names the domain the field belongs to.
|
|
99
|
+
|
|
100
|
+
### Test factory migration
|
|
101
|
+
|
|
102
|
+
Both test files (`foreground-runner.test.ts`, `background-spawner.test.ts`) have `makeConfig()` factories that construct the full 15-field flat object.
|
|
103
|
+
These will be updated to construct the nested structure.
|
|
104
|
+
The `spawn-config.test.ts` assertions will shift from `result.subagentType` to `result.identity.subagentType` etc.
|
|
105
|
+
|
|
106
|
+
## Module-Level Changes
|
|
107
|
+
|
|
108
|
+
| File | Change |
|
|
109
|
+
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
110
|
+
| `src/tools/spawn-config.ts` | Add `SpawnIdentity`, `SpawnExecution`, `SpawnPresentation` interfaces. Change `ResolvedSpawnConfig` to nest them. Update `resolveSpawnConfig` return to build nested structure. |
|
|
111
|
+
| `src/tools/agent-tool.ts` | Update config access: `config.execution.inheritContext`, `config.execution.runInBackground`, `config.presentation.detailBase`. |
|
|
112
|
+
| `src/tools/foreground-runner.ts` | Destructure `config` into `identity`, `execution`, `presentation`. Update all field accesses. |
|
|
113
|
+
| `src/tools/background-spawner.ts` | Destructure `config` into `identity`, `execution`, `presentation`. Update all field accesses. |
|
|
114
|
+
| `test/tools/spawn-config.test.ts` | Update all assertions to use nested paths (`result.identity.subagentType`, etc.). |
|
|
115
|
+
| `test/tools/foreground-runner.test.ts` | Update `makeConfig()` factory to build nested structure. |
|
|
116
|
+
| `test/tools/background-spawner.test.ts` | Update `makeConfig()` factory to build nested structure. |
|
|
117
|
+
|
|
118
|
+
## Test Impact Analysis
|
|
119
|
+
|
|
120
|
+
1. No new unit tests are needed — this is a structural refactor, not new behavior.
|
|
121
|
+
2. No existing tests become redundant — all current `spawn-config.test.ts` assertions still verify the same computation.
|
|
122
|
+
3. All existing tests must be updated to match the new nested access paths, but they continue to exercise the same logic.
|
|
123
|
+
|
|
124
|
+
## TDD Order
|
|
125
|
+
|
|
126
|
+
1. **Red→Green: introduce sub-interfaces and nest `ResolvedSpawnConfig`** — change `spawn-config.ts` to export the three sub-interfaces and restructure `ResolvedSpawnConfig`.
|
|
127
|
+
Update `resolveSpawnConfig` to return the nested shape.
|
|
128
|
+
Update `spawn-config.test.ts` assertions to match.
|
|
129
|
+
Commit: `refactor(pi-subagents): introduce SpawnIdentity, SpawnExecution, SpawnPresentation`
|
|
130
|
+
|
|
131
|
+
2. **Green: update `agent-tool.ts`** — migrate the three field accesses (`inheritContext`, `runInBackground`, `detailBase`) to nested paths.
|
|
132
|
+
Run `pnpm run check` to confirm types pass.
|
|
133
|
+
Commit: `refactor(pi-subagents): update agent-tool to use nested spawn config`
|
|
134
|
+
|
|
135
|
+
3. **Green: update `foreground-runner.ts` and its test** — destructure config and update all field accesses.
|
|
136
|
+
Update `makeConfig()` factory in `foreground-runner.test.ts`.
|
|
137
|
+
Commit: `refactor(pi-subagents): update foreground-runner to use nested spawn config`
|
|
138
|
+
|
|
139
|
+
4. **Green: update `background-spawner.ts` and its test** — destructure config and update all field accesses.
|
|
140
|
+
Update `makeConfig()` factory in `background-spawner.test.ts`.
|
|
141
|
+
Commit: `refactor(pi-subagents): update background-spawner to use nested spawn config`
|
|
142
|
+
|
|
143
|
+
5. **Verify: full suite** — run `pnpm vitest run` and `pnpm run check` to confirm no regressions.
|
|
144
|
+
Commit (if any lint/type cleanup needed): `chore(pi-subagents): post-decomposition cleanup`
|
|
145
|
+
|
|
146
|
+
## Risks and Mitigations
|
|
147
|
+
|
|
148
|
+
| Risk | Mitigation |
|
|
149
|
+
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
150
|
+
| Step 1 breaks type checking for all consumers simultaneously | Steps 2–4 must land in the same branch before pushing; or use a transitional type alias that spreads the sub-interfaces flat, removing it in the final step. |
|
|
151
|
+
| Test factories diverge from production shape | Each step updates the test factory in the same commit as the source change. |
|
|
152
|
+
| `modelName` and `agentTags` on `SpawnPresentation` look unused | They are unused by external consumers today but provide inspection affordance for debugging/logging. Keep them; #166 or later work may consume them. |
|
|
153
|
+
|
|
154
|
+
## Open Questions
|
|
155
|
+
|
|
156
|
+
- None — the issue's proposed shape aligns with actual field usage patterns.
|
|
157
|
+
The only observation is that `modelName` and `agentTags` are only consumed internally, but exposing them on `SpawnPresentation` is harmless and aids debuggability.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 165
|
|
3
|
+
issue_title: "refactor(pi-subagents): decompose ResolvedSpawnConfig (15 fields)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #165 — decompose ResolvedSpawnConfig (15 fields)
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-05-24T13:41:41Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Produced a 5-step TDD plan to decompose the 15-field `ResolvedSpawnConfig` into three nested sub-interfaces (`SpawnIdentity`, `SpawnExecution`, `SpawnPresentation`).
|
|
13
|
+
Also improved skill descriptions for `colgrep` and `markdown-conventions` to signal decision-relevant content rather than tool reference material.
|
|
14
|
+
|
|
15
|
+
### Observations
|
|
16
|
+
|
|
17
|
+
- The proposed decomposition in the issue aligns well with actual field usage patterns — no adjustments needed.
|
|
18
|
+
- `modelName` and `agentTags` are never accessed by external consumers; they're intermediate computation exposed on the return type.
|
|
19
|
+
Keeping them on `SpawnPresentation` is harmless and aids debuggability.
|
|
20
|
+
- Step 1 (interface change + return restructure) will break type checking for all consumers simultaneously.
|
|
21
|
+
The plan addresses this by landing steps 1–4 in rapid succession on the same branch.
|
|
22
|
+
- Both test files have `makeConfig()` factories that must be updated in lock-step with their respective source files.
|
|
23
|
+
- Issue #164 (directory reorganization) is closed, so import paths are already in their final `#src/<domain>/` form.
|
|
24
|
+
|
|
25
|
+
## Stage: Implementation — TDD (2026-05-24T14:32:58Z)
|
|
26
|
+
|
|
27
|
+
### Session summary
|
|
28
|
+
|
|
29
|
+
Completed all 4 TDD cycles plus full-suite verification in one session.
|
|
30
|
+
The decomposition touched 7 files (4 source, 3 test) and kept the test count flat at 805 — no new tests needed for a pure structural refactor.
|
|
31
|
+
|
|
32
|
+
### Observations
|
|
33
|
+
|
|
34
|
+
- The `Partial<ResolvedSpawnConfig>` spread pattern in `makeConfig` factories doesn't deep-merge into nested sub-objects.
|
|
35
|
+
Two tests (`foreground-runner.test.ts` and `background-spawner.test.ts`) used flat field overrides (`{ fellBack: true }`, `{ description: "my task" }`) that silently stopped working after nesting.
|
|
36
|
+
Fixed by writing out the full nested sub-object at the override call site.
|
|
37
|
+
Future factories for nested config types should either deep-merge or avoid the `Partial<T>` spread pattern — see the `testing` skill's warning about this.
|
|
38
|
+
- Step 1 breaking all consumers simultaneously was handled smoothly by completing all steps before pushing, as planned.
|
|
39
|
+
No transitional alias was needed.
|
|
40
|
+
- The `background-spawner.test.ts` description-override test was the only unexpected friction point — the flat spread issue wasn't caught by the plan.
|
package/package.json
CHANGED
package/src/tools/agent-tool.ts
CHANGED
|
@@ -301,7 +301,7 @@ Guidelines:
|
|
|
301
301
|
if ("error" in config) return textResult(config.error);
|
|
302
302
|
|
|
303
303
|
// ---- Boundary extraction (after config so inheritContext is resolved) ----
|
|
304
|
-
const snapshot = buildSnapshot(config.inheritContext);
|
|
304
|
+
const snapshot = buildSnapshot(config.execution.inheritContext);
|
|
305
305
|
const { parentSessionFile, parentSessionId } = getSessionInfo();
|
|
306
306
|
|
|
307
307
|
// ---- Resume existing agent ----
|
|
@@ -327,12 +327,12 @@ Guidelines:
|
|
|
327
327
|
}
|
|
328
328
|
return textResult(
|
|
329
329
|
record.result?.trim() ?? record.error?.trim() ?? "No output.",
|
|
330
|
-
buildDetails(config.detailBase, record),
|
|
330
|
+
buildDetails(config.presentation.detailBase, record),
|
|
331
331
|
);
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
// ---- Background execution ----
|
|
335
|
-
if (config.runInBackground) {
|
|
335
|
+
if (config.execution.runInBackground) {
|
|
336
336
|
return spawnBackground(
|
|
337
337
|
manager,
|
|
338
338
|
widget,
|
|
@@ -40,23 +40,23 @@ export function spawnBackground(
|
|
|
40
40
|
agentActivity: AgentActivityAccess,
|
|
41
41
|
params: BackgroundParams,
|
|
42
42
|
) {
|
|
43
|
-
const {
|
|
44
|
-
const bgState = new AgentActivityTracker(
|
|
43
|
+
const { identity, execution, presentation } = params.config;
|
|
44
|
+
const bgState = new AgentActivityTracker(execution.effectiveMaxTurns);
|
|
45
45
|
|
|
46
46
|
let id: string;
|
|
47
47
|
try {
|
|
48
|
-
id = manager.spawn(params.snapshot,
|
|
48
|
+
id = manager.spawn(params.snapshot, identity.subagentType, execution.prompt, {
|
|
49
49
|
parentSessionFile: params.parentSessionFile,
|
|
50
50
|
parentSessionId: params.parentSessionId,
|
|
51
|
-
description:
|
|
52
|
-
model:
|
|
53
|
-
maxTurns:
|
|
54
|
-
isolated:
|
|
55
|
-
inheritContext:
|
|
56
|
-
thinkingLevel:
|
|
51
|
+
description: execution.description,
|
|
52
|
+
model: execution.model,
|
|
53
|
+
maxTurns: execution.effectiveMaxTurns,
|
|
54
|
+
isolated: execution.isolated,
|
|
55
|
+
inheritContext: execution.inheritContext,
|
|
56
|
+
thinkingLevel: execution.thinking,
|
|
57
57
|
isBackground: true,
|
|
58
|
-
isolation:
|
|
59
|
-
invocation:
|
|
58
|
+
isolation: execution.isolation,
|
|
59
|
+
invocation: execution.agentInvocation,
|
|
60
60
|
toolCallId: params.toolCallId,
|
|
61
61
|
onSessionCreated: (session) => {
|
|
62
62
|
bgState.setSession(session);
|
|
@@ -77,8 +77,8 @@ export function spawnBackground(
|
|
|
77
77
|
return textResult(
|
|
78
78
|
`Agent ${isQueued ? "queued" : "started"} in background.\n` +
|
|
79
79
|
`Agent ID: ${id}\n` +
|
|
80
|
-
`Type: ${
|
|
81
|
-
`Description: ${
|
|
80
|
+
`Type: ${identity.displayName}\n` +
|
|
81
|
+
`Description: ${execution.description}\n` +
|
|
82
82
|
(record?.outputFile ? `Output file: ${record.outputFile}\n` : "") +
|
|
83
83
|
(isQueued
|
|
84
84
|
? `Position: queued (max ${manager.getMaxConcurrent()} concurrent)\n`
|
|
@@ -87,7 +87,7 @@ export function spawnBackground(
|
|
|
87
87
|
`Use get_subagent_result to retrieve full results, or steer_subagent to send it messages.\n` +
|
|
88
88
|
`Do not duplicate this agent's work.`,
|
|
89
89
|
{
|
|
90
|
-
...
|
|
90
|
+
...presentation.detailBase,
|
|
91
91
|
toolUses: 0,
|
|
92
92
|
tokens: "",
|
|
93
93
|
durationMs: 0,
|
|
@@ -56,19 +56,19 @@ export async function runForeground(
|
|
|
56
56
|
signal: AbortSignal | undefined,
|
|
57
57
|
onUpdate: ((update: AgentToolResult<any>) => void) | undefined,
|
|
58
58
|
) {
|
|
59
|
-
const {
|
|
59
|
+
const { identity, execution, presentation } = params.config;
|
|
60
60
|
let spinnerFrame = 0;
|
|
61
61
|
const startedAt = Date.now();
|
|
62
62
|
let fgId: string | undefined;
|
|
63
63
|
|
|
64
|
-
const fgState = new AgentActivityTracker(
|
|
64
|
+
const fgState = new AgentActivityTracker(execution.effectiveMaxTurns);
|
|
65
65
|
let unsubUI: (() => void) | undefined;
|
|
66
66
|
let recordRef: AgentRecord | undefined;
|
|
67
67
|
|
|
68
68
|
const streamUpdate = () => {
|
|
69
69
|
const toolUses = recordRef?.toolUses ?? 0;
|
|
70
70
|
const details: AgentDetails = {
|
|
71
|
-
...
|
|
71
|
+
...presentation.detailBase,
|
|
72
72
|
toolUses,
|
|
73
73
|
tokens: recordRef ? formatLifetimeTokens(recordRef) : "",
|
|
74
74
|
turnCount: fgState.turnCount,
|
|
@@ -97,17 +97,17 @@ export async function runForeground(
|
|
|
97
97
|
try {
|
|
98
98
|
record = await manager.spawnAndWait(
|
|
99
99
|
params.snapshot,
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
identity.subagentType,
|
|
101
|
+
execution.prompt,
|
|
102
102
|
{
|
|
103
|
-
description:
|
|
104
|
-
model:
|
|
105
|
-
maxTurns:
|
|
106
|
-
isolated:
|
|
107
|
-
inheritContext:
|
|
108
|
-
thinkingLevel:
|
|
109
|
-
isolation:
|
|
110
|
-
invocation:
|
|
103
|
+
description: execution.description,
|
|
104
|
+
model: execution.model,
|
|
105
|
+
maxTurns: execution.effectiveMaxTurns,
|
|
106
|
+
isolated: execution.isolated,
|
|
107
|
+
inheritContext: execution.inheritContext,
|
|
108
|
+
thinkingLevel: execution.thinking,
|
|
109
|
+
isolation: execution.isolation,
|
|
110
|
+
invocation: execution.agentInvocation,
|
|
111
111
|
signal,
|
|
112
112
|
parentSessionFile: params.parentSessionFile,
|
|
113
113
|
parentSessionId: params.parentSessionId,
|
|
@@ -137,10 +137,10 @@ export async function runForeground(
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
const tokenText = formatLifetimeTokens(record);
|
|
140
|
-
const details = buildDetails(
|
|
140
|
+
const details = buildDetails(presentation.detailBase, record, fgState, { tokens: tokenText });
|
|
141
141
|
|
|
142
|
-
const fallbackNote =
|
|
143
|
-
? `Note: Unknown agent type "${
|
|
142
|
+
const fallbackNote = identity.fellBack
|
|
143
|
+
? `Note: Unknown agent type "${identity.rawType}" — using general-purpose.\n\n`
|
|
144
144
|
: "";
|
|
145
145
|
|
|
146
146
|
if (record.status === "error") {
|
|
@@ -26,12 +26,16 @@ export interface ModelInfo {
|
|
|
26
26
|
modelRegistry: unknown;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
export interface
|
|
29
|
+
/** Identity: who is being spawned. */
|
|
30
|
+
export interface SpawnIdentity {
|
|
31
31
|
subagentType: string;
|
|
32
32
|
rawType: SubagentType;
|
|
33
33
|
fellBack: boolean;
|
|
34
34
|
displayName: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Execution: how the agent will run. */
|
|
38
|
+
export interface SpawnExecution {
|
|
35
39
|
prompt: string;
|
|
36
40
|
description: string;
|
|
37
41
|
model: Model<any> | undefined;
|
|
@@ -41,12 +45,23 @@ export interface ResolvedSpawnConfig {
|
|
|
41
45
|
runInBackground: boolean;
|
|
42
46
|
isolated: boolean;
|
|
43
47
|
isolation: IsolationMode | undefined;
|
|
44
|
-
modelName: string | undefined;
|
|
45
48
|
agentInvocation: AgentInvocation;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Presentation: display/UI values derived from identity and execution. */
|
|
52
|
+
export interface SpawnPresentation {
|
|
53
|
+
modelName: string | undefined;
|
|
46
54
|
agentTags: string[];
|
|
47
55
|
detailBase: Pick<AgentDetails, "displayName" | "description" | "subagentType" | "modelName" | "tags">;
|
|
48
56
|
}
|
|
49
57
|
|
|
58
|
+
/** Fully resolved config for spawning an agent — composed of domain-aligned sub-interfaces. */
|
|
59
|
+
export interface ResolvedSpawnConfig {
|
|
60
|
+
identity: SpawnIdentity;
|
|
61
|
+
execution: SpawnExecution;
|
|
62
|
+
presentation: SpawnPresentation;
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
/** Error result when model resolution fails. */
|
|
51
66
|
export interface SpawnConfigError {
|
|
52
67
|
error: string;
|
|
@@ -126,22 +141,19 @@ export function resolveSpawnConfig(
|
|
|
126
141
|
};
|
|
127
142
|
|
|
128
143
|
return {
|
|
129
|
-
subagentType,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
modelName,
|
|
143
|
-
agentInvocation,
|
|
144
|
-
agentTags,
|
|
145
|
-
detailBase,
|
|
144
|
+
identity: { subagentType, rawType, fellBack, displayName },
|
|
145
|
+
execution: {
|
|
146
|
+
prompt: params.prompt as string,
|
|
147
|
+
description: params.description as string,
|
|
148
|
+
model,
|
|
149
|
+
effectiveMaxTurns,
|
|
150
|
+
thinking,
|
|
151
|
+
inheritContext,
|
|
152
|
+
runInBackground,
|
|
153
|
+
isolated,
|
|
154
|
+
isolation,
|
|
155
|
+
agentInvocation,
|
|
156
|
+
},
|
|
157
|
+
presentation: { modelName, agentTags, detailBase },
|
|
146
158
|
};
|
|
147
159
|
}
|