hammer-ai 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.
@@ -0,0 +1,2231 @@
1
+ import * as xstate from 'xstate';
2
+
3
+ /**
4
+ * Core type definitions shared across mjolno apps.
5
+ *
6
+ * These types are intentionally decoupled from any CLI or desktop-specific
7
+ * concerns so they can be consumed by both apps/hammer and apps/tauri.
8
+ */
9
+ interface ChatMessage {
10
+ role: "system" | "user" | "assistant" | "tool";
11
+ content: string;
12
+ }
13
+ interface FetchResponseLike {
14
+ ok: boolean;
15
+ status: number;
16
+ text(): Promise<string>;
17
+ json(): Promise<unknown>;
18
+ body?: unknown;
19
+ }
20
+ type FetchLike = (url: string, init?: any) => Promise<FetchResponseLike>;
21
+ /** Configuration for an LLM provider endpoint. */
22
+ interface LLMProviderConfig {
23
+ apiKey: string;
24
+ baseUrl: string;
25
+ model: string;
26
+ /** Extra headers merged into every request (e.g. HTTP-Referer for OpenRouter). */
27
+ extraHeaders?: Record<string, string>;
28
+ /** Custom fetch implementation (e.g. expo/fetch for RN streaming support). */
29
+ fetchImpl?: FetchLike;
30
+ }
31
+ /** Options for a single chat completion request. */
32
+ interface LLMRequestOptions {
33
+ messages: ChatMessage[];
34
+ temperature?: number;
35
+ maxTokens?: number;
36
+ stream?: boolean;
37
+ frequencyPenalty?: number;
38
+ presencePenalty?: number;
39
+ signal?: AbortSignal;
40
+ }
41
+ /** Callbacks invoked during streaming. All optional – pass only what you need. */
42
+ interface StreamCallbacks {
43
+ /**
44
+ * Fired for every content token received.
45
+ *
46
+ * Return `true` to signal early termination — the stream will be
47
+ * aborted cleanly and `onComplete` will still fire with the content
48
+ * accumulated so far.
49
+ */
50
+ onToken?: (token: string) => void | boolean;
51
+ /**
52
+ * Fired once when the first SSE data chunk arrives from the model.
53
+ * Useful for closing premature-cancellation windows: Qwen 3+ models
54
+ * send reasoning tokens (delta.reasoning_content) for several seconds
55
+ * before any delta.content arrives, so onToken alone is insufficient
56
+ * to detect that the model is actively processing.
57
+ */
58
+ onStreamStart?: () => void;
59
+ /** Fired once when the stream finishes (includes full concatenated content). */
60
+ onComplete?: (fullContent: string, finishReason: string) => void;
61
+ /** Fired on recoverable stream errors (e.g. frozen stream detected). */
62
+ onError?: (error: Error) => void;
63
+ /** Informational log messages (replaces console.log / chalk in shared code). */
64
+ onLog?: (message: string, level: "info" | "warn" | "error") => void;
65
+ }
66
+ /** Result returned by LLMClient.chat(). */
67
+ interface LLMClientResponse {
68
+ content: string;
69
+ finishReason: string;
70
+ usage?: {
71
+ promptTokens: number;
72
+ completionTokens: number;
73
+ };
74
+ }
75
+ type ToolDataPrimitive = string | number | boolean | null;
76
+ type ToolDataValue = ToolDataPrimitive | ToolDataValue[] | {
77
+ [key: string]: ToolDataValue;
78
+ };
79
+ interface ToolDataSchema {
80
+ type?: string | string[];
81
+ description?: string;
82
+ enum?: readonly ToolDataPrimitive[];
83
+ items?: ToolDataSchema;
84
+ properties?: Record<string, ToolDataSchema>;
85
+ required?: string[];
86
+ additionalProperties?: boolean | ToolDataSchema;
87
+ default?: ToolDataValue;
88
+ }
89
+ interface ToolParameterDefinition {
90
+ type: string | string[];
91
+ description: string;
92
+ required?: boolean;
93
+ positional?: boolean;
94
+ enum?: readonly ToolDataPrimitive[];
95
+ items?: ToolDataSchema;
96
+ properties?: Record<string, ToolDataSchema>;
97
+ additionalProperties?: boolean | ToolDataSchema;
98
+ default?: ToolDataValue;
99
+ }
100
+ type ToolMemoryNoteScope = "none" | "tool";
101
+ type ToolMemoryNoteKind = "workflow" | "operation" | "change";
102
+ type ToolMemoryEvidenceKind = "codebase" | "research";
103
+ type ToolMemoryCitationKind = "path" | "query" | "url" | "command";
104
+ interface ToolMemoryNotePolicy {
105
+ scope: ToolMemoryNoteScope;
106
+ kind?: ToolMemoryNoteKind;
107
+ }
108
+ interface ToolMemoryEvidencePolicy {
109
+ kind: ToolMemoryEvidenceKind;
110
+ citation?: ToolMemoryCitationKind;
111
+ }
112
+ interface ToolMemoryMetadata {
113
+ note?: ToolMemoryNotePolicy;
114
+ evidence?: ToolMemoryEvidencePolicy[];
115
+ }
116
+ interface ToolDefinitionMetadata {
117
+ category: string;
118
+ capabilities: string[];
119
+ requirements?: string[];
120
+ memory?: ToolMemoryMetadata;
121
+ source?: "builtin" | "mcp";
122
+ mcpServerName?: string;
123
+ }
124
+ interface ToolDefinition {
125
+ name: string;
126
+ description: string;
127
+ usageExample?: string;
128
+ parameters: Record<string, ToolParameterDefinition>;
129
+ metadata?: ToolDefinitionMetadata;
130
+ }
131
+ interface ToolCall {
132
+ name: string;
133
+ parameters: Record<string, any>;
134
+ kind?: "tool" | "bash" | "background_bash";
135
+ rawInvocation?: string;
136
+ truncated?: boolean;
137
+ }
138
+ interface ToolResult {
139
+ success: boolean;
140
+ error?: string;
141
+ [key: string]: any;
142
+ }
143
+ type LoopOutcome = "continue" | "success" | "failure";
144
+ interface LLMRequest {
145
+ role: "agent";
146
+ task: string;
147
+ tools?: ToolDefinition[];
148
+ action_count: number;
149
+ }
150
+ interface LLMResponse {
151
+ content: string;
152
+ reasoning: string;
153
+ scratchpad?: string;
154
+ selectedToolCall?: ToolCall;
155
+ outcome: LoopOutcome;
156
+ finishReason?: string;
157
+ }
158
+ type ProviderName = "qwen-max" | "qwen-plus" | "openrouter-claude" | "openrouter-gemini" | "minimax" | "minimax-her" | "chatglm" | "kimi" | "doubao";
159
+
160
+ /**
161
+ * configure.ts — global configuration store for hammer-agent.
162
+ *
163
+ * Call `configure(options)` once at app startup to register provider configs
164
+ * and optional overrides (timeouts, memory limits, etc.).
165
+ *
166
+ * Provider configs are looked up by name via `getProviderConfig(name)`.
167
+ * Any name not explicitly configured falls back to a runtime error explaining
168
+ * what to register — no hardcoded API keys ship in this library.
169
+ */
170
+
171
+ interface HammerAgentProviderPreset {
172
+ apiKey: string;
173
+ baseUrl: string;
174
+ model: string;
175
+ /** Extra headers merged into every request (e.g. HTTP-Referer for OpenRouter). */
176
+ extraHeaders?: Record<string, string>;
177
+ }
178
+ /**
179
+ * Options accepted by `configure()`.
180
+ *
181
+ * `providers` is a map from provider name to provider config. You only need
182
+ * to register the providers your agent actually uses.
183
+ *
184
+ * All other fields have sensible defaults and are optional.
185
+ */
186
+ interface HammerAgentConfig {
187
+ /**
188
+ * Named provider presets. Keys are `ProviderName` strings, values are
189
+ * provider configs. Pass at minimum the provider(s) your agent uses.
190
+ *
191
+ * @example
192
+ * configure({
193
+ * providers: {
194
+ * openrouter: { apiKey: process.env.OPENROUTER_KEY!, baseUrl: '...', model: '...' },
195
+ * minimax: { apiKey: process.env.MINIMAX_KEY!, baseUrl: '...', model: '...' },
196
+ * },
197
+ * compactionProvider: 'minimax',
198
+ * })
199
+ */
200
+ providers?: Partial<Record<ProviderName, HammerAgentProviderPreset>>;
201
+ /**
202
+ * Which provider to use for memory compaction LLM calls.
203
+ * Must be a key in `providers`.
204
+ * @default first key in `providers`, or throws if not configured
205
+ */
206
+ compactionProvider?: ProviderName;
207
+ /**
208
+ * Milliseconds without any SSE data before treating the stream as frozen.
209
+ * @default 60_000
210
+ */
211
+ streamInactivityTimeout?: number;
212
+ /**
213
+ * Maximum milliseconds to wait for the very first SSE chunk.
214
+ * @default 120_000
215
+ */
216
+ firstChunkTimeout?: number;
217
+ /** @default 100 */
218
+ maxRetainedNotes?: number;
219
+ /** @default 50 */
220
+ maxFailedAttempts?: number;
221
+ /** @default 100 */
222
+ maxCodebaseEvidence?: number;
223
+ /** @default 50 */
224
+ maxResearchEvidence?: number;
225
+ /** @default 50 */
226
+ maxConstraints?: number;
227
+ /** @default 50 */
228
+ maxOpenTasks?: number;
229
+ /** @default 50 */
230
+ maxResolvedTasks?: number;
231
+ /** @default 0.1 */
232
+ compactionLlmTemperature?: number;
233
+ /** @default 4096 */
234
+ compactionLlmMaxTokens?: number;
235
+ }
236
+ /**
237
+ * Configure hammer-agent at startup.
238
+ *
239
+ * This function is safe to call multiple times — later calls merge over
240
+ * earlier ones, so you can split configuration across modules.
241
+ *
242
+ * @example
243
+ * import { configure } from 'hammer-agent'
244
+ *
245
+ * configure({
246
+ * providers: {
247
+ * minimax: {
248
+ * apiKey: process.env.MINIMAX_KEY!,
249
+ * baseUrl: 'https://api.minimaxi.com/v1',
250
+ * model: 'MiniMax-M2.7-highspeed',
251
+ * },
252
+ * },
253
+ * compactionProvider: 'minimax',
254
+ * })
255
+ */
256
+ declare function configure(options: HammerAgentConfig): void;
257
+ /**
258
+ * Return the `LLMProviderConfig` registered for `name`.
259
+ * Throws a descriptive error if the provider has not been configured.
260
+ */
261
+ declare function getProviderConfig(name: ProviderName): LLMProviderConfig;
262
+
263
+ /**
264
+ * LLMClient – generic, browser-compatible streaming client for
265
+ * OpenAI-compatible chat completion endpoints.
266
+ *
267
+ * This is the heart of the shared library. It handles:
268
+ * • Building the request payload
269
+ * • SSE streaming with inactivity / first-chunk timeouts
270
+ * • Retry with exponential back-off on network and transient HTTP errors
271
+ * • Pluggable callbacks so the consumer decides how to render output
272
+ *
273
+ * No Node.js-only APIs (chalk, process, fs) are used here – it works in
274
+ * both Node 22+ and modern browsers.
275
+ */
276
+
277
+ declare class LLMClient {
278
+ private config;
279
+ constructor(config: LLMProviderConfig);
280
+ /** Swap the underlying provider at runtime. */
281
+ setConfig(config: LLMProviderConfig): void;
282
+ /** Read-only access to the current provider config. */
283
+ getConfig(): LLMProviderConfig;
284
+ /**
285
+ * Send a chat completion request.
286
+ *
287
+ * When `options.stream` is true (the default) the response is streamed
288
+ * token-by-token through the supplied `callbacks`.
289
+ */
290
+ chat(options: LLMRequestOptions, callbacks?: StreamCallbacks): Promise<LLMClientResponse>;
291
+ private handleStream;
292
+ }
293
+ /** Custom error class for HTTP-level API errors (preserves status code). */
294
+ declare class ApiError extends Error {
295
+ readonly status: number;
296
+ constructor(status: number, body: string);
297
+ }
298
+
299
+ /** All possible states the agent can be in. */
300
+ type AgentMachineState = "idle" | "prompting" | "analyzing" | "executing" | "updating" | "done" | "failed";
301
+ declare const AGENT_MACHINE_STATES: readonly AgentMachineState[];
302
+ /** Truncated-tool metadata carried across iterations (Hammer-specific). */
303
+ interface TruncatedToolInfo {
304
+ name: string;
305
+ filePath?: string;
306
+ executionSucceeded: boolean;
307
+ }
308
+ /** Machine context — all mutable state for the agent loop. */
309
+ interface AgentMachineContext {
310
+ /** The user's task. */
311
+ task: string;
312
+ /** Current action counter (resumes from persisted state). */
313
+ actionCount: number;
314
+ /** Truncated-tool metadata for continuation guidance. */
315
+ truncatedToolInfo: TruncatedToolInfo | undefined;
316
+ /** Last tool result JSON string (for resume persistence). */
317
+ lastToolResult: string | undefined;
318
+ /** Error message when in failed state. */
319
+ error: string | undefined;
320
+ /** Final outcome that caused loop exit. */
321
+ finalOutcome: string;
322
+ /** Whether ListSkills has been called this session. */
323
+ hasCalledListSkills: boolean;
324
+ }
325
+ /** Events the machine accepts. */
326
+ type AgentMachineEvent = {
327
+ type: "START";
328
+ task: string;
329
+ actionCount?: number;
330
+ } | {
331
+ type: "PROMPT_COMPLETE";
332
+ } | {
333
+ type: "LLM_SUCCESS";
334
+ } | {
335
+ type: "LLM_VALIDATION_ERROR";
336
+ error: string;
337
+ } | {
338
+ type: "LLM_API_ERROR";
339
+ error: string;
340
+ } | {
341
+ type: "OUTCOME_SUCCESS";
342
+ } | {
343
+ type: "OUTCOME_FAILURE";
344
+ error?: string;
345
+ } | {
346
+ type: "TOOLS_EXECUTED";
347
+ lastToolResult?: string;
348
+ } | {
349
+ type: "ENFORCEMENT_BREAK";
350
+ lastToolResult: string;
351
+ } | {
352
+ type: "NO_TOOLS";
353
+ } | {
354
+ type: "UPDATE_COMPLETE";
355
+ } | {
356
+ type: "SET_TRUNCATION";
357
+ info: TruncatedToolInfo | undefined;
358
+ } | {
359
+ type: "RESTORE_CONTEXT";
360
+ context: Partial<AgentMachineContext>;
361
+ };
362
+ declare const agentMachine: xstate.StateMachine<AgentMachineContext, {
363
+ type: "START";
364
+ task: string;
365
+ actionCount?: number;
366
+ } | {
367
+ type: "PROMPT_COMPLETE";
368
+ } | {
369
+ type: "LLM_SUCCESS";
370
+ } | {
371
+ type: "LLM_VALIDATION_ERROR";
372
+ error: string;
373
+ } | {
374
+ type: "LLM_API_ERROR";
375
+ error: string;
376
+ } | {
377
+ type: "OUTCOME_SUCCESS";
378
+ } | {
379
+ type: "OUTCOME_FAILURE";
380
+ error?: string;
381
+ } | {
382
+ type: "TOOLS_EXECUTED";
383
+ lastToolResult?: string;
384
+ } | {
385
+ type: "ENFORCEMENT_BREAK";
386
+ lastToolResult: string;
387
+ } | {
388
+ type: "NO_TOOLS";
389
+ } | {
390
+ type: "UPDATE_COMPLETE";
391
+ } | {
392
+ type: "SET_TRUNCATION";
393
+ info: TruncatedToolInfo | undefined;
394
+ } | {
395
+ type: "RESTORE_CONTEXT";
396
+ context: Partial<AgentMachineContext>;
397
+ }, {}, never, {
398
+ type: "assignTask";
399
+ params: {
400
+ task: string;
401
+ actionCount?: number;
402
+ };
403
+ } | {
404
+ type: "assignLlmError";
405
+ params: {
406
+ error: string;
407
+ };
408
+ } | {
409
+ type: "assignOutcomeFailure";
410
+ params: {
411
+ error?: string;
412
+ };
413
+ } | {
414
+ type: "assignOutcomeSuccess";
415
+ params: xstate.NonReducibleUnknown;
416
+ } | {
417
+ type: "assignToolsExecuted";
418
+ params: {
419
+ lastToolResult?: string;
420
+ };
421
+ } | {
422
+ type: "assignEnforcementBreak";
423
+ params: {
424
+ lastToolResult: string;
425
+ };
426
+ } | {
427
+ type: "incrementAction";
428
+ params: xstate.NonReducibleUnknown;
429
+ } | {
430
+ type: "setTruncation";
431
+ params: {
432
+ info: TruncatedToolInfo | undefined;
433
+ };
434
+ } | {
435
+ type: "restoreContext";
436
+ params: {
437
+ context: Partial<AgentMachineContext>;
438
+ };
439
+ }, never, never, "done" | "idle" | "prompting" | "analyzing" | "executing" | "updating" | "failed", string, xstate.NonReducibleUnknown, xstate.NonReducibleUnknown, xstate.EventObject, xstate.MetaObject, {
440
+ id: "agent";
441
+ states: {
442
+ readonly idle: {};
443
+ readonly prompting: {};
444
+ readonly analyzing: {};
445
+ readonly executing: {};
446
+ readonly updating: {};
447
+ readonly done: {};
448
+ readonly failed: {};
449
+ };
450
+ }>;
451
+
452
+ /**
453
+ * BaseValidationEnforcer — shared error-handling infrastructure for
454
+ * any LLM agent loop (Hammer CLI, Tauri voice agent, etc.).
455
+ *
456
+ * Provides:
457
+ * • `surfaceError` / `surfaceWarning` → conversation injection
458
+ * • `handleValidationError` (run-line/Zod parse failures)
459
+ * • `handleApiError` (rate-limit, 5xx, etc.)
460
+ *
461
+ * Subclasses add domain-specific enforcement (skills-first policy,
462
+ * TTS error announcements, etc.).
463
+ */
464
+ interface ConversationSink {
465
+ addMessage(role: "user" | "assistant" | "system" | "tool", content: string): Promise<void>;
466
+ }
467
+ declare function createConversationSink(appendMessage: (role: "user" | "assistant" | "system" | "tool", content: string) => void | Promise<void>): ConversationSink;
468
+ interface EnforcerResult {
469
+ /** Error message surfaced to the agent as a user-visible message. */
470
+ errorMessage: string;
471
+ /** JSON-serialised `{ success, error, data }` suitable for `lastToolResult`. */
472
+ lastToolResult: string;
473
+ }
474
+ declare abstract class BaseValidationEnforcer {
475
+ protected sink: ConversationSink;
476
+ constructor(sink: ConversationSink);
477
+ /**
478
+ * Warn the agent that it sent a non-terminal response without any
479
+ * executable control block — this wastes an action turn.
480
+ */
481
+ handleMissingToolCall(actionCount: number): Promise<void>;
482
+ /**
483
+ * Handle an LLM response validation error (shell-line parse, Zod, etc.).
484
+ *
485
+ * - surfaces a structured corrective error as a user message
486
+ */
487
+ handleValidationError(error: Error): Promise<EnforcerResult>;
488
+ /**
489
+ * Handle a non-validation LLM API error (rate-limit, 5xx, etc.).
490
+ * Surfaces the message to the agent so it can retry on the next turn.
491
+ */
492
+ handleApiError(error: Error): Promise<EnforcerResult>;
493
+ /**
494
+ * Handle an unrecoverable runtime error that terminates the current run.
495
+ * Surfaces the message through the same user-visible error channel as
496
+ * validation and API failures.
497
+ */
498
+ handleFatalError(error: Error | string): Promise<EnforcerResult>;
499
+ /**
500
+ * Override to route warnings to a domain-specific logger.
501
+ * Default: `console.warn`.
502
+ */
503
+ protected logWarning(message: string): void;
504
+ /**
505
+ * Override to route errors to a domain-specific logger.
506
+ * Default: `console.error`.
507
+ */
508
+ protected logError(message: string): void;
509
+ protected surfaceValidationContext(error: Error): Promise<void>;
510
+ protected readValidationRawContent(error: Error): string | null;
511
+ protected surfaceError(errorMessage: string): Promise<EnforcerResult>;
512
+ protected surfaceWarning(warningMessage: string): Promise<void>;
513
+ }
514
+
515
+ /** Abstraction over conversation storage (memory layer / conversation manager). */
516
+ interface ConversationAdapter {
517
+ appendMessage(role: "user" | "assistant" | "tool", content: string): Promise<void>;
518
+ /** Append a tool result with tool name for compaction tracking. Falls back to appendMessage("tool",...) if not provided. */
519
+ appendToolMessage?(content: string, toolCallId: string): Promise<void>;
520
+ getLastMessageRole(): string | undefined;
521
+ getLastMessageContent(): string | undefined;
522
+ triggerCompactionIfNeeded(): Promise<void>;
523
+ }
524
+ /** Callbacks for platform-specific UI / logging. */
525
+ interface AgentLoopCallbacks {
526
+ onToolStart?(name: string, parameters: Record<string, unknown>, index: number, total: number): void;
527
+ onToolComplete?(name: string, result: ToolResult, truncatedResultStr: string): void;
528
+ onPhaseChange?(state: AgentMachineState): void;
529
+ /** Called when the agent reports success. */
530
+ onComplete?(): void;
531
+ /** Called when the agent reports failure. */
532
+ onFail?(): void;
533
+ }
534
+ /** Everything the AgentLoop needs from its host environment. */
535
+ interface AgentLoopDeps {
536
+ /** Execute a single tool call. */
537
+ executeTool: (call: ToolCall) => Promise<ToolResult>;
538
+ /** Validation / error enforcer. */
539
+ enforcer: BaseValidationEnforcer;
540
+ /** Conversation storage adapter. */
541
+ conversation: ConversationAdapter;
542
+ /** Optional: transform raw ToolResult before truncation (e.g. spinner cleanup). */
543
+ formatToolResult?: (result: ToolResult, toolName: string) => ToolResult;
544
+ /**
545
+ * Optional: pre-tool hook. Return non-null to **block** execution and
546
+ * break out of the tool loop (e.g. Hammer's ListSkills-first enforcement).
547
+ * The returned `lastToolResult` is stored and the machine transitions
548
+ * to ENFORCEMENT_BREAK.
549
+ */
550
+ onBeforeToolExecution?: (name: string, params: Record<string, unknown>) => Promise<{
551
+ lastToolResult: string;
552
+ } | null>;
553
+ /** Platform-specific callbacks for UI/logging. */
554
+ callbacks?: AgentLoopCallbacks;
555
+ }
556
+ /** Parsed LLM step response — the caller parses, AgentLoop processes. */
557
+ interface ParsedStepInput {
558
+ outcome: LoopOutcome;
559
+ reasoning?: string;
560
+ selectedToolCall?: ToolCall;
561
+ wasTruncated: boolean;
562
+ /**
563
+ * Raw LLM content string. When provided, `processStep` records it
564
+ * as an assistant message in the conversation adapter before
565
+ * processing, so callers don't need to do it themselves.
566
+ */
567
+ rawContent?: string;
568
+ }
569
+ /** Result of a single tool execution (for the caller to build UI). */
570
+ interface ToolExecutionResult {
571
+ name: string;
572
+ result: ToolResult;
573
+ truncatedResultStr: string;
574
+ }
575
+ /** What processStep returns — outcome is the loop-control signal. */
576
+ interface StepResult {
577
+ outcome: LoopOutcome;
578
+ toolResults: ToolExecutionResult[];
579
+ }
580
+ declare class AgentLoop {
581
+ private deps;
582
+ private actor;
583
+ private _truncatedToolInfo?;
584
+ private _lastToolResult?;
585
+ constructor(deps: AgentLoopDeps);
586
+ /**
587
+ * Send an event to the actor, silently swallowing errors if the actor
588
+ * was stopped concurrently (e.g. abort/destroy during an async gap).
589
+ */
590
+ private safeSend;
591
+ /** Current XState machine state. */
592
+ get machineState(): AgentMachineState;
593
+ /** Current action count from the machine context. */
594
+ get actionCount(): number;
595
+ /** Last truncation info (for the caller to store / display). */
596
+ get truncatedToolInfo(): TruncatedToolInfo | undefined;
597
+ /** Last tool result string (independent of machine state). */
598
+ get lastToolResult(): string | undefined;
599
+ /** Transition: idle → prompting. */
600
+ start(task: string): void;
601
+ /**
602
+ * Inject the step user-message and transition to analyzing.
603
+ *
604
+ * @param actionCountOverride If provided, use this count instead of the
605
+ * machine's internal count (useful when the caller tracks total count
606
+ * across sessions).
607
+ * @returns `true` if a user message was injected, `false` if skipped.
608
+ */
609
+ prepareStep(actionCountOverride?: number): Promise<boolean>;
610
+ /**
611
+ * Process a fully-parsed LLM step response end-to-end.
612
+ *
613
+ * The caller is responsible for:
614
+ * 1. The LLM call (streaming or not)
615
+ * 2. Parsing the response into {@link ParsedStepInput}
616
+ * 3. UI updates based on the returned {@link StepResult}
617
+ *
618
+ * This method handles:
619
+ * 1. Recording the assistant message (when `rawContent` is provided)
620
+ * 2. Terminal outcomes (success / failure)
621
+ * 3. State machine transitions
622
+ * 4. Tool execution (via deps.executeTool)
623
+ * 5. Result formatting, truncation, and conversation storage
624
+ * 6. Truncation tracking across iterations
625
+ */
626
+ processStep(input: ParsedStepInput, signal?: AbortSignal): Promise<StepResult>;
627
+ /** Handle an LLM validation error (JSON parse, Zod, etc.). */
628
+ handleLLMValidationError(error: Error): Promise<EnforcerResult>;
629
+ /** Handle an LLM API error (network, rate limit, 5xx). */
630
+ handleLLMApiError(error: Error): Promise<EnforcerResult>;
631
+ /**
632
+ * Classify a step LLM error as validation or API error and
633
+ * delegate to the appropriate handler.
634
+ */
635
+ classifyStepError(error: Error): Promise<void>;
636
+ /**
637
+ * Handle an unrecoverable error and transition the machine to a
638
+ * terminal state.
639
+ */
640
+ handleFatalError(errorMsg: string): Promise<void>;
641
+ /** Send an arbitrary event to the machine (for platform-specific transitions). */
642
+ sendEvent(event: Parameters<typeof this$1.actor.send>[0]): void;
643
+ /** Get the raw machine context snapshot. */
644
+ getContext(): AgentMachineContext;
645
+ /** Restore truncated tool info from a previous session. */
646
+ restoreTruncatedToolInfo(info: TruncatedToolInfo | undefined): void;
647
+ /** Stop the XState actor. Call during cleanup / unmount. */
648
+ destroy(): void;
649
+ /**
650
+ * Reset the AgentLoop by stopping the current actor and creating
651
+ * a new one. Returns to idle state.
652
+ */
653
+ reset(): void;
654
+ }
655
+
656
+ /**
657
+ * Base Memory Layer — shared infrastructure for memory compaction.
658
+ *
659
+ * Both workspace agents and the Voice Agent use episodic compaction with
660
+ * structured state extraction. This module provides the shared skeleton:
661
+ *
662
+ * - Generic base class (`BaseMemoryLayer<TState, TMessage>`)
663
+ * - Shared types: `MemoryMessage`, `CompactionCursor`, `MemoryStorage`, etc.
664
+ * - Token estimation strategy (pluggable: tiktoken vs char-based)
665
+ * - Shared algorithms: sliding-window, buildMessages, triggerCompaction
666
+ *
667
+ * Core Invariants (enforced by this base):
668
+ * 1. Raw history is NEVER modified in-place; entries are pruned after compaction
669
+ * 2. Compaction is a state transition, not a view transform
670
+ * 3. Compressed state is NEVER recursively summarized
671
+ * 4. State compaction removes obsolete structure but preserves meaning
672
+ * 5. buildMessages is a PURE function (cache writes are benign memoization)
673
+ */
674
+
675
+ /**
676
+ * Minimal interface for an LLM client used during compaction.
677
+ * Kept abstract so memory-layer has no hard dependency on LLMClient.
678
+ * The shared `LLMClient` class satisfies this interface.
679
+ */
680
+ interface CompactionLLMClient {
681
+ chat(options: {
682
+ messages: ChatMessage[];
683
+ temperature?: number;
684
+ maxTokens?: number;
685
+ stream?: boolean;
686
+ }): Promise<{
687
+ content: string;
688
+ }>;
689
+ }
690
+ /** Base message stored in raw history. */
691
+ interface MemoryMessage {
692
+ id: string;
693
+ role: "system" | "user" | "assistant" | "tool";
694
+ content: string;
695
+ timestamp: number;
696
+ /** Monotonic message counter. */
697
+ turn: number;
698
+ /** Cached token count for this message (computed once at creation). */
699
+ tokenCount: number;
700
+ }
701
+ interface MemoryProvenance {
702
+ source: "rule" | "llm" | "tool" | "assistant" | "user";
703
+ detail?: string;
704
+ }
705
+ /** Tracks how far compaction has progressed. */
706
+ interface CompactionCursor {
707
+ lastCompactedTurn: number;
708
+ }
709
+ /**
710
+ * Strategy for estimating token counts.
711
+ *
712
+ * - Workspace agents share the same estimator strategy by default
713
+ * - CharTokenEstimator is the cross-platform baseline implementation
714
+ */
715
+ interface TokenEstimator {
716
+ /** Estimate token count for a string. */
717
+ estimateTokens(text: string): number;
718
+ /** Dispose any resources (e.g. tiktoken WASM). No-op by default. */
719
+ dispose?(): void;
720
+ }
721
+ /**
722
+ * Character-based token estimator.
723
+ * GPT/Claude average ~4 chars/token for English; 3.5 is conservative.
724
+ */
725
+ declare class CharTokenEstimator implements TokenEstimator {
726
+ private charsPerToken;
727
+ constructor(charsPerToken?: number);
728
+ estimateTokens(text: string): number;
729
+ }
730
+ /**
731
+ * Platform-agnostic persistence adapter (generic over data shape).
732
+ *
733
+ * Implementations: LocalStorageAdapter, HttpAdapter, ExpoFileAdapter,
734
+ * and the Bun-server FileMemoryAdapter.
735
+ */
736
+ interface MemoryStorage<T = unknown> {
737
+ load(): Promise<T | null>;
738
+ save(data: T): Promise<void>;
739
+ clear(): Promise<void>;
740
+ }
741
+ /** Base shape of persisted memory data. Apps may extend with extra fields. */
742
+ interface PersistedMemoryData<TState = unknown, TMessage extends MemoryMessage = MemoryMessage> {
743
+ rawHistory: TMessage[];
744
+ compressedState: TState;
745
+ compactionCursor: CompactionCursor;
746
+ currentTurn: number;
747
+ compactionCount: number;
748
+ }
749
+ interface MemoryLayerConfig {
750
+ /** Trigger compaction when estimated context exceeds this many tokens. */
751
+ compactionTokenThreshold: number;
752
+ /** Keep at least this many tokens of recent messages uncompressed. */
753
+ protectedContextTokens: number;
754
+ /** Maximum tokens for the rendered compressed state. */
755
+ stateBudgetTokens: number;
756
+ /** Hard cap on raw history entries (safety net). */
757
+ maxRawHistory: number;
758
+ /** Minimum turns between compaction attempts. */
759
+ compactionDebounceTurns: number;
760
+ /** Baseline token overhead for system prompt (conservative estimate). */
761
+ systemPromptOverhead: number;
762
+ /** Strategy for estimating token counts. */
763
+ tokenEstimator: TokenEstimator;
764
+ /** Optional LLM client for enhanced compaction (e.g. MiniMax). */
765
+ compactionClient?: CompactionLLMClient;
766
+ /** Temperature for compaction LLM calls (default 0.1). */
767
+ compactionTemperature?: number;
768
+ /** Max tokens for compaction LLM output (default 4096). */
769
+ compactionMaxTokens?: number;
770
+ }
771
+ /**
772
+ * Abstract base class for memory layers with episodic compaction.
773
+ *
774
+ * Subclasses provide:
775
+ * - Message creation (TMessage construction with app-specific fields)
776
+ * - Compaction logic (extraction plus pruning obsolete state entries)
777
+ * - State rendering (converting TState to human-readable text)
778
+ *
779
+ * The base class provides:
780
+ * - Append-only raw history with hard cap enforcement
781
+ * - Token-budgeted sliding window for recent messages
782
+ * - buildMessages assembly (system → state → recent)
783
+ * - Compaction triggering (debounce, threshold check, prune)
784
+ * - Rendered state caching with invalidation
785
+ * - Persistence lifecycle (save/load/clear)
786
+ * - Accessors for metrics
787
+ */
788
+ declare abstract class BaseMemoryLayer<TState, TMessage extends MemoryMessage> {
789
+ protected readonly config: MemoryLayerConfig;
790
+ protected rawHistory: TMessage[];
791
+ protected compressedState: TState;
792
+ protected compactionCursor: CompactionCursor;
793
+ protected currentTurn: number;
794
+ protected compactionCount: number;
795
+ /** Rendered state cache (invalidated on mutation). */
796
+ protected renderedStateCache: string | null;
797
+ protected stateTokenCountCache: number | null;
798
+ /** Debounce: turns since last compaction attempt. */
799
+ protected lastCompactionAttempt: number;
800
+ /**
801
+ * Static context: rules/instructions injected once, always included in
802
+ * buildMessages, never compressed or pruned.
803
+ */
804
+ protected staticContext: string | null;
805
+ constructor(config: MemoryLayerConfig, initialState: TState);
806
+ /**
807
+ * Create a TMessage from the given parts.
808
+ * Subclass should compute and cache the tokenCount (or use tokenEstimator).
809
+ */
810
+ protected abstract createMessage(id: string, role: "system" | "user" | "assistant" | "tool", content: string, turn: number, timestamp: number): TMessage;
811
+ /**
812
+ * Extract structured facts from raw messages into compressedState and
813
+ * enforce any dedupe, aging, and hard-cap rules for the durable state.
814
+ * This is deterministic pattern matching, NOT LLM summarization.
815
+ * Should mutate `this.compressedState` in place.
816
+ */
817
+ protected abstract performCompaction(messages: TMessage[]): void;
818
+ /**
819
+ * Render compressedState as human-readable structured text for LLM context.
820
+ * Result is cached — only called when cache is invalidated.
821
+ */
822
+ protected abstract renderState(): string;
823
+ /**
824
+ * Create a fresh initial state (used by clear()).
825
+ */
826
+ protected abstract createInitialState(): TState;
827
+ /** Label for the compressed state section in buildMessages. */
828
+ protected getStateLabel(): string;
829
+ /** Placeholder user message when no user message precedes the first assistant. */
830
+ protected getResumePlaceholder(): string;
831
+ /** Called after a message is appended (e.g. for failure tracking). */
832
+ protected onMessageAppended(_msg: TMessage): void;
833
+ /** Called after compaction + prune (e.g. for extra persistence). */
834
+ protected onCompactionComplete(): void;
835
+ /**
836
+ * LLM-enhanced compaction. Override in subclass to provide LLM-based
837
+ * summarization using `this.config.compactionClient`.
838
+ *
839
+ * When this returns `true`, the pattern-matching `performCompaction()`
840
+ * is skipped. When it returns `false` (or throws), pattern matching
841
+ * runs as a deterministic fallback.
842
+ */
843
+ protected performLLMCompaction(_messages: TMessage[]): Promise<boolean>;
844
+ /** Append a message to raw history. Returns the message ID. */
845
+ appendMessage(role: "system" | "user" | "assistant" | "tool", content: string): string;
846
+ /**
847
+ * Build the message array for LLM context.
848
+ *
849
+ * Assembly order:
850
+ * 1. System prompt
851
+ * 2. Static context (if any)
852
+ * 3. Compressed state
853
+ * 4. Recent raw messages (token-budgeted sliding window)
854
+ *
855
+ * Post-processing:
856
+ * - Tool messages converted to user role ("Tool result: …")
857
+ * - Consecutive same-role messages merged
858
+ * - Guard: user message inserted before first assistant if missing
859
+ * - Leading system messages merged into one
860
+ */
861
+ buildMessages(systemPrompt: string): ChatMessage[];
862
+ /** Trigger compaction if conversation context exceeds token threshold. */
863
+ triggerCompactionIfNeeded(): Promise<void>;
864
+ /** Get recent messages that fit within the protected token budget. */
865
+ protected getRecentMessages(): TMessage[];
866
+ /** Estimate total context tokens (recent + state + system overhead). */
867
+ estimateContextTokens(): number;
868
+ /** Estimate compressed state tokens. Cached alongside rendered state. */
869
+ estimateStateTokens(): number;
870
+ /** Get rendered state, using cache when valid. */
871
+ protected getCachedRenderedState(): string;
872
+ /** Invalidate rendered state cache (call after any state mutation). */
873
+ invalidateStateCache(): void;
874
+ /**
875
+ * Set static context (rules/instructions injected once into every
876
+ * buildMessages call). Never compressed or pruned.
877
+ */
878
+ setStaticContext(content: string): void;
879
+ /** Check if static context has been set. */
880
+ hasStaticContext(): boolean;
881
+ /** Serialize base memory state. Subclasses can extend. */
882
+ serializeBase(): PersistedMemoryData<TState, TMessage>;
883
+ /** Restore base memory state from persisted data. */
884
+ loadBase(data: PersistedMemoryData<TState, TMessage>): void;
885
+ /**
886
+ * Sanitize raw history messages loaded from persistence.
887
+ *
888
+ * Guards against corrupted or older persisted data by providing safe defaults
889
+ * for all base MemoryMessage fields. Subclasses can extend to handle
890
+ * app-specific fields (e.g. charCount, toolCallId).
891
+ */
892
+ protected sanitizeHistory(rawHistory: Array<Partial<TMessage> & Record<string, any>>): TMessage[];
893
+ /** Reset all state. */
894
+ reset(): void;
895
+ getCurrentTurn(): number;
896
+ getCompactionCount(): number;
897
+ getRawHistoryLength(): number;
898
+ getRawHistory(): TMessage[];
899
+ getCompressedState(): TState;
900
+ getCompactionCursor(): CompactionCursor;
901
+ getLastMessageRole(): string | null;
902
+ getLastMessageContent(): string | null;
903
+ getLastCompactionAttempt(): number;
904
+ /**
905
+ * Remove and return the last message from raw history.
906
+ * Useful for cancelling a premature assistant response.
907
+ */
908
+ popLastMessage(): TMessage | undefined;
909
+ /**
910
+ * Find the last message with the given role, scanning backward.
911
+ */
912
+ getLastMessageByRole(role: string): TMessage | undefined;
913
+ /**
914
+ * Update the content of the last message matching `role`.
915
+ * Re-estimates token count automatically. Returns `true` if
916
+ * a matching message was found and updated.
917
+ */
918
+ updateLastMessageByRole(role: string, content: string): boolean;
919
+ }
920
+
921
+ /**
922
+ * Shared Agent Memory Layer with Episodic Compaction
923
+ *
924
+ * Extends BaseMemoryLayer with agent-specific compressed state:
925
+ * - Retained notes, constraints, structured evidence, failed attempts
926
+ * - Compaction patterns (shell-line command parsing, tool/result extraction)
927
+ * - Compaction rules (retained-note canonicalization, failure subsuming)
928
+ * - LLM-based compaction with structured prompts
929
+ *
930
+ * Used by both Hammer CLI and Magic browser agents. Consumers configure
931
+ * agent-specific behaviour via AgentMemoryLayerConfig (logger, tool names,
932
+ * token budgets, etc.).
933
+ */
934
+
935
+ interface RawMessage extends MemoryMessage {
936
+ toolCallId?: string;
937
+ }
938
+ interface AgentMemoryCitation {
939
+ kind: ToolMemoryCitationKind;
940
+ value: string;
941
+ }
942
+ interface AgentMemoryMetadata {
943
+ provenance: MemoryProvenance;
944
+ }
945
+ interface AgentMemoryNote extends AgentMemoryMetadata {
946
+ summary: string;
947
+ category?: string;
948
+ rationale?: string;
949
+ kind?: ToolMemoryNoteKind;
950
+ citations?: AgentMemoryCitation[];
951
+ turn: number;
952
+ }
953
+ interface AgentMemoryEvidence extends AgentMemoryMetadata {
954
+ summary: string;
955
+ kind: ToolMemoryEvidenceKind;
956
+ citations?: AgentMemoryCitation[];
957
+ turn: number;
958
+ }
959
+ interface AgentMemoryConstraint extends AgentMemoryMetadata {
960
+ text: string;
961
+ canonical: string;
962
+ turn: number;
963
+ }
964
+ interface AgentMemoryTask extends AgentMemoryMetadata {
965
+ task: string;
966
+ canonical: string;
967
+ turn: number;
968
+ }
969
+ interface AgentMemoryResolvedTask extends AgentMemoryMetadata {
970
+ task: string;
971
+ canonical: string;
972
+ resolvedTurn: number;
973
+ }
974
+ interface AgentMemoryFailedAttempt extends AgentMemoryMetadata {
975
+ approach: string;
976
+ reason: string;
977
+ canonicalApproach: string;
978
+ turn: number;
979
+ }
980
+ type CompressedAgentState = {
981
+ scratchpad?: string;
982
+ retainedNotes: AgentMemoryNote[];
983
+ constraints: AgentMemoryConstraint[];
984
+ evidence: AgentMemoryEvidence[];
985
+ failedAttempts: AgentMemoryFailedAttempt[];
986
+ openTasks: AgentMemoryTask[];
987
+ resolvedTasks: AgentMemoryResolvedTask[];
988
+ lastUpdatedTurn: number;
989
+ };
990
+ type MemoryMetrics = {
991
+ totalTurns: number;
992
+ totalMessages: number;
993
+ compactedTurns: number;
994
+ compactionCount: number;
995
+ recentWindowSize: number;
996
+ protectedContextTokens: number;
997
+ compressedStateTokens: number;
998
+ estimatedContextTokens: number;
999
+ };
1000
+ /** Optional structured logger — avoids coupling to Hammer's `log` import. */
1001
+ interface AgentMemoryLogger {
1002
+ warn(label: string, message: string): void;
1003
+ }
1004
+ interface ToolMemoryExtractionContext {
1005
+ toolName: string;
1006
+ parsed: Record<string, any>;
1007
+ toolDefinition?: ToolDefinition;
1008
+ turn: number;
1009
+ }
1010
+ /**
1011
+ * Defines how successful tool results should be converted into structured
1012
+ * durable memory entries.
1013
+ */
1014
+ interface ToolMemoryExtractor {
1015
+ /** Add retained notes derived from successful tool results. */
1016
+ extractNotes?(context: ToolMemoryExtractionContext): AgentMemoryNote[];
1017
+ /** Add durable evidence entries derived from successful tool results. */
1018
+ extractEvidence?(context: ToolMemoryExtractionContext): AgentMemoryEvidence[];
1019
+ }
1020
+ /**
1021
+ * Default tool-memory extractor for Hammer-style tools. Tool definitions carry
1022
+ * the policy; this default implementation turns those policies into structured
1023
+ * evidence entries.
1024
+ */
1025
+ declare const DEFAULT_TOOL_MEMORY_EXTRACTOR: ToolMemoryExtractor;
1026
+ interface AgentMemoryLayerConfig {
1027
+ /** Token threshold that triggers compaction. */
1028
+ compactionTokenThreshold: number;
1029
+ /** Tokens reserved for recent uncompressed messages. */
1030
+ protectedContextTokens: number;
1031
+ /** Token budget for the rendered compressed state block. */
1032
+ stateBudgetTokens: number;
1033
+ /** Hard cap on raw history entries. */
1034
+ maxRawHistory: number;
1035
+ /** Minimum turns between compaction attempts. */
1036
+ compactionDebounceTurns: number;
1037
+ /** Token estimate for the system prompt. */
1038
+ systemPromptOverhead: number;
1039
+ /** Token estimator implementation. */
1040
+ tokenEstimator: TokenEstimator;
1041
+ /** Optional LLM client for LLM-based compaction. */
1042
+ compactionClient?: CompactionLLMClient;
1043
+ /** Temperature for compaction LLM calls (default: 0.1). */
1044
+ compactionTemperature?: number;
1045
+ /** Max tokens for compaction LLM output (default: 4096). */
1046
+ compactionMaxTokens?: number;
1047
+ /** Registry-backed tool definitions used to classify tool calls during compaction. */
1048
+ getToolDefinitions?: () => ToolDefinition[];
1049
+ /** Optional tool-memory extractor for converting tool results to state entries. */
1050
+ toolMemoryExtractor?: ToolMemoryExtractor;
1051
+ /** Optional structured logger. */
1052
+ logger?: AgentMemoryLogger;
1053
+ }
1054
+ declare class AgentMemoryLayer extends BaseMemoryLayer<CompressedAgentState, RawMessage> {
1055
+ /** Temporary storage for toolCallId during appendMessage. */
1056
+ private pendingToolCallId?;
1057
+ /** The agent-level config (superset of base MemoryLayerConfig). */
1058
+ private readonly agentConfig;
1059
+ constructor(config: AgentMemoryLayerConfig);
1060
+ /**
1061
+ * Append a message to raw history.
1062
+ * Extends the base signature with an optional toolCallId.
1063
+ */
1064
+ appendMessage(role: "system" | "user" | "assistant" | "tool", content: string): string;
1065
+ appendMessage(role: "system" | "user" | "assistant" | "tool", content: string, toolCallId: string | undefined): string;
1066
+ appendMessage(message: {
1067
+ role: "system" | "user" | "assistant" | "tool";
1068
+ content: string;
1069
+ toolCallId?: string;
1070
+ }): string;
1071
+ protected createMessage(id: string, role: "system" | "user" | "assistant" | "tool", content: string, turn: number, timestamp: number): RawMessage;
1072
+ protected performCompaction(messages: RawMessage[]): void;
1073
+ protected performLLMCompaction(messages: RawMessage[]): Promise<boolean>;
1074
+ private parseLLMCompactionObject;
1075
+ private finalizeCompactedState;
1076
+ protected renderState(): string;
1077
+ protected createInitialState(): CompressedAgentState;
1078
+ protected getStateLabel(): string;
1079
+ protected getResumePlaceholder(): string;
1080
+ reset(): void;
1081
+ /**
1082
+ * Get comprehensive metrics for monitoring and observability
1083
+ */
1084
+ getMetrics(): MemoryMetrics;
1085
+ /**
1086
+ * Create a ConversationAdapter backed by this memory layer.
1087
+ *
1088
+ * Eliminates boilerplate glue code that both Magic and Hammer
1089
+ * previously duplicated when wiring AgentLoop.
1090
+ */
1091
+ toConversationAdapter(): ConversationAdapter;
1092
+ /**
1093
+ * Free token estimator resources.
1094
+ * Call when the memory layer is no longer needed to prevent memory leaks.
1095
+ */
1096
+ dispose(): void;
1097
+ /**
1098
+ * Serialize full agent memory state for persistence.
1099
+ * Combines base fields with agent-specific state.
1100
+ */
1101
+ serialize(): PersistedMemoryData<CompressedAgentState, RawMessage> & {
1102
+ lastCompactionAttempt: number;
1103
+ };
1104
+ /**
1105
+ * Load from persisted data with comprehensive validation.
1106
+ * Ensures all fields have valid structure and reasonable defaults.
1107
+ */
1108
+ loadFromPersisted(data: {
1109
+ rawHistory: RawMessage[];
1110
+ compressedState: any;
1111
+ compactionCursor: {
1112
+ lastCompactedTurn: number;
1113
+ };
1114
+ currentTurn: number;
1115
+ lastCompactionAttempt?: number;
1116
+ compactionCount?: number;
1117
+ }): void;
1118
+ /**
1119
+ * Extract structured assistant response data from prose plus
1120
+ * standalone control segments.
1121
+ */
1122
+ private extractStructuredResponse;
1123
+ /**
1124
+ * Safe parsing of tool response with fallback.
1125
+ * Returns structured data instead of string matching.
1126
+ */
1127
+ private parseToolResponse;
1128
+ }
1129
+
1130
+ /**
1131
+ * Shared non-voice tool-loop runtime for Hammer, Magic, Monoslides, and Monospace.
1132
+ *
1133
+ * This base owns the common runtime seam across non-voice agents: memory
1134
+ * bootstrap, static context injection, AgentLoop setup, per-step LLM calls,
1135
+ * and parsed step execution.
1136
+ */
1137
+
1138
+ interface ToolLoopRuntimeStepContext<TStepInput = undefined> {
1139
+ task: string;
1140
+ actionCount: number;
1141
+ tools: ToolDefinition[];
1142
+ stepInput?: TStepInput;
1143
+ }
1144
+ interface ToolLoopRuntimeRunStepOptions<TStepInput = undefined> {
1145
+ task: string;
1146
+ actionCount: number;
1147
+ signal?: AbortSignal;
1148
+ stepInput?: TStepInput;
1149
+ }
1150
+ interface ToolLoopRuntimeLLMRequest {
1151
+ messages: ChatMessage[];
1152
+ temperature?: number;
1153
+ maxTokens?: number;
1154
+ frequencyPenalty?: number;
1155
+ presencePenalty?: number;
1156
+ onToken?: (token: string) => void | boolean;
1157
+ normalizeResponseContent?: (content: string) => string;
1158
+ }
1159
+ interface ToolLoopRuntimeLLMResponse {
1160
+ content: string;
1161
+ finishReason: string;
1162
+ usage?: LLMClientResponse["usage"];
1163
+ }
1164
+ interface ToolLoopRuntimeHooks<TStepInput = undefined> {
1165
+ onBeforeStep?(context: ToolLoopRuntimeStepContext<TStepInput>): Promise<void> | void;
1166
+ onAfterStep?(context: ToolLoopRuntimeStepContext<TStepInput>, result: StepResult): Promise<void> | void;
1167
+ onToken?(token: string): void | boolean;
1168
+ }
1169
+ interface ToolLoopAgentRuntimeDeps<TStepInput = undefined> {
1170
+ llmClient?: LLMClient;
1171
+ loopCallbacks?: AgentLoopCallbacks;
1172
+ hooks?: ToolLoopRuntimeHooks<TStepInput>;
1173
+ requireTodoListOnFirstResponse?: boolean;
1174
+ }
1175
+ interface ToolLoopRuntimeSetup<TMemory extends AgentMemoryLayer, TEnforcer extends BaseValidationEnforcer> {
1176
+ memory: TMemory;
1177
+ enforcer: TEnforcer;
1178
+ conversation: ConversationAdapter;
1179
+ loopDeps: Omit<AgentLoopDeps, "enforcer" | "conversation" | "callbacks">;
1180
+ }
1181
+ interface ToolLoopRuntimeInfrastructure<TMemory extends AgentMemoryLayer, TEnforcer extends BaseValidationEnforcer> extends ToolLoopRuntimeSetup<TMemory, TEnforcer> {
1182
+ loop: AgentLoop;
1183
+ }
1184
+ interface ToolLoopRuntimeRunStepResult<TStepInput = undefined> {
1185
+ context: ToolLoopRuntimeStepContext<TStepInput>;
1186
+ response: ToolLoopRuntimeLLMResponse;
1187
+ parsed: ParsedStepInput;
1188
+ result: StepResult;
1189
+ }
1190
+ interface ToolLoopRuntimeExecuteStepResult {
1191
+ response: ToolLoopRuntimeLLMResponse;
1192
+ parsed: ParsedStepInput;
1193
+ }
1194
+ declare abstract class ToolLoopAgentRuntime<TMemory extends AgentMemoryLayer = AgentMemoryLayer, TEnforcer extends BaseValidationEnforcer = BaseValidationEnforcer, TStepInput = undefined> {
1195
+ protected readonly llmClient: LLMClient | null;
1196
+ protected readonly loopCallbacks: AgentLoopCallbacks;
1197
+ protected readonly hooks: ToolLoopRuntimeHooks<TStepInput>;
1198
+ private readonly requireTodoListOnFirstResponse;
1199
+ private infrastructure;
1200
+ private infrastructurePromise;
1201
+ protected constructor(deps: ToolLoopAgentRuntimeDeps<TStepInput>);
1202
+ protected abstract createRuntimeSetup(): Promise<ToolLoopRuntimeSetup<TMemory, TEnforcer>>;
1203
+ protected abstract getToolDefinitions(): ToolDefinition[];
1204
+ protected buildSystemPrompt(_context: ToolLoopRuntimeStepContext<TStepInput>): string;
1205
+ protected buildLLMRequest(_context: ToolLoopRuntimeStepContext<TStepInput>, _messages: ChatMessage[]): ToolLoopRuntimeLLMRequest;
1206
+ protected parseStepResponse(_response: ToolLoopRuntimeLLMResponse, _tools: ToolDefinition[]): ParsedStepInput;
1207
+ protected get runtimeLoop(): AgentLoop | null;
1208
+ protected get runtimeMemory(): TMemory | null;
1209
+ protected get runtimeEnforcer(): TEnforcer | null;
1210
+ protected createStepContext(options: ToolLoopRuntimeRunStepOptions<TStepInput>, tools: ToolDefinition[]): ToolLoopRuntimeStepContext<TStepInput>;
1211
+ protected ensureStaticContext(_memory: TMemory): Promise<void>;
1212
+ protected prepareMemoryForRun(memory: TMemory, task: string): Promise<void>;
1213
+ protected prepareMemoryForStep(memory: TMemory): Promise<void>;
1214
+ protected buildStepMessages(memory: TMemory, context: ToolLoopRuntimeStepContext<TStepInput>): Promise<ChatMessage[]>;
1215
+ protected invokeLLM(request: ToolLoopRuntimeLLMRequest): Promise<ToolLoopRuntimeLLMResponse>;
1216
+ protected executeLLMStep(context: ToolLoopRuntimeStepContext<TStepInput>, messages: ChatMessage[]): Promise<ToolLoopRuntimeExecuteStepResult>;
1217
+ protected validateParsedStep(context: ToolLoopRuntimeStepContext<TStepInput>, parsed: ParsedStepInput): Promise<void>;
1218
+ /**
1219
+ * Called after the LLM response is parsed but before the tool
1220
+ * executes. Web runtimes override this to flush streaming content
1221
+ * to the store and transition the UI to a "tool running" state.
1222
+ */
1223
+ protected onBeforeToolDispatch(_context: ToolLoopRuntimeStepContext<TStepInput>, _parsed: ParsedStepInput): Promise<void>;
1224
+ protected ensureInfrastructure(): Promise<ToolLoopRuntimeInfrastructure<TMemory, TEnforcer>>;
1225
+ beginRun(task: string): Promise<ToolLoopRuntimeInfrastructure<TMemory, TEnforcer>>;
1226
+ runStep(options: ToolLoopRuntimeRunStepOptions<TStepInput>): Promise<ToolLoopRuntimeRunStepResult<TStepInput>>;
1227
+ protected onDestroy(_infrastructure: ToolLoopRuntimeInfrastructure<TMemory, TEnforcer>): Promise<void>;
1228
+ destroy(): Promise<void>;
1229
+ }
1230
+
1231
+ interface CommandRuntime {
1232
+ getToolDefinitions(): ToolDefinition[];
1233
+ executeTool(name: string, parameters: Record<string, any>): Promise<ToolResult>;
1234
+ executeBash?(command: string): Promise<ToolResult>;
1235
+ executeBackgroundBash?(command: string): Promise<ToolResult>;
1236
+ }
1237
+ interface CommandTargetInfo {
1238
+ name: string;
1239
+ path?: string;
1240
+ command?: string;
1241
+ }
1242
+ declare function isBashToolCall(toolCall: Pick<ToolCall, "name" | "kind">): boolean;
1243
+ declare function isBackgroundBashToolCall(toolCall: Pick<ToolCall, "name" | "kind">): boolean;
1244
+ declare function resolveToolDefinitionForInvocation(tools: ToolDefinition[], invocationName: string): ToolDefinition | undefined;
1245
+ declare function formatUnixToolSurface(tools: ToolDefinition[]): string;
1246
+ declare function formatToolCallAsUnixCommand(toolCall: Pick<ToolCall, "name" | "kind" | "parameters" | "rawInvocation">, toolDefinitions?: ToolDefinition[]): string | undefined;
1247
+ declare function enrichToolResultWithUnixMetadata(toolCall: Pick<ToolCall, "name" | "kind" | "parameters" | "rawInvocation">, result: ToolResult, toolDefinitions?: ToolDefinition[]): ToolResult;
1248
+ declare function extractPrimaryCommandMetadata(command: string): {
1249
+ name?: string;
1250
+ path?: string;
1251
+ command?: string;
1252
+ };
1253
+ declare function tokenizeUnixCommand(command: string, options?: {
1254
+ allowTruncated?: boolean;
1255
+ }): string[] | null;
1256
+ declare function parseUnixToolCommand(tool: ToolDefinition, command: string, options?: {
1257
+ allowTruncated?: boolean;
1258
+ }): {
1259
+ ok: true;
1260
+ parameters: Record<string, unknown>;
1261
+ } | {
1262
+ ok: false;
1263
+ error: string;
1264
+ };
1265
+ declare function executeUnixCommandString(command: string, runtime: CommandRuntime): Promise<ToolResult>;
1266
+ declare function executeBackgroundUnixCommandString(command: string, runtime: CommandRuntime): Promise<ToolResult>;
1267
+
1268
+ type RunInvocationTarget = "tool" | "bash" | "background_bash";
1269
+ interface ExtractedRunInvocationLike {
1270
+ target: RunInvocationTarget;
1271
+ command: string;
1272
+ truncated: boolean;
1273
+ }
1274
+ interface RunCommandParseResult {
1275
+ outcome?: LoopOutcome;
1276
+ selectedToolCall?: ToolCall;
1277
+ selectedToolCallCount?: number;
1278
+ }
1279
+ interface RunCommandPromptAvailability {
1280
+ bashAvailable: boolean;
1281
+ backgroundBashAvailable: boolean;
1282
+ }
1283
+ type BackgroundBashAction = "start" | "status" | "logs" | "stop";
1284
+ interface ParsedBackgroundBashCommand {
1285
+ action: BackgroundBashAction;
1286
+ name: string;
1287
+ rawCommand: string;
1288
+ startCommand?: string;
1289
+ port?: number;
1290
+ tailBytes?: number;
1291
+ }
1292
+ declare const DEFAULT_ALLOWED_RUN_TARGETS: readonly RunInvocationTarget[];
1293
+ declare const SUPPORTED_RUN_TARGETS: readonly RunInvocationTarget[];
1294
+ declare function createBackgroundBashDefinition({ description, portDescription, }: {
1295
+ description: string;
1296
+ portDescription: string;
1297
+ }): ToolDefinition;
1298
+ declare abstract class RunCommand {
1299
+ readonly target: RunInvocationTarget;
1300
+ protected constructor(target: RunInvocationTarget);
1301
+ abstract isAvailable(runtime: CommandRuntime): boolean;
1302
+ abstract parseInvocation(invocation: ExtractedRunInvocationLike, options?: {
1303
+ allowTruncated?: boolean;
1304
+ }): RunCommandParseResult | null;
1305
+ abstract execute(toolCall: ToolCall, runtime: CommandRuntime): Promise<ToolResult>;
1306
+ matchesToolCall(toolCall: ToolCall): boolean;
1307
+ }
1308
+ declare class ToolRunCommand extends RunCommand {
1309
+ constructor();
1310
+ isAvailable(runtime: CommandRuntime): boolean;
1311
+ parseInvocation(invocation: ExtractedRunInvocationLike, options?: {
1312
+ allowTruncated?: boolean;
1313
+ }): RunCommandParseResult | null;
1314
+ execute(toolCall: ToolCall, runtime: CommandRuntime): Promise<ToolResult>;
1315
+ }
1316
+ declare class BashRunCommand extends RunCommand {
1317
+ constructor();
1318
+ protected getCommand(toolCall: ToolCall): string;
1319
+ isAvailable(runtime: CommandRuntime): boolean;
1320
+ parseInvocation(invocation: ExtractedRunInvocationLike): RunCommandParseResult | null;
1321
+ protected executeCommand(command: string, runtime?: CommandRuntime): Promise<ToolResult>;
1322
+ executeRaw(command: string, runtime?: CommandRuntime): Promise<ToolResult>;
1323
+ execute(toolCall: ToolCall, runtime: CommandRuntime): Promise<ToolResult>;
1324
+ }
1325
+ declare class BackgroundBashRunCommand extends RunCommand {
1326
+ constructor();
1327
+ protected getCommand(toolCall: ToolCall): string;
1328
+ isAvailable(runtime: CommandRuntime): boolean;
1329
+ parseInvocation(invocation: ExtractedRunInvocationLike): RunCommandParseResult | null;
1330
+ protected executeCommand(command: string, runtime?: CommandRuntime): Promise<ToolResult>;
1331
+ executeRaw(command: string, runtime?: CommandRuntime): Promise<ToolResult>;
1332
+ protected buildBackgroundBashErrorResult(command: string, error: string): ToolResult;
1333
+ protected parseBackgroundBashCommand(definition: ToolDefinition, command: string): {
1334
+ ok: true;
1335
+ value: ParsedBackgroundBashCommand;
1336
+ } | {
1337
+ ok: false;
1338
+ result: ToolResult;
1339
+ };
1340
+ execute(toolCall: ToolCall, runtime: CommandRuntime): Promise<ToolResult>;
1341
+ }
1342
+ declare class RunCommandRegistry {
1343
+ private readonly commands;
1344
+ constructor(commands: readonly RunCommand[]);
1345
+ getCommand(target: RunInvocationTarget): RunCommand | undefined;
1346
+ getSupportedTargets(): readonly RunInvocationTarget[];
1347
+ getAllowedTargets(runtime: CommandRuntime): readonly RunInvocationTarget[];
1348
+ getPromptAvailability(allowedTargets: readonly RunInvocationTarget[]): RunCommandPromptAvailability;
1349
+ parseInvocation(invocation: ExtractedRunInvocationLike, options?: {
1350
+ allowTruncated?: boolean;
1351
+ }): RunCommandParseResult | null;
1352
+ executeToolCall(runtime: CommandRuntime, toolCall: ToolCall): Promise<ToolResult>;
1353
+ private resolveCommandForToolCall;
1354
+ }
1355
+ declare const DEFAULT_RUN_COMMAND_REGISTRY: RunCommandRegistry;
1356
+ declare function createCustomRunCommandRegistry(bashCommand: BashRunCommand, backgroundBashCommand: BackgroundBashRunCommand): RunCommandRegistry;
1357
+ declare function createRunCommandRuntimeBindings(bashCommand: BashRunCommand, backgroundBashCommand: BackgroundBashRunCommand): {
1358
+ executeBash: (command: string) => Promise<ToolResult>;
1359
+ executeBackgroundBash: (command: string) => Promise<ToolResult>;
1360
+ runCommandRegistry: RunCommandRegistry;
1361
+ };
1362
+ declare function getRunCommandPromptAvailability(allowedTargets: readonly RunInvocationTarget[]): RunCommandPromptAvailability;
1363
+ declare function executeToolCallWithRunCommands(runtime: CommandRuntime, toolCall: ToolCall, commandRegistry?: RunCommandRegistry): Promise<ToolResult>;
1364
+
1365
+ declare function formatToolDefinitions(tools: ToolDefinition[], style?: "compact" | "verbose" | "unix"): string;
1366
+ declare function coerceToolCallToDefinition(call: ToolCall | undefined, tools?: ToolDefinition[]): ToolCall | undefined;
1367
+ interface ParsedStructuredAgentText {
1368
+ prose: string;
1369
+ outcome?: LoopOutcome;
1370
+ selectedToolCall?: ToolCall;
1371
+ selectedToolCallCount: number;
1372
+ }
1373
+ declare function containsStandaloneStructuredInvocationStart(content: string, allowedTargets?: readonly RunInvocationTarget[]): boolean;
1374
+ declare function parseStructuredAgentText(content: string, options?: {
1375
+ allowTruncated?: boolean;
1376
+ allowedTargets?: readonly RunInvocationTarget[];
1377
+ commandRegistry?: RunCommandRegistry;
1378
+ }): ParsedStructuredAgentText | null;
1379
+
1380
+ type RuntimeSubscriber = () => void;
1381
+ type RuntimeSnapshotUpdater<TSnapshot> = TSnapshot | ((current: TSnapshot) => TSnapshot);
1382
+ interface RuntimeStore<TSnapshot> {
1383
+ subscribe: (subscriber: RuntimeSubscriber) => () => void;
1384
+ getSnapshot: () => TSnapshot;
1385
+ getServerSnapshot: () => TSnapshot;
1386
+ setSnapshot: (updater: RuntimeSnapshotUpdater<TSnapshot>) => TSnapshot;
1387
+ resetSnapshot: () => TSnapshot;
1388
+ }
1389
+ interface RuntimeController<TSnapshot, TActions extends Record<string, unknown>, TRefs extends Record<string, unknown> = {}> {
1390
+ store: RuntimeStore<TSnapshot>;
1391
+ actions: TActions;
1392
+ refs: TRefs;
1393
+ }
1394
+ declare function createRuntimeStore<TSnapshot>(createInitialSnapshot: () => TSnapshot): RuntimeStore<TSnapshot>;
1395
+ declare function defineRuntimeController<TSnapshot, TActions extends Record<string, unknown>, TRefs extends Record<string, unknown> = {}>(controller: RuntimeController<TSnapshot, TActions, TRefs>): RuntimeController<TSnapshot, TActions, TRefs>;
1396
+
1397
+ type TodoStatus = "not-started" | "in-progress" | "completed";
1398
+ interface TodoItem {
1399
+ id: number;
1400
+ title: string;
1401
+ status: TodoStatus;
1402
+ }
1403
+
1404
+ type AgentPhase = "idle" | "thinking" | "tool-calling" | "complete" | "error";
1405
+ interface AgentMessage {
1406
+ id: string;
1407
+ role: "user" | "assistant" | "tool";
1408
+ content: string;
1409
+ diagnosticLevel?: "error" | "warning";
1410
+ diagnosticSource?: "agent-feedback";
1411
+ toolName?: string;
1412
+ toolSuccess?: boolean;
1413
+ reasoning?: string;
1414
+ rawContent?: string;
1415
+ timestamp: number;
1416
+ }
1417
+ interface AgentState {
1418
+ phase: AgentPhase;
1419
+ messages: AgentMessage[];
1420
+ streamingContent: string;
1421
+ actionCount: number;
1422
+ error: string | null;
1423
+ activeToolName: string | null;
1424
+ todoItems: TodoItem[];
1425
+ }
1426
+ declare function createInitialWebAgentState(): AgentState;
1427
+ declare function machineStateToWebAgentPhase(state: AgentMachineState): AgentPhase;
1428
+ declare function createWebAgentMessageIdGenerator(prefix?: string): () => string;
1429
+
1430
+ declare class PendingAgentMessageBuffer<TMessage> {
1431
+ private messages;
1432
+ push(message: TMessage): void;
1433
+ clear(): void;
1434
+ takeForRun(runId: number, isRunActive: (runId: number) => boolean): TMessage[];
1435
+ }
1436
+ declare function createToolAgentMessage(options: {
1437
+ id: string;
1438
+ name: string;
1439
+ content: string;
1440
+ success: boolean;
1441
+ timestamp?: number;
1442
+ }): AgentMessage;
1443
+ interface CreateWebToolLoopCallbacksOptions {
1444
+ getToolDefinitions: () => ToolDefinition[];
1445
+ updateActiveToolName: (activeToolName: string | null) => void;
1446
+ onToolCompleteMessage: (name: string, result: ToolResult, truncatedResult: string) => void;
1447
+ onPhaseChange: (state: AgentMachineState) => void;
1448
+ }
1449
+ declare function applyInitialWebAgentRunState<TState extends WebToolLoopRuntimeStateLike<TMessage>, TMessage extends AgentMessage>(state: TState, userMessage: TMessage): TState;
1450
+ declare function applyIdleWebAgentState<TState extends WebToolLoopRuntimeStateLike>(state: TState): TState;
1451
+ declare function createWebToolLoopCallbacks(options: CreateWebToolLoopCallbacksOptions): AgentLoopCallbacks;
1452
+
1453
+ /**
1454
+ * WebValidationEnforcer — shared subclass of BaseValidationEnforcer
1455
+ * for browser-based agents (Magic webapp, future web agents).
1456
+ *
1457
+ * Provides:
1458
+ * • Missing tool-call warning (non-terminal response without an executable control segment)
1459
+ * • API error surfacing
1460
+ * • Browser-compatible logging (no chalk, no file I/O)
1461
+ *
1462
+ * Unlike Hammer's ValidationEnforcer, this does NOT include:
1463
+ * • ListSkills/ReadSkill-first enforcement (no skills concept in web)
1464
+ * • Init error handlers (web agents use simpler init flow)
1465
+ * • File system session persistence
1466
+ */
1467
+
1468
+ /**
1469
+ * WebValidationEnforcer — lightweight validation enforcement for
1470
+ * browser-based agents.
1471
+ *
1472
+ * Wire up with `createConversationSink(...)` against the underlying
1473
+ * message store:
1474
+ * ```ts
1475
+ * const sink = createConversationSink((role, content) =>
1476
+ * memory.appendMessage(role, content)
1477
+ * )
1478
+ * const enforcer = new WebValidationEnforcer(sink)
1479
+ * ```
1480
+ */
1481
+ declare class WebValidationEnforcer extends BaseValidationEnforcer {
1482
+ /** Optional callback for logging (default: console). */
1483
+ private onLog?;
1484
+ constructor(sink: ConversationSink, opts?: {
1485
+ onLog?: (level: "warn" | "error", message: string) => void;
1486
+ });
1487
+ protected logWarning(message: string): void;
1488
+ protected logError(message: string): void;
1489
+ protected surfaceValidationContext(error: Error): Promise<void>;
1490
+ }
1491
+
1492
+ interface WebToolLoopRuntimeStateLike<TMessage extends AgentMessage = AgentMessage> {
1493
+ phase: AgentPhase;
1494
+ messages: TMessage[];
1495
+ streamingContent: string;
1496
+ actionCount: number;
1497
+ error: string | null;
1498
+ activeToolName: string | null;
1499
+ todoItems: TodoItem[];
1500
+ }
1501
+ declare const suppressWebValidationLog: (_level: "warn" | "error", _message: string) => void;
1502
+ declare function readDiagnosticLevel(content: string): AgentMessage["diagnosticLevel"] | undefined;
1503
+ declare function readDiagnosticSource(content: string): AgentMessage["diagnosticSource"] | undefined;
1504
+ declare function mapConversationRoleToAgentRole(role: "user" | "assistant" | "system" | "tool"): AgentMessage["role"];
1505
+ interface CreateWebRuntimeSetupOptions {
1506
+ memoryPreset: string;
1507
+ getToolDefinitions: () => ToolDefinition[];
1508
+ executeTool: AgentLoopDeps["executeTool"];
1509
+ toolMemoryExtractor?: ToolMemoryExtractor;
1510
+ validationLog?: (level: "warn" | "error", message: string) => void;
1511
+ }
1512
+ interface ExecuteWebLoopRunOptions<TState extends WebToolLoopRuntimeStateLike<TMessage>, TMessage extends AgentMessage, TStepInput extends {
1513
+ runId: number;
1514
+ }> {
1515
+ runId: number;
1516
+ userTask: string;
1517
+ stepInput: TStepInput;
1518
+ createUserMessage: () => TMessage;
1519
+ applyInitialState: (state: TState, userMessage: TMessage) => TState;
1520
+ onStepCompleted: (step: ToolLoopRuntimeRunStepResult<TStepInput>) => Promise<void> | void;
1521
+ createParseFailureMessage?: () => TMessage;
1522
+ applyParseFailureState?: (state: TState, message: TMessage) => TState;
1523
+ onBeforeRun?: () => void;
1524
+ onAfterRun?: () => void;
1525
+ }
1526
+ interface WebToolLoopAgentRuntimeConstructorOptions<TState, TStepInput> extends ToolLoopAgentRuntimeDeps<TStepInput> {
1527
+ store: RuntimeStore<TState>;
1528
+ messageIdPrefix: string;
1529
+ getToolDefinitions: () => ToolDefinition[];
1530
+ memoryPreset: string;
1531
+ executeTool: AgentLoopDeps["executeTool"];
1532
+ systemIdentity?: string;
1533
+ extraRules: string;
1534
+ temperature: number;
1535
+ maxTokens: number;
1536
+ toolMemoryExtractor?: ToolMemoryExtractor;
1537
+ validationLog?: (level: "warn" | "error", message: string) => void;
1538
+ allowedRunTargets?: readonly RunInvocationTarget[];
1539
+ logLabel?: string;
1540
+ logFullSystemPromptOnChange?: boolean;
1541
+ logFullLlmMessages?: boolean;
1542
+ }
1543
+ declare abstract class WebToolLoopAgentRuntime<TState extends WebToolLoopRuntimeStateLike<TMessage>, TMessage extends AgentMessage = AgentMessage, TStepInput extends {
1544
+ runId: number;
1545
+ } = {
1546
+ runId: number;
1547
+ }> extends ToolLoopAgentRuntime<AgentMemoryLayer, WebValidationEnforcer, TStepInput> {
1548
+ protected readonly store: RuntimeStore<TState>;
1549
+ protected readonly pendingToolMessages: PendingAgentMessageBuffer<TMessage>;
1550
+ private readonly createMessageId;
1551
+ private readonly runtimeToolDefinitions;
1552
+ private readonly memoryPreset;
1553
+ private readonly executeTool;
1554
+ private readonly systemIdentity?;
1555
+ private readonly extraRules;
1556
+ private readonly temperature;
1557
+ private readonly maxTokens;
1558
+ private readonly toolMemoryExtractor?;
1559
+ private readonly validationLog?;
1560
+ private readonly allowedRunTargets;
1561
+ private readonly logLabel;
1562
+ private readonly logFullSystemPromptOnChange;
1563
+ private readonly logFullLlmMessages;
1564
+ private lastLoggedSystemPrompt;
1565
+ protected abortController: AbortController | null;
1566
+ protected memory: AgentMemoryLayer | null;
1567
+ protected enforcer: WebValidationEnforcer | null;
1568
+ protected running: boolean;
1569
+ protected activeRunId: number | null;
1570
+ protected nextRunId: number;
1571
+ protected inFlightRunPromise: Promise<void> | null;
1572
+ private pendingDestroyPromise;
1573
+ private pendingSurfacedMessages;
1574
+ private pendingStreamingContent;
1575
+ private pendingStreamingTokenCounts;
1576
+ protected constructor(options: WebToolLoopAgentRuntimeConstructorOptions<TState, TStepInput>);
1577
+ protected createRuntimeSetup(): Promise<ToolLoopRuntimeSetup<AgentMemoryLayer, WebValidationEnforcer>>;
1578
+ protected getToolDefinitions(): ToolDefinition[];
1579
+ protected ensureStaticContext(memory: AgentMemoryLayer): Promise<void>;
1580
+ protected getAllowedRunTargets(): readonly RunInvocationTarget[];
1581
+ /**
1582
+ * Run targets for the parser layer. Always includes "bash" so the
1583
+ * universal finish signals (`---bash--- exit 0` / `exit 1`) are
1584
+ * recognized even when the agent has no bash execution capability.
1585
+ */
1586
+ private getParserRunTargets;
1587
+ protected buildSystemPrompt(context: ToolLoopRuntimeStepContext<TStepInput>): string;
1588
+ protected buildLLMRequest(context: {
1589
+ stepInput?: TStepInput;
1590
+ }, messages: ChatMessage[]): ToolLoopRuntimeLLMRequest;
1591
+ protected invokeLLM(request: ToolLoopRuntimeLLMRequest): Promise<ToolLoopRuntimeLLMResponse>;
1592
+ protected parseStepResponse(response: {
1593
+ content: string;
1594
+ finishReason: string;
1595
+ }, tools: ToolDefinition[]): {
1596
+ outcome: LoopOutcome;
1597
+ reasoning: string;
1598
+ selectedToolCall: ToolCall | undefined;
1599
+ wasTruncated: boolean;
1600
+ rawContent: string;
1601
+ };
1602
+ protected onBeforeToolDispatch(context: ToolLoopRuntimeStepContext<TStepInput>, parsed: ParsedStepInput): Promise<void>;
1603
+ protected nextMessageId(): string;
1604
+ protected updateSnapshot(updater: RuntimeSnapshotUpdater<TState>): void;
1605
+ protected isRunActive(runId: number): boolean;
1606
+ protected updateSnapshotForActiveRun(updater: RuntimeSnapshotUpdater<TState>): boolean;
1607
+ protected updateSnapshotForRun(runId: number, updater: RuntimeSnapshotUpdater<TState>): boolean;
1608
+ protected surfaceFatalError(error: unknown, runId: number): Promise<Error>;
1609
+ protected appendSurfacedConversationMessage(role: "user" | "assistant" | "system" | "tool", content: string): void;
1610
+ protected takePendingSurfacedMessagesForRun(runId: number): TMessage[];
1611
+ protected flushPendingSurfacedMessagesForRun(runId: number): void;
1612
+ protected clearPendingSurfacedMessages(): void;
1613
+ protected appendStreamingToken(runId: number, token: string): void;
1614
+ protected flushPendingStreamingContentForRun(runId: number): void;
1615
+ protected clearPendingStreamingContent(runId?: number): void;
1616
+ protected syncPhase(machineState?: AgentMachineState): void;
1617
+ protected createWebRuntimeSetup(options: CreateWebRuntimeSetupOptions): Promise<ToolLoopRuntimeSetup<AgentMemoryLayer, WebValidationEnforcer>>;
1618
+ protected buildStreamingLLMRequest(context: {
1619
+ stepInput?: TStepInput;
1620
+ }, messages: ChatMessage[], options: {
1621
+ temperature: number;
1622
+ maxTokens: number;
1623
+ }): ToolLoopRuntimeLLMRequest;
1624
+ protected parseRecoveringStepResponse(response: {
1625
+ content: string;
1626
+ finishReason: string;
1627
+ }, tools: ToolDefinition[]): {
1628
+ outcome: LoopOutcome;
1629
+ reasoning: string;
1630
+ selectedToolCall: ToolCall | undefined;
1631
+ wasTruncated: boolean;
1632
+ rawContent: string;
1633
+ };
1634
+ protected startSerializedRun(execute: (runId: number) => Promise<void>): Promise<void>;
1635
+ protected executeWebLoopRun(options: ExecuteWebLoopRunOptions<TState, TMessage, TStepInput>): Promise<void>;
1636
+ protected abortWebRuntime(applyAbortState: (state: TState) => TState, cleanup?: () => void): void;
1637
+ private scheduleRuntimeDestroy;
1638
+ protected resetWebRuntime(cleanup?: () => void): void;
1639
+ protected clearWebRuntimeState(cleanup?: () => void): Promise<void>;
1640
+ protected defaultWebAbort(): void;
1641
+ protected defaultWebReset(): void;
1642
+ protected onDestroy(): Promise<void>;
1643
+ protected executeDefaultWebRun(userTask: string, options?: {
1644
+ shouldSurfaceAssistantContent?: (reasoning?: string, rawContent?: string) => boolean;
1645
+ onNonContinueStep?: () => Promise<void>;
1646
+ onBeforeRun?: () => void;
1647
+ onAfterRun?: () => void;
1648
+ }): Promise<void>;
1649
+ }
1650
+
1651
+ /**
1652
+ * Agent Memory Layer Factory — Shared factory for creating pre-configured
1653
+ * AgentMemoryLayer instances.
1654
+ *
1655
+ * Centralizes the shared Hammer/Magic/Monoslides/Monospace memory-layer presets so each
1656
+ * agent only provides the small set of overrides it actually owns.
1657
+ *
1658
+ * Usage:
1659
+ * import { createAgentMemoryLayer } from "./agent-memory-factory"
1660
+ * const memory = createAgentMemoryLayer("hammer", { logger: myLogger })
1661
+ * const memory = createAgentMemoryLayer("magic", { toolMemoryExtractor: canvasExtractor })
1662
+ */
1663
+
1664
+ interface AgentMemoryFactoryOverrides {
1665
+ /** Override the compaction LLM client. */
1666
+ compactionClient?: CompactionLLMClient;
1667
+ /** Override the token estimator (default: CharTokenEstimator for browser safety). */
1668
+ tokenEstimator?: AgentMemoryLayerConfig["tokenEstimator"];
1669
+ /** Provide registry-backed tool definitions for compaction-time classification. */
1670
+ getToolDefinitions?: () => ToolDefinition[];
1671
+ /** Override tool memory extractor. */
1672
+ toolMemoryExtractor?: ToolMemoryExtractor;
1673
+ /** Optional structured logger. */
1674
+ logger?: AgentMemoryLogger;
1675
+ }
1676
+ /**
1677
+ * Create an AgentMemoryLayer pre-configured for a specific agent preset.
1678
+ *
1679
+ * @param preset - Agent preset name ("hammer" | "magic" | "monoslides" | "monospace").
1680
+ * @param overrides - Optional overrides for any preset value.
1681
+ * @returns Configured AgentMemoryLayer instance.
1682
+ *
1683
+ * @example
1684
+ * ```ts
1685
+ * // Hammer (Node.js — use TiktokenEstimator for precision)
1686
+ * const memory = createAgentMemoryLayer("hammer", {
1687
+ * compactionClient,
1688
+ * tokenEstimator: new TiktokenEstimator(),
1689
+ * logger: hammerLogger,
1690
+ * })
1691
+ *
1692
+ * // Magic/Monoslides/Monospace (browser — CharTokenEstimator is default)
1693
+ * const memory = createAgentMemoryLayer("magic", {
1694
+ * compactionClient,
1695
+ * getToolDefinitions: () => registry.getToolDefinitions(),
1696
+ * toolMemoryExtractor: MAGIC_TOOL_MEMORY_EXTRACTOR,
1697
+ * })
1698
+ * ```
1699
+ */
1700
+ declare function createAgentMemoryLayer(preset: string, overrides?: AgentMemoryFactoryOverrides): AgentMemoryLayer;
1701
+
1702
+ interface BashCommandsSectionOptions {
1703
+ shellNativeWorkflowCommandExamples?: string;
1704
+ additionalGuidanceLines?: string[];
1705
+ }
1706
+
1707
+ /**
1708
+ * Shared prompt building utilities for agentic loops.
1709
+ *
1710
+ * Used by Hammer CLI agent, Magic webapp agent, Monoslides, and Monospace to format
1711
+ * tool surfaces and construct core prompt templates.
1712
+ */
1713
+
1714
+ declare const DEFAULT_AGENT_FALLBACK_SYSTEM_PROMPT = "You are an AI agent.";
1715
+ interface FormatToolsSectionOptions {
1716
+ bashAvailable?: boolean;
1717
+ backgroundBashAvailable?: boolean;
1718
+ allowedRunTargets?: readonly RunInvocationTarget[];
1719
+ bashCommandsSectionOptions?: BashCommandsSectionOptions;
1720
+ }
1721
+ interface SystemPromptSections {
1722
+ basePrompt: string;
1723
+ toolsHeading?: string;
1724
+ toolsSection: string;
1725
+ supplementalRules?: string;
1726
+ }
1727
+ interface SystemPromptBuildContext {
1728
+ tools: ToolDefinition[];
1729
+ toolsSectionOptions: FormatToolsSectionOptions;
1730
+ }
1731
+ type SystemPromptCustomizer = (sections: Readonly<SystemPromptSections>, context: Readonly<SystemPromptBuildContext>) => Partial<SystemPromptSections> | void;
1732
+ declare function createAppendToolsSectionCustomizer(block: string): SystemPromptCustomizer;
1733
+ declare function createToolsSectionOverrideCustomizer(buildToolsSection: (context: Readonly<SystemPromptBuildContext>) => string): SystemPromptCustomizer;
1734
+ /**
1735
+ * Format tool definitions for injection into a system prompt.
1736
+ * Thin wrapper over `formatToolDefinitions` with configurable bash guidance.
1737
+ */
1738
+ declare function formatToolsSection(tools: ToolDefinition[], options?: FormatToolsSectionOptions): string;
1739
+ declare function buildAgentSystemPrompt(options: {
1740
+ identityLine?: string;
1741
+ tools: ToolDefinition[];
1742
+ supplementalRules?: string;
1743
+ bashAvailable?: boolean;
1744
+ backgroundBashAvailable?: boolean;
1745
+ allowedRunTargets?: readonly RunInvocationTarget[];
1746
+ toolsSectionOptions?: Omit<FormatToolsSectionOptions, "bashAvailable" | "backgroundBashAvailable" | "allowedRunTargets">;
1747
+ systemPromptCustomizers?: readonly SystemPromptCustomizer[];
1748
+ }): string;
1749
+ /**
1750
+ * Core static rules shared by all agent loops (Hammer, Magic, Monoslides, Monospace).
1751
+ *
1752
+ * Covers:
1753
+ * - Shell-style output format specification
1754
+ * - Terminal exit requirements
1755
+ * - Tool call formatting
1756
+ * - Scratchpad usage
1757
+ *
1758
+ * Hammer extends this with file-path rules, skills-first policy,
1759
+ * incremental testing, etc. Magic uses this as-is or with lighter extensions.
1760
+ *
1761
+ * Designed to be injected once via `memoryLayer.setStaticContext()`,
1762
+ * saving ~1500 tokens per action vs repeating in every system prompt.
1763
+ */
1764
+ declare function buildCoreStaticRules(): string;
1765
+ interface StepUserMessageOptions {
1766
+ /** Current action number. */
1767
+ actionCount: number;
1768
+ /** Truncated tool info from the previous iteration (if any). */
1769
+ truncatedToolInfo?: TruncatedToolInfo;
1770
+ }
1771
+ /**
1772
+ * Build the user message injected at the start of each agentic loop step.
1773
+ *
1774
+ * Handles truncation-specific continuation guidance (Write → "Use Append",
1775
+ * generic tool → "continue from where you left off") and the default
1776
+ * "Continue working" prompt.
1777
+ *
1778
+ * Shared by Hammer's UnifiedAgent, Magic's useAgent hook, and Monoslides/Monospace runtimes.
1779
+ */
1780
+ declare function buildStepUserMessage(opts: StepUserMessageOptions): string;
1781
+ /**
1782
+ * Check whether the last message in the conversation is a user error
1783
+ * message (prefixed with ⚠️), which means we should skip injecting
1784
+ * another user message to avoid double user messages.
1785
+ *
1786
+ * Shared by Hammer's UnifiedAgent, Magic's useAgent hook, and Monoslides/Monospace runtimes.
1787
+ */
1788
+ declare function shouldSkipStepUserMessage(lastRole: string | null, lastContent: string | null): boolean;
1789
+ interface ToolCallLike {
1790
+ name: string;
1791
+ parameters?: Record<string, unknown>;
1792
+ }
1793
+ /**
1794
+ * Extract `TruncatedToolInfo` from the first tool call in a response
1795
+ * when the LLM response was truncated (finishReason === "length").
1796
+ *
1797
+ * Returns `undefined` if no tool calls are provided.
1798
+ */
1799
+ declare function extractTruncatedToolInfo(calls: ToolCallLike[]): TruncatedToolInfo | undefined;
1800
+
1801
+ /**
1802
+ * Shared agent response parsing pipeline.
1803
+ *
1804
+ * Unifies the response parsing logic used by both Hammer CLI agent
1805
+ * (`BaseLLMProvider.parseAgentResponse`) and Magic webapp agent
1806
+ * (`useAgent.parseResponse`).
1807
+ *
1808
+ * Pipeline: raw content → structured control-segment
1809
+ * extraction → Zod validation
1810
+ */
1811
+
1812
+ interface ParsedAgentResponse {
1813
+ /** Zod-validated (or raw fallback) parsed response object. */
1814
+ parsed: Record<string, unknown>;
1815
+ /** The single executable tool call selected for this turn, if any. */
1816
+ selectedToolCall?: ToolCall;
1817
+ /** The outcome field ("continue", "success", "failure"). */
1818
+ outcome: LoopOutcome;
1819
+ /** The reasoning string from the LLM. */
1820
+ reasoning: string;
1821
+ /** Validation error message, if validation failed but raw data was usable. */
1822
+ validationError?: string;
1823
+ /** Original raw content string. */
1824
+ raw: string;
1825
+ }
1826
+ interface ParseAgentResponseOptions {
1827
+ /**
1828
+ * If `true`, throw on Zod validation failure (Hammer behavior).
1829
+ * If `false`, fall back to raw parsed data on Zod failure (Magic behavior).
1830
+ * Default: `false`.
1831
+ */
1832
+ throwOnValidationError?: boolean;
1833
+ /** Allow truncated structured control segments during recovery paths. */
1834
+ allowTruncatedRuns?: boolean;
1835
+ /**
1836
+ * Custom error formatter called when `throwOnValidationError` is `true`.
1837
+ * Receives the Zod error and raw content, should return an Error to throw.
1838
+ */
1839
+ formatValidationError?: (zodError: unknown, rawContent: string) => Error;
1840
+ /** Allowed structured control targets for this agent. Defaults to tool/bash. */
1841
+ allowedRunTargets?: readonly RunInvocationTarget[];
1842
+ }
1843
+ /**
1844
+ * Parse an LLM response string into a structured agent response.
1845
+ *
1846
+ * Shared pipeline:
1847
+ * 1. Extract prose + structured control segments
1848
+ * 2. Validate via `LLMResponseSchema` (Zod)
1849
+ * 3. Return structured result or null if extraction fails
1850
+ *
1851
+ * @returns Parsed response, or `null` if no structured control segments could be extracted.
1852
+ */
1853
+ declare function parseAgentResponse(content: string, options?: ParseAgentResponseOptions): ParsedAgentResponse | null;
1854
+
1855
+ /**
1856
+ * Shared tool-call recovery utilities for the structured control-segment protocol.
1857
+ *
1858
+ * Extracts truncation recovery, validation error formatting, and error
1859
+ * message templates so Hammer, Magic, Monoslides, Monospace, and the voice agent share one
1860
+ * response contract.
1861
+ */
1862
+
1863
+ /** Error message sent to the LLM when its response was truncated mid-generation. */
1864
+ declare const ERROR_TRUNCATED_RESPONSE = "VALIDATION_ERROR: Your response was truncated before the final executable control block could be extracted.\n\nRetry with normal prose and one final executable control block only. Put the standalone slug header on its own line and put the payload on the following line(s). Do not discuss the control syntax.";
1865
+ /**
1866
+ * Format a Zod validation error (or generic Error) into a human-readable string.
1867
+ *
1868
+ * Returns per-field details showing which parts are missing or have wrong types,
1869
+ * suitable for feeding back to the LLM so it can self-correct.
1870
+ */
1871
+ declare function formatZodValidationError(zodError: unknown): string;
1872
+ /**
1873
+ * Build a complete VALIDATION_ERROR string with Zod details + fix reference.
1874
+ *
1875
+ * Suitable for throwing as an `Error.message` that gets sent back to the LLM.
1876
+ */
1877
+ declare function buildValidationErrorMessage(zodError: unknown): string;
1878
+ /**
1879
+ * Build a "no structured control segment found" error message.
1880
+ *
1881
+ * Keep this message focused on the repair instruction instead of duplicating
1882
+ * the failed output inline.
1883
+ */
1884
+ declare function buildNoStructuredResponseFoundError(): string;
1885
+
1886
+ /**
1887
+ * Build a user-feedback message to send to the LLM when its response failed
1888
+ * to parse. Gives more actionable guidance than a generic "parse failed" message.
1889
+ *
1890
+ * @param content The raw LLM content that failed to parse
1891
+ * @param zodError Optional Zod validation error for field-specific feedback
1892
+ * @returns A feedback string to send as a user message
1893
+ */
1894
+ declare function buildParseFeedback(_content: string, zodError?: unknown): string;
1895
+ interface ParseResponseWithRecoveryOptions extends ParseAgentResponseOptions {
1896
+ /**
1897
+ * The LLM's `finishReason` string. When `"length"`, Tier 2 truncation
1898
+ * recovery is attempted.
1899
+ */
1900
+ finishReason?: string;
1901
+ }
1902
+ /**
1903
+ * Three-tier parsing cascade for LLM step responses.
1904
+ *
1905
+ * Shared by Magic (`use-agent.ts`) and Hammer (`BaseLLMProvider`).
1906
+ *
1907
+ * Tier 1: `parseAgentResponse` — control-segment extraction + Zod validation
1908
+ * Tier 2: `recoverTruncatedResponse` — if `finishReason === "length"`
1909
+ *
1910
+ * Tier 1 respects `throwOnValidationError`: if structured lines are found but
1911
+ * validation fails in strict mode, the error propagates immediately.
1912
+ *
1913
+ * @returns Parsed response, or `null` if nothing could be extracted.
1914
+ */
1915
+ declare function parseResponseWithRecovery(content: string, options?: ParseResponseWithRecoveryOptions): ParsedAgentResponse | null;
1916
+
1917
+ /**
1918
+ * Shared tool execution utilities for agentic loops.
1919
+ *
1920
+ * Used by both Hammer CLI agent and Magic webapp agent for
1921
+ * consistent tool result truncation and safe execution wrapping.
1922
+ */
1923
+
1924
+ /** Default maximum characters for tool result strings. */
1925
+ declare const MAX_TOOL_RESULT_CHARS = 30000;
1926
+ interface TruncateOptions {
1927
+ /** Maximum character length (default: 30,000). */
1928
+ maxChars?: number;
1929
+ /**
1930
+ * Truncation strategy:
1931
+ * - `"head-tail"`: Keep first half + last half (preserves end of large outputs).
1932
+ * - `"head-only"`: Keep the first N characters.
1933
+ */
1934
+ strategy?: "head-tail" | "head-only";
1935
+ }
1936
+ /**
1937
+ * Truncate an oversized tool result string to stay within LLM context limits.
1938
+ *
1939
+ * @returns The original string if within limit, otherwise a truncated version.
1940
+ */
1941
+ declare function truncateToolResult(resultStr: string, options?: TruncateOptions): string;
1942
+ declare function formatToolResultMessage(toolCall: ToolCall, result: ToolResult): string;
1943
+ declare function parseToolResultMessage(content: string): {
1944
+ success: boolean;
1945
+ toolName?: string;
1946
+ error?: string;
1947
+ parsed?: Record<string, any>;
1948
+ };
1949
+ /**
1950
+ * Execute a tool call with standard error handling.
1951
+ * Catches any thrown error and returns `{ success: false, error: message }`.
1952
+ */
1953
+ declare function executeToolSafe(fn: () => Promise<ToolResult>): Promise<ToolResult>;
1954
+
1955
+ /**
1956
+ * StreamingToolParser — separates speech from structured control segments
1957
+ * during streaming. It normally parses tool calls at stream end, but if a
1958
+ * second control header appears it immediately seals the stream and keeps only
1959
+ * the first segment.
1960
+ */
1961
+
1962
+ interface StreamingToolParserCallbacks {
1963
+ /** Fired for every token that is regular speech (not part of a control segment). */
1964
+ onSpeechToken?: (token: string) => void;
1965
+ /** Fired when structured control segments have been parsed into tool calls. */
1966
+ onToolCall?: (selectedToolCall: ToolCall) => void;
1967
+ /** Fired if structured command text was detected but could not be parsed. */
1968
+ onToolCallError?: (error: Error, rawCommandText: string) => void;
1969
+ /** Informational log messages. */
1970
+ onLog?: (msg: string) => void;
1971
+ /** Allowed control-segment targets for this parser. Defaults to tool/bash. */
1972
+ allowedRunTargets?: readonly RunInvocationTarget[];
1973
+ }
1974
+ declare class StreamingToolParser {
1975
+ private speechBuffer;
1976
+ private segmentBuffer;
1977
+ private inSegment;
1978
+ private _sealed;
1979
+ private callbacks;
1980
+ /** The full raw content accumulated so far (speech + structured lines). */
1981
+ private fullContent;
1982
+ private static buildControlHeaderPattern;
1983
+ private static getControlHeaders;
1984
+ private static findControlHeader;
1985
+ private static findSecondControlHeader;
1986
+ private getRunPrefixLookback;
1987
+ constructor(callbacks?: StreamingToolParserCallbacks);
1988
+ /** Replace callbacks (e.g. after construction). */
1989
+ on(cb: StreamingToolParserCallbacks): void;
1990
+ /**
1991
+ * Whether the parser has sealed after stream completion.
1992
+ */
1993
+ get sealed(): boolean;
1994
+ /** Feed a new token from the SSE stream. */
1995
+ push(token: string): void;
1996
+ /**
1997
+ * Signal that the stream has ended.
1998
+ *
1999
+ * If we're mid-command (truncated response), buffer it and attempt
2000
+ * recovery only after the full streamed response is available.
2001
+ */
2002
+ finish(): void;
2003
+ /** Reset internal state for reuse across turns. */
2004
+ reset(): void;
2005
+ /** Get all content accumulated so far. */
2006
+ getFullContent(): string;
2007
+ private captureSegmentHeaderIfPresent;
2008
+ private flushSafeSpeechTail;
2009
+ private sealAtFirstSegmentIfSecondHeaderDetected;
2010
+ private tryParseFullContent;
2011
+ private tryParseContent;
2012
+ private containsStandaloneRunStart;
2013
+ }
2014
+
2015
+ declare const DEFAULT_THREAD_AUTO_SCROLL_BOTTOM_THRESHOLD = 24;
2016
+ type ScrollMetrics = {
2017
+ scrollHeight: number;
2018
+ scrollTop: number;
2019
+ clientHeight: number;
2020
+ };
2021
+ declare function shouldAutoScrollThread(scrollMetrics: ScrollMetrics, threshold?: number): boolean;
2022
+ declare function getToolLogSummaryLine(content: string): string;
2023
+ declare function stripDiagnosticMessagePrefix(content: string): string;
2024
+ declare function getDiagnosticSummaryLine(content: string): string;
2025
+ declare function buildToolLogRevealFrames(content: string, targetFrameCount?: number): string[];
2026
+
2027
+ type WebSearchToolActionInput = {
2028
+ tool: "BraveWebSearch" | "BochaWebSearch";
2029
+ input: Record<string, unknown>;
2030
+ };
2031
+ type WebSearchToolActionDeps = {
2032
+ executeWebSearch: (tool: "BraveWebSearch" | "BochaWebSearch", input: Record<string, unknown>) => Promise<ToolResult>;
2033
+ };
2034
+ declare function createWebSearchToolActions(deps: WebSearchToolActionDeps): {
2035
+ executeWebTool: (input: WebSearchToolActionInput) => Promise<ToolResult>;
2036
+ };
2037
+
2038
+ /**
2039
+ * Base Tool class — all shared tools extend this.
2040
+ *
2041
+ * Mirrors the Hammer agent tool format so code is shared between
2042
+ * apps/hammer and apps/tauri via the DRY principle.
2043
+ *
2044
+ * Filesystem-aware tools should extend SystemTool directly, which adds
2045
+ * `validatePath()` on top of this base class.
2046
+ */
2047
+
2048
+ /** Tool parameter schema — same shape as ToolDefinition["parameters"]. */
2049
+ type ToolSchema = ToolDefinition["parameters"];
2050
+ /** Tool metadata — generic version (Hammer extends this with constrained categories). */
2051
+ interface ToolMetadata extends ToolDefinitionMetadata {
2052
+ requirements: string[];
2053
+ }
2054
+ declare abstract class Tool {
2055
+ protected workspaceRoot: string;
2056
+ constructor(workspaceRoot?: string);
2057
+ /**
2058
+ * Get tool metadata — capabilities, categories, requirements.
2059
+ * Override in subclasses.
2060
+ */
2061
+ getMetadata(): ToolMetadata;
2062
+ /** Execute the tool — must be implemented by subclasses. */
2063
+ abstract execute(params: Record<string, any>): Promise<ToolResult>;
2064
+ /** Get tool name. */
2065
+ abstract getName(): string;
2066
+ /** Get tool description. */
2067
+ abstract getDescription(): string;
2068
+ /** Get parameter schema. */
2069
+ abstract getSchema(): ToolSchema;
2070
+ /** Get an explicit usage example shown in tool prompts. */
2071
+ getUsageExample(): string | undefined;
2072
+ /** Convert this tool instance to a ToolDefinition for LLM consumption. */
2073
+ toDefinition(): ToolDefinition;
2074
+ }
2075
+
2076
+ /**
2077
+ * Shared ToolRegistry infrastructure.
2078
+ *
2079
+ * This mirrors the Hammer and Magic registry shape: a real
2080
+ * Map<string, Tool> registry with registration, lookup, execution,
2081
+ * definition generation, and executor creation.
2082
+ */
2083
+
2084
+ type ToolExecutor = (toolCall: ToolCall) => Promise<ToolResult>;
2085
+ type BashExecutor = (command: string) => Promise<ToolResult>;
2086
+ type BackgroundBashExecutor = (command: string) => Promise<ToolResult>;
2087
+ interface ToolRegistryMissingToolContext {
2088
+ name: string;
2089
+ availableToolNames: string[];
2090
+ }
2091
+ interface ToolRegistryBeforeExecuteContext {
2092
+ name: string;
2093
+ parameters: Record<string, any>;
2094
+ registry: ToolRegistry;
2095
+ }
2096
+ interface ToolRegistryOptions {
2097
+ tools?: Iterable<Tool>;
2098
+ canExecute?: boolean | (() => boolean);
2099
+ executeBash?: BashExecutor;
2100
+ executeBackgroundBash?: BackgroundBashExecutor;
2101
+ runCommandRegistry?: RunCommandRegistry;
2102
+ onMissingTool?: (context: ToolRegistryMissingToolContext) => Error | string;
2103
+ beforeExecute?: (context: ToolRegistryBeforeExecuteContext) => Promise<ToolResult | void> | ToolResult | void;
2104
+ }
2105
+ declare function createToolRegistry(tools?: Iterable<Tool>, options?: Omit<ToolRegistryOptions, "tools">): ToolRegistry;
2106
+ declare class ToolRegistry {
2107
+ protected tools: Map<string, Tool>;
2108
+ private canExecuteOverride?;
2109
+ private bashExecutor?;
2110
+ private backgroundBashExecutor?;
2111
+ private runCommandRegistry;
2112
+ private onMissingTool?;
2113
+ private beforeExecute?;
2114
+ constructor(toolsOrOptions?: Iterable<Tool> | ToolRegistryOptions);
2115
+ registerTool(tool: Tool): void;
2116
+ registerTools(tools: Iterable<Tool>): void;
2117
+ replaceTools(tools: Iterable<Tool>): void;
2118
+ hasTool(name: string): boolean;
2119
+ getTool(name: string): Tool;
2120
+ protected buildMissingToolError(name: string): Error;
2121
+ getTools(): Tool[];
2122
+ executeTool(name: string, parameters: Record<string, any>): Promise<ToolResult>;
2123
+ executeToolCall(toolCall: ToolCall): Promise<ToolResult>;
2124
+ private createConcreteRuntime;
2125
+ private executeConcreteTool;
2126
+ getToolDefinitions(): ToolDefinition[];
2127
+ getToolNames(): string[];
2128
+ hasTools(): boolean;
2129
+ canExecuteBash(): boolean;
2130
+ canExecuteBackgroundBash(): boolean;
2131
+ getAvailableRunTargets(): readonly RunInvocationTarget[];
2132
+ getRunCommandRegistry(): RunCommandRegistry;
2133
+ canExecute(): boolean;
2134
+ createExecutor(): ToolExecutor;
2135
+ }
2136
+
2137
+ type ProxyToolExecutor = (parameters: Record<string, any>, definition: ToolDefinition) => Promise<ToolResult>;
2138
+ /**
2139
+ * Shared adapter for exposing a ToolDefinition as a concrete Tool while
2140
+ * delegating execution across a runtime boundary.
2141
+ */
2142
+ declare class ProxyTool extends Tool {
2143
+ private definition;
2144
+ private executor;
2145
+ constructor(definition: ToolDefinition, executor: ProxyToolExecutor);
2146
+ getName(): string;
2147
+ getDescription(): string;
2148
+ getSchema(): ToolDefinition["parameters"];
2149
+ getMetadata(): ToolMetadata;
2150
+ execute(parameters: Record<string, any>): Promise<ToolResult>;
2151
+ }
2152
+
2153
+ interface ToolLoopStepExecutorResponse {
2154
+ content: string;
2155
+ finishReason: string;
2156
+ usage?: LLMClientResponse["usage"];
2157
+ }
2158
+ interface ToolLoopStepExecutorCallbacks {
2159
+ onMessagesBuilt?(messages: ChatMessage[]): void;
2160
+ onToken?(token: string): void;
2161
+ onComplete?(response: ToolLoopStepExecutorResponse): void;
2162
+ onStreamError?(error: Error): void;
2163
+ onLog?(message: string, level: "info" | "warn" | "error"): void;
2164
+ }
2165
+ interface ToolLoopStepExecutorOptions<TResult> {
2166
+ llmClient: LLMClient;
2167
+ memoryLayer: Pick<AgentMemoryLayer, "buildMessages">;
2168
+ systemPrompt: string;
2169
+ temperature: number;
2170
+ maxTokens: number;
2171
+ frequencyPenalty?: number;
2172
+ presencePenalty?: number;
2173
+ callbacks?: ToolLoopStepExecutorCallbacks;
2174
+ parseResponse(response: {
2175
+ content: string;
2176
+ finishReason: string;
2177
+ usage?: LLMClientResponse["usage"];
2178
+ messages: ChatMessage[];
2179
+ }): TResult;
2180
+ }
2181
+ interface ToolLoopStepExecutionResult<TResult> {
2182
+ messages: ChatMessage[];
2183
+ response: ToolLoopStepExecutorResponse;
2184
+ parsed: TResult;
2185
+ }
2186
+ declare function executeToolLoopStep<TResult>(options: ToolLoopStepExecutorOptions<TResult>): Promise<ToolLoopStepExecutionResult<TResult>>;
2187
+
2188
+ declare function decodeEscapedShellText(value: string): string;
2189
+
2190
+ declare function buildToolUsageExample(tool: Pick<ToolDefinition, "name" | "parameters" | "metadata">): string;
2191
+
2192
+ interface BuildMemoryCompactionPromptOptions {
2193
+ persona: string;
2194
+ currentState: string;
2195
+ messageBlock: string;
2196
+ schema: string;
2197
+ rules: string[];
2198
+ }
2199
+
2200
+ interface MemoryMetadataLike {
2201
+ provenance: MemoryProvenance;
2202
+ }
2203
+ declare function buildCompactionEntry<T>(options: {
2204
+ text: string;
2205
+ provenance: MemoryProvenance;
2206
+ summarize: (text: string) => string;
2207
+ canonicalize: (text: string) => string;
2208
+ preprocess?: (text: string) => string;
2209
+ canonicalizeSource?: (preprocessed: string, normalized: string) => string;
2210
+ build: (normalized: string, canonical: string, metadata: MemoryMetadataLike) => T;
2211
+ }): T | null;
2212
+ declare function canonicalizeCompactionText(text: string, options?: {
2213
+ stopWords?: Iterable<string>;
2214
+ stripQuotes?: boolean;
2215
+ }): string;
2216
+ declare function selectLatestByKey<T>(entries: T[], keyOf: (entry: T) => string, recencyOf: (entry: T) => number): T[];
2217
+ declare function limitEntriesByRecency<T>(entries: T[], limit: number, recencyOf: (entry: T) => number): T[];
2218
+ declare function runStructuredLLMCompaction<TMessage extends MemoryMessage, TState>(options: {
2219
+ client?: CompactionLLMClient;
2220
+ currentState: string;
2221
+ messages: TMessage[];
2222
+ formatMessage: (message: TMessage) => string;
2223
+ persona: string;
2224
+ schema: string;
2225
+ rules: string[];
2226
+ temperature?: number;
2227
+ maxTokens?: number;
2228
+ parseState: (obj: Record<string, unknown>) => TState | null;
2229
+ }): Promise<TState | null>;
2230
+
2231
+ export { AGENT_MACHINE_STATES, AgentLoop, type AgentLoopCallbacks, type AgentLoopDeps, type AgentMachineContext, type AgentMachineEvent, type AgentMachineState, type AgentMemoryCitation, type AgentMemoryConstraint, type AgentMemoryEvidence, type AgentMemoryFactoryOverrides, AgentMemoryLayer, type AgentMemoryLayerConfig, type AgentMemoryLogger, type AgentMemoryNote, type AgentMemoryTask, type AgentMessage, type AgentPhase, type AgentState, ApiError, type BackgroundBashAction, BackgroundBashRunCommand, BaseMemoryLayer, BaseValidationEnforcer, BashRunCommand, type BuildMemoryCompactionPromptOptions, CharTokenEstimator, type ChatMessage, type CommandRuntime, type CommandTargetInfo, type CompactionCursor, type CompactionLLMClient, type ConversationAdapter, type ConversationSink, type CreateWebRuntimeSetupOptions, DEFAULT_AGENT_FALLBACK_SYSTEM_PROMPT, DEFAULT_ALLOWED_RUN_TARGETS, DEFAULT_RUN_COMMAND_REGISTRY, DEFAULT_THREAD_AUTO_SCROLL_BOTTOM_THRESHOLD, DEFAULT_TOOL_MEMORY_EXTRACTOR, ERROR_TRUNCATED_RESPONSE, type EnforcerResult, type ExecuteWebLoopRunOptions, type FetchLike, type FetchResponseLike, type HammerAgentConfig, type HammerAgentProviderPreset, LLMClient, type LLMClientResponse, type LLMProviderConfig, type LLMRequest, type LLMRequestOptions, type LLMResponse, type LoopOutcome, MAX_TOOL_RESULT_CHARS, type MemoryMessage, type MemoryProvenance, type MemoryStorage, type ParseAgentResponseOptions, type ParsedAgentResponse, type ParsedBackgroundBashCommand, type ParsedStepInput, PendingAgentMessageBuffer, type PersistedMemoryData, type ProviderName, ProxyTool, type ProxyToolExecutor, type RawMessage, RunCommand, type RunCommandParseResult, type RunCommandPromptAvailability, RunCommandRegistry, type RunInvocationTarget, type RuntimeController, type RuntimeSnapshotUpdater, type RuntimeStore, type RuntimeSubscriber, SUPPORTED_RUN_TARGETS, type StepResult, type StreamCallbacks, StreamingToolParser, type StreamingToolParserCallbacks, type SystemPromptCustomizer, type TodoItem, type TodoStatus, type TokenEstimator, Tool, type ToolCall, type ToolDataPrimitive, type ToolDataSchema, type ToolDataValue, type ToolDefinition, type ToolDefinitionMetadata, type ToolExecutionResult, ToolLoopAgentRuntime, type ToolLoopAgentRuntimeDeps, type ToolLoopRuntimeExecuteStepResult, type ToolLoopRuntimeHooks, type ToolLoopRuntimeInfrastructure, type ToolLoopRuntimeLLMRequest, type ToolLoopRuntimeLLMResponse, type ToolLoopRuntimeRunStepOptions, type ToolLoopRuntimeRunStepResult, type ToolLoopRuntimeSetup, type ToolLoopRuntimeStepContext, type ToolLoopStepExecutionResult, type ToolLoopStepExecutorCallbacks, type ToolLoopStepExecutorOptions, type ToolLoopStepExecutorResponse, type ToolMemoryCitationKind, type ToolMemoryEvidenceKind, type ToolMemoryEvidencePolicy, type ToolMemoryExtractor, type ToolMemoryMetadata, type ToolMemoryNoteKind, type ToolMemoryNotePolicy, type ToolMemoryNoteScope, type ToolMetadata, type ToolParameterDefinition, ToolRegistry, type ToolRegistryBeforeExecuteContext, type ToolRegistryMissingToolContext, type ToolRegistryOptions, type ToolResult, ToolRunCommand, type ToolSchema, type TruncatedToolInfo, type WebSearchToolActionInput, WebToolLoopAgentRuntime, type WebToolLoopAgentRuntimeConstructorOptions, type WebToolLoopRuntimeStateLike, WebValidationEnforcer, agentMachine, applyIdleWebAgentState, applyInitialWebAgentRunState, buildAgentSystemPrompt, buildCompactionEntry, buildCoreStaticRules, buildNoStructuredResponseFoundError, buildParseFeedback, buildStepUserMessage, buildToolLogRevealFrames, buildToolUsageExample, buildValidationErrorMessage, canonicalizeCompactionText, coerceToolCallToDefinition, configure, containsStandaloneStructuredInvocationStart, createAgentMemoryLayer, createAppendToolsSectionCustomizer, createBackgroundBashDefinition, createConversationSink, createCustomRunCommandRegistry, createInitialWebAgentState, createRunCommandRuntimeBindings, createRuntimeStore, createToolAgentMessage, createToolRegistry, createToolsSectionOverrideCustomizer, createWebAgentMessageIdGenerator, createWebSearchToolActions, createWebToolLoopCallbacks, decodeEscapedShellText, defineRuntimeController, enrichToolResultWithUnixMetadata, executeBackgroundUnixCommandString, executeToolCallWithRunCommands, executeToolLoopStep, executeToolSafe, executeUnixCommandString, extractPrimaryCommandMetadata, extractTruncatedToolInfo, formatToolCallAsUnixCommand, formatToolDefinitions, formatToolResultMessage, formatToolsSection, formatUnixToolSurface, formatZodValidationError, getDiagnosticSummaryLine, getProviderConfig, getRunCommandPromptAvailability, getToolLogSummaryLine, isBackgroundBashToolCall, isBashToolCall, limitEntriesByRecency, machineStateToWebAgentPhase, mapConversationRoleToAgentRole, parseAgentResponse, parseResponseWithRecovery, parseStructuredAgentText, parseToolResultMessage, parseUnixToolCommand, readDiagnosticLevel, readDiagnosticSource, resolveToolDefinitionForInvocation, runStructuredLLMCompaction, selectLatestByKey, shouldAutoScrollThread, shouldSkipStepUserMessage, stripDiagnosticMessagePrefix, suppressWebValidationLog, tokenizeUnixCommand, truncateToolResult };