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.
- package/dist/base-process-manager.d.ts +30 -0
- package/dist/budget.d.ts +315 -0
- package/dist/checkpoint-git.d.ts +98 -0
- package/dist/claude-agent-sdk-manager.d.ts +144 -0
- package/dist/claude-log-parser.d.ts +63 -0
- package/dist/claude-runtime-extractor.d.ts +73 -0
- package/dist/codex-runtime-extractor.d.ts +107 -0
- package/dist/codon-runner.d.ts +278 -0
- package/dist/config-validation/model-validator.d.ts +16 -0
- package/dist/config-validation/sentinel.schema.d.ts +6967 -0
- package/dist/config.d.ts +40815 -0
- package/dist/cost-tracker.d.ts +72 -0
- package/dist/execution-planner.d.ts +62 -0
- package/dist/execution-thread.d.ts +71 -0
- package/dist/exports/schemas.d.ts +9 -0
- package/dist/exports/schemas.js +1019 -0
- package/dist/exports/types.d.ts +15 -0
- package/dist/exports/types.js +60 -0
- package/dist/file-resolver.d.ts +33 -0
- package/dist/index.js +233 -233
- package/dist/index.js.map +7 -7
- package/dist/llm/llm-provider-registry.d.ts +207 -0
- package/dist/llm/models-dev-schema.d.ts +679 -0
- package/dist/llm/provider-config.d.ts +30 -0
- package/dist/prompt-builder.d.ts +75 -0
- package/dist/prompt-frontmatter.d.ts +61 -0
- package/dist/replay-process-manager.d.ts +82 -0
- package/dist/runtime-extractor-base.d.ts +120 -0
- package/dist/schemas/event-schemas.d.ts +8389 -0
- package/dist/schemas/websocket-log-schemas.d.ts +4502 -0
- package/dist/shim-process-manager.d.ts +98 -0
- package/dist/shim-runtime-extractor.d.ts +51 -0
- package/dist/shims/codex/index.js +67 -68
- package/dist/state-manager.d.ts +161 -0
- package/dist/state-transition-guards.d.ts +37 -0
- package/dist/telemetry/telemetry-types.d.ts +206 -0
- package/dist/typed-event-emitter.d.ts +57 -0
- package/dist/types/branded-types.d.ts +15 -0
- package/dist/types/budget-types.d.ts +82 -0
- package/dist/types/claude-session-schema.d.ts +2430 -0
- package/dist/types/error-types.d.ts +44 -0
- package/dist/types/input-ai-types.d.ts +1070 -0
- package/dist/types/llm-call-types.d.ts +3829 -0
- package/dist/types/sentinel-types.d.ts +66 -0
- package/dist/types/state-types.d.ts +1099 -0
- package/dist/types/tool-types.d.ts +86 -0
- package/dist/types/types.d.ts +367 -0
- package/dist/types/websocket-log-types.d.ts +7 -0
- package/dist/utils.d.ts +452 -0
- 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
|
|
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
|
|
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 (
|
|
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 (
|
|
1339
|
+
if (fs4.existsSync(command)) return command;
|
|
1357
1340
|
if (isWindows) {
|
|
1358
1341
|
for (const ext of [".cmd", ".exe"]) {
|
|
1359
|
-
if (
|
|
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 (!
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|