@zhijiewang/openharness 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/cybergotchi.d.ts.map +1 -1
- package/dist/commands/cybergotchi.js +22 -19
- package/dist/commands/cybergotchi.js.map +1 -1
- package/dist/components/App.d.ts +1 -1
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +58 -2
- package/dist/components/App.js.map +1 -1
- package/dist/components/CompanionFooter.d.ts +9 -0
- package/dist/components/CompanionFooter.d.ts.map +1 -0
- package/dist/components/CompanionFooter.js +13 -0
- package/dist/components/CompanionFooter.js.map +1 -0
- package/dist/components/CybergotchiBubble.d.ts +2 -1
- package/dist/components/CybergotchiBubble.d.ts.map +1 -1
- package/dist/components/CybergotchiBubble.js +4 -5
- package/dist/components/CybergotchiBubble.js.map +1 -1
- package/dist/components/CybergotchiPanel.d.ts +5 -4
- package/dist/components/CybergotchiPanel.d.ts.map +1 -1
- package/dist/components/CybergotchiPanel.js +4 -2
- package/dist/components/CybergotchiPanel.js.map +1 -1
- package/dist/components/CybergotchiPanelConnected.js +4 -4
- package/dist/components/CybergotchiPanelConnected.js.map +1 -1
- package/dist/components/CybergotchiSetup.d.ts.map +1 -1
- package/dist/components/CybergotchiSetup.js +25 -61
- package/dist/components/CybergotchiSetup.js.map +1 -1
- package/dist/components/CybergotchiSprite.d.ts +5 -4
- package/dist/components/CybergotchiSprite.d.ts.map +1 -1
- package/dist/components/CybergotchiSprite.js +16 -9
- package/dist/components/CybergotchiSprite.js.map +1 -1
- package/dist/components/DiffView.d.ts +27 -0
- package/dist/components/DiffView.d.ts.map +1 -0
- package/dist/components/DiffView.js +74 -0
- package/dist/components/DiffView.js.map +1 -0
- package/dist/components/Messages.js +1 -1
- package/dist/components/Messages.js.map +1 -1
- package/dist/components/PermissionPrompt.d.ts.map +1 -1
- package/dist/components/PermissionPrompt.js +61 -8
- package/dist/components/PermissionPrompt.js.map +1 -1
- package/dist/components/REPL.d.ts.map +1 -1
- package/dist/components/REPL.js +75 -21
- package/dist/components/REPL.js.map +1 -1
- package/dist/components/TextInput.d.ts +2 -1
- package/dist/components/TextInput.d.ts.map +1 -1
- package/dist/components/TextInput.js +110 -6
- package/dist/components/TextInput.js.map +1 -1
- package/dist/cybergotchi/bones.d.ts +12 -0
- package/dist/cybergotchi/bones.d.ts.map +1 -0
- package/dist/cybergotchi/bones.js +89 -0
- package/dist/cybergotchi/bones.js.map +1 -0
- package/dist/cybergotchi/config.d.ts +14 -4
- package/dist/cybergotchi/config.d.ts.map +1 -1
- package/dist/cybergotchi/config.js +62 -9
- package/dist/cybergotchi/config.js.map +1 -1
- package/dist/cybergotchi/needs.d.ts +4 -4
- package/dist/cybergotchi/needs.d.ts.map +1 -1
- package/dist/cybergotchi/needs.js.map +1 -1
- package/dist/cybergotchi/personality.d.ts +11 -0
- package/dist/cybergotchi/personality.d.ts.map +1 -0
- package/dist/cybergotchi/personality.js +26 -0
- package/dist/cybergotchi/personality.js.map +1 -0
- package/dist/cybergotchi/species.d.ts +0 -1
- package/dist/cybergotchi/species.d.ts.map +1 -1
- package/dist/cybergotchi/species.js +253 -229
- package/dist/cybergotchi/species.js.map +1 -1
- package/dist/cybergotchi/types.d.ts +45 -7
- package/dist/cybergotchi/types.d.ts.map +1 -1
- package/dist/cybergotchi/types.js +36 -5
- package/dist/cybergotchi/types.js.map +1 -1
- package/dist/cybergotchi/useCybergotchi.d.ts +7 -5
- package/dist/cybergotchi/useCybergotchi.d.ts.map +1 -1
- package/dist/cybergotchi/useCybergotchi.js +27 -16
- package/dist/cybergotchi/useCybergotchi.js.map +1 -1
- package/dist/git/index.d.ts +9 -0
- package/dist/git/index.d.ts.map +1 -1
- package/dist/git/index.js +31 -0
- package/dist/git/index.js.map +1 -1
- package/dist/harness/config.d.ts +5 -0
- package/dist/harness/config.d.ts.map +1 -1
- package/dist/harness/config.js.map +1 -1
- package/dist/harness/credentials.d.ts +24 -0
- package/dist/harness/credentials.d.ts.map +1 -0
- package/dist/harness/credentials.js +104 -0
- package/dist/harness/credentials.js.map +1 -0
- package/dist/harness/hooks.d.ts +8 -0
- package/dist/harness/hooks.d.ts.map +1 -1
- package/dist/harness/hooks.js +98 -22
- package/dist/harness/hooks.js.map +1 -1
- package/dist/harness/keybindings.d.ts +37 -0
- package/dist/harness/keybindings.d.ts.map +1 -0
- package/dist/harness/keybindings.js +134 -0
- package/dist/harness/keybindings.js.map +1 -0
- package/dist/harness/memory.d.ts +35 -0
- package/dist/harness/memory.d.ts.map +1 -0
- package/dist/harness/memory.js +115 -0
- package/dist/harness/memory.js.map +1 -0
- package/dist/harness/plugins.d.ts +57 -0
- package/dist/harness/plugins.d.ts.map +1 -0
- package/dist/harness/plugins.js +142 -0
- package/dist/harness/plugins.js.map +1 -0
- package/dist/harness/session.d.ts +5 -0
- package/dist/harness/session.d.ts.map +1 -1
- package/dist/harness/session.js +39 -1
- package/dist/harness/session.js.map +1 -1
- package/dist/lsp/client.d.ts +58 -0
- package/dist/lsp/client.d.ts.map +1 -0
- package/dist/lsp/client.js +174 -0
- package/dist/lsp/client.js.map +1 -0
- package/dist/main.js +160 -0
- package/dist/main.js.map +1 -1
- package/dist/mcp/DeferredMcpTool.d.ts +30 -0
- package/dist/mcp/DeferredMcpTool.d.ts.map +1 -0
- package/dist/mcp/DeferredMcpTool.js +75 -0
- package/dist/mcp/DeferredMcpTool.js.map +1 -0
- package/dist/mcp/client.d.ts +7 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +56 -8
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/loader.d.ts +9 -0
- package/dist/mcp/loader.d.ts.map +1 -1
- package/dist/mcp/loader.js +43 -2
- package/dist/mcp/loader.js.map +1 -1
- package/dist/mcp/schema.d.ts +7 -0
- package/dist/mcp/schema.d.ts.map +1 -0
- package/dist/mcp/schema.js +41 -0
- package/dist/mcp/schema.js.map +1 -0
- package/dist/mcp/server.d.ts +16 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +94 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/base.d.ts +15 -0
- package/dist/providers/base.d.ts.map +1 -1
- package/dist/providers/base.js +13 -1
- package/dist/providers/base.js.map +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js.map +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +91 -14
- package/dist/query.js.map +1 -1
- package/dist/remote/server.d.ts +31 -0
- package/dist/remote/server.d.ts.map +1 -0
- package/dist/remote/server.js +163 -0
- package/dist/remote/server.js.map +1 -0
- package/dist/services/AgentDispatcher.d.ts +42 -0
- package/dist/services/AgentDispatcher.d.ts.map +1 -0
- package/dist/services/AgentDispatcher.js +181 -0
- package/dist/services/AgentDispatcher.js.map +1 -0
- package/dist/services/StreamingToolExecutor.d.ts +2 -1
- package/dist/services/StreamingToolExecutor.d.ts.map +1 -1
- package/dist/services/StreamingToolExecutor.js +11 -2
- package/dist/services/StreamingToolExecutor.js.map +1 -1
- package/dist/tools/AgentTool/index.d.ts +3 -0
- package/dist/tools/AgentTool/index.d.ts.map +1 -1
- package/dist/tools/AgentTool/index.js +54 -16
- package/dist/tools/AgentTool/index.js.map +1 -1
- package/dist/tools/DiagnosticsTool/index.d.ts +21 -0
- package/dist/tools/DiagnosticsTool/index.d.ts.map +1 -0
- package/dist/tools/DiagnosticsTool/index.js +108 -0
- package/dist/tools/DiagnosticsTool/index.js.map +1 -0
- package/dist/tools/GrepTool/index.d.ts +2 -2
- package/dist/tools/ParallelAgentTool/index.d.ts +37 -0
- package/dist/tools/ParallelAgentTool/index.d.ts.map +1 -0
- package/dist/tools/ParallelAgentTool/index.js +53 -0
- package/dist/tools/ParallelAgentTool/index.js.map +1 -0
- package/dist/tools/SkillTool/index.d.ts.map +1 -1
- package/dist/tools/SkillTool/index.js +22 -27
- package/dist/tools/SkillTool/index.js.map +1 -1
- package/dist/tools/ToolSearchTool/index.d.ts +15 -0
- package/dist/tools/ToolSearchTool/index.d.ts.map +1 -0
- package/dist/tools/ToolSearchTool/index.js +50 -0
- package/dist/tools/ToolSearchTool/index.js.map +1 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +9 -0
- package/dist/tools.js.map +1 -1
- package/dist/types/message.d.ts +2 -0
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js +3 -0
- package/dist/types/message.js.map +1 -1
- package/dist/types/permissions.d.ts +2 -0
- package/dist/types/permissions.d.ts.map +1 -1
- package/dist/types/permissions.js +30 -0
- package/dist/types/permissions.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure credential storage — stores API keys encrypted on disk
|
|
3
|
+
* instead of plaintext in config.yaml.
|
|
4
|
+
*
|
|
5
|
+
* Uses AES-256-GCM with a key derived from machine identity.
|
|
6
|
+
* Not bulletproof (key derivation is deterministic from machine info),
|
|
7
|
+
* but far better than plaintext YAML.
|
|
8
|
+
*/
|
|
9
|
+
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { homedir, hostname, userInfo } from 'node:os';
|
|
13
|
+
const CRED_DIR = join(homedir(), '.oh');
|
|
14
|
+
const CRED_PATH = join(CRED_DIR, 'credentials.enc');
|
|
15
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
16
|
+
/** Derive an encryption key from machine identity */
|
|
17
|
+
function deriveKey() {
|
|
18
|
+
const identity = `${hostname()}-${userInfo().username}-openharness`;
|
|
19
|
+
return scryptSync(identity, 'oh-credential-salt', 32);
|
|
20
|
+
}
|
|
21
|
+
function encrypt(data) {
|
|
22
|
+
const key = deriveKey();
|
|
23
|
+
const iv = randomBytes(12);
|
|
24
|
+
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
25
|
+
const encrypted = Buffer.concat([cipher.update(data, 'utf-8'), cipher.final()]);
|
|
26
|
+
const tag = cipher.getAuthTag();
|
|
27
|
+
// Format: [iv (12)] [tag (16)] [encrypted data]
|
|
28
|
+
return Buffer.concat([iv, tag, encrypted]);
|
|
29
|
+
}
|
|
30
|
+
function decrypt(data) {
|
|
31
|
+
const key = deriveKey();
|
|
32
|
+
const iv = data.subarray(0, 12);
|
|
33
|
+
const tag = data.subarray(12, 28);
|
|
34
|
+
const encrypted = data.subarray(28);
|
|
35
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
36
|
+
decipher.setAuthTag(tag);
|
|
37
|
+
return decipher.update(encrypted, undefined, 'utf-8') + decipher.final('utf-8');
|
|
38
|
+
}
|
|
39
|
+
function loadStore() {
|
|
40
|
+
if (!existsSync(CRED_PATH))
|
|
41
|
+
return {};
|
|
42
|
+
try {
|
|
43
|
+
const raw = readFileSync(CRED_PATH);
|
|
44
|
+
const json = decrypt(raw);
|
|
45
|
+
return JSON.parse(json);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function saveStore(store) {
|
|
52
|
+
mkdirSync(CRED_DIR, { recursive: true });
|
|
53
|
+
const json = JSON.stringify(store);
|
|
54
|
+
writeFileSync(CRED_PATH, encrypt(json));
|
|
55
|
+
}
|
|
56
|
+
/** Get a stored credential by key (e.g., "anthropic-api-key") */
|
|
57
|
+
export function getCredential(key) {
|
|
58
|
+
return loadStore()[key];
|
|
59
|
+
}
|
|
60
|
+
/** Store a credential */
|
|
61
|
+
export function setCredential(key, value) {
|
|
62
|
+
const store = loadStore();
|
|
63
|
+
store[key] = value;
|
|
64
|
+
saveStore(store);
|
|
65
|
+
}
|
|
66
|
+
/** Delete a credential */
|
|
67
|
+
export function deleteCredential(key) {
|
|
68
|
+
const store = loadStore();
|
|
69
|
+
delete store[key];
|
|
70
|
+
saveStore(store);
|
|
71
|
+
}
|
|
72
|
+
/** List credential keys (not values) */
|
|
73
|
+
export function listCredentials() {
|
|
74
|
+
return Object.keys(loadStore());
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get API key for a provider, checking:
|
|
78
|
+
* 1. Environment variable (highest priority)
|
|
79
|
+
* 2. Encrypted credential store
|
|
80
|
+
* 3. Config file (legacy plaintext, with migration prompt)
|
|
81
|
+
*/
|
|
82
|
+
export function resolveApiKey(provider, configApiKey) {
|
|
83
|
+
// Environment variable names by provider
|
|
84
|
+
const envVarMap = {
|
|
85
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
86
|
+
openai: 'OPENAI_API_KEY',
|
|
87
|
+
openrouter: 'OPENROUTER_API_KEY',
|
|
88
|
+
};
|
|
89
|
+
const envVar = envVarMap[provider];
|
|
90
|
+
if (envVar && process.env[envVar])
|
|
91
|
+
return process.env[envVar];
|
|
92
|
+
// Encrypted store
|
|
93
|
+
const stored = getCredential(`${provider}-api-key`);
|
|
94
|
+
if (stored)
|
|
95
|
+
return stored;
|
|
96
|
+
// Legacy config (migrate on use)
|
|
97
|
+
if (configApiKey) {
|
|
98
|
+
// Auto-migrate to encrypted store
|
|
99
|
+
setCredential(`${provider}-api-key`, configApiKey);
|
|
100
|
+
return configApiKey;
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/harness/credentials.ts"],"names":[],"mappings":"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"}
|
package/dist/harness/hooks.d.ts
CHANGED
|
@@ -12,6 +12,14 @@ export type HookContext = {
|
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
14
|
* Emit a hook event. For preToolUse, returns false if any hook blocks the call.
|
|
15
|
+
*
|
|
16
|
+
* preToolUse hooks run synchronously (they must block before tool execution).
|
|
17
|
+
* All other hooks run asynchronously to avoid blocking the event loop.
|
|
15
18
|
*/
|
|
16
19
|
export declare function emitHook(event: HookEvent, ctx?: HookContext): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Async version of emitHook that waits for all hooks to complete.
|
|
22
|
+
* Useful for sessionEnd where you want to ensure hooks finish.
|
|
23
|
+
*/
|
|
24
|
+
export declare function emitHookAsync(event: HookEvent, ctx?: HookContext): Promise<boolean>;
|
|
17
25
|
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/harness/hooks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAC;AAErF,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/harness/hooks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAC;AAErF,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAqEF;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,GAAE,WAAgB,GAAG,OAAO,CA8BzE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,GAAE,WAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAe7F"}
|
package/dist/harness/hooks.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* preToolUse hooks can block tool execution (exit code 1 = block).
|
|
5
5
|
* All other hooks are fire-and-forget (errors are silently ignored).
|
|
6
6
|
*/
|
|
7
|
-
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
8
8
|
import { readOhConfig } from "./config.js";
|
|
9
9
|
let cachedHooks;
|
|
10
10
|
function getHooks() {
|
|
@@ -14,37 +14,113 @@ function getHooks() {
|
|
|
14
14
|
cachedHooks = cfg?.hooks ?? null;
|
|
15
15
|
return cachedHooks;
|
|
16
16
|
}
|
|
17
|
+
function buildEnv(event, ctx) {
|
|
18
|
+
const env = {
|
|
19
|
+
...process.env,
|
|
20
|
+
OH_EVENT: event,
|
|
21
|
+
};
|
|
22
|
+
if (ctx.toolName)
|
|
23
|
+
env.OH_TOOL_NAME = ctx.toolName;
|
|
24
|
+
if (ctx.toolArgs)
|
|
25
|
+
env.OH_TOOL_ARGS = ctx.toolArgs;
|
|
26
|
+
if (ctx.toolOutput)
|
|
27
|
+
env.OH_TOOL_OUTPUT = ctx.toolOutput;
|
|
28
|
+
return env;
|
|
29
|
+
}
|
|
30
|
+
function matchesHook(def, ctx) {
|
|
31
|
+
if (def.match && ctx.toolName && !ctx.toolName.includes(def.match)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Run a single hook command asynchronously.
|
|
38
|
+
* Returns a promise that resolves with the exit code (0 = success).
|
|
39
|
+
*/
|
|
40
|
+
function runHookAsync(command, env, timeoutMs = 10_000) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const proc = spawn(command, {
|
|
43
|
+
shell: true,
|
|
44
|
+
timeout: timeoutMs,
|
|
45
|
+
stdio: "pipe",
|
|
46
|
+
env,
|
|
47
|
+
});
|
|
48
|
+
let settled = false;
|
|
49
|
+
const timer = setTimeout(() => {
|
|
50
|
+
if (!settled) {
|
|
51
|
+
settled = true;
|
|
52
|
+
proc.kill();
|
|
53
|
+
resolve(1); // timeout = failure
|
|
54
|
+
}
|
|
55
|
+
}, timeoutMs);
|
|
56
|
+
proc.on("exit", (code) => {
|
|
57
|
+
if (!settled) {
|
|
58
|
+
settled = true;
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
resolve(code ?? 1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
proc.on("error", () => {
|
|
64
|
+
if (!settled) {
|
|
65
|
+
settled = true;
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
resolve(1);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
17
72
|
/**
|
|
18
73
|
* Emit a hook event. For preToolUse, returns false if any hook blocks the call.
|
|
74
|
+
*
|
|
75
|
+
* preToolUse hooks run synchronously (they must block before tool execution).
|
|
76
|
+
* All other hooks run asynchronously to avoid blocking the event loop.
|
|
19
77
|
*/
|
|
20
78
|
export function emitHook(event, ctx = {}) {
|
|
21
79
|
const hooks = getHooks();
|
|
22
80
|
if (!hooks)
|
|
23
81
|
return true;
|
|
24
82
|
const defs = hooks[event] ?? [];
|
|
83
|
+
const env = buildEnv(event, ctx);
|
|
84
|
+
if (event === "preToolUse") {
|
|
85
|
+
// preToolUse must be synchronous — it gates tool execution
|
|
86
|
+
for (const def of defs) {
|
|
87
|
+
if (!matchesHook(def, ctx))
|
|
88
|
+
continue;
|
|
89
|
+
const result = spawnSync(def.command, {
|
|
90
|
+
shell: true,
|
|
91
|
+
timeout: 10_000,
|
|
92
|
+
stdio: "pipe",
|
|
93
|
+
env,
|
|
94
|
+
});
|
|
95
|
+
if (result.status !== 0 || result.error) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
// All other hooks run asynchronously (fire-and-forget)
|
|
25
102
|
for (const def of defs) {
|
|
26
|
-
|
|
27
|
-
if (def.match && ctx.toolName && !ctx.toolName.includes(def.match)) {
|
|
103
|
+
if (!matchesHook(def, ctx))
|
|
28
104
|
continue;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (event === "preToolUse" &&
|
|
105
|
+
runHookAsync(def.command, env).catch(() => { });
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Async version of emitHook that waits for all hooks to complete.
|
|
111
|
+
* Useful for sessionEnd where you want to ensure hooks finish.
|
|
112
|
+
*/
|
|
113
|
+
export async function emitHookAsync(event, ctx = {}) {
|
|
114
|
+
const hooks = getHooks();
|
|
115
|
+
if (!hooks)
|
|
116
|
+
return true;
|
|
117
|
+
const defs = hooks[event] ?? [];
|
|
118
|
+
const env = buildEnv(event, ctx);
|
|
119
|
+
for (const def of defs) {
|
|
120
|
+
if (!matchesHook(def, ctx))
|
|
121
|
+
continue;
|
|
122
|
+
const code = await runHookAsync(def.command, env);
|
|
123
|
+
if (event === "preToolUse" && code !== 0) {
|
|
48
124
|
return false;
|
|
49
125
|
}
|
|
50
126
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/harness/hooks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/harness/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keybinding customization — load user-defined keybindings from ~/.oh/keybindings.json.
|
|
3
|
+
*
|
|
4
|
+
* Format:
|
|
5
|
+
* [
|
|
6
|
+
* { "key": "ctrl+s", "action": "/commit" },
|
|
7
|
+
* { "key": "ctrl+k ctrl+d", "action": "/diff" },
|
|
8
|
+
* { "key": "ctrl+p", "action": "/compact" }
|
|
9
|
+
* ]
|
|
10
|
+
*
|
|
11
|
+
* Supports single keys and two-key chord sequences (e.g., "ctrl+k ctrl+d").
|
|
12
|
+
*/
|
|
13
|
+
export type Keybinding = {
|
|
14
|
+
key: string;
|
|
15
|
+
action: string;
|
|
16
|
+
};
|
|
17
|
+
/** Load keybindings from ~/.oh/keybindings.json */
|
|
18
|
+
export declare function loadKeybindings(): Keybinding[];
|
|
19
|
+
/**
|
|
20
|
+
* Keybinding matcher — handles single keys and chord sequences.
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* const matcher = createKeybindingMatcher();
|
|
24
|
+
* // In useInput callback:
|
|
25
|
+
* const action = matcher.match(input, key);
|
|
26
|
+
* if (action) handleAction(action);
|
|
27
|
+
*/
|
|
28
|
+
export declare function createKeybindingMatcher(): {
|
|
29
|
+
match(input: string, inkKey: {
|
|
30
|
+
ctrl: boolean;
|
|
31
|
+
meta: boolean;
|
|
32
|
+
shift: boolean;
|
|
33
|
+
}): string | null;
|
|
34
|
+
/** Get all keybinding descriptions for help display */
|
|
35
|
+
getBindings(): Keybinding[];
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=keybindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keybindings.d.ts","sourceRoot":"","sources":["../../src/harness/keybindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAaF,mDAAmD;AACnD,wBAAgB,eAAe,IAAI,UAAU,EAAE,CAmB9C;AAkCD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB;iBAO1B,MAAM,UACL;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GACvD,MAAM,GAAG,IAAI;IA6ChB,uDAAuD;mBACxC,UAAU,EAAE;EAI9B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keybinding customization — load user-defined keybindings from ~/.oh/keybindings.json.
|
|
3
|
+
*
|
|
4
|
+
* Format:
|
|
5
|
+
* [
|
|
6
|
+
* { "key": "ctrl+s", "action": "/commit" },
|
|
7
|
+
* { "key": "ctrl+k ctrl+d", "action": "/diff" },
|
|
8
|
+
* { "key": "ctrl+p", "action": "/compact" }
|
|
9
|
+
* ]
|
|
10
|
+
*
|
|
11
|
+
* Supports single keys and two-key chord sequences (e.g., "ctrl+k ctrl+d").
|
|
12
|
+
*/
|
|
13
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { homedir } from 'node:os';
|
|
16
|
+
const KEYBINDINGS_PATH = join(homedir(), '.oh', 'keybindings.json');
|
|
17
|
+
let cachedBindings = null;
|
|
18
|
+
/** Load keybindings from ~/.oh/keybindings.json */
|
|
19
|
+
export function loadKeybindings() {
|
|
20
|
+
if (cachedBindings !== null)
|
|
21
|
+
return cachedBindings;
|
|
22
|
+
if (!existsSync(KEYBINDINGS_PATH)) {
|
|
23
|
+
cachedBindings = defaultKeybindings();
|
|
24
|
+
return cachedBindings;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const raw = readFileSync(KEYBINDINGS_PATH, 'utf-8');
|
|
28
|
+
const parsed = JSON.parse(raw);
|
|
29
|
+
if (Array.isArray(parsed)) {
|
|
30
|
+
cachedBindings = parsed;
|
|
31
|
+
return cachedBindings;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch { /* ignore parse errors */ }
|
|
35
|
+
cachedBindings = defaultKeybindings();
|
|
36
|
+
return cachedBindings;
|
|
37
|
+
}
|
|
38
|
+
/** Default keybindings */
|
|
39
|
+
function defaultKeybindings() {
|
|
40
|
+
return [
|
|
41
|
+
{ key: 'ctrl+d', action: '/diff' },
|
|
42
|
+
{ key: 'ctrl+l', action: '/clear' },
|
|
43
|
+
{ key: 'ctrl+u', action: '/undo' },
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
/** Parse a key string like "ctrl+s" into components */
|
|
47
|
+
function parseKeyString(keyStr) {
|
|
48
|
+
const parts = keyStr.toLowerCase().split('+');
|
|
49
|
+
return {
|
|
50
|
+
ctrl: parts.includes('ctrl'),
|
|
51
|
+
alt: parts.includes('alt'),
|
|
52
|
+
shift: parts.includes('shift'),
|
|
53
|
+
key: parts.filter(p => p !== 'ctrl' && p !== 'alt' && p !== 'shift')[0] ?? '',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Check if an Ink key event matches a parsed key */
|
|
57
|
+
function keyMatches(parsed, input, inkKey) {
|
|
58
|
+
if (parsed.ctrl !== inkKey.ctrl)
|
|
59
|
+
return false;
|
|
60
|
+
if (parsed.alt !== inkKey.meta)
|
|
61
|
+
return false;
|
|
62
|
+
if (parsed.shift !== inkKey.shift)
|
|
63
|
+
return false;
|
|
64
|
+
return input.toLowerCase() === parsed.key;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Keybinding matcher — handles single keys and chord sequences.
|
|
68
|
+
*
|
|
69
|
+
* Usage:
|
|
70
|
+
* const matcher = createKeybindingMatcher();
|
|
71
|
+
* // In useInput callback:
|
|
72
|
+
* const action = matcher.match(input, key);
|
|
73
|
+
* if (action) handleAction(action);
|
|
74
|
+
*/
|
|
75
|
+
export function createKeybindingMatcher() {
|
|
76
|
+
const bindings = loadKeybindings();
|
|
77
|
+
let pendingChord = null;
|
|
78
|
+
let pendingTimeout = null;
|
|
79
|
+
return {
|
|
80
|
+
match(input, inkKey) {
|
|
81
|
+
// Parse all bindings
|
|
82
|
+
for (const binding of bindings) {
|
|
83
|
+
const parts = binding.key.split(/\s+/);
|
|
84
|
+
if (parts.length === 1) {
|
|
85
|
+
// Single key binding
|
|
86
|
+
const parsed = parseKeyString(parts[0]);
|
|
87
|
+
if (keyMatches(parsed, input, inkKey)) {
|
|
88
|
+
pendingChord = null;
|
|
89
|
+
return binding.action;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (parts.length === 2) {
|
|
93
|
+
// Chord sequence
|
|
94
|
+
const first = parseKeyString(parts[0]);
|
|
95
|
+
const second = parseKeyString(parts[1]);
|
|
96
|
+
if (pendingChord && keyMatches(second, input, inkKey)) {
|
|
97
|
+
// Second key of chord matches
|
|
98
|
+
pendingChord = null;
|
|
99
|
+
if (pendingTimeout) {
|
|
100
|
+
clearTimeout(pendingTimeout);
|
|
101
|
+
pendingTimeout = null;
|
|
102
|
+
}
|
|
103
|
+
return binding.action;
|
|
104
|
+
}
|
|
105
|
+
if (keyMatches(first, input, inkKey)) {
|
|
106
|
+
// First key of chord matches — wait for second
|
|
107
|
+
pendingChord = first;
|
|
108
|
+
if (pendingTimeout)
|
|
109
|
+
clearTimeout(pendingTimeout);
|
|
110
|
+
pendingTimeout = setTimeout(() => {
|
|
111
|
+
pendingChord = null;
|
|
112
|
+
pendingTimeout = null;
|
|
113
|
+
}, 1000); // 1s chord timeout
|
|
114
|
+
return null; // consumed but no action yet
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// No match — clear pending chord
|
|
119
|
+
if (pendingChord) {
|
|
120
|
+
pendingChord = null;
|
|
121
|
+
if (pendingTimeout) {
|
|
122
|
+
clearTimeout(pendingTimeout);
|
|
123
|
+
pendingTimeout = null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
},
|
|
128
|
+
/** Get all keybinding descriptions for help display */
|
|
129
|
+
getBindings() {
|
|
130
|
+
return bindings;
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=keybindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keybindings.js","sourceRoot":"","sources":["../../src/harness/keybindings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-memory system — saves learnings across sessions.
|
|
3
|
+
*
|
|
4
|
+
* Memories are stored as markdown files in .oh/memory/ (project-level)
|
|
5
|
+
* or ~/.oh/memory/ (global). Each has YAML frontmatter with name, type, description.
|
|
6
|
+
*
|
|
7
|
+
* The system detects learnable patterns from assistant responses and saves them
|
|
8
|
+
* without user intervention.
|
|
9
|
+
*/
|
|
10
|
+
import type { Message } from '../types/message.js';
|
|
11
|
+
import type { Provider } from '../providers/base.js';
|
|
12
|
+
export type MemoryEntry = {
|
|
13
|
+
name: string;
|
|
14
|
+
type: 'convention' | 'preference' | 'project' | 'debugging';
|
|
15
|
+
description: string;
|
|
16
|
+
content: string;
|
|
17
|
+
filePath: string;
|
|
18
|
+
};
|
|
19
|
+
/** Load all memories from project and global dirs */
|
|
20
|
+
export declare function loadMemories(): MemoryEntry[];
|
|
21
|
+
/** Build a system prompt section from loaded memories */
|
|
22
|
+
export declare function memoriesToPrompt(memories: MemoryEntry[]): string;
|
|
23
|
+
/** Save a memory entry to the project memory directory */
|
|
24
|
+
export declare function saveMemory(name: string, type: MemoryEntry['type'], description: string, content: string, global?: boolean): string;
|
|
25
|
+
/**
|
|
26
|
+
* Detect if recent assistant messages contain learnable patterns.
|
|
27
|
+
* Returns structured memories to save, or empty array.
|
|
28
|
+
*/
|
|
29
|
+
export declare function detectMemories(provider: Provider, recentMessages: Message[], model?: string): Promise<Array<{
|
|
30
|
+
name: string;
|
|
31
|
+
type: MemoryEntry['type'];
|
|
32
|
+
description: string;
|
|
33
|
+
content: string;
|
|
34
|
+
}>>;
|
|
35
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/harness/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAMrD,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,CAAC;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,qDAAqD;AACrD,wBAAgB,YAAY,IAAI,WAAW,EAAE,CAgB5C;AAsBD,yDAAyD;AACzD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CAMhE;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EACzB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,MAAM,UAAQ,GACb,MAAM,CAkBR;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,OAAO,EAAE,EACzB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CA0CnG"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-memory system — saves learnings across sessions.
|
|
3
|
+
*
|
|
4
|
+
* Memories are stored as markdown files in .oh/memory/ (project-level)
|
|
5
|
+
* or ~/.oh/memory/ (global). Each has YAML frontmatter with name, type, description.
|
|
6
|
+
*
|
|
7
|
+
* The system detects learnable patterns from assistant responses and saves them
|
|
8
|
+
* without user intervention.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync, existsSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { createUserMessage } from '../types/message.js';
|
|
14
|
+
const PROJECT_MEMORY_DIR = join('.oh', 'memory');
|
|
15
|
+
const GLOBAL_MEMORY_DIR = join(homedir(), '.oh', 'memory');
|
|
16
|
+
/** Load all memories from project and global dirs */
|
|
17
|
+
export function loadMemories() {
|
|
18
|
+
const entries = [];
|
|
19
|
+
for (const dir of [PROJECT_MEMORY_DIR, GLOBAL_MEMORY_DIR]) {
|
|
20
|
+
if (!existsSync(dir))
|
|
21
|
+
continue;
|
|
22
|
+
for (const file of readdirSync(dir).filter(f => f.endsWith('.md'))) {
|
|
23
|
+
try {
|
|
24
|
+
const filePath = join(dir, file);
|
|
25
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
26
|
+
const entry = parseMemory(raw, filePath);
|
|
27
|
+
if (entry)
|
|
28
|
+
entries.push(entry);
|
|
29
|
+
}
|
|
30
|
+
catch { /* skip */ }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return entries;
|
|
34
|
+
}
|
|
35
|
+
/** Parse a memory markdown file */
|
|
36
|
+
function parseMemory(raw, filePath) {
|
|
37
|
+
const nameMatch = raw.match(/^name:\s*(.+)$/m);
|
|
38
|
+
const typeMatch = raw.match(/^type:\s*(.+)$/m);
|
|
39
|
+
const descMatch = raw.match(/^description:\s*(.+)$/m);
|
|
40
|
+
if (!nameMatch)
|
|
41
|
+
return null;
|
|
42
|
+
// Content is everything after the frontmatter closing ---
|
|
43
|
+
const fmEnd = raw.indexOf('---', raw.indexOf('---') + 3);
|
|
44
|
+
const content = fmEnd > 0 ? raw.slice(fmEnd + 3).trim() : '';
|
|
45
|
+
return {
|
|
46
|
+
name: nameMatch[1].trim(),
|
|
47
|
+
type: (typeMatch?.[1]?.trim() ?? 'convention'),
|
|
48
|
+
description: descMatch?.[1]?.trim() ?? '',
|
|
49
|
+
content,
|
|
50
|
+
filePath,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Build a system prompt section from loaded memories */
|
|
54
|
+
export function memoriesToPrompt(memories) {
|
|
55
|
+
if (memories.length === 0)
|
|
56
|
+
return '';
|
|
57
|
+
const lines = memories.map(m => `- **${m.name}** (${m.type}): ${m.content.slice(0, 200)}`);
|
|
58
|
+
return `# Remembered Context\n${lines.join('\n')}`;
|
|
59
|
+
}
|
|
60
|
+
/** Save a memory entry to the project memory directory */
|
|
61
|
+
export function saveMemory(name, type, description, content, global = false) {
|
|
62
|
+
const dir = global ? GLOBAL_MEMORY_DIR : PROJECT_MEMORY_DIR;
|
|
63
|
+
mkdirSync(dir, { recursive: true });
|
|
64
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 50);
|
|
65
|
+
const filePath = join(dir, `${slug}.md`);
|
|
66
|
+
const md = `---
|
|
67
|
+
name: ${name}
|
|
68
|
+
type: ${type}
|
|
69
|
+
description: ${description}
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
${content}
|
|
73
|
+
`;
|
|
74
|
+
writeFileSync(filePath, md);
|
|
75
|
+
return filePath;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Detect if recent assistant messages contain learnable patterns.
|
|
79
|
+
* Returns structured memories to save, or empty array.
|
|
80
|
+
*/
|
|
81
|
+
export async function detectMemories(provider, recentMessages, model) {
|
|
82
|
+
// Only analyze if there are enough messages
|
|
83
|
+
if (recentMessages.length < 4)
|
|
84
|
+
return [];
|
|
85
|
+
// Extract assistant messages from recent turns
|
|
86
|
+
const assistantMsgs = recentMessages
|
|
87
|
+
.filter(m => m.role === 'assistant' && m.content.length > 50)
|
|
88
|
+
.slice(-3);
|
|
89
|
+
if (assistantMsgs.length === 0)
|
|
90
|
+
return [];
|
|
91
|
+
const contextText = assistantMsgs
|
|
92
|
+
.map(m => m.content.slice(0, 500))
|
|
93
|
+
.join('\n---\n');
|
|
94
|
+
const prompt = `Analyze this conversation snippet. If there are reusable learnings (coding conventions, project patterns, user preferences, debugging insights), extract them. Respond ONLY with a JSON array of objects with {name, type, description, content} or [] if nothing worth remembering.
|
|
95
|
+
|
|
96
|
+
Types: "convention" (code style/patterns), "preference" (user likes/dislikes), "project" (architecture/decisions), "debugging" (solutions to problems)
|
|
97
|
+
|
|
98
|
+
Keep each memory concise (1-2 sentences). Only extract non-obvious learnings.
|
|
99
|
+
|
|
100
|
+
${contextText}`;
|
|
101
|
+
try {
|
|
102
|
+
const response = await provider.complete([createUserMessage(prompt)], 'You are a memory extraction system. Respond ONLY with valid JSON.', undefined, model);
|
|
103
|
+
const jsonMatch = response.content.match(/\[[\s\S]*\]/);
|
|
104
|
+
if (!jsonMatch)
|
|
105
|
+
return [];
|
|
106
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
107
|
+
if (!Array.isArray(parsed))
|
|
108
|
+
return [];
|
|
109
|
+
return parsed.filter((m) => m.name && m.type && m.content && typeof m.content === 'string');
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/harness/memory.ts"],"names":[],"mappings":"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"}
|