tryassay 0.2.0 → 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.
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import { regenerateCommand } from './commands/regenerate.js';
10
10
  import { remediateCommand } from './commands/remediate.js';
11
11
  import { reverseCommand } from './commands/reverse.js';
12
12
  import { assessCommand } from './commands/assess.js';
13
- import { runtimeCommand } from './commands/runtime.js';
13
+ import { runtimeCommand, runtimeInitCommand, runtimeStatusCommand, runtimeStopCommand } from './commands/runtime.js';
14
14
  const program = new Command();
15
15
  program
16
16
  .name('assay')
@@ -92,6 +92,26 @@ runtime
92
92
  .option('-c, --commands <cmds...>', 'Allowed shell commands')
93
93
  .option('-u, --urls <urls...>', 'Allowed URLs for API calls')
94
94
  .option('-m, --model <model>', 'Model ID for reasoning', 'claude-sonnet-4-5-20250929')
95
+ .option('--control-port <port>', 'HTTP control server port', '3700')
96
+ .option('--json', 'Output structured JSON logs to stdout')
97
+ .option('--max-cost <cents>', 'Max session cost in cents before auto-pause', '500')
95
98
  .action(runtimeCommand);
99
+ runtime
100
+ .command('init')
101
+ .description('Generate a default .assay/agent.json config file')
102
+ .argument('[path]', 'Path to the project directory', '.')
103
+ .action(runtimeInitCommand);
104
+ runtime
105
+ .command('status')
106
+ .description('Query the running agent status via HTTP control server')
107
+ .option('--port <port>', 'Control server port', '3700')
108
+ .option('--token <token>', 'Bearer token (printed at agent startup)')
109
+ .action(runtimeStatusCommand);
110
+ runtime
111
+ .command('stop')
112
+ .description('Stop a running agent via HTTP control server')
113
+ .option('--port <port>', 'Control server port', '3700')
114
+ .option('--token <token>', 'Bearer token (printed at agent startup)')
115
+ .action(runtimeStopCommand);
96
116
  program.parse();
97
117
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,18 @@ interface RuntimeCommandOptions {
6
6
  commands?: string[];
7
7
  urls?: string[];
8
8
  model?: string;
9
+ controlPort?: string;
10
+ json?: boolean;
11
+ maxCost?: string;
9
12
  }
10
13
  export declare function runtimeCommand(targetPath: string, opts: RuntimeCommandOptions): Promise<void>;
14
+ export declare function runtimeInitCommand(targetPath: string): Promise<void>;
15
+ export declare function runtimeStatusCommand(opts: {
16
+ port?: string;
17
+ token?: string;
18
+ }): Promise<void>;
19
+ export declare function runtimeStopCommand(opts: {
20
+ port?: string;
21
+ token?: string;
22
+ }): Promise<void>;
11
23
  export {};
@@ -1,8 +1,9 @@
1
1
  // ============================================================
2
- // CLI Command: tryassay runtime start <path>
3
- // Launches the Assay Verified Agent Runtime
2
+ // CLI Command: tryassay runtime start|init|status|stop <path>
3
+ // Launches and manages the Assay Verified Agent Runtime
4
4
  // ============================================================
5
5
  import { AgentLoop, buildAgentConfig } from '../runtime/agent-loop.js';
6
+ import { loadAgentConfigFile, mergeConfigFile, generateDefaultConfig } from '../runtime/config-loader.js';
6
7
  export async function runtimeCommand(targetPath, opts) {
7
8
  console.log('');
8
9
  console.log(' Assay Verified Agent Runtime');
@@ -16,22 +17,34 @@ export async function runtimeCommand(targetPath, opts) {
16
17
  commands: opts.commands,
17
18
  urls: opts.urls,
18
19
  modelId: opts.model,
20
+ controlPort: opts.controlPort ? parseInt(opts.controlPort, 10) : undefined,
21
+ jsonLogs: opts.json,
22
+ maxSessionCostCents: opts.maxCost ? parseInt(opts.maxCost, 10) : undefined,
19
23
  };
20
- const config = buildAgentConfig(targetPath, runtimeOpts);
24
+ // Build default config from CLI args
25
+ let config = buildAgentConfig(targetPath, runtimeOpts);
26
+ // Check for .assay/agent.json and merge if present
27
+ const fileConfig = await loadAgentConfigFile(targetPath);
28
+ if (fileConfig) {
29
+ config = mergeConfigFile(config, fileConfig);
30
+ console.log(' Config: loaded from .assay/agent.json');
31
+ }
21
32
  console.log(` Agent: ${config.name}`);
22
33
  console.log(` Scope: ${config.scope.allowedDirectories.join(', ')}`);
23
34
  console.log(` Model: ${config.modelId}`);
24
35
  console.log(` Signals: ${config.signals.map(s => s.type).join(', ')}`);
25
36
  console.log(` Experiences: ${config.experienceStorePath}`);
37
+ console.log(` Control port: ${config.controlPort ?? 'disabled'}`);
38
+ console.log(` Cost limit: ${config.limits.maxSessionCostCents ?? 'none'}c`);
26
39
  console.log('');
27
40
  console.log(' Pipeline: observe -> reason -> plan -> verify -> execute -> reflect');
28
41
  console.log(' Press Ctrl+C to stop');
29
42
  console.log('');
30
43
  const agent = new AgentLoop(config);
31
44
  // Log audit events
32
- agent.on('audit', (entry) => {
33
- // Audit events are logged by the agent loop itself
34
- // This hook is for future persistence (file, database, etc.)
45
+ agent.on('audit', (_entry) => {
46
+ // Audit events are now persisted by the AuditLog class
47
+ // This hook is for future external integrations
35
48
  });
36
49
  // Handle approval requests
37
50
  agent.on('approval_needed', (info) => {
@@ -60,4 +73,58 @@ export async function runtimeCommand(targetPath, opts) {
60
73
  // Start the agent loop (blocks until stopped)
61
74
  await agent.start();
62
75
  }
76
+ export async function runtimeInitCommand(targetPath) {
77
+ const configPath = await generateDefaultConfig(targetPath);
78
+ console.log(` Generated default agent config: ${configPath}`);
79
+ console.log(' Edit this file to customize your agent, then run: tryassay runtime start .');
80
+ }
81
+ export async function runtimeStatusCommand(opts) {
82
+ const port = opts.port ? parseInt(opts.port, 10) : 3700;
83
+ const token = opts.token;
84
+ if (!token) {
85
+ console.error(' Error: --token is required. The token is printed when the agent starts.');
86
+ process.exit(1);
87
+ }
88
+ try {
89
+ const response = await fetch(`http://localhost:${port}/status`, {
90
+ headers: { Authorization: `Bearer ${token}` },
91
+ });
92
+ if (!response.ok) {
93
+ console.error(` Error: HTTP ${response.status} ${response.statusText}`);
94
+ process.exit(1);
95
+ }
96
+ const state = await response.json();
97
+ console.log(JSON.stringify(state, null, 2));
98
+ }
99
+ catch (err) {
100
+ const msg = err instanceof Error ? err.message : String(err);
101
+ console.error(` Error: Could not connect to agent on port ${port}: ${msg}`);
102
+ process.exit(1);
103
+ }
104
+ }
105
+ export async function runtimeStopCommand(opts) {
106
+ const port = opts.port ? parseInt(opts.port, 10) : 3700;
107
+ const token = opts.token;
108
+ if (!token) {
109
+ console.error(' Error: --token is required. The token is printed when the agent starts.');
110
+ process.exit(1);
111
+ }
112
+ try {
113
+ const response = await fetch(`http://localhost:${port}/stop`, {
114
+ method: 'POST',
115
+ headers: { Authorization: `Bearer ${token}` },
116
+ });
117
+ if (!response.ok) {
118
+ console.error(` Error: HTTP ${response.status} ${response.statusText}`);
119
+ process.exit(1);
120
+ }
121
+ const result = await response.json();
122
+ console.log(` Agent stopping: ${JSON.stringify(result)}`);
123
+ }
124
+ catch (err) {
125
+ const msg = err instanceof Error ? err.message : String(err);
126
+ console.error(` Error: Could not connect to agent on port ${port}: ${msg}`);
127
+ process.exit(1);
128
+ }
129
+ }
63
130
  //# sourceMappingURL=runtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/commands/runtime.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,6CAA6C;AAC7C,4CAA4C;AAC5C,+DAA+D;AAE/D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAavE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAAkB,EAClB,IAA2B;IAE3B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,WAAW,GAAmB;QAClC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;QAC1E,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,KAAK;KACpB,CAAC;IAEF,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAEzD,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtE,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IAEpC,mBAAmB;IACnB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC1B,mDAAmD;QACnD,6DAA6D;IAC/D,CAAC,CAAC,CAAC;IAEH,2BAA2B;IAC3B,KAAK,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,EAAE;QACnC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7E,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,sCAAsC;IACtC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEhC,+CAA+C;IAC/C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACzB,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACnD,KAAK,CAAC,MAAM,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,8CAA8C;IAC9C,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/commands/runtime.ts"],"names":[],"mappings":"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"}
@@ -9,9 +9,13 @@ export declare class AgentLoop extends EventEmitter {
9
9
  private executor;
10
10
  private reflector;
11
11
  private experienceStore;
12
+ private auditLog;
13
+ private logger;
14
+ private controlServer;
12
15
  private state;
13
16
  private startTime;
14
17
  private hourWindow;
18
+ private sessionCostCents;
15
19
  private running;
16
20
  private paused;
17
21
  private pauseResolve;
@@ -20,16 +24,18 @@ export declare class AgentLoop extends EventEmitter {
20
24
  stop(): Promise<void>;
21
25
  pause(): void;
22
26
  resume(): void;
27
+ getCostCents(): number;
23
28
  getState(): AgentState;
24
29
  private tick;
25
30
  private registerAdapters;
26
31
  private setPhase;
27
32
  private checkHourWindow;
28
33
  private trackTokens;
34
+ /** Estimate cost in cents from token usage (rough Sonnet pricing). */
35
+ private trackCost;
29
36
  private recordError;
30
37
  private audit;
31
38
  private describePayload;
32
- private log;
33
39
  private sleep;
34
40
  }
35
41
  export interface RuntimeOptions {
@@ -42,5 +48,8 @@ export interface RuntimeOptions {
42
48
  blocked?: string[];
43
49
  modelId?: string;
44
50
  experiencesPath?: string;
51
+ controlPort?: number;
52
+ jsonLogs?: boolean;
53
+ maxSessionCostCents?: number;
45
54
  }
46
55
  export declare function buildAgentConfig(targetPath: string, opts?: RuntimeOptions): AgentConfig;
@@ -5,13 +5,16 @@
5
5
  // ============================================================
6
6
  import { EventEmitter } from 'node:events';
7
7
  import { randomUUID } from 'node:crypto';
8
- import { resolve } from 'node:path';
8
+ import { resolve, join } from 'node:path';
9
9
  import { Observer, FileWatcherAdapter, WebhookAdapter, ScheduleAdapter, } from './observer.js';
10
10
  import { Reasoner } from './reasoner.js';
11
11
  import { Planner } from './planner.js';
12
12
  import { RuntimeVerifier } from './verifier.js';
13
13
  import { Executor } from './executor.js';
14
14
  import { Reflector, ExperienceStore } from './reflector.js';
15
+ import { AuditLog } from './audit-log.js';
16
+ import { Logger } from './logger.js';
17
+ import { ControlServer } from './control-server.js';
15
18
  // ── Agent Loop ──────────────────────────────────────────────
16
19
  export class AgentLoop extends EventEmitter {
17
20
  config;
@@ -22,6 +25,9 @@ export class AgentLoop extends EventEmitter {
22
25
  executor;
23
26
  reflector;
24
27
  experienceStore;
28
+ auditLog;
29
+ logger;
30
+ controlServer = null;
25
31
  state;
26
32
  startTime = 0;
27
33
  hourWindow = {
@@ -29,12 +35,15 @@ export class AgentLoop extends EventEmitter {
29
35
  actions: 0,
30
36
  tokens: 0,
31
37
  };
38
+ sessionCostCents = 0;
32
39
  running = false;
33
40
  paused = false;
34
41
  pauseResolve = null;
35
42
  constructor(config) {
36
43
  super();
37
44
  this.config = config;
45
+ // Derive .assay directory from experience store path
46
+ const assayDir = resolve(config.experienceStorePath, '..');
38
47
  // Initialize components
39
48
  this.observer = new Observer();
40
49
  this.reasoner = new Reasoner();
@@ -43,6 +52,21 @@ export class AgentLoop extends EventEmitter {
43
52
  this.executor = new Executor(config.scope);
44
53
  this.reflector = new Reflector();
45
54
  this.experienceStore = new ExperienceStore(resolve(config.experienceStorePath));
55
+ this.auditLog = new AuditLog(join(assayDir, 'audit.ndjson'));
56
+ this.logger = new Logger({
57
+ jsonMode: config.jsonLogs ?? false,
58
+ logFilePath: join(assayDir, 'agent.log'),
59
+ });
60
+ // Initialize control server if port is configured
61
+ if (config.controlPort) {
62
+ this.controlServer = new ControlServer({
63
+ getState: () => this.getState(),
64
+ pause: () => this.pause(),
65
+ resume: () => this.resume(),
66
+ stop: () => this.stop(),
67
+ getCostCents: () => this.sessionCostCents,
68
+ }, { port: config.controlPort });
69
+ }
46
70
  // Initialize state
47
71
  this.state = {
48
72
  phase: 'idle',
@@ -66,12 +90,19 @@ export class AgentLoop extends EventEmitter {
66
90
  return;
67
91
  this.running = true;
68
92
  this.startTime = Date.now();
93
+ this.sessionCostCents = 0;
69
94
  this.hourWindow = { start: Date.now(), actions: 0, tokens: 0 };
70
95
  this.setPhase('observing');
71
96
  this.audit('agent_started', { name: this.config.name });
72
- this.log(`Agent "${this.config.name}" started`);
73
- this.log(` Watching: ${this.config.scope.allowedDirectories.join(', ')}`);
74
- this.log(` Signals: ${this.config.signals.map(s => s.type).join(', ')}`);
97
+ this.logger.info(`Agent "${this.config.name}" started`);
98
+ this.logger.info(` Watching: ${this.config.scope.allowedDirectories.join(', ')}`);
99
+ this.logger.info(` Signals: ${this.config.signals.map(s => s.type).join(', ')}`);
100
+ // Start control server
101
+ if (this.controlServer) {
102
+ await this.controlServer.start();
103
+ this.logger.info(` Control: http://localhost:${this.config.controlPort}/status`);
104
+ this.logger.info(` Token: ${this.controlServer.getToken()}`);
105
+ }
75
106
  await this.observer.start();
76
107
  // Main loop
77
108
  while (this.running) {
@@ -81,15 +112,22 @@ export class AgentLoop extends EventEmitter {
81
112
  catch (err) {
82
113
  const message = err instanceof Error ? err.message : String(err);
83
114
  this.recordError('observing', message, true);
84
- this.log(`Loop error (recoverable): ${message}`);
115
+ this.logger.error(`Loop error (recoverable): ${message}`);
85
116
  // Brief pause before retrying
86
117
  await this.sleep(1000);
87
118
  }
88
119
  }
89
120
  await this.observer.stop();
121
+ // Stop control server
122
+ if (this.controlServer) {
123
+ await this.controlServer.stop();
124
+ }
90
125
  this.setPhase('stopped');
91
- this.audit('agent_stopped', { uptime: Date.now() - this.startTime });
92
- this.log(`Agent "${this.config.name}" stopped`);
126
+ this.audit('agent_stopped', {
127
+ uptime: Date.now() - this.startTime,
128
+ totalCostCents: this.sessionCostCents,
129
+ });
130
+ this.logger.info(`Agent "${this.config.name}" stopped (cost: ${this.sessionCostCents.toFixed(2)}c)`);
93
131
  }
94
132
  async stop() {
95
133
  this.running = false;
@@ -98,7 +136,7 @@ export class AgentLoop extends EventEmitter {
98
136
  pause() {
99
137
  this.paused = true;
100
138
  this.setPhase('paused');
101
- this.log('Agent paused');
139
+ this.logger.info('Agent paused');
102
140
  }
103
141
  resume() {
104
142
  if (!this.paused)
@@ -108,7 +146,10 @@ export class AgentLoop extends EventEmitter {
108
146
  this.pauseResolve();
109
147
  this.pauseResolve = null;
110
148
  }
111
- this.log('Agent resumed');
149
+ this.logger.info('Agent resumed');
150
+ }
151
+ getCostCents() {
152
+ return this.sessionCostCents;
112
153
  }
113
154
  getState() {
114
155
  return {
@@ -133,10 +174,17 @@ export class AgentLoop extends EventEmitter {
133
174
  this.checkHourWindow();
134
175
  // Check rate limits
135
176
  if (this.hourWindow.actions >= this.config.limits.maxActionsPerHour) {
136
- this.log('Hourly action limit reached, waiting...');
177
+ this.logger.warn('Hourly action limit reached, waiting...');
137
178
  await this.sleep(60_000);
138
179
  return;
139
180
  }
181
+ // Check cost limits
182
+ const maxCost = this.config.limits.maxSessionCostCents;
183
+ if (maxCost && this.sessionCostCents >= maxCost) {
184
+ this.logger.warn(`Session cost limit reached (${this.sessionCostCents.toFixed(2)}c / ${maxCost}c), pausing...`);
185
+ this.pause();
186
+ return;
187
+ }
140
188
  // Phase 1: Observe — wait for next observation
141
189
  this.setPhase('observing');
142
190
  const observation = await this.observer.dequeue();
@@ -149,25 +197,33 @@ export class AgentLoop extends EventEmitter {
149
197
  source: observation.source,
150
198
  urgency: observation.urgency,
151
199
  });
152
- this.log(`Observation: [${observation.urgency}] ${observation.source} — ${this.describePayload(observation)}`);
200
+ this.logger.info(`Observation: [${observation.urgency}] ${observation.source} — ${this.describePayload(observation)}`);
201
+ // Fetch relevant past experiences before reasoning
202
+ const contextString = `${observation.source} ${JSON.stringify(observation.payload).slice(0, 200)}`;
203
+ const relevantExperiences = await this.experienceStore.getRelevantExperiences(contextString, 3);
153
204
  // Phase 2: Reason — decide what to do
154
205
  this.setPhase('reasoning');
155
- const decision = await this.reasoner.reason(observation, this.config, this.getState());
206
+ const decision = await this.reasoner.reason(observation, this.config, this.getState(), relevantExperiences.length > 0 ? relevantExperiences : undefined);
156
207
  this.state.currentDecision = decision;
157
- this.trackTokens(decision.modelUsage.inputTokens + decision.modelUsage.outputTokens);
208
+ const reasoningTokens = decision.modelUsage.inputTokens + decision.modelUsage.outputTokens;
209
+ this.trackTokens(reasoningTokens);
210
+ this.trackCost(reasoningTokens);
158
211
  this.audit('decision_made', {
159
212
  id: decision.id,
160
213
  action: decision.action,
161
214
  confidence: decision.confidence,
215
+ experiencesUsed: relevantExperiences.length,
216
+ });
217
+ this.logger.info(`Decision: ${decision.action} (confidence: ${decision.confidence.toFixed(2)})`, {
218
+ costCents: this.sessionCostCents,
162
219
  });
163
- this.log(`Decision: ${decision.action} (confidence: ${decision.confidence.toFixed(2)})`);
164
220
  // If ignore or defer, skip the rest
165
221
  if (decision.action === 'ignore') {
166
- this.log(' Action: ignore — skipping');
222
+ this.logger.info(' Action: ignore — skipping');
167
223
  return;
168
224
  }
169
225
  if (decision.action === 'defer') {
170
- this.log(` Action: defer — "${decision.reasoning}"`);
226
+ this.logger.info(` Action: defer — "${decision.reasoning}"`);
171
227
  return;
172
228
  }
173
229
  // Phase 3: Plan — decompose into steps
@@ -179,7 +235,7 @@ export class AgentLoop extends EventEmitter {
179
235
  totalSteps: plan.totalSteps,
180
236
  overallRisk: plan.overallRisk,
181
237
  });
182
- this.log(`Plan: ${plan.totalSteps} steps, risk: ${plan.overallRisk}`);
238
+ this.logger.info(`Plan: ${plan.totalSteps} steps, risk: ${plan.overallRisk}`);
183
239
  // Phase 4: Verify — pre-execution safety check
184
240
  this.setPhase('verifying');
185
241
  const verification = await this.verifier.verifyPlan(plan);
@@ -190,11 +246,11 @@ export class AgentLoop extends EventEmitter {
190
246
  passedClaims: verification.passedClaims,
191
247
  failedClaims: verification.failedClaims,
192
248
  });
193
- this.log(`Verification: ${verification.overallVerdict} (${verification.passedClaims}/${verification.totalClaims} claims passed)`);
249
+ this.logger.info(`Verification: ${verification.overallVerdict} (${verification.passedClaims}/${verification.totalClaims} claims passed)`);
194
250
  // Handle escalation — pause and wait for approval
195
251
  if (verification.overallVerdict === 'escalate') {
196
252
  this.setPhase('awaiting_approval');
197
- this.log(' Escalated — waiting for human approval');
253
+ this.logger.warn(' Escalated — waiting for human approval');
198
254
  this.audit('approval_requested', {
199
255
  planId: plan.id,
200
256
  escalatedSteps: verification.escalatedSteps,
@@ -214,7 +270,7 @@ export class AgentLoop extends EventEmitter {
214
270
  }
215
271
  // Handle failure — skip execution, still reflect
216
272
  if (verification.overallVerdict === 'fail') {
217
- this.log(' Verification FAILED — skipping execution');
273
+ this.logger.warn(' Verification FAILED — skipping execution');
218
274
  // Still reflect on the failure
219
275
  const experience = this.reflector.reflect(observation, decision, plan, verification, {
220
276
  planId: plan.id,
@@ -235,10 +291,10 @@ export class AgentLoop extends EventEmitter {
235
291
  }
236
292
  // Phase 5: Execute — run verified steps
237
293
  this.setPhase('executing');
238
- this.log(' Executing plan...');
294
+ this.logger.info(' Executing plan...');
239
295
  const execution = await this.executor.execute(plan, verification);
240
296
  this.hourWindow.actions++;
241
- this.log(` Execution: ${execution.status} (${execution.completedSteps}/${execution.totalSteps} steps)`);
297
+ this.logger.info(` Execution: ${execution.status} (${execution.completedSteps}/${execution.totalSteps} steps)`);
242
298
  for (const se of execution.stepExecutions) {
243
299
  const step = plan.steps.find(s => s.id === se.stepId);
244
300
  const desc = step?.description ?? se.stepId;
@@ -248,19 +304,35 @@ export class AgentLoop extends EventEmitter {
248
304
  durationMs: se.durationMs,
249
305
  });
250
306
  if (se.status !== 'success') {
251
- this.log(` [${se.status}] ${desc}${se.error ? ': ' + se.error : ''}`);
307
+ this.logger.warn(` [${se.status}] ${desc}${se.error ? ': ' + se.error : ''}`);
252
308
  }
253
309
  else {
254
- this.log(` [ok] ${desc} (${se.durationMs}ms)`);
310
+ this.logger.info(` [ok] ${desc} (${se.durationMs}ms)`);
255
311
  }
256
312
  }
257
313
  // Invalidate codebase index after code changes
258
314
  if (plan.steps.some(s => s.operation.type === 'code_write')) {
259
315
  this.verifier.invalidateIndex();
260
316
  }
261
- // Phase 6: Reflect — capture experience
317
+ // Phase 5.5: Post-execution verification
318
+ const postVerification = await this.verifier.verifyPostExecution(plan, execution);
319
+ this.audit('verification_completed', {
320
+ planId: plan.id,
321
+ stage: 'post_execution',
322
+ verdict: postVerification.overallVerdict,
323
+ totalClaims: postVerification.totalClaims,
324
+ passedClaims: postVerification.passedClaims,
325
+ failedClaims: postVerification.failedClaims,
326
+ });
327
+ this.logger.info(` Post-exec verification: ${postVerification.overallVerdict} (${postVerification.passedClaims}/${postVerification.totalClaims})`);
328
+ // Phase 6: Reflect — capture experience (include post-verification outcome)
262
329
  this.setPhase('reflecting');
263
330
  const experience = this.reflector.reflect(observation, decision, plan, verification, execution);
331
+ // Override outcome if post-execution verification failed
332
+ if (postVerification.overallVerdict === 'fail' && experience.outcome === 'success') {
333
+ experience.outcome = 'partial_success';
334
+ experience.lessons.push('Post-execution verification failed — execution may not have achieved intended results');
335
+ }
264
336
  await this.experienceStore.save(experience);
265
337
  this.state.totalExperiences++;
266
338
  this.audit('experience_stored', {
@@ -268,7 +340,7 @@ export class AgentLoop extends EventEmitter {
268
340
  outcome: experience.outcome,
269
341
  lessons: experience.lessons,
270
342
  });
271
- this.log(` Experience: ${experience.outcome} — ${experience.lessons.length} lesson(s)`);
343
+ this.logger.info(` Experience: ${experience.outcome} — ${experience.lessons.length} lesson(s)`);
272
344
  // Clear current state
273
345
  this.state.currentObservation = null;
274
346
  this.state.currentDecision = null;
@@ -294,6 +366,7 @@ export class AgentLoop extends EventEmitter {
294
366
  // ── Helpers ────────────────────────────────────────────────
295
367
  setPhase(phase) {
296
368
  this.state.phase = phase;
369
+ this.logger.setPhase(phase);
297
370
  this.emit('phase', phase);
298
371
  }
299
372
  checkHourWindow() {
@@ -305,6 +378,12 @@ export class AgentLoop extends EventEmitter {
305
378
  trackTokens(tokens) {
306
379
  this.hourWindow.tokens += tokens;
307
380
  }
381
+ /** Estimate cost in cents from token usage (rough Sonnet pricing). */
382
+ trackCost(tokens) {
383
+ // Approximate: $3/MTok input, $15/MTok output, blended ~$6/MTok
384
+ const costPerToken = 6 / 1_000_000; // $6 per million tokens
385
+ this.sessionCostCents += tokens * costPerToken * 100;
386
+ }
308
387
  recordError(phase, message, recoverable) {
309
388
  const error = {
310
389
  phase,
@@ -332,6 +411,11 @@ export class AgentLoop extends EventEmitter {
332
411
  planId: this.state.currentPlan?.id,
333
412
  },
334
413
  };
414
+ // Persist to hash-chained audit log (fire-and-forget)
415
+ this.auditLog.append(entry).catch((err) => {
416
+ this.logger.error(`Audit log write failed: ${err instanceof Error ? err.message : String(err)}`);
417
+ });
418
+ // Still emit for external listeners
335
419
  this.emit('audit', entry);
336
420
  }
337
421
  describePayload(obs) {
@@ -349,10 +433,6 @@ export class AgentLoop extends EventEmitter {
349
433
  return `${p.url} (${p.statusCode})`;
350
434
  }
351
435
  }
352
- log(message) {
353
- const ts = new Date().toISOString().slice(11, 19);
354
- console.log(`[${ts}] ${message}`);
355
- }
356
436
  sleep(ms) {
357
437
  return new Promise((resolve) => setTimeout(resolve, ms));
358
438
  }
@@ -403,6 +483,7 @@ export function buildAgentConfig(targetPath, opts = {}) {
403
483
  maxConcurrentSteps: 3,
404
484
  maxPlanSteps: 10,
405
485
  commandTimeoutMs: 30_000,
486
+ maxSessionCostCents: opts.maxSessionCostCents ?? 500,
406
487
  },
407
488
  signals,
408
489
  approvalDefaults: {
@@ -414,6 +495,8 @@ export function buildAgentConfig(targetPath, opts = {}) {
414
495
  },
415
496
  modelId: opts.modelId ?? 'claude-sonnet-4-5-20250929',
416
497
  experienceStorePath: opts.experiencesPath ?? `${rootDir}/.assay/experiences.jsonl`,
498
+ controlPort: opts.controlPort ?? 3700,
499
+ jsonLogs: opts.jsonLogs ?? false,
417
500
  };
418
501
  }
419
502
  //# sourceMappingURL=agent-loop.js.map