@mystilleef/pi-subagent 0.8.0 → 0.10.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/README.md +76 -27
- package/package.json +9 -9
- package/src/agent/agent-cache.ts +1 -0
- package/src/agent/agents.ts +25 -4
- package/src/child/child-events.ts +22 -20
- package/src/child/process.ts +288 -125
- package/src/child/termination.ts +320 -5
- package/src/env.d.ts +15 -0
- package/src/notification/delivery.ts +231 -0
- package/src/notification/desktop-notification.ts +73 -0
- package/src/orchestration/run-command.ts +1 -1
- package/src/orchestration/run-registry.ts +1 -2
- package/src/orchestration/subagent-orchestrator.ts +57 -82
- package/src/output/normalize.ts +2 -2
- package/src/output/ui.ts +17 -28
- package/src/progress/progress-format.ts +111 -0
- package/src/progress/progress-state.ts +55 -140
- package/src/progress/progress.ts +40 -31
- package/src/progress/result-details.ts +146 -39
- package/src/shared/types.ts +18 -16
- package/src/shared/utils.ts +60 -11
- package/tsconfig.json +9 -11
package/README.md
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Subagents for pi
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[
|
|
5
|
-
|
|
6
|
-
separate child Pi processes. Designed especially for the `SPAE`
|
|
7
|
-
framework, but doesn't require it.
|
|
8
|
-
|
|
9
|
-
## Action
|
|
10
|
-
|
|
11
|
-
Agents in
|
|
3
|
+
Designed to orchestrate agents for the
|
|
4
|
+
[SPAE framework](https://github.com/mystilleef/spae-framework). Agents
|
|
5
|
+
in
|
|
12
6
|
[action](https://raw.githubusercontent.com/mystilleef/pi-subagent/main/assets/parallel-agents-demo.mp4).
|
|
13
7
|
|
|
8
|
+
---
|
|
9
|
+
|
|
14
10
|
## Installation
|
|
15
11
|
|
|
16
12
|
**Install from `npm`:**
|
|
@@ -25,13 +21,19 @@ pi install npm:@mystilleef/pi-subagent
|
|
|
25
21
|
pi -e npm:@mystilleef/pi-subagent
|
|
26
22
|
```
|
|
27
23
|
|
|
24
|
+
---
|
|
25
|
+
|
|
28
26
|
## Features
|
|
29
27
|
|
|
30
28
|
- **Asynchronous:** Agents run in the background.
|
|
31
29
|
- **Parallel:** Run many agents simultaneously.
|
|
32
30
|
- **Isolated:** Each delegated task receives a separate context window.
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
31
|
+
- **Nested:** `Subagents` can spawn other `subagents`.
|
|
32
|
+
- **Simple:** No complex orchestration syntax.
|
|
33
|
+
- **Bloat-free:** No bundled agents. Write your own. Or use those from
|
|
34
|
+
the [SPAE Framework](https://github.com/mystilleef/spae-framework).
|
|
35
|
+
|
|
36
|
+
---
|
|
35
37
|
|
|
36
38
|
## Usage
|
|
37
39
|
|
|
@@ -70,12 +72,16 @@ commits; use the query agent to summarize the project.
|
|
|
70
72
|
/cancel-subagent
|
|
71
73
|
```
|
|
72
74
|
|
|
75
|
+
---
|
|
76
|
+
|
|
73
77
|
## _SPAE_ Workflow
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
[
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
The official extension for the
|
|
80
|
+
[SPAE Framework](https://github.com/mystilleef/spae-framework). The
|
|
81
|
+
framework provides pre-built agents and skills for a structured, or
|
|
82
|
+
orchestrated, workflow.
|
|
83
|
+
|
|
84
|
+
### Structured
|
|
79
85
|
|
|
80
86
|
| Phase | Agent | Purpose |
|
|
81
87
|
| ----- | ------------------------- | --------------------------------------------- |
|
|
@@ -85,32 +91,44 @@ structured workflow.
|
|
|
85
91
|
| 4 | `/run build` | Carry out tasks from `PLAN.md` |
|
|
86
92
|
| 5 | `/run verify` | Verify implementation against `SPEC.md` |
|
|
87
93
|
|
|
94
|
+
### Orchestrated
|
|
95
|
+
|
|
96
|
+
| Agent | Purpose |
|
|
97
|
+
| -------------------------------- | --------------------------------------------- |
|
|
98
|
+
| `/run orchestrate <requirement>` | Run all phases of the `SPAE` workflow |
|
|
99
|
+
| `/run prepare <requirement>` | Run preparatory phases of the `SPAE` workflow |
|
|
100
|
+
| `/run spawn` | Spawn build agents for each task in a plan |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
88
104
|
## Agent definitions
|
|
89
105
|
|
|
90
|
-
This
|
|
91
|
-
`frontmatter` and a Markdown system prompt body.
|
|
106
|
+
This extension ships no agents. Define agents as Markdown files with
|
|
107
|
+
YAML `frontmatter` and a Markdown system prompt body.
|
|
92
108
|
|
|
93
|
-
|
|
109
|
+
### Discovery locations
|
|
94
110
|
|
|
95
111
|
- User-global agents: `~/.pi/agents/*.md`
|
|
96
112
|
- Project-local agents: nearest `.pi/agents/*.md`
|
|
97
113
|
|
|
98
|
-
|
|
114
|
+
### Required front matter
|
|
99
115
|
|
|
100
116
|
```yaml
|
|
101
117
|
name: review
|
|
102
118
|
description: Review code for correctness and maintainability.
|
|
103
119
|
```
|
|
104
120
|
|
|
105
|
-
|
|
121
|
+
### Optional front matter
|
|
106
122
|
|
|
107
123
|
```yaml
|
|
108
124
|
tools: read, bash, edit
|
|
109
125
|
skills: code-review
|
|
110
126
|
thinking: medium
|
|
127
|
+
provider: deepseek
|
|
128
|
+
model: deepseek-v4-flash
|
|
111
129
|
```
|
|
112
130
|
|
|
113
|
-
|
|
131
|
+
### Accepted thinking values
|
|
114
132
|
|
|
115
133
|
- `off`
|
|
116
134
|
- `minimal`
|
|
@@ -119,6 +137,8 @@ thinking: medium
|
|
|
119
137
|
- `high`
|
|
120
138
|
- `xhigh`
|
|
121
139
|
|
|
140
|
+
---
|
|
141
|
+
|
|
122
142
|
## Tool
|
|
123
143
|
|
|
124
144
|
The extension also registers a `subagent` tool for model-driven
|
|
@@ -130,8 +150,11 @@ delegation.
|
|
|
130
150
|
- `task`: task prompt for the child agent.
|
|
131
151
|
- `agentScope`: optional lookup scope, one of `user`, `project`, or
|
|
132
152
|
`both`.
|
|
133
|
-
- `debug`: optional flag that
|
|
134
|
-
|
|
153
|
+
- `debug`: optional flag that requests child diagnostic details. Full
|
|
154
|
+
child messages and raw internals require `PI_SUBAGENT_DEBUG_ENABLED=1`
|
|
155
|
+
in the host environment.
|
|
156
|
+
|
|
157
|
+
---
|
|
135
158
|
|
|
136
159
|
## Security
|
|
137
160
|
|
|
@@ -143,19 +166,43 @@ executable automation.
|
|
|
143
166
|
|
|
144
167
|
- Review project-local agents before running them.
|
|
145
168
|
- Avoid delegating secrets unless the agent and tools need them.
|
|
169
|
+
- Treat child-agent prompts, tool arguments, `stderr`, and debug
|
|
170
|
+
transcripts as potentially sensitive.
|
|
171
|
+
- Enable debug details only for trusted investigations. `debug: true` or
|
|
172
|
+
`/run --debug` can expose child conversation transcripts, termination
|
|
173
|
+
internals, and `stderr` only when the host explicitly sets
|
|
174
|
+
`PI_SUBAGENT_DEBUG_ENABLED=1`.
|
|
146
175
|
- Prefer trusted repositories for shared agent definitions.
|
|
147
176
|
- Remember that child agents can call their configured tools.
|
|
148
177
|
|
|
178
|
+
---
|
|
179
|
+
|
|
149
180
|
## Configuration and limits
|
|
150
181
|
|
|
151
182
|
**Environment variables:**
|
|
152
183
|
|
|
153
|
-
- `PI_SUBAGENT_DEPTH`: nested subagent depth
|
|
154
|
-
|
|
184
|
+
- `PI_SUBAGENT_DEPTH`: current nested subagent depth counter set
|
|
185
|
+
internally for child processes.
|
|
186
|
+
- `PI_SUBAGENT_MAX_DEPTH`: max nested subagent depth. Default: `3`.
|
|
187
|
+
Values above `10` clamp to the internal ceiling `10`; deeper nesting
|
|
188
|
+
increases cost, latency, and runaway delegation risk.
|
|
189
|
+
- `PI_SUBAGENT_AGENT_END_GRACE_MS`: child process grace period after
|
|
190
|
+
`agent_end` before forced termination. Default: `250`.
|
|
191
|
+
- `PI_SUBAGENT_MAX_STDERR_BYTES`: max captured child `stderr` bytes.
|
|
192
|
+
Default: `10000`.
|
|
155
193
|
- `PI_SUBAGENT_MAX_OUTPUT_BYTES`: max returned output bytes. Default:
|
|
156
194
|
`50000`.
|
|
157
195
|
- `PI_SUBAGENT_MAX_OUTPUT_LINES`: max returned output lines. Default:
|
|
158
196
|
`500`.
|
|
197
|
+
- `PI_SUBAGENT_DEBUG_ENABLED`: debug detail authorization. Set to `1` to
|
|
198
|
+
allow `debug: true` or `/run --debug` to include sanitized child
|
|
199
|
+
messages, termination internals, and `stderr`; unset values keep
|
|
200
|
+
non-debug detail behavior.
|
|
201
|
+
|
|
202
|
+
Limit variables parse as positive integers. Empty, zero, negative,
|
|
203
|
+
decimal, `Infinity`, and non-numeric values fall back to defaults.
|
|
204
|
+
|
|
205
|
+
---
|
|
159
206
|
|
|
160
207
|
## Troubleshooting
|
|
161
208
|
|
|
@@ -173,7 +220,7 @@ executable automation.
|
|
|
173
220
|
|
|
174
221
|
**Nested subagent blocked:**
|
|
175
222
|
|
|
176
|
-
- Nested delegation hits the `
|
|
223
|
+
- Nested delegation hits the `PI_SUBAGENT_MAX_DEPTH` safety limit.
|
|
177
224
|
- Run the child task directly from the parent session instead.
|
|
178
225
|
|
|
179
226
|
**Truncated output:**
|
|
@@ -182,6 +229,8 @@ executable automation.
|
|
|
182
229
|
`PI_SUBAGENT_MAX_OUTPUT_LINES`.
|
|
183
230
|
- Ask the child agent for a shorter summary.
|
|
184
231
|
|
|
232
|
+
---
|
|
233
|
+
|
|
185
234
|
## Development
|
|
186
235
|
|
|
187
236
|
**Install dependencies:**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mystilleef/pi-subagent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Pi subagent for the SPAE Framework",
|
|
5
5
|
"author": "Lateef Alabi-Oki <mystilleef@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"typecheck": "tsc --noEmit",
|
|
51
51
|
"lint": "biome check --write --unsafe --error-on-warnings .",
|
|
52
52
|
"migrate": "biome migrate --write",
|
|
53
|
-
"coverage": "bun test --
|
|
53
|
+
"coverage": "bun test --parallel --parallel-delay=0",
|
|
54
54
|
"check": "bun lint && bun typecheck",
|
|
55
55
|
"verify": "bun migrate && bun check && bun coverage",
|
|
56
56
|
"pack:smoke": "bun scripts/pack-smoke.ts",
|
|
@@ -64,14 +64,14 @@
|
|
|
64
64
|
"typebox": "*"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@biomejs/biome": "^2.
|
|
68
|
-
"@earendil-works/pi-agent-core": "^0.
|
|
69
|
-
"@earendil-works/pi-ai": "^0.
|
|
70
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
71
|
-
"@earendil-works/pi-tui": "^0.
|
|
67
|
+
"@biomejs/biome": "^2.5.0",
|
|
68
|
+
"@earendil-works/pi-agent-core": "^0.79.3",
|
|
69
|
+
"@earendil-works/pi-ai": "^0.79.3",
|
|
70
|
+
"@earendil-works/pi-coding-agent": "^0.79.3",
|
|
71
|
+
"@earendil-works/pi-tui": "^0.79.3",
|
|
72
72
|
"@types/bun": "^1.3.14",
|
|
73
|
-
"@types/node": "^25.9.
|
|
74
|
-
"typebox": "^1.2.
|
|
73
|
+
"@types/node": "^25.9.3",
|
|
74
|
+
"typebox": "^1.2.9",
|
|
75
75
|
"typescript": "^6.0.3"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/agent/agent-cache.ts
CHANGED
package/src/agent/agents.ts
CHANGED
|
@@ -20,9 +20,11 @@ export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
|
20
20
|
export interface AgentConfig {
|
|
21
21
|
name: string;
|
|
22
22
|
description: string;
|
|
23
|
-
tools?: string[];
|
|
24
|
-
skills?: string[];
|
|
25
|
-
thinking?: ThinkingLevel;
|
|
23
|
+
tools?: string[] | undefined;
|
|
24
|
+
skills?: string[] | undefined;
|
|
25
|
+
thinking?: ThinkingLevel | undefined;
|
|
26
|
+
model?: string | undefined;
|
|
27
|
+
provider?: string | undefined;
|
|
26
28
|
systemPrompt: string;
|
|
27
29
|
source: AgentSource;
|
|
28
30
|
filePath: string;
|
|
@@ -74,6 +76,12 @@ function parseThinkingLevel(raw: unknown): ThinkingLevel | undefined {
|
|
|
74
76
|
: undefined;
|
|
75
77
|
}
|
|
76
78
|
|
|
79
|
+
function parseOptionalString(raw: unknown): string | undefined {
|
|
80
|
+
if (typeof raw !== "string") return undefined;
|
|
81
|
+
const normalized = raw.trim();
|
|
82
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
77
85
|
function parseAgentConfig(
|
|
78
86
|
content: string,
|
|
79
87
|
source: AgentSource,
|
|
@@ -83,6 +91,7 @@ function parseAgentConfig(
|
|
|
83
91
|
try {
|
|
84
92
|
parsed = parseFrontmatter<Record<string, unknown>>(content);
|
|
85
93
|
} catch {
|
|
94
|
+
/* malformed frontmatter returns null to skip invalid agent files */
|
|
86
95
|
return null;
|
|
87
96
|
}
|
|
88
97
|
const { frontmatter, body } = parsed;
|
|
@@ -98,22 +107,31 @@ function parseAgentConfig(
|
|
|
98
107
|
tools: rawTools,
|
|
99
108
|
skills: rawSkills,
|
|
100
109
|
thinking: rawThinking,
|
|
110
|
+
model: rawModel,
|
|
111
|
+
provider: rawProvider,
|
|
101
112
|
} = frontmatter;
|
|
102
113
|
if (typeof name !== "string" || typeof description !== "string") return null;
|
|
103
114
|
if (rawTools != null && typeof rawTools !== "string") return null;
|
|
104
115
|
if (rawSkills != null && typeof rawSkills !== "string") return null;
|
|
105
116
|
if (rawThinking != null && typeof rawThinking !== "string") return null;
|
|
117
|
+
if (rawModel != null && typeof rawModel !== "string") return null;
|
|
118
|
+
if (rawProvider != null && typeof rawProvider !== "string") return null;
|
|
106
119
|
const tools = parseCommaList(rawTools);
|
|
107
120
|
const skills = Object.hasOwn(frontmatter, "skills")
|
|
108
121
|
? (parseCommaList(rawSkills) ?? [])
|
|
109
122
|
: undefined;
|
|
110
123
|
const thinking = parseThinkingLevel(rawThinking);
|
|
124
|
+
const model = parseOptionalString(rawModel);
|
|
125
|
+
const provider = parseOptionalString(rawProvider);
|
|
126
|
+
if (provider !== undefined && model === undefined) return null;
|
|
111
127
|
return {
|
|
112
128
|
name,
|
|
113
129
|
description,
|
|
114
130
|
tools,
|
|
115
131
|
skills,
|
|
116
132
|
thinking,
|
|
133
|
+
model,
|
|
134
|
+
provider,
|
|
117
135
|
systemPrompt: body,
|
|
118
136
|
source,
|
|
119
137
|
filePath,
|
|
@@ -130,6 +148,7 @@ async function loadAgentEntryAsync(
|
|
|
130
148
|
try {
|
|
131
149
|
content = await fsPromises.readFile(filePath, "utf-8");
|
|
132
150
|
} catch {
|
|
151
|
+
/* unreadable files are skipped silently during agent discovery */
|
|
133
152
|
return null;
|
|
134
153
|
}
|
|
135
154
|
return parseAgentConfig(content, source, filePath);
|
|
@@ -156,7 +175,7 @@ async function loadAgentsFromDirAsync(
|
|
|
156
175
|
return { agents, markdownFiles };
|
|
157
176
|
}
|
|
158
177
|
|
|
159
|
-
|
|
178
|
+
function isMarkdownDirent(entry: Dirent): boolean {
|
|
160
179
|
return (
|
|
161
180
|
entry.name.endsWith(".md") && (entry.isFile() || entry.isSymbolicLink())
|
|
162
181
|
);
|
|
@@ -175,6 +194,7 @@ export async function readMarkdownDirWithStatusAsync(
|
|
|
175
194
|
const entries = await fsPromises.readdir(dir, { withFileTypes: true });
|
|
176
195
|
return { entries: entries.filter(isMarkdownDirent), ok: true };
|
|
177
196
|
} catch {
|
|
197
|
+
/* missing or inaccessible directories return empty listing */
|
|
178
198
|
return { entries: [], ok: false };
|
|
179
199
|
}
|
|
180
200
|
}
|
|
@@ -187,6 +207,7 @@ export async function isDirectoryAsync(p: string): Promise<boolean> {
|
|
|
187
207
|
try {
|
|
188
208
|
return (await fsPromises.stat(p)).isDirectory();
|
|
189
209
|
} catch {
|
|
210
|
+
/* stat failures indicate non-existent or inaccessible paths */
|
|
190
211
|
return false;
|
|
191
212
|
}
|
|
192
213
|
}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import { makeToolPreview } from "../output/normalize.js";
|
|
2
2
|
import type { ToolActivity } from "../shared/types.js";
|
|
3
3
|
|
|
4
|
-
// Extracts results[0] from details; null-safe for malformed input.
|
|
5
4
|
function tryFirstResult(details: unknown): Record<string, unknown> | null {
|
|
6
5
|
try {
|
|
7
6
|
if (typeof details !== "object" || details === null) return null;
|
|
8
|
-
const results = (details as Record<string, unknown>)
|
|
7
|
+
const results = (details as Record<string, unknown>)["results"];
|
|
9
8
|
if (!Array.isArray(results) || results.length === 0) return null;
|
|
10
9
|
const nested = results[0];
|
|
11
10
|
if (typeof nested !== "object" || nested === null) return null;
|
|
12
11
|
return nested as Record<string, unknown>;
|
|
13
12
|
} catch {
|
|
13
|
+
/* malformed details structure returns null to use fallback preview */
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
// Malformed details (null result) falls back to { toolName }.
|
|
19
18
|
function parseToolActivity(
|
|
20
19
|
toolName: string,
|
|
21
20
|
partialResult: { content?: unknown; details?: unknown },
|
|
@@ -24,23 +23,25 @@ function parseToolActivity(
|
|
|
24
23
|
if (!nestedRecord) return { toolName, inputSummary: toolName };
|
|
25
24
|
const isSubagent = toolName === "subagent";
|
|
26
25
|
const activity: ToolActivity = { toolName };
|
|
27
|
-
const agent =
|
|
26
|
+
const agent =
|
|
27
|
+
typeof nestedRecord["agent"] === "string" && nestedRecord["agent"];
|
|
28
28
|
activity.inputSummary =
|
|
29
29
|
isSubagent && agent ? makeToolPreview(toolName, nestedRecord) : toolName;
|
|
30
30
|
if (
|
|
31
|
-
typeof nestedRecord
|
|
32
|
-
nestedRecord
|
|
31
|
+
typeof nestedRecord["instanceName"] === "string" &&
|
|
32
|
+
nestedRecord["instanceName"]
|
|
33
33
|
) {
|
|
34
|
-
activity.instanceName = nestedRecord
|
|
34
|
+
activity.instanceName = nestedRecord["instanceName"] as string;
|
|
35
35
|
}
|
|
36
|
-
const progress = nestedRecord
|
|
36
|
+
const progress = nestedRecord["progress"];
|
|
37
37
|
if (typeof progress === "object" && progress !== null) {
|
|
38
|
-
const activeToolActivity = (progress as Record<string, unknown>)
|
|
39
|
-
|
|
38
|
+
const activeToolActivity = (progress as Record<string, unknown>)[
|
|
39
|
+
"activeToolActivity"
|
|
40
|
+
];
|
|
40
41
|
if (
|
|
41
42
|
typeof activeToolActivity === "object" &&
|
|
42
43
|
activeToolActivity !== null &&
|
|
43
|
-
typeof (activeToolActivity as Record<string, unknown>)
|
|
44
|
+
typeof (activeToolActivity as Record<string, unknown>)["toolName"] ===
|
|
44
45
|
"string"
|
|
45
46
|
) {
|
|
46
47
|
const childActivity = activeToolActivity as ToolActivity;
|
|
@@ -90,27 +91,28 @@ export function parseChildEventLine(line: string): ChildEventParseResult {
|
|
|
90
91
|
try {
|
|
91
92
|
event = JSON.parse(line);
|
|
92
93
|
} catch {
|
|
94
|
+
/* malformed JSON lines are marked invalid for diagnostic reporting */
|
|
93
95
|
return { kind: "invalid", line };
|
|
94
96
|
}
|
|
95
97
|
if (
|
|
96
98
|
typeof event === "object" &&
|
|
97
99
|
event !== null &&
|
|
98
100
|
"type" in event &&
|
|
99
|
-
typeof (event as Record<string, unknown>)
|
|
100
|
-
KNOWN_TYPES.has((event as Record<string, unknown>)
|
|
101
|
+
typeof (event as Record<string, unknown>)["type"] === "string" &&
|
|
102
|
+
KNOWN_TYPES.has((event as Record<string, unknown>)["type"] as string)
|
|
101
103
|
) {
|
|
102
104
|
const record = event as Record<string, unknown>;
|
|
103
|
-
if (record
|
|
105
|
+
if (record["type"] === TOOL_EXECUTION_UPDATE_EVENT) {
|
|
104
106
|
if (
|
|
105
|
-
typeof record
|
|
106
|
-
typeof record
|
|
107
|
-
record
|
|
107
|
+
typeof record["toolName"] !== "string" ||
|
|
108
|
+
typeof record["partialResult"] !== "object" ||
|
|
109
|
+
record["partialResult"] === null
|
|
108
110
|
) {
|
|
109
111
|
return { kind: "unknown", event };
|
|
110
112
|
}
|
|
111
|
-
record
|
|
112
|
-
record
|
|
113
|
-
record
|
|
113
|
+
record["toolActivity"] = parseToolActivity(
|
|
114
|
+
record["toolName"] as string,
|
|
115
|
+
record["partialResult"] as { content?: unknown; details?: unknown },
|
|
114
116
|
);
|
|
115
117
|
}
|
|
116
118
|
return { kind: "known", event: event as ChildKnownEvent };
|