tryassay 0.1.2 → 0.3.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.
Files changed (42) hide show
  1. package/dist/cli.js +37 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/runtime.d.ts +23 -0
  4. package/dist/commands/runtime.js +130 -0
  5. package/dist/commands/runtime.js.map +1 -0
  6. package/dist/runtime/agent-loop.d.ts +55 -0
  7. package/dist/runtime/agent-loop.js +502 -0
  8. package/dist/runtime/agent-loop.js.map +1 -0
  9. package/dist/runtime/audit-log.d.ts +35 -0
  10. package/dist/runtime/audit-log.js +115 -0
  11. package/dist/runtime/audit-log.js.map +1 -0
  12. package/dist/runtime/config-loader.d.ts +41 -0
  13. package/dist/runtime/config-loader.js +116 -0
  14. package/dist/runtime/config-loader.js.map +1 -0
  15. package/dist/runtime/control-server.d.ts +25 -0
  16. package/dist/runtime/control-server.js +83 -0
  17. package/dist/runtime/control-server.js.map +1 -0
  18. package/dist/runtime/executor.d.ts +37 -0
  19. package/dist/runtime/executor.js +518 -0
  20. package/dist/runtime/executor.js.map +1 -0
  21. package/dist/runtime/logger.d.ts +20 -0
  22. package/dist/runtime/logger.js +73 -0
  23. package/dist/runtime/logger.js.map +1 -0
  24. package/dist/runtime/observer.d.ts +48 -0
  25. package/dist/runtime/observer.js +294 -0
  26. package/dist/runtime/observer.js.map +1 -0
  27. package/dist/runtime/planner.d.ts +4 -0
  28. package/dist/runtime/planner.js +299 -0
  29. package/dist/runtime/planner.js.map +1 -0
  30. package/dist/runtime/reasoner.d.ts +4 -0
  31. package/dist/runtime/reasoner.js +238 -0
  32. package/dist/runtime/reasoner.js.map +1 -0
  33. package/dist/runtime/reflector.d.ts +67 -0
  34. package/dist/runtime/reflector.js +393 -0
  35. package/dist/runtime/reflector.js.map +1 -0
  36. package/dist/runtime/types.d.ts +321 -0
  37. package/dist/runtime/types.js +6 -0
  38. package/dist/runtime/types.js.map +1 -0
  39. package/dist/runtime/verifier.d.ts +46 -0
  40. package/dist/runtime/verifier.js +404 -0
  41. package/dist/runtime/verifier.js.map +1 -0
  42. package/package.json +1 -1
@@ -0,0 +1,48 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { SignalSourceType, Observation, FileSystemSignalConfig, WebhookSignalConfig, ScheduleSignalConfig } from './types.js';
3
+ export interface SignalAdapter {
4
+ readonly type: SignalSourceType;
5
+ start(emit: (obs: Observation) => void): Promise<void>;
6
+ stop(): Promise<void>;
7
+ }
8
+ export declare class FileWatcherAdapter implements SignalAdapter {
9
+ readonly type: SignalSourceType;
10
+ private watchers;
11
+ private config;
12
+ private debounceMs;
13
+ private pendingChanges;
14
+ private debounceTimer;
15
+ constructor(config: FileSystemSignalConfig, debounceMs?: number);
16
+ start(emit: (obs: Observation) => void): Promise<void>;
17
+ stop(): Promise<void>;
18
+ private flushPendingChanges;
19
+ }
20
+ export declare class WebhookAdapter implements SignalAdapter {
21
+ readonly type: SignalSourceType;
22
+ private server;
23
+ private config;
24
+ constructor(config: WebhookSignalConfig);
25
+ start(emit: (obs: Observation) => void): Promise<void>;
26
+ stop(): Promise<void>;
27
+ }
28
+ export declare class ScheduleAdapter implements SignalAdapter {
29
+ readonly type: SignalSourceType;
30
+ private timer;
31
+ private tickCount;
32
+ private config;
33
+ constructor(config: ScheduleSignalConfig);
34
+ start(emit: (obs: Observation) => void): Promise<void>;
35
+ stop(): Promise<void>;
36
+ }
37
+ export declare class Observer extends EventEmitter {
38
+ private adapters;
39
+ private queue;
40
+ private running;
41
+ private waitResolve;
42
+ register(adapter: SignalAdapter): void;
43
+ start(): Promise<void>;
44
+ stop(): Promise<void>;
45
+ dequeue(): Promise<Observation | null>;
46
+ get queueDepth(): number;
47
+ get isRunning(): boolean;
48
+ }
@@ -0,0 +1,294 @@
1
+ // ============================================================
2
+ // Assay Verified Agent Runtime — Observer Component
3
+ // Watches external signals and converts them to Observations
4
+ // ============================================================
5
+ import { EventEmitter } from 'node:events';
6
+ import { watch } from 'node:fs';
7
+ import { createServer } from 'node:http';
8
+ import { randomUUID } from 'node:crypto';
9
+ import { resolve } from 'node:path';
10
+ // ── FileWatcherAdapter ────────────────────────────────────────
11
+ export class FileWatcherAdapter {
12
+ type = 'filesystem';
13
+ watchers = [];
14
+ config;
15
+ debounceMs;
16
+ pendingChanges = new Map();
17
+ debounceTimer = null;
18
+ constructor(config, debounceMs = 500) {
19
+ this.config = config;
20
+ this.debounceMs = debounceMs;
21
+ }
22
+ async start(emit) {
23
+ for (const dirPath of this.config.paths) {
24
+ const resolved = resolve(dirPath);
25
+ const watcher = watch(resolved, { recursive: true }, (eventType, filename) => {
26
+ if (!filename)
27
+ return;
28
+ if (this.config.ignorePatterns?.some(p => filename.includes(p))) {
29
+ return;
30
+ }
31
+ const event = eventType === 'rename' ? 'create' : 'modify';
32
+ const fullPath = `${resolved}/${filename}`;
33
+ // Accumulate changes in the debounce window
34
+ this.pendingChanges.set(fullPath, { event, path: fullPath });
35
+ // Reset the debounce timer
36
+ if (this.debounceTimer) {
37
+ clearTimeout(this.debounceTimer);
38
+ }
39
+ this.debounceTimer = setTimeout(() => {
40
+ this.flushPendingChanges(emit);
41
+ }, this.debounceMs);
42
+ });
43
+ this.watchers.push(watcher);
44
+ }
45
+ }
46
+ async stop() {
47
+ if (this.debounceTimer) {
48
+ clearTimeout(this.debounceTimer);
49
+ this.debounceTimer = null;
50
+ }
51
+ this.pendingChanges.clear();
52
+ for (const watcher of this.watchers) {
53
+ watcher.close();
54
+ }
55
+ this.watchers = [];
56
+ }
57
+ flushPendingChanges(emit) {
58
+ if (this.pendingChanges.size === 0)
59
+ return;
60
+ const changes = Array.from(this.pendingChanges.values());
61
+ this.pendingChanges.clear();
62
+ this.debounceTimer = null;
63
+ if (changes.length === 1) {
64
+ // Single file change — emit as-is
65
+ const change = changes[0];
66
+ const payload = {
67
+ type: 'file_change',
68
+ event: change.event,
69
+ path: change.path,
70
+ };
71
+ emit({
72
+ id: randomUUID(),
73
+ source: 'filesystem',
74
+ urgency: 'normal',
75
+ timestamp: new Date().toISOString(),
76
+ payload,
77
+ });
78
+ }
79
+ else {
80
+ // Multiple file changes — emit first as representative, include all paths in observation
81
+ const primary = changes[0];
82
+ const payload = {
83
+ type: 'file_change',
84
+ event: primary.event,
85
+ path: primary.path,
86
+ };
87
+ emit({
88
+ id: randomUUID(),
89
+ source: 'filesystem',
90
+ urgency: 'normal',
91
+ timestamp: new Date().toISOString(),
92
+ payload,
93
+ // Store all changed paths for context
94
+ relatedExperienceIds: changes.map(c => c.path),
95
+ });
96
+ }
97
+ }
98
+ }
99
+ // ── WebhookAdapter ────────────────────────────────────────────
100
+ export class WebhookAdapter {
101
+ type = 'webhook';
102
+ server = null;
103
+ config;
104
+ constructor(config) {
105
+ this.config = config;
106
+ }
107
+ async start(emit) {
108
+ this.server = createServer((req, res) => {
109
+ if (req.method !== 'POST' || req.url !== this.config.path) {
110
+ res.writeHead(404);
111
+ res.end('Not Found');
112
+ return;
113
+ }
114
+ if (this.config.secret) {
115
+ const provided = req.headers['x-webhook-secret'];
116
+ if (provided !== this.config.secret) {
117
+ res.writeHead(401);
118
+ res.end('Unauthorized');
119
+ return;
120
+ }
121
+ }
122
+ const chunks = [];
123
+ req.on('data', (chunk) => chunks.push(chunk));
124
+ req.on('end', () => {
125
+ const rawBody = Buffer.concat(chunks).toString('utf-8');
126
+ let body;
127
+ try {
128
+ body = JSON.parse(rawBody);
129
+ }
130
+ catch {
131
+ body = rawBody;
132
+ }
133
+ const headers = {};
134
+ for (const [key, value] of Object.entries(req.headers)) {
135
+ if (typeof value === 'string') {
136
+ headers[key] = value;
137
+ }
138
+ }
139
+ const payload = {
140
+ type: 'webhook',
141
+ method: req.method ?? 'POST',
142
+ path: req.url ?? this.config.path,
143
+ headers,
144
+ body,
145
+ };
146
+ const obs = {
147
+ id: randomUUID(),
148
+ source: 'webhook',
149
+ urgency: 'high',
150
+ timestamp: new Date().toISOString(),
151
+ payload,
152
+ };
153
+ emit(obs);
154
+ res.writeHead(200, { 'Content-Type': 'application/json' });
155
+ res.end(JSON.stringify({ received: true, observationId: obs.id }));
156
+ });
157
+ });
158
+ await new Promise((resolve, reject) => {
159
+ this.server.listen(this.config.port, () => resolve());
160
+ this.server.on('error', reject);
161
+ });
162
+ }
163
+ async stop() {
164
+ if (!this.server)
165
+ return;
166
+ await new Promise((resolve, reject) => {
167
+ this.server.close((err) => {
168
+ if (err)
169
+ reject(err);
170
+ else
171
+ resolve();
172
+ });
173
+ });
174
+ this.server = null;
175
+ }
176
+ }
177
+ // ── ScheduleAdapter ───────────────────────────────────────────
178
+ export class ScheduleAdapter {
179
+ type = 'schedule';
180
+ timer = null;
181
+ tickCount = 0;
182
+ config;
183
+ constructor(config) {
184
+ this.config = config;
185
+ }
186
+ async start(emit) {
187
+ this.tickCount = 0;
188
+ this.timer = setInterval(() => {
189
+ this.tickCount++;
190
+ const payload = {
191
+ type: 'schedule_tick',
192
+ label: this.config.label,
193
+ tickNumber: this.tickCount,
194
+ };
195
+ const obs = {
196
+ id: randomUUID(),
197
+ source: 'schedule',
198
+ urgency: 'low',
199
+ timestamp: new Date().toISOString(),
200
+ payload,
201
+ };
202
+ emit(obs);
203
+ }, this.config.intervalMs);
204
+ }
205
+ async stop() {
206
+ if (this.timer !== null) {
207
+ clearInterval(this.timer);
208
+ this.timer = null;
209
+ }
210
+ }
211
+ }
212
+ // ── Priority Queue ────────────────────────────────────────────
213
+ const URGENCY_ORDER = {
214
+ critical: 0,
215
+ high: 1,
216
+ normal: 2,
217
+ low: 3,
218
+ };
219
+ class PriorityQueue {
220
+ items = [];
221
+ enqueue(obs) {
222
+ this.items.push(obs);
223
+ this.items.sort((a, b) => URGENCY_ORDER[a.urgency] - URGENCY_ORDER[b.urgency]);
224
+ }
225
+ dequeue() {
226
+ return this.items.shift();
227
+ }
228
+ get length() {
229
+ return this.items.length;
230
+ }
231
+ drain() {
232
+ const all = this.items.splice(0);
233
+ return all;
234
+ }
235
+ }
236
+ // ── Observer ──────────────────────────────────────────────────
237
+ export class Observer extends EventEmitter {
238
+ adapters = [];
239
+ queue = new PriorityQueue();
240
+ running = false;
241
+ waitResolve = null;
242
+ register(adapter) {
243
+ this.adapters.push(adapter);
244
+ }
245
+ async start() {
246
+ if (this.running)
247
+ return;
248
+ this.running = true;
249
+ const emitFn = (obs) => {
250
+ if (!this.running)
251
+ return;
252
+ if (this.waitResolve) {
253
+ const resolve = this.waitResolve;
254
+ this.waitResolve = null;
255
+ resolve(obs);
256
+ }
257
+ else {
258
+ this.queue.enqueue(obs);
259
+ }
260
+ this.emit('observation', obs);
261
+ };
262
+ await Promise.all(this.adapters.map(a => a.start(emitFn)));
263
+ }
264
+ async stop() {
265
+ this.running = false;
266
+ if (this.waitResolve) {
267
+ const resolve = this.waitResolve;
268
+ this.waitResolve = null;
269
+ resolve(null);
270
+ }
271
+ await Promise.all(this.adapters.map(a => a.stop()));
272
+ }
273
+ async dequeue() {
274
+ const immediate = this.queue.dequeue();
275
+ if (immediate)
276
+ return immediate;
277
+ if (!this.running)
278
+ return null;
279
+ return new Promise((resolve) => {
280
+ if (!this.running) {
281
+ resolve(null);
282
+ return;
283
+ }
284
+ this.waitResolve = resolve;
285
+ });
286
+ }
287
+ get queueDepth() {
288
+ return this.queue.length;
289
+ }
290
+ get isRunning() {
291
+ return this.running;
292
+ }
293
+ }
294
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.js","sourceRoot":"","sources":["../../src/runtime/observer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Decision, AgentConfig, ActionPlan } from './types.js';
2
+ export declare class Planner {
3
+ plan(decision: Decision, config: AgentConfig): Promise<ActionPlan>;
4
+ }
@@ -0,0 +1,299 @@
1
+ // ============================================================
2
+ // Assay Verified Agent Runtime — Planner
3
+ // Decomposes a Decision into concrete, verifiable ActionSteps
4
+ // ============================================================
5
+ import { randomUUID } from 'node:crypto';
6
+ import { getClient, MODEL } from '../lib/anthropic.js';
7
+ // ── Approval level ranking (for comparison) ─────────────────
8
+ const APPROVAL_RANK = {
9
+ auto: 0,
10
+ single: 1,
11
+ escalate: 2,
12
+ };
13
+ function highestApproval(a, b) {
14
+ return APPROVAL_RANK[a] >= APPROVAL_RANK[b] ? a : b;
15
+ }
16
+ // ── System prompt ───────────────────────────────────────────
17
+ function buildSystemPrompt(config) {
18
+ return `You are an operational planner for a verified agent runtime.
19
+
20
+ Your job: decompose a high-level decision into concrete, ordered action steps.
21
+
22
+ ## Agent scope
23
+ - Name: ${config.name}
24
+ - Allowed directories: ${config.scope.allowedDirectories.join(', ')}
25
+ - Allowed commands: ${config.scope.allowedCommands.join(', ')}
26
+ - Allowed URLs: ${config.scope.allowedUrls.join(', ')}
27
+ - Blocked patterns: ${config.scope.blockedPatterns.join(', ')}
28
+ - Max plan steps: ${config.limits.maxPlanSteps}
29
+ - Command timeout: ${config.limits.commandTimeoutMs}ms
30
+
31
+ ## Rules
32
+ 1. Each step must have ONE operation (code_write, code_run, api_call, git, or message).
33
+ 2. Every step needs preConditions (what must be true before) and postConditions (what must be true after).
34
+ 3. Use dependsOn (array of step indices) to express ordering constraints.
35
+ 4. Stay within the agent's allowed scope. Never produce steps that violate blocked patterns.
36
+ 5. Be concrete — file paths, exact commands, real URLs. No placeholders.
37
+ 6. Keep the total number of steps at or below ${config.limits.maxPlanSteps}.
38
+
39
+ ## Output format
40
+ Respond with ONLY a JSON array of steps. No markdown, no explanation, no wrapping.
41
+
42
+ Each step object:
43
+ {
44
+ "index": 0,
45
+ "description": "Human-readable description",
46
+ "operation": { "type": "code_write"|"code_run"|"api_call"|"git"|"message", ...fields },
47
+ "preConditions": [{ "description": "...", "check": "..." }],
48
+ "postConditions": [{ "description": "...", "check": "..." }],
49
+ "dependsOn": [],
50
+ "estimatedDurationMs": 5000
51
+ }
52
+
53
+ Operation schemas:
54
+ - code_write: { type: "code_write", filePath: string, content: string, mode: "create"|"edit"|"append", editTarget?: string }
55
+ - code_run: { type: "code_run", command: string, cwd?: string, timeoutMs: number, env?: Record<string,string> }
56
+ - api_call: { type: "api_call", method: "GET"|"POST"|"PUT"|"DELETE"|"PATCH", url: string, headers?: Record<string,string>, body?: any, expectedStatus?: number }
57
+ - git: { type: "git", command: "add"|"commit"|"push"|"branch"|"checkout", args: string[], cwd?: string }
58
+ - message: { type: "message", channel: "console"|"slack"|"email", recipient?: string, subject?: string, content: string }`;
59
+ }
60
+ // ── User prompt ─────────────────────────────────────────────
61
+ function buildUserPrompt(decision) {
62
+ const actions = decision.proposedActions
63
+ .map((a, i) => `${i + 1}. [${a.operationType}] ${a.description}\n Target: ${a.target}\n Details: ${a.details}`)
64
+ .join('\n');
65
+ return `Decompose this decision into concrete action steps.
66
+
67
+ ## Decision
68
+ ID: ${decision.id}
69
+ Reasoning: ${decision.reasoning}
70
+ Confidence: ${decision.confidence}
71
+ Risks: ${decision.risks.join('; ')}
72
+
73
+ ## Proposed actions
74
+ ${actions}
75
+
76
+ Produce the JSON array of steps now.`;
77
+ }
78
+ // ── Parse helpers ───────────────────────────────────────────
79
+ function extractJsonArray(raw) {
80
+ // Try direct parse first
81
+ const trimmed = raw.trim();
82
+ try {
83
+ const parsed = JSON.parse(trimmed);
84
+ if (Array.isArray(parsed))
85
+ return parsed;
86
+ }
87
+ catch {
88
+ // fall through
89
+ }
90
+ // Try extracting from markdown code block
91
+ const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
92
+ if (fenceMatch) {
93
+ try {
94
+ const parsed = JSON.parse(fenceMatch[1].trim());
95
+ if (Array.isArray(parsed))
96
+ return parsed;
97
+ }
98
+ catch {
99
+ // fall through
100
+ }
101
+ }
102
+ // Try finding array boundaries
103
+ const start = trimmed.indexOf('[');
104
+ const end = trimmed.lastIndexOf(']');
105
+ if (start !== -1 && end > start) {
106
+ try {
107
+ const parsed = JSON.parse(trimmed.slice(start, end + 1));
108
+ if (Array.isArray(parsed))
109
+ return parsed;
110
+ }
111
+ catch {
112
+ // fall through
113
+ }
114
+ }
115
+ throw new Error('Failed to extract JSON array from Claude response');
116
+ }
117
+ function isValidOperationType(t) {
118
+ return typeof t === 'string' && ['code_write', 'code_run', 'api_call', 'git', 'message'].includes(t);
119
+ }
120
+ function parseConditions(raw) {
121
+ if (!Array.isArray(raw))
122
+ return [];
123
+ return raw
124
+ .filter((c) => c !== null && typeof c === 'object')
125
+ .map((c) => ({
126
+ description: typeof c.description === 'string' ? c.description : 'unknown',
127
+ check: typeof c.check === 'string' ? c.check : '',
128
+ }));
129
+ }
130
+ function parseOperation(raw) {
131
+ if (raw === null || typeof raw !== 'object')
132
+ return null;
133
+ const op = raw;
134
+ if (!isValidOperationType(op.type))
135
+ return null;
136
+ switch (op.type) {
137
+ case 'code_write':
138
+ return {
139
+ type: 'code_write',
140
+ filePath: typeof op.filePath === 'string' ? op.filePath : '',
141
+ content: typeof op.content === 'string' ? op.content : '',
142
+ mode: op.mode === 'create' || op.mode === 'edit' || op.mode === 'append'
143
+ ? op.mode
144
+ : 'create',
145
+ editTarget: typeof op.editTarget === 'string' ? op.editTarget : undefined,
146
+ };
147
+ case 'code_run':
148
+ return {
149
+ type: 'code_run',
150
+ command: typeof op.command === 'string' ? op.command : '',
151
+ cwd: typeof op.cwd === 'string' ? op.cwd : undefined,
152
+ timeoutMs: typeof op.timeoutMs === 'number' ? op.timeoutMs : 30_000,
153
+ env: op.env !== null && typeof op.env === 'object' ? op.env : undefined,
154
+ };
155
+ case 'api_call':
156
+ return {
157
+ type: 'api_call',
158
+ method: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(op.method)
159
+ ? op.method
160
+ : 'GET',
161
+ url: typeof op.url === 'string' ? op.url : '',
162
+ headers: op.headers !== null && typeof op.headers === 'object' ? op.headers : undefined,
163
+ body: op.body,
164
+ expectedStatus: typeof op.expectedStatus === 'number' ? op.expectedStatus : undefined,
165
+ };
166
+ case 'git':
167
+ return {
168
+ type: 'git',
169
+ command: ['add', 'commit', 'push', 'branch', 'checkout'].includes(op.command)
170
+ ? op.command
171
+ : 'add',
172
+ args: Array.isArray(op.args) ? op.args.filter((a) => typeof a === 'string') : [],
173
+ cwd: typeof op.cwd === 'string' ? op.cwd : undefined,
174
+ };
175
+ case 'message':
176
+ return {
177
+ type: 'message',
178
+ channel: op.channel === 'console' || op.channel === 'slack' || op.channel === 'email'
179
+ ? op.channel
180
+ : 'console',
181
+ recipient: typeof op.recipient === 'string' ? op.recipient : undefined,
182
+ subject: typeof op.subject === 'string' ? op.subject : undefined,
183
+ content: typeof op.content === 'string' ? op.content : '',
184
+ };
185
+ default:
186
+ return null;
187
+ }
188
+ }
189
+ function parseRawStep(raw) {
190
+ if (raw === null || typeof raw !== 'object')
191
+ return null;
192
+ const obj = raw;
193
+ const operation = parseOperation(obj.operation);
194
+ if (!operation)
195
+ return null;
196
+ return {
197
+ index: typeof obj.index === 'number' ? obj.index : 0,
198
+ description: typeof obj.description === 'string' ? obj.description : 'unnamed step',
199
+ operation,
200
+ preConditions: parseConditions(obj.preConditions),
201
+ postConditions: parseConditions(obj.postConditions),
202
+ dependsOn: Array.isArray(obj.dependsOn)
203
+ ? obj.dependsOn.filter((d) => typeof d === 'number')
204
+ : [],
205
+ estimatedDurationMs: typeof obj.estimatedDurationMs === 'number' ? obj.estimatedDurationMs : 5000,
206
+ };
207
+ }
208
+ // ── Planner ─────────────────────────────────────────────────
209
+ export class Planner {
210
+ async plan(decision, config) {
211
+ const planId = randomUUID();
212
+ const client = getClient();
213
+ const systemPrompt = buildSystemPrompt(config);
214
+ const userPrompt = buildUserPrompt(decision);
215
+ // Stream the response and collect content
216
+ let content = '';
217
+ let inputTokens = 0;
218
+ let outputTokens = 0;
219
+ const stream = client.messages.stream({
220
+ model: config.modelId || MODEL,
221
+ max_tokens: 8_000,
222
+ system: systemPrompt,
223
+ messages: [{ role: 'user', content: userPrompt }],
224
+ });
225
+ stream.on('text', (text) => {
226
+ content += text;
227
+ });
228
+ const finalMessage = await stream.finalMessage();
229
+ inputTokens = finalMessage.usage.input_tokens;
230
+ outputTokens = finalMessage.usage.output_tokens;
231
+ // Parse the response into steps
232
+ let rawSteps;
233
+ try {
234
+ const parsed = extractJsonArray(content);
235
+ rawSteps = parsed
236
+ .map(parseRawStep)
237
+ .filter((s) => s !== null);
238
+ }
239
+ catch (err) {
240
+ // Graceful fallback: return a single-step plan that logs the failure
241
+ rawSteps = [{
242
+ index: 0,
243
+ description: `Plan generation failed: ${err instanceof Error ? err.message : String(err)}`,
244
+ operation: {
245
+ type: 'message',
246
+ channel: 'console',
247
+ content: `Planner could not parse Claude response. Raw output length: ${content.length} chars.`,
248
+ },
249
+ preConditions: [],
250
+ postConditions: [],
251
+ dependsOn: [],
252
+ estimatedDurationMs: 0,
253
+ }];
254
+ }
255
+ // Enforce max plan steps
256
+ if (rawSteps.length > config.limits.maxPlanSteps) {
257
+ rawSteps = rawSteps.slice(0, config.limits.maxPlanSteps);
258
+ }
259
+ // Build index-to-ID map for dependsOn resolution
260
+ const stepIds = new Map();
261
+ for (const raw of rawSteps) {
262
+ stepIds.set(raw.index, randomUUID());
263
+ }
264
+ // Convert raw steps to ActionSteps
265
+ let overallRisk = 'auto';
266
+ const steps = rawSteps.map((raw, i) => {
267
+ const stepId = stepIds.get(raw.index) ?? randomUUID();
268
+ const opType = raw.operation.type;
269
+ const approvalLevel = config.approvalDefaults[opType] ?? 'single';
270
+ overallRisk = highestApproval(overallRisk, approvalLevel);
271
+ // Resolve dependsOn indices to step IDs
272
+ const dependsOn = raw.dependsOn
273
+ .map((depIndex) => stepIds.get(depIndex))
274
+ .filter((id) => id !== undefined);
275
+ return {
276
+ id: stepId,
277
+ planId,
278
+ index: i,
279
+ description: raw.description,
280
+ operation: raw.operation,
281
+ preConditions: raw.preConditions,
282
+ postConditions: raw.postConditions,
283
+ approvalLevel,
284
+ dependsOn,
285
+ };
286
+ });
287
+ const estimatedDurationMs = rawSteps.reduce((sum, s) => sum + (s.estimatedDurationMs ?? 5000), 0);
288
+ return {
289
+ id: planId,
290
+ decisionId: decision.id,
291
+ steps,
292
+ totalSteps: steps.length,
293
+ estimatedDurationMs,
294
+ overallRisk,
295
+ timestamp: new Date().toISOString(),
296
+ };
297
+ }
298
+ }
299
+ //# sourceMappingURL=planner.js.map