hankweave 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/base-process-manager.d.ts +30 -0
  2. package/dist/budget.d.ts +315 -0
  3. package/dist/checkpoint-git.d.ts +98 -0
  4. package/dist/claude-agent-sdk-manager.d.ts +144 -0
  5. package/dist/claude-log-parser.d.ts +63 -0
  6. package/dist/claude-runtime-extractor.d.ts +73 -0
  7. package/dist/codex-runtime-extractor.d.ts +107 -0
  8. package/dist/codon-runner.d.ts +278 -0
  9. package/dist/config-validation/model-validator.d.ts +16 -0
  10. package/dist/config-validation/sentinel.schema.d.ts +6967 -0
  11. package/dist/config.d.ts +40815 -0
  12. package/dist/cost-tracker.d.ts +72 -0
  13. package/dist/execution-planner.d.ts +62 -0
  14. package/dist/execution-thread.d.ts +71 -0
  15. package/dist/exports/schemas.d.ts +9 -0
  16. package/dist/exports/schemas.js +1019 -0
  17. package/dist/exports/types.d.ts +15 -0
  18. package/dist/exports/types.js +60 -0
  19. package/dist/file-resolver.d.ts +33 -0
  20. package/dist/index.js +233 -233
  21. package/dist/index.js.map +7 -7
  22. package/dist/llm/llm-provider-registry.d.ts +207 -0
  23. package/dist/llm/models-dev-schema.d.ts +679 -0
  24. package/dist/llm/provider-config.d.ts +30 -0
  25. package/dist/prompt-builder.d.ts +75 -0
  26. package/dist/prompt-frontmatter.d.ts +61 -0
  27. package/dist/replay-process-manager.d.ts +82 -0
  28. package/dist/runtime-extractor-base.d.ts +120 -0
  29. package/dist/schemas/event-schemas.d.ts +8389 -0
  30. package/dist/schemas/websocket-log-schemas.d.ts +4502 -0
  31. package/dist/shim-process-manager.d.ts +98 -0
  32. package/dist/shim-runtime-extractor.d.ts +51 -0
  33. package/dist/shims/codex/index.js +67 -68
  34. package/dist/state-manager.d.ts +161 -0
  35. package/dist/state-transition-guards.d.ts +37 -0
  36. package/dist/telemetry/telemetry-types.d.ts +206 -0
  37. package/dist/typed-event-emitter.d.ts +57 -0
  38. package/dist/types/branded-types.d.ts +15 -0
  39. package/dist/types/budget-types.d.ts +82 -0
  40. package/dist/types/claude-session-schema.d.ts +2430 -0
  41. package/dist/types/error-types.d.ts +44 -0
  42. package/dist/types/input-ai-types.d.ts +1070 -0
  43. package/dist/types/llm-call-types.d.ts +3829 -0
  44. package/dist/types/sentinel-types.d.ts +66 -0
  45. package/dist/types/state-types.d.ts +1099 -0
  46. package/dist/types/tool-types.d.ts +86 -0
  47. package/dist/types/types.d.ts +367 -0
  48. package/dist/types/websocket-log-types.d.ts +7 -0
  49. package/dist/utils.d.ts +452 -0
  50. package/package.json +14 -1
@@ -0,0 +1,98 @@
1
+ import { BaseProcessManager } from "./base-process-manager.js";
2
+ import type { ClaudeLogParser } from "./claude-log-parser.js";
3
+ import type { Codon, ShimSelfTestResult } from "./types/types.js";
4
+ import { type Logger } from "./utils.js";
5
+ /**
6
+ * Manages shim subprocess lifecycle, including spawning, monitoring, and cleanup.
7
+ * Handles log stream creation and process argument building.
8
+ * Works with any shim that supports the standardized argument interface.
9
+ */
10
+ export declare class ShimProcessManager extends BaseProcessManager {
11
+ private executionPath;
12
+ private agentRootPath;
13
+ private anthropicBaseUrl?;
14
+ private globalSystemPrompt?;
15
+ private defaultShimIdleTimeout?;
16
+ private process;
17
+ private logStream;
18
+ private stdoutReader;
19
+ private killed;
20
+ private promptBuilder;
21
+ constructor(executionPath: string, agentRootPath: string, logger: Logger, logParser: ClaudeLogParser, anthropicBaseUrl?: string | undefined, globalSystemPrompt?: string | null | undefined, defaultShimIdleTimeout?: number | undefined);
22
+ /** Frontmatter metadata from the prompt file (if any) */
23
+ get promptFrontmatter(): import("./prompt-frontmatter.js").PromptFrontmatter | undefined;
24
+ /**
25
+ * Spawn a shim process for the given codon configuration.
26
+ * Sets up logging, environment, and process monitoring.
27
+ *
28
+ * This unified method handles both normal codon execution and exhaustion extensions.
29
+ * From the shim's perspective, both are identical: resume a session with a new prompt.
30
+ * The difference is only where the prompt comes from.
31
+ *
32
+ * @param command - Command to execute (e.g., ["claude"] or ["bun", "run", "shims/gemini/dist/index.mjs"])
33
+ * @param codon - Codon configuration (not Loop - loops must be expanded first)
34
+ * @param sessionToResume - Session ID to resume (if any)
35
+ * @param options - Optional spawn configuration
36
+ * @param options.logPath - Custom log file path (defaults to .hankweave/logs/)
37
+ * @param options.exhaustionPrompt - If provided, activates exhaustion mode: uses this prompt
38
+ * instead of codon config, appends to log.
39
+ */
40
+ spawn(command: string[], codon: Codon, sessionToResume: string | null, options?: {
41
+ logPath?: string;
42
+ exhaustionPrompt?: string;
43
+ }): Promise<string>;
44
+ /**
45
+ * Build environment variables for the shim process.
46
+ */
47
+ private buildEnvironment;
48
+ /**
49
+ * Build command line arguments for shim.
50
+ * Only includes arguments supported by all shims.
51
+ *
52
+ * @param codon - Codon configuration
53
+ * @param previousSessionId - Session ID to resume (if any)
54
+ * @param isExhaustionMode - True if this is an extension (exhaustion mode)
55
+ */
56
+ private buildShimArgs;
57
+ /**
58
+ * Set up process event handlers.
59
+ */
60
+ private setupProcessHandlers;
61
+ /**
62
+ * Kill the shim process gracefully.
63
+ * Sends SIGTERM and waits up to PROCESS_KILL_GRACE_MS for the process to exit.
64
+ * If the process doesn't exit in time, escalates to SIGKILL.
65
+ */
66
+ kill(signal?: NodeJS.Signals): Promise<void>;
67
+ /**
68
+ * Force-kill the shim process immediately with SIGKILL.
69
+ * Used by forceShutdown() when the user presses q/Ctrl+C a second time.
70
+ */
71
+ forceKill(): Promise<void>;
72
+ /**
73
+ * Clean up resources.
74
+ */
75
+ private cleanup;
76
+ /**
77
+ * Check if process is running.
78
+ */
79
+ isRunning(): boolean;
80
+ /**
81
+ * Get process PID.
82
+ */
83
+ getPid(): number | undefined;
84
+ /**
85
+ * Close log stream explicitly (for external cleanup).
86
+ */
87
+ closeLogStream(): Promise<void>;
88
+ /**
89
+ * Run the shim's self-test to verify environment setup.
90
+ * Executes the shim with --self-test flag and returns the results.
91
+ *
92
+ * @param command - Command to execute shim (e.g., ["bun", "shims/gemini/index.js"])
93
+ * @param providerId - Optional provider ID (e.g., "openai", "google") to set up provider-specific requirements
94
+ * @returns Promise resolving to self-test results
95
+ * @throws Error if self-test execution fails or returns invalid JSON
96
+ */
97
+ runSelfTest(command: string[], providerId?: string): Promise<ShimSelfTestResult>;
98
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Shim Runtime Extractor
3
+ *
4
+ * This module handles the extraction of bundled shim files at runtime for
5
+ * standalone executables. When compiled with Bun, shim files are embedded
6
+ * in the executable and need to be extracted to disk before they can be
7
+ * spawned as subprocesses.
8
+ *
9
+ * The extraction is done to a versioned directory to avoid re-extraction
10
+ * on every run and to handle version updates cleanly.
11
+ *
12
+ * Build Process:
13
+ * The build script (scripts/build-executable.ts) embeds shim files using:
14
+ * bun build --compile --embed shims/gemini/index.js ...
15
+ *
16
+ * Note: We use .js extension instead of .mjs for better embedding compatibility.
17
+ *
18
+ * At runtime, these embedded files are accessible via Bun.file() using their
19
+ * original paths.
20
+ */
21
+ declare const SHIM_NAMES: readonly ["gemini", "codex", "pi", "opencode"];
22
+ type ShimName = (typeof SHIM_NAMES)[number];
23
+ /**
24
+ * Get the extraction directory path for shims.
25
+ * Uses ~/.hankweave/shims/<version>/ by default.
26
+ */
27
+ export declare function getShimExtractionDir(): string;
28
+ /**
29
+ * Get the path to an extracted shim file.
30
+ * Note: We use .js extension for embedding compatibility, even though the
31
+ * source file is .mjs. The file works the same regardless of extension.
32
+ */
33
+ export declare function getExtractedShimPath(shimName: ShimName): string;
34
+ /**
35
+ * Check if extraction is needed for a specific shim.
36
+ * Returns true if the file doesn't exist or is outdated.
37
+ */
38
+ export declare function needsShimExtraction(shimName: ShimName): boolean;
39
+ /**
40
+ * Extract embedded shim files to the cache directory.
41
+ * This should be called when running from a compiled executable.
42
+ *
43
+ * Shims are embedded as .bundle (not .js) because Bun's --embed flag
44
+ * rebundles .js files instead of preserving raw bytes, which truncates
45
+ * large bundles like the Pi shim (10 MB → 28 KB). The .bundle extension
46
+ * bypasses this behaviour. We write them back out as .js on extraction.
47
+ *
48
+ * @returns Path to the extracted gemini shim (for backward compatibility)
49
+ */
50
+ export declare function extractShimFiles(): Promise<string>;
51
+ export {};
@@ -2,7 +2,8 @@
2
2
 
3
3
  // src/shim.ts
4
4
  import { spawn as spawn2 } from "child_process";
5
- import fs5 from "fs";
5
+ import fs4 from "fs";
6
+ import os2 from "os";
6
7
  import path6 from "path";
7
8
 
8
9
  // node_modules/@openai/codex-sdk/dist/index.js
@@ -753,8 +754,6 @@ var DebugRecorder = class {
753
754
 
754
755
  // src/utils.ts
755
756
  import { randomBytes, randomUUID as randomUUID2 } from "crypto";
756
- import fs4 from "fs";
757
- import os2 from "os";
758
757
  import path5 from "path";
759
758
  var NIL_UUID = "00000000-0000-0000-0000-000000000000";
760
759
  var SESSION_ID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
@@ -804,22 +803,6 @@ function mapSandbox(level) {
804
803
  return "danger-full-access";
805
804
  }
806
805
  }
807
- function detectApiKeySource() {
808
- if (process.env.OPENAI_API_KEY) {
809
- return "OPENAI_API_KEY";
810
- }
811
- if (process.env.CODEX_API_KEY) {
812
- return "CODEX_API_KEY";
813
- }
814
- const authPath = path5.join(os2.homedir(), ".codex", "auth.json");
815
- if (fs4.existsSync(authPath)) {
816
- return "~/.codex/auth.json";
817
- }
818
- return "none";
819
- }
820
- function hasAnyAuthConfigured() {
821
- return detectApiKeySource() !== "none";
822
- }
823
806
  function getCodexPathOverride() {
824
807
  const value = process.env.CODEX_PATH_OVERRIDE?.trim();
825
808
  return value ? value : void 0;
@@ -1243,7 +1226,7 @@ function usageToTokenUsage(usage) {
1243
1226
  cache_read_input_tokens: usage.cached_input_tokens
1244
1227
  };
1245
1228
  }
1246
- function createSystemMessage(cwd, sessionId, model) {
1229
+ function createSystemMessage(cwd, sessionId, model, apiKeySource) {
1247
1230
  return {
1248
1231
  type: "system",
1249
1232
  subtype: "init",
@@ -1252,7 +1235,7 @@ function createSystemMessage(cwd, sessionId, model) {
1252
1235
  tools: DEFAULT_TOOLS,
1253
1236
  model,
1254
1237
  permissionMode: "bypassPermissions",
1255
- apiKeySource: detectApiKeySource(),
1238
+ apiKeySource,
1256
1239
  mcp_servers: []
1257
1240
  };
1258
1241
  }
@@ -1346,17 +1329,17 @@ function findVendoredCodexExe(npmPrefix) {
1346
1329
  path6.join(npmPrefix, "node_modules", "@openai", "codex", "node_modules", tail)
1347
1330
  ];
1348
1331
  for (const candidate of candidates) {
1349
- if (fs5.existsSync(candidate)) return candidate;
1332
+ if (fs4.existsSync(candidate)) return candidate;
1350
1333
  }
1351
1334
  return null;
1352
1335
  }
1353
1336
  async function resolveCodexPath(command) {
1354
1337
  const isWindows = process.platform === "win32";
1355
1338
  if (path6.isAbsolute(command) || command.includes(path6.sep)) {
1356
- if (fs5.existsSync(command)) return command;
1339
+ if (fs4.existsSync(command)) return command;
1357
1340
  if (isWindows) {
1358
1341
  for (const ext of [".cmd", ".exe"]) {
1359
- if (fs5.existsSync(command + ext)) return command + ext;
1342
+ if (fs4.existsSync(command + ext)) return command + ext;
1360
1343
  }
1361
1344
  }
1362
1345
  return null;
@@ -1415,43 +1398,6 @@ async function resolveAgentVersion(codexPath) {
1415
1398
  });
1416
1399
  });
1417
1400
  }
1418
- async function runSelfTest() {
1419
- const apiKeySource = detectApiKeySource();
1420
- const override = getCodexPathOverride() || "codex";
1421
- const resolvedPath = await resolveCodexPath(override);
1422
- const agentFound = resolvedPath !== null;
1423
- const agentVersion = resolvedPath ? await resolveAgentVersion(resolvedPath) : "unknown";
1424
- const checks = [
1425
- {
1426
- name: "agent_found",
1427
- passed: agentFound,
1428
- message: agentFound ? `Found codex at ${resolvedPath}` : `Could not find codex via ${override}`
1429
- },
1430
- {
1431
- name: "api_key",
1432
- passed: apiKeySource !== "none",
1433
- message: apiKeySource !== "none" ? `Authentication source available: ${apiKeySource}` : "No OPENAI_API_KEY, CODEX_API_KEY, or ~/.codex/auth.json found"
1434
- }
1435
- ];
1436
- const overallPassed = checks.every((check) => check.passed);
1437
- process.stdout.write(
1438
- `${JSON.stringify(
1439
- {
1440
- shim: { name: "codex-shim", version: package_default.version },
1441
- agent: { name: "codex", version: agentVersion, found: agentFound },
1442
- checks,
1443
- overall: {
1444
- passed: overallPassed,
1445
- message: overallPassed ? "All checks passed" : "One or more checks failed"
1446
- }
1447
- },
1448
- null,
1449
- 2
1450
- )}
1451
- `
1452
- );
1453
- return overallPassed ? 0 : 1;
1454
- }
1455
1401
  var CodexShim = class {
1456
1402
  args;
1457
1403
  prompt;
@@ -1477,16 +1423,64 @@ var CodexShim = class {
1477
1423
  this.sessionManager = new SessionManager({ debugDir: args.debugDir });
1478
1424
  this.sessionId = args.resume || generateSessionId();
1479
1425
  }
1426
+ get resolvedApiKey() {
1427
+ return process.env.OPENAI_API_KEY || process.env.CODEX_API_KEY || void 0;
1428
+ }
1429
+ get apiKeySource() {
1430
+ if (process.env.OPENAI_API_KEY) return "OPENAI_API_KEY";
1431
+ if (process.env.CODEX_API_KEY) return "CODEX_API_KEY";
1432
+ if (fs4.existsSync(path6.join(os2.homedir(), ".codex", "auth.json"))) return "~/.codex/auth.json";
1433
+ return "none";
1434
+ }
1435
+ get isAuthConfigured() {
1436
+ return this.apiKeySource !== "none";
1437
+ }
1438
+ async runSelfTest() {
1439
+ const override = getCodexPathOverride() || "codex";
1440
+ const resolvedPath = await resolveCodexPath(override);
1441
+ const agentFound = resolvedPath !== null;
1442
+ const agentVersion = resolvedPath ? await resolveAgentVersion(resolvedPath) : "unknown";
1443
+ const checks = [
1444
+ {
1445
+ name: "agent_found",
1446
+ passed: agentFound,
1447
+ message: agentFound ? `Found codex at ${resolvedPath}` : `Could not find codex via ${override}`
1448
+ },
1449
+ {
1450
+ name: "api_key",
1451
+ passed: this.isAuthConfigured,
1452
+ message: this.isAuthConfigured ? `Authentication source available: ${this.apiKeySource}` : "No OPENAI_API_KEY, CODEX_API_KEY, or ~/.codex/auth.json found"
1453
+ }
1454
+ ];
1455
+ const overallPassed = checks.every((check) => check.passed);
1456
+ process.stdout.write(
1457
+ `${JSON.stringify(
1458
+ {
1459
+ shim: { name: "codex-shim", version: package_default.version },
1460
+ agent: { name: "codex", version: agentVersion, found: agentFound },
1461
+ checks,
1462
+ overall: {
1463
+ passed: overallPassed,
1464
+ message: overallPassed ? "All checks passed" : "One or more checks failed"
1465
+ }
1466
+ },
1467
+ null,
1468
+ 2
1469
+ )}
1470
+ `
1471
+ );
1472
+ return overallPassed ? 0 : 1;
1473
+ }
1480
1474
  async run() {
1481
1475
  const codexPath = await resolveCodexPath(getCodexPathOverride() || "codex");
1482
1476
  if (!codexPath) {
1483
1477
  writeStartupError("Agent not found: could not locate codex via CODEX_PATH_OVERRIDE or PATH", this.args.debugDir);
1484
1478
  }
1485
- if (!hasAnyAuthConfigured()) {
1479
+ if (!this.isAuthConfigured) {
1486
1480
  writeStartupError("Missing API key: set OPENAI_API_KEY, CODEX_API_KEY, or ~/.codex/auth.json", this.args.debugDir);
1487
1481
  }
1488
1482
  this.codex = new Codex({
1489
- apiKey: process.env.OPENAI_API_KEY || process.env.CODEX_API_KEY,
1483
+ apiKey: this.resolvedApiKey,
1490
1484
  codexPathOverride: codexPath,
1491
1485
  env: Object.fromEntries(
1492
1486
  Object.entries(process.env).filter((entry) => entry[1] !== void 0)
@@ -1509,7 +1503,7 @@ var CodexShim = class {
1509
1503
  } else {
1510
1504
  thread = this.codex.startThread(this.getThreadOptions());
1511
1505
  }
1512
- const systemMessage = createSystemMessage(this.cwd, this.sessionId, this.model.publicModel);
1506
+ const systemMessage = createSystemMessage(this.cwd, this.sessionId, this.model.publicModel, this.apiKeySource);
1513
1507
  emit(systemMessage);
1514
1508
  this.debug.setSession(this.sessionId, { cwd: this.cwd, model: this.model.publicModel });
1515
1509
  const startedAt = Date.now();
@@ -1624,11 +1618,15 @@ var CodexShim = class {
1624
1618
  throw new Error(event.message);
1625
1619
  }
1626
1620
  case "item.started": {
1627
- await this.emitToolUseIfNeeded(event.item);
1621
+ if (event.item.type !== "web_search") {
1622
+ await this.emitToolUseIfNeeded(event.item);
1623
+ }
1628
1624
  break;
1629
1625
  }
1630
1626
  case "item.updated": {
1631
- await this.emitToolUseIfNeeded(event.item);
1627
+ if (event.item.type !== "web_search") {
1628
+ await this.emitToolUseIfNeeded(event.item);
1629
+ }
1632
1630
  break;
1633
1631
  }
1634
1632
  case "item.completed": {
@@ -1664,7 +1662,8 @@ var CodexShim = class {
1664
1662
  return;
1665
1663
  }
1666
1664
  if (item.type === "error") {
1667
- throw new Error(item.message);
1665
+ this.logVerbose(`Codex non-fatal error item: ${item.message}`);
1666
+ return;
1668
1667
  }
1669
1668
  if (shouldTreatAsToolItem(item)) {
1670
1669
  const toolId = await this.emitToolUseIfNeeded(item);
@@ -1741,7 +1740,7 @@ async function main(argv = process.argv.slice(2)) {
1741
1740
  return 0;
1742
1741
  }
1743
1742
  if (args.selfTest) {
1744
- return await runSelfTest();
1743
+ return await new CodexShim(args, "").runSelfTest();
1745
1744
  }
1746
1745
  const prompt = await readStdin();
1747
1746
  if (!prompt) {
@@ -0,0 +1,161 @@
1
+ import type { CheckpointGit } from "./checkpoint-git.js";
2
+ import { type ExecutionCodonEntry } from "./execution-planner.js";
3
+ import { type ExecutionThread } from "./execution-thread.js";
4
+ import { type StateManagerEvents, TypedEventEmitter } from "./typed-event-emitter.js";
5
+ import { type CodonId, type RunId } from "./types/branded-types.js";
6
+ import type * as ST from "./types/state-types.js";
7
+ import type { CodonConfig } from "./types/types.js";
8
+ import { type Logger } from "./utils.js";
9
+ export declare class InvalidTransitionError extends Error {
10
+ constructor(from: ST.CodonStatus, to: ST.CodonStatus);
11
+ }
12
+ export declare class PersistenceError extends Error {
13
+ constructor(operation: string, cause: Error);
14
+ }
15
+ /**
16
+ * Result of expanding the next iteration of a loop.
17
+ * If a loop terminated, includes the loop ID and its archiveOnSuccess paths.
18
+ */
19
+ export interface ExpandIterationResult {
20
+ loopTerminated?: {
21
+ loopId: string;
22
+ archiveOnSuccess?: string[];
23
+ completedIterations: number;
24
+ };
25
+ }
26
+ export declare class StateManager extends TypedEventEmitter<StateManagerEvents> implements ST.StateManager {
27
+ private readonly hankweaveDir;
28
+ private readonly codonConfigs?;
29
+ private state;
30
+ private readonly statePath;
31
+ private readonly stateBackupPath;
32
+ private readonly logger;
33
+ private transitionQueue;
34
+ private isProcessing;
35
+ private readonly planner;
36
+ private costCache;
37
+ constructor(hankweaveDir: string, logger: Logger, codonConfigs?: CodonConfig[] | undefined);
38
+ initialize(): Promise<void>;
39
+ /**
40
+ * Load and validate a state file from disk.
41
+ * @throws Error if file is corrupted or cannot be read
42
+ */
43
+ private loadAndValidateStateFile;
44
+ /**
45
+ * Restore state from a parsed and validated state object.
46
+ */
47
+ private restoreStateFromParsed;
48
+ /**
49
+ * Attempt to restore state from backup file.
50
+ */
51
+ private tryRestoreFromBackup;
52
+ getState(): Readonly<ST.HankweaveState>;
53
+ /**
54
+ * Get codon entry from execution plan by codon ID.
55
+ * This handles generated IDs like "review#0", "review#1" from loop expansion.
56
+ *
57
+ * @param codonId - The codon ID to look up
58
+ * @returns The execution codon entry, or null if not found
59
+ */
60
+ getCodonById(codonId: CodonId): ExecutionCodonEntry | null;
61
+ /**
62
+ * Build initial execution plan for a fresh start.
63
+ * Expands only the first iteration of each loop.
64
+ * Automatically validates and stores the plan.
65
+ * Called automatically when RunStarted transition occurs (unless continuation mode).
66
+ */
67
+ private buildInitialPlan;
68
+ /**
69
+ * Expand next iteration of a loop after codon completion.
70
+ * Checks if this completed codon is part of a loop and expands the next iteration if needed.
71
+ * Automatically validates and stores the updated plan.
72
+ *
73
+ * @returns Information about loop termination if a loop ended
74
+ */
75
+ expandNextIterationForCodon(params: {
76
+ codonId: CodonId;
77
+ contextExceeded?: boolean;
78
+ budgetExceeded?: boolean;
79
+ }): Promise<ExpandIterationResult>;
80
+ /**
81
+ * Checks if context exceeded is an acceptable termination condition for the given codon.
82
+ *
83
+ * Returns true only if:
84
+ * - Codon is part of a loop (has loopContext)
85
+ * - That loop terminates on contextExceeded
86
+ *
87
+ * This is a pure query method with no side effects.
88
+ *
89
+ * @param codonId - The codon to check
90
+ * @returns true if context exceeded is acceptable, false otherwise
91
+ */
92
+ isContextExceededAcceptable(codonId: CodonId): boolean;
93
+ transition(event: ST.StateTransition): void;
94
+ private processQueue;
95
+ private updateCostCache;
96
+ private updateExecutionPlan;
97
+ private rebuildCostCache;
98
+ validate(state: unknown): ST.StateValidation;
99
+ private isValidStateStructure;
100
+ getCurrentRunCost(): number;
101
+ getTotalCost(): number;
102
+ getCurrentRun(): ST.Run | null;
103
+ getCurrentlyRunningCodon(): ST.CodonExecution | null;
104
+ getCodonInCurrentRun(codonId: CodonId): ST.CodonExecution | null;
105
+ /**
106
+ * Get the next codon that should be executed based on current state.
107
+ * Uses the execution thread to determine where we are in the workflow.
108
+ *
109
+ * @returns CodonId of next codon to execute, or null if all codons are complete
110
+ */
111
+ getNextCodonToExecute(): Promise<CodonId | null>;
112
+ getRun(runId: RunId): ST.Run | null;
113
+ getCodonHistory(codonId: CodonId): Promise<Array<{
114
+ run: ST.Run;
115
+ codon: ST.CodonExecution;
116
+ }>>;
117
+ getCostSince(runId: RunId): number;
118
+ canContinueFrom(runId: RunId, afterCodon: CodonId | null): boolean;
119
+ getCheckpointForContinuation(runId: RunId, afterCodon: CodonId | null): string | null;
120
+ getRunById(runId: RunId): ST.Run | null;
121
+ private checkpointGit?;
122
+ /**
123
+ * Set the checkpoint git instance for git operations.
124
+ * Called by HankweaveRuntime after initializing CheckpointGit.
125
+ */
126
+ setCheckpointGit(checkpointGit: CheckpointGit): void;
127
+ /**
128
+ * Get the execution thread for the current state.
129
+ * This provides a unified view of codon execution across all runs.
130
+ *
131
+ * @param targetRunId - Optional run ID to start from (defaults to latest)
132
+ * @param includeCheckpointValidation - Whether to validate checkpoints against git
133
+ * @returns Complete execution thread with all metadata
134
+ *
135
+ * NOTE: The codonConfigs fallback exists for initialization timing issues where the plan
136
+ * hasn't been built yet (e.g., during HankweaveRuntime.start() before startNewRun()).
137
+ */
138
+ getExecutionThread(targetRunId?: RunId, includeCheckpointValidation?: boolean): Promise<ExecutionThread>;
139
+ /**
140
+ * Helper to convert checkpoint array to map for execution thread
141
+ */
142
+ private getCheckpointDataMap;
143
+ /**
144
+ * Get all checkpoints with detailed information, ordered by time.
145
+ * This exposes the checkpoint history for advanced use cases.
146
+ *
147
+ * @returns Array of checkpoint information ordered by timestamp (newest first), or null if git unavailable
148
+ */
149
+ getAllCheckpoints(): Promise<Array<{
150
+ sha: string;
151
+ message: string;
152
+ timestamp: string;
153
+ branch: string;
154
+ }> | null>;
155
+ private validateTransition;
156
+ private applyTransition;
157
+ save(): Promise<void>;
158
+ detectCrashedRuns(): Promise<void>;
159
+ recover(): Promise<ST.RecoveryResult>;
160
+ waitForPendingTransitions(): Promise<void>;
161
+ }
@@ -0,0 +1,37 @@
1
+ import type { SessionId } from "./types/branded-types.js";
2
+ import type { BudgetExceededData } from "./types/budget-types.js";
3
+ import type { CodonStatus } from "./types/state-types.js";
4
+ import type { FailureReason } from "./types/types.js";
5
+ export interface InitializingMetadata {
6
+ claudePid: number;
7
+ claudeLogPath: string;
8
+ }
9
+ export interface RunningMetadata {
10
+ claudeSessionId: SessionId;
11
+ }
12
+ export interface CompletedMetadata {
13
+ checkpointSha: string;
14
+ resultMessageReceived?: boolean;
15
+ budgetExceeded?: BudgetExceededData;
16
+ }
17
+ export interface FailedMetadata {
18
+ exitCode: number;
19
+ failureReason: FailureReason;
20
+ failedDuring: CodonStatus;
21
+ checkpointSha?: string;
22
+ }
23
+ export interface SkippedMetadata {
24
+ skippedDuring: CodonStatus;
25
+ checkpointSha?: string;
26
+ }
27
+ export declare function hasInitializingMetadata(metadata: unknown): metadata is InitializingMetadata;
28
+ export declare function hasRunningMetadata(metadata: unknown): metadata is RunningMetadata;
29
+ export declare function hasCompletedMetadata(metadata: unknown): metadata is CompletedMetadata;
30
+ export declare function hasFailedMetadata(metadata: unknown): metadata is FailedMetadata;
31
+ export declare function hasSkippedMetadata(metadata: unknown): metadata is SkippedMetadata;
32
+ export declare class MetadataValidationError extends Error {
33
+ readonly transitionTo: CodonStatus;
34
+ readonly missingFields: string[];
35
+ constructor(transitionTo: CodonStatus, missingFields: string[]);
36
+ }
37
+ export declare function validateTransitionMetadata(to: CodonStatus, metadata: unknown): void;