pi-harness-delegate 0.1.0 → 0.2.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/README.md +8 -6
- package/extensions/activity.ts +225 -201
- package/extensions/command.ts +61 -61
- package/extensions/config.ts +137 -131
- package/extensions/harnesses/amp.ts +122 -115
- package/extensions/harnesses/claude.ts +136 -111
- package/extensions/harnesses/codex.ts +191 -147
- package/extensions/harnesses/opencode.ts +133 -116
- package/extensions/harnesses/registry.ts +22 -22
- package/extensions/harnesses/types.ts +60 -60
- package/extensions/hint.ts +22 -19
- package/extensions/index.ts +1128 -743
- package/extensions/progress.ts +104 -104
- package/extensions/run-claude.ts +44 -35
- package/extensions/runner.ts +111 -99
- package/extensions/stream-parse.ts +32 -24
- package/extensions/templates.ts +134 -117
- package/extensions/usage.ts +24 -24
- package/package.json +69 -56
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# pi-harness-delegate
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/pi-harness-delegate) [](https://github.com/yorch/pi-harness-delegate/actions/workflows/ci.yml) [](https://github.com/yorch/pi-harness-delegate/actions/workflows/release.yml) [](https://nodejs.org) [](https://bun.sh) [](https://biomejs.dev) [](LICENSE)
|
|
4
|
+
|
|
3
5
|
Delegate work to **any harness** ([Claude Code](https://github.com/anthropics/claude-code), [Muse](https://github.com/openai/codex), [OpenCode](https://opencode.ai), [Amp](https://ampcode.com)) from the [pi coding agent](https://github.com/badlogic/pi-mono): code reviews, detailed plans, implementation, security audits, docs — or your own custom templates.
|
|
4
6
|
|
|
5
7
|
Each harness runs headless in your repo with a normalized permission (`readonly` / `edit` / `danger`). Results stream back live, and token/cost usage feeds into pi's footer stats. Templates are portable — prompt bodies live in `templates/shared/`, harness-specific frontmatter selects the native permission.
|
|
@@ -44,7 +46,7 @@ The `delegate` tool takes: `harness`, `task`, `mode`, `scope` (`diff` = git diff
|
|
|
44
46
|
## Harnesses
|
|
45
47
|
|
|
46
48
|
| Harness | Binary | Permission mapping | Notes |
|
|
47
|
-
|
|
49
|
+
| --- | --- | --- | --- |
|
|
48
50
|
| `claude` | `claude` | `readonly→plan`, `edit→acceptEdits`, `danger→bypassPermissions` | Full stream-json, cost + context% |
|
|
49
51
|
| `codex` | `codex` | `readonly→read-only`, `edit→workspace-write`, `danger→danger-full-access` | `codex exec --json`, best-effort JSONL |
|
|
50
52
|
| `opencode` | `opencode` | `readonly→read-only`, `edit→allow-edit`, `danger→danger` | `opencode run --format json` |
|
|
@@ -55,7 +57,7 @@ Detect availability: `delegate` checks `harness --version` at startup; missing h
|
|
|
55
57
|
## Modes (templates)
|
|
56
58
|
|
|
57
59
|
| Mode | Permission | Purpose |
|
|
58
|
-
|
|
60
|
+
| --- | --- | --- |
|
|
59
61
|
| `review` | `readonly` | Code review, cites `file:line`, prioritized findings |
|
|
60
62
|
| `plan` | `readonly` | Detailed implementation plan with steps + risks |
|
|
61
63
|
| `implement` | `edit` | Implements a task, runs checks, reports changes |
|
|
@@ -167,12 +169,12 @@ Review what the harness is asked to do before granting broad permissions.
|
|
|
167
169
|
## Development
|
|
168
170
|
|
|
169
171
|
```bash
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
bun install
|
|
173
|
+
bun run typecheck
|
|
174
|
+
bun test
|
|
173
175
|
```
|
|
174
176
|
|
|
175
|
-
See
|
|
177
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the project layout, the release dev-loop, and the npm publish gotchas. Agents working in this repo should read [AGENTS.md](AGENTS.md).
|
|
176
178
|
|
|
177
179
|
## License
|
|
178
180
|
|
package/extensions/activity.ts
CHANGED
|
@@ -3,252 +3,276 @@ import { join } from 'node:path';
|
|
|
3
3
|
import type { ActivityEvent } from './harnesses/types.ts';
|
|
4
4
|
|
|
5
5
|
function truncate(s: string, max: number): string {
|
|
6
|
-
|
|
6
|
+
return s.length > max ? `${s.slice(0, max - 1)}…` : s;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/** Make a template/mode name safe for use in a filename. */
|
|
10
10
|
export function safeSegmentName(name: string): string {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const safe = name.replace(/[^a-zA-Z0-9_-]+/g, '_').replace(/^_+|_+$/g, '');
|
|
12
|
+
return safe.length > 0 ? safe : 'delegate';
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface MetricsInput {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
numTurns: number;
|
|
17
|
+
totalCostUsd: number;
|
|
18
|
+
promptTokens: number;
|
|
19
|
+
contextPercent: number | null;
|
|
20
|
+
durationMs: number | null;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/** Compact run summary: `3 turn(s) · $0.54 · 62k tok · 6.2% ctx · 12s`. */
|
|
24
24
|
export function formatMetrics(m: MetricsInput): string {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
const parts: Array<string | null> = [
|
|
26
|
+
`${m.numTurns} turn(s)`,
|
|
27
|
+
`$${m.totalCostUsd.toFixed(3)}`,
|
|
28
|
+
m.promptTokens > 0 ? `${Math.round(m.promptTokens / 1000)}k tok` : null,
|
|
29
|
+
typeof m.contextPercent === 'number' ? `${m.contextPercent.toFixed(1)}% ctx` : null,
|
|
30
|
+
typeof m.durationMs === 'number' && m.durationMs !== null ? `${(m.durationMs / 1000).toFixed(0)}s` : null,
|
|
31
|
+
];
|
|
32
|
+
return parts.filter((p): p is string => Boolean(p)).join(' · ');
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/** Parse the metadata header of a transcript file (without loading the whole body). */
|
|
36
36
|
export function parseTranscriptMeta(head: string): {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
mode: string;
|
|
38
|
+
cost: number;
|
|
39
|
+
sessionId: string | null;
|
|
40
|
+
harness: string | null;
|
|
41
41
|
} {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
let mode = 'delegate';
|
|
43
|
+
let cost = 0;
|
|
44
|
+
let sessionId: string | null = null;
|
|
45
|
+
let harness: string | null = null;
|
|
46
|
+
const mm = /^# Delegated (?:Claude|Harness) run — (.+)$/m.exec(head);
|
|
47
|
+
if (mm) mode = mm[1];
|
|
48
|
+
// Also match new header: # Delegated <harness> run — <mode>
|
|
49
|
+
const hm = /^# Delegated (\w+) run —/m.exec(head);
|
|
50
|
+
if (hm) harness = hm[1].toLowerCase();
|
|
51
|
+
const cm = /\bcost: \$([\d.]+)/.exec(head);
|
|
52
|
+
if (cm) cost = Number(cm[1]);
|
|
53
|
+
const sm = /\bsession: ([0-9a-f-]+)/.exec(head);
|
|
54
|
+
if (sm) sessionId = sm[1];
|
|
55
|
+
// harness explicit field
|
|
56
|
+
const hfm = /^-\s*harness:\s*(\w+)/m.exec(head);
|
|
57
|
+
if (hfm) harness = hfm[1];
|
|
58
|
+
return { mode, cost, sessionId, harness };
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/** Build the markdown report content injected into the session on the next turn. */
|
|
62
62
|
export function buildReportContent(opts: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
harness?: string;
|
|
64
|
+
mode: string;
|
|
65
|
+
metrics: string;
|
|
66
|
+
body: string;
|
|
67
|
+
file?: string;
|
|
68
|
+
sessionId?: string;
|
|
69
69
|
}): string {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
const harness = opts.harness ?? 'claude';
|
|
71
|
+
const header = `## ${harness} ${opts.mode} (${opts.metrics})`;
|
|
72
|
+
const foot: string[] = [];
|
|
73
|
+
if (opts.file) foot.push(`transcript: ${opts.file}`);
|
|
74
|
+
if (opts.sessionId)
|
|
75
|
+
foot.push(
|
|
76
|
+
`resume: \`/delegate --harness=${opts.harness} --resume=${opts.sessionId} <prompt>\` (or /${opts.harness} --resume=${opts.sessionId})`,
|
|
77
|
+
);
|
|
78
|
+
return [header, '', opts.body, foot.length > 0 ? `\n_${foot.join(' · ')}_` : ''].join('\n');
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
/** Legacy wrapper for compat */
|
|
79
82
|
export function buildClaudeReportContent(opts: {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
mode: string;
|
|
84
|
+
metrics: string;
|
|
85
|
+
body: string;
|
|
86
|
+
file?: string;
|
|
87
|
+
sessionId?: string;
|
|
85
88
|
}): string {
|
|
86
|
-
|
|
89
|
+
return buildReportContent({ harness: 'claude', ...opts });
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
/** Delete oldest transcript files beyond `maxCount` (0 = keep everything). */
|
|
90
93
|
export function pruneOutputs(dir: string, maxCount: number): void {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
if (maxCount <= 0) return;
|
|
95
|
+
let files: string[];
|
|
96
|
+
try {
|
|
97
|
+
files = readdirSync(dir);
|
|
98
|
+
} catch {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const byMtime = files
|
|
102
|
+
.filter(f => f.endsWith('.md'))
|
|
103
|
+
.map(f => ({ f, mtime: statSync(join(dir, f), { throwIfNoEntry: false })?.mtimeMs ?? 0 }))
|
|
104
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
105
|
+
for (const { f } of byMtime.slice(maxCount)) {
|
|
106
|
+
try {
|
|
107
|
+
rmSync(join(dir, f));
|
|
108
|
+
} catch {
|
|
109
|
+
// best-effort
|
|
110
|
+
}
|
|
111
|
+
}
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
/** Human-readable one-liner for a tool call (uses Claude's `description` when present). */
|
|
112
115
|
export function formatToolUse(name: string, input: Record<string, unknown>): string {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
if (typeof input.description === 'string' && input.description) {
|
|
117
|
+
return `${name}: ${truncate(input.description, 90)}`;
|
|
118
|
+
}
|
|
119
|
+
if (typeof input.command === 'string') return `${name}: ${truncate(input.command.split('\n')[0], 90)}`;
|
|
120
|
+
if (typeof input.file_path === 'string') return `${name}: ${input.file_path}`;
|
|
121
|
+
if (typeof input.pattern === 'string') return `${name}: ${input.pattern}`;
|
|
122
|
+
if (typeof input.url === 'string') return `${name}: ${input.url}`;
|
|
123
|
+
const first = Object.values(input).find((v): v is string => typeof v === 'string' && v.length > 0);
|
|
124
|
+
return first ? `${name}: ${truncate(first, 90)}` : name;
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
/** Compact per-line activity log for the transcript (tool_input + results only). */
|
|
125
128
|
export function collectActivityLog(events: ActivityEvent[]): string[] {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
const log: string[] = [];
|
|
130
|
+
for (const ev of events) {
|
|
131
|
+
if (ev.kind === 'tool_input') {
|
|
132
|
+
log.push(`▶ ${formatToolUse(ev.name, ev.input)}`);
|
|
133
|
+
} else if (ev.kind === 'tool_result') {
|
|
134
|
+
const last = log.length - 1;
|
|
135
|
+
if (last >= 0 && log[last].startsWith('▶')) {
|
|
136
|
+
log[last] += ev.isError ? ' ✗ error' : ' ✓';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return log;
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
/** Full transcript written to the outputs dir: metadata + activity + output. */
|
|
141
|
-
export function buildTranscript(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
144
|
+
export function buildTranscript(
|
|
145
|
+
opts: {
|
|
146
|
+
harness?: string;
|
|
147
|
+
mode: string;
|
|
148
|
+
permission?: string;
|
|
149
|
+
permissionMode?: string;
|
|
150
|
+
nativePermission?: string;
|
|
151
|
+
model: string | null;
|
|
152
|
+
cwd: string;
|
|
153
|
+
sessionId: string | null;
|
|
154
|
+
resumed: boolean;
|
|
155
|
+
numTurns: number;
|
|
156
|
+
totalCostUsd: number;
|
|
157
|
+
isError: boolean;
|
|
158
|
+
stopReason: string | null;
|
|
159
|
+
durationMs: number | null;
|
|
160
|
+
usage: {
|
|
161
|
+
inputTokens: number;
|
|
162
|
+
outputTokens: number;
|
|
163
|
+
cacheCreationInputTokens: number;
|
|
164
|
+
cacheReadInputTokens: number;
|
|
165
|
+
} | null;
|
|
166
|
+
contextPercent: number | null;
|
|
167
|
+
contextWindow: number | null;
|
|
168
|
+
activityLog: string[];
|
|
169
|
+
output: string;
|
|
170
|
+
} & Record<string, unknown>,
|
|
171
|
+
): string {
|
|
172
|
+
const harness = (opts.harness as string | undefined) ?? 'claude';
|
|
173
|
+
const permissionRaw =
|
|
174
|
+
(opts.permission as string | undefined) ?? (opts.permissionMode as string | undefined) ?? 'edit';
|
|
175
|
+
let permission = permissionRaw;
|
|
176
|
+
let nativePermission = opts.nativePermission as string | undefined;
|
|
177
|
+
// map legacy permissionMode to normalized if needed
|
|
178
|
+
if ((opts as Record<string, unknown>).permissionMode && !opts.permission) {
|
|
179
|
+
const pm = (opts as Record<string, unknown>).permissionMode as string;
|
|
180
|
+
if (pm === 'plan') {
|
|
181
|
+
permission = 'readonly';
|
|
182
|
+
nativePermission = pm;
|
|
183
|
+
} else if (pm === 'bypassPermissions') {
|
|
184
|
+
permission = 'danger';
|
|
185
|
+
nativePermission = pm;
|
|
186
|
+
} else {
|
|
187
|
+
permission = 'edit';
|
|
188
|
+
nativePermission = pm;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const u = opts.usage;
|
|
192
|
+
const tokens = u
|
|
193
|
+
? [
|
|
194
|
+
`input ${u.inputTokens}`,
|
|
195
|
+
`output ${u.outputTokens}`,
|
|
196
|
+
`cache+${u.cacheCreationInputTokens}`,
|
|
197
|
+
`cache ${u.cacheReadInputTokens}`,
|
|
198
|
+
].join(' · ')
|
|
199
|
+
: null;
|
|
200
|
+
const context =
|
|
201
|
+
opts.contextPercent !== null && opts.contextWindow
|
|
202
|
+
? `${opts.contextPercent.toFixed(1)}% of ${opts.contextWindow.toLocaleString()} window`
|
|
203
|
+
: null;
|
|
204
|
+
const duration = opts.durationMs !== null ? `${(opts.durationMs / 1000).toFixed(1)}s` : null;
|
|
205
|
+
const permLine = nativePermission
|
|
206
|
+
? `- permission: ${permission} (${nativePermission})`
|
|
207
|
+
: `- permission: ${permission}`;
|
|
190
208
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
return [
|
|
210
|
+
`# Delegated ${harness.charAt(0).toUpperCase() + harness.slice(1)} run — ${opts.mode}`,
|
|
211
|
+
'',
|
|
212
|
+
`- harness: ${harness}`,
|
|
213
|
+
`- mode: ${opts.mode}`,
|
|
214
|
+
permLine,
|
|
215
|
+
`- model: ${opts.model ?? 'default'}`,
|
|
216
|
+
`- cwd: ${opts.cwd}`,
|
|
217
|
+
`- session: ${opts.sessionId ?? 'n/a'}${opts.resumed ? ' (resumed)' : ''}`,
|
|
218
|
+
`- turns: ${opts.numTurns} · cost: $${opts.totalCostUsd.toFixed(4)} · isError: ${opts.isError}`,
|
|
219
|
+
`- tokens: ${tokens ?? 'n/a'}`,
|
|
220
|
+
`- context: ${context ?? 'n/a'}`,
|
|
221
|
+
`- duration: ${duration ?? 'n/a'}`,
|
|
222
|
+
`- stop reason: ${opts.stopReason ?? 'n/a'}`,
|
|
223
|
+
'',
|
|
224
|
+
'## Activity',
|
|
225
|
+
opts.activityLog.length > 0 ? opts.activityLog.join('\n') : '(no tool activity)',
|
|
226
|
+
'',
|
|
227
|
+
'## Output',
|
|
228
|
+
opts.output || '(empty)',
|
|
229
|
+
'',
|
|
230
|
+
].join('\n');
|
|
213
231
|
}
|
|
214
232
|
|
|
215
233
|
/** Legacy wrapper */
|
|
216
234
|
export function buildClaudeTranscript(opts: {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
235
|
+
mode: string;
|
|
236
|
+
permissionMode: string;
|
|
237
|
+
model: string | null;
|
|
238
|
+
cwd: string;
|
|
239
|
+
sessionId: string | null;
|
|
240
|
+
resumed: boolean;
|
|
241
|
+
numTurns: number;
|
|
242
|
+
totalCostUsd: number;
|
|
243
|
+
isError: boolean;
|
|
244
|
+
stopReason: string | null;
|
|
245
|
+
durationMs: number | null;
|
|
246
|
+
usage: {
|
|
247
|
+
inputTokens: number;
|
|
248
|
+
outputTokens: number;
|
|
249
|
+
cacheCreationInputTokens: number;
|
|
250
|
+
cacheReadInputTokens: number;
|
|
251
|
+
} | null;
|
|
252
|
+
contextPercent: number | null;
|
|
253
|
+
contextWindow: number | null;
|
|
254
|
+
activityLog: string[];
|
|
255
|
+
output: string;
|
|
233
256
|
}): string {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
257
|
+
return buildTranscript({
|
|
258
|
+
harness: 'claude',
|
|
259
|
+
mode: opts.mode,
|
|
260
|
+
permission:
|
|
261
|
+
opts.permissionMode === 'plan' ? 'readonly' : opts.permissionMode === 'bypassPermissions' ? 'danger' : 'edit',
|
|
262
|
+
nativePermission: opts.permissionMode,
|
|
263
|
+
model: opts.model,
|
|
264
|
+
cwd: opts.cwd,
|
|
265
|
+
sessionId: opts.sessionId,
|
|
266
|
+
resumed: opts.resumed,
|
|
267
|
+
numTurns: opts.numTurns,
|
|
268
|
+
totalCostUsd: opts.totalCostUsd,
|
|
269
|
+
isError: opts.isError,
|
|
270
|
+
stopReason: opts.stopReason,
|
|
271
|
+
durationMs: opts.durationMs,
|
|
272
|
+
usage: opts.usage,
|
|
273
|
+
contextPercent: opts.contextPercent,
|
|
274
|
+
contextWindow: opts.contextWindow,
|
|
275
|
+
activityLog: opts.activityLog,
|
|
276
|
+
output: opts.output,
|
|
277
|
+
});
|
|
254
278
|
}
|