visionclaw 0.1.191-beta.2 → 0.1.191-beta.4
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/agent/engines/claude/cli-resolver.d.ts +16 -0
- package/dist/agent/engines/claude/cli-resolver.d.ts.map +1 -0
- package/dist/agent/engines/claude/cli-resolver.js +83 -0
- package/dist/agent/engines/claude/cli-resolver.js.map +1 -0
- package/dist/agent/engines/claude/session-browser-policy.d.ts +9 -0
- package/dist/agent/engines/claude/session-browser-policy.d.ts.map +1 -0
- package/dist/agent/engines/claude/session-browser-policy.js +49 -0
- package/dist/agent/engines/claude/session-browser-policy.js.map +1 -0
- package/dist/agent/engines/claude/session.d.ts +275 -0
- package/dist/agent/engines/claude/session.d.ts.map +1 -0
- package/dist/agent/engines/claude/session.js +1140 -0
- package/dist/agent/engines/claude/session.js.map +1 -0
- package/dist/agent/engines/client-factory.d.ts +58 -0
- package/dist/agent/engines/client-factory.d.ts.map +1 -0
- package/dist/agent/engines/client-factory.js +376 -0
- package/dist/agent/engines/client-factory.js.map +1 -0
- package/dist/agent/engines/engine.d.ts +8 -0
- package/dist/agent/engines/engine.d.ts.map +1 -0
- package/dist/agent/engines/engine.js +15 -0
- package/dist/agent/engines/engine.js.map +1 -0
- package/dist/agent/engines/openai/file-session.d.ts +49 -0
- package/dist/agent/engines/openai/file-session.d.ts.map +1 -0
- package/dist/agent/engines/openai/file-session.js +108 -0
- package/dist/agent/engines/openai/file-session.js.map +1 -0
- package/dist/agent/engines/openai/file-tools.d.ts +35 -0
- package/dist/agent/engines/openai/file-tools.d.ts.map +1 -0
- package/dist/agent/engines/openai/file-tools.js +194 -0
- package/dist/agent/engines/openai/file-tools.js.map +1 -0
- package/dist/agent/engines/openai/session.d.ts +160 -0
- package/dist/agent/engines/openai/session.d.ts.map +1 -0
- package/dist/agent/engines/openai/session.js +799 -0
- package/dist/agent/engines/openai/session.js.map +1 -0
- package/dist/agent/engines/openai/tools.d.ts +13 -0
- package/dist/agent/engines/openai/tools.d.ts.map +1 -0
- package/dist/agent/engines/openai/tools.js +248 -0
- package/dist/agent/engines/openai/tools.js.map +1 -0
- package/dist/agent/engines/session-types.d.ts +130 -0
- package/dist/agent/engines/session-types.d.ts.map +1 -0
- package/dist/agent/engines/session-types.js +2 -0
- package/dist/agent/engines/session-types.js.map +1 -0
- package/dist/agent/engines/system-prompt-log.d.ts +9 -0
- package/dist/agent/engines/system-prompt-log.d.ts.map +1 -0
- package/dist/agent/engines/system-prompt-log.js +46 -0
- package/dist/agent/engines/system-prompt-log.js.map +1 -0
- package/dist/agent/heartbeat-manager.d.ts +4 -0
- package/dist/agent/heartbeat-manager.d.ts.map +1 -1
- package/dist/agent/heartbeat-manager.js +27 -0
- package/dist/agent/heartbeat-manager.js.map +1 -1
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +19 -17
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/provider-fallback.d.ts +97 -0
- package/dist/agent/provider-fallback.d.ts.map +1 -0
- package/dist/agent/provider-fallback.js +223 -0
- package/dist/agent/provider-fallback.js.map +1 -0
- package/dist/agent/providers/claude/session.d.ts +16 -1
- package/dist/agent/providers/claude/session.d.ts.map +1 -1
- package/dist/agent/providers/claude/session.js +35 -2
- package/dist/agent/providers/claude/session.js.map +1 -1
- package/dist/agent/providers/client-factory.d.ts.map +1 -1
- package/dist/agent/providers/client-factory.js +7 -5
- package/dist/agent/providers/client-factory.js.map +1 -1
- package/dist/agent/providers/openai/session.d.ts +1 -0
- package/dist/agent/providers/openai/session.d.ts.map +1 -1
- package/dist/agent/providers/openai/session.js +3 -0
- package/dist/agent/providers/openai/session.js.map +1 -1
- package/dist/agent/providers/session-types.d.ts +8 -0
- package/dist/agent/providers/session-types.d.ts.map +1 -1
- package/dist/agent/session-manager.d.ts +8 -0
- package/dist/agent/session-manager.d.ts.map +1 -1
- package/dist/agent/session-manager.js +11 -0
- package/dist/agent/session-manager.js.map +1 -1
- package/dist/agent/wake-cycle-runner.d.ts +8 -0
- package/dist/agent/wake-cycle-runner.d.ts.map +1 -1
- package/dist/agent/wake-cycle-runner.js +59 -6
- package/dist/agent/wake-cycle-runner.js.map +1 -1
- package/dist/i18n/messages.d.ts +8 -0
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/i18n/messages.js +9 -0
- package/dist/i18n/messages.js.map +1 -1
- package/dist/onboarding/onboarding-service-client.d.ts +23 -0
- package/dist/onboarding/onboarding-service-client.d.ts.map +1 -1
- package/dist/onboarding/onboarding-service-client.js.map +1 -1
- package/dist/openai-credentials.d.ts +31 -0
- package/dist/openai-credentials.d.ts.map +1 -0
- package/dist/openai-credentials.js +38 -0
- package/dist/openai-credentials.js.map +1 -0
- package/dist/skills/installed.d.ts +11 -0
- package/dist/skills/installed.d.ts.map +1 -0
- package/dist/skills/installed.js +35 -0
- package/dist/skills/installed.js.map +1 -0
- package/dist-agent/bundle.cjs +19179 -21809
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type ClaudeCliResolution = {
|
|
2
|
+
kind: "native";
|
|
3
|
+
path: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: "legacy-cli-js";
|
|
6
|
+
path: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "sdk-default";
|
|
9
|
+
};
|
|
10
|
+
export declare function resolveClaudeCliExecutable(): ClaudeCliResolution;
|
|
11
|
+
/** Convenience wrapper — return just the path, or `undefined` when we should
|
|
12
|
+
* leave the option unset and let the SDK do its own lookup. */
|
|
13
|
+
export declare function getClaudeExecutablePath(): string | undefined;
|
|
14
|
+
/** For tests. */
|
|
15
|
+
export declare function _resetClaudeCliResolverCache(): void;
|
|
16
|
+
//# sourceMappingURL=cli-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-resolver.d.ts","sourceRoot":"","sources":["../../../../src/agent/engines/claude/cli-resolver.ts"],"names":[],"mappings":"AA8BA,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAQ5B,wBAAgB,0BAA0B,IAAI,mBAAmB,CAIhE;AAED;gEACgE;AAChE,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,SAAS,CAG5D;AAsCD,iBAAiB;AACjB,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the executable path that `@anthropic-ai/claude-agent-sdk` should
|
|
3
|
+
* spawn as its "Claude Code" subprocess.
|
|
4
|
+
*
|
|
5
|
+
* Why we need this:
|
|
6
|
+
* - In 0.2.108–0.2.112 the SDK shipped a `cli.js` alongside `sdk.mjs`, and
|
|
7
|
+
* the intended way to pin it from a bundled app was to pass
|
|
8
|
+
* `pathToClaudeCodeExecutable` pointing at that file.
|
|
9
|
+
* - Starting with 0.2.113 (published 2026-04-14) the SDK tarball dropped
|
|
10
|
+
* `cli.js` entirely and switched to a platform-specific native binary
|
|
11
|
+
* shipped via an optional peer package
|
|
12
|
+
* `@anthropic-ai/claude-agent-sdk-<platform>-<arch>`.
|
|
13
|
+
* - Our previous implementation hard-coded `cli.js`, so a stock
|
|
14
|
+
* `pnpm install` that pulled the newer SDK caused the child process to
|
|
15
|
+
* fail with `MODULE_NOT_FOUND`, which in turn surfaced as an async EPIPE
|
|
16
|
+
* in the parent.
|
|
17
|
+
*
|
|
18
|
+
* Resolution order:
|
|
19
|
+
* 1. The native binary from `claude-agent-sdk-<platform>-<arch>/claude`
|
|
20
|
+
* (the forward-compatible choice for 0.2.113+).
|
|
21
|
+
* 2. A legacy `cli.js` next to `sdk.mjs` (still valid for 0.2.112 and older).
|
|
22
|
+
* 3. `null`, meaning: let the SDK resolve the executable itself. Recent SDK
|
|
23
|
+
* versions implement the same fallback chain internally and throw a
|
|
24
|
+
* clear `ReferenceError` if nothing is found, which is much easier to
|
|
25
|
+
* diagnose than a silent EPIPE.
|
|
26
|
+
*/
|
|
27
|
+
import { createRequire } from "module";
|
|
28
|
+
import fs from "fs";
|
|
29
|
+
import path from "path";
|
|
30
|
+
/**
|
|
31
|
+
* Resolve once per process — filesystem lookups here are cheap but the result
|
|
32
|
+
* never changes for a given installation.
|
|
33
|
+
*/
|
|
34
|
+
let cached;
|
|
35
|
+
export function resolveClaudeCliExecutable() {
|
|
36
|
+
if (cached)
|
|
37
|
+
return cached;
|
|
38
|
+
cached = doResolve();
|
|
39
|
+
return cached;
|
|
40
|
+
}
|
|
41
|
+
/** Convenience wrapper — return just the path, or `undefined` when we should
|
|
42
|
+
* leave the option unset and let the SDK do its own lookup. */
|
|
43
|
+
export function getClaudeExecutablePath() {
|
|
44
|
+
const r = resolveClaudeCliExecutable();
|
|
45
|
+
return r.kind === "sdk-default" ? undefined : r.path;
|
|
46
|
+
}
|
|
47
|
+
function doResolve() {
|
|
48
|
+
const _require = createRequire(import.meta.url);
|
|
49
|
+
// 1. Modern native binary (claude-agent-sdk >= 0.2.113).
|
|
50
|
+
const nativePkg = `@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}`;
|
|
51
|
+
try {
|
|
52
|
+
const pkgJsonPath = _require.resolve(`${nativePkg}/package.json`);
|
|
53
|
+
const pkgDir = path.dirname(pkgJsonPath);
|
|
54
|
+
// The package ships a single executable named `claude`.
|
|
55
|
+
const candidate = path.join(pkgDir, "claude");
|
|
56
|
+
if (fs.existsSync(candidate)) {
|
|
57
|
+
return { kind: "native", path: candidate };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Optional dep not installed — fall through to legacy cli.js.
|
|
62
|
+
}
|
|
63
|
+
// 2. Legacy JS entry (claude-agent-sdk <= 0.2.112).
|
|
64
|
+
try {
|
|
65
|
+
const sdkDir = path.dirname(_require.resolve("@anthropic-ai/claude-agent-sdk"));
|
|
66
|
+
const candidate = path.join(sdkDir, "cli.js");
|
|
67
|
+
if (fs.existsSync(candidate)) {
|
|
68
|
+
return { kind: "legacy-cli-js", path: candidate };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// No SDK at all — let the caller's catch handle this.
|
|
73
|
+
}
|
|
74
|
+
// 3. Give up and let the SDK resolve on its own. Newer versions still have
|
|
75
|
+
// a reasonable internal lookup and will throw a clear error if nothing
|
|
76
|
+
// works, rather than silently spawning a missing module.
|
|
77
|
+
return { kind: "sdk-default" };
|
|
78
|
+
}
|
|
79
|
+
/** For tests. */
|
|
80
|
+
export function _resetClaudeCliResolverCache() {
|
|
81
|
+
cached = undefined;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=cli-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-resolver.js","sourceRoot":"","sources":["../../../../src/agent/engines/claude/cli-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAOxB;;;GAGG;AACH,IAAI,MAAuC,CAAC;AAE5C,MAAM,UAAU,0BAA0B;IACxC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,GAAG,SAAS,EAAE,CAAC;IACrB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;gEACgE;AAChE,MAAM,UAAU,uBAAuB;IACrC,MAAM,CAAC,GAAG,0BAA0B,EAAE,CAAC;IACvC,OAAO,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvD,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEhD,yDAAyD;IACzD,MAAM,SAAS,GAAG,kCAAkC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IACvF,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,SAAS,eAAe,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACzC,wDAAwD;QACxD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9C,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC7C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;IAED,oDAAoD;IACpD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CACzB,QAAQ,CAAC,OAAO,CAAC,gCAAgC,CAAC,CACnD,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9C,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QACpD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sDAAsD;IACxD,CAAC;IAED,2EAA2E;IAC3E,0EAA0E;IAC1E,4DAA4D;IAC5D,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;AACjC,CAAC;AAED,iBAAiB;AACjB,MAAM,UAAU,4BAA4B;IAC1C,MAAM,GAAG,SAAS,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure Chrome is running (Playwriter mode). On the first Playwriter tool call
|
|
3
|
+
* of each turn we force a clean restart to reset stale extension connections;
|
|
4
|
+
* later calls in the same turn only ensure Chrome is up.
|
|
5
|
+
*
|
|
6
|
+
* Returns true when a connection reset was performed for this turn.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ensurePlaywriterChrome(didResetConnectionsThisTurn: boolean): boolean;
|
|
9
|
+
//# sourceMappingURL=session-browser-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-browser-policy.d.ts","sourceRoot":"","sources":["../../../../src/agent/engines/claude/session-browser-policy.ts"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,2BAA2B,EAAE,OAAO,GACnC,OAAO,CAmBT"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { logger } from "../../../logger.js";
|
|
3
|
+
/**
|
|
4
|
+
* Force Chrome to restart once per turn before the first Playwriter tool call.
|
|
5
|
+
* This clears stale extension relay connections from earlier sessions so the
|
|
6
|
+
* Playwriter MCP sees a single fresh extension connection.
|
|
7
|
+
*/
|
|
8
|
+
function restartChromeForPlaywriter() {
|
|
9
|
+
try {
|
|
10
|
+
execSync("pkill -x 'Google Chrome'", { stdio: "ignore" });
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// pkill exits non-zero when Chrome is not running — expected.
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
execSync('open -a "Google Chrome"', { stdio: "ignore" });
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
logger.warn(`Failed to restart Chrome for Playwriter: ${err instanceof Error ? err.message : String(err)}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Ensure Chrome is running (Playwriter mode). On the first Playwriter tool call
|
|
24
|
+
* of each turn we force a clean restart to reset stale extension connections;
|
|
25
|
+
* later calls in the same turn only ensure Chrome is up.
|
|
26
|
+
*
|
|
27
|
+
* Returns true when a connection reset was performed for this turn.
|
|
28
|
+
*/
|
|
29
|
+
export function ensurePlaywriterChrome(didResetConnectionsThisTurn) {
|
|
30
|
+
if (!didResetConnectionsThisTurn) {
|
|
31
|
+
logger.system("Resetting Chrome before Playwriter session");
|
|
32
|
+
restartChromeForPlaywriter();
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
execSync("pgrep -x 'Google Chrome'", { stdio: "ignore" });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
logger.system("Chrome not running — launching for Playwriter extension");
|
|
40
|
+
try {
|
|
41
|
+
execSync('open -a "Google Chrome"', { stdio: "ignore" });
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
logger.warn(`Failed to launch Chrome: ${err instanceof Error ? err.message : String(err)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=session-browser-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-browser-policy.js","sourceRoot":"","sources":["../../../../src/agent/engines/claude/session-browser-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C;;;;GAIG;AACH,SAAS,0BAA0B;IACjC,IAAI,CAAC;QACH,QAAQ,CAAC,0BAA0B,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;IAED,IAAI,CAAC;QACH,QAAQ,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,4CAA4C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,2BAAoC;IAEpC,IAAI,CAAC,2BAA2B,EAAE,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,4CAA4C,CAAC,CAAC;QAC5D,0BAA0B,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,QAAQ,CAAC,0BAA0B,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,MAAM,CAAC,yDAAyD,CAAC,CAAC;QACzE,IAAI,CAAC;YACH,QAAQ,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { type Query, type SDKMessage, type SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import type { MessageParam } from "@anthropic-ai/sdk/resources";
|
|
3
|
+
import type { SessionMode } from "../../mailbox.js";
|
|
4
|
+
import type { ScreenshotInjector } from "../../screenshot-injector.js";
|
|
5
|
+
import type { AgentSessionConstructorArgs, AgentSessionLike, AgentStreamMessage, InjectMessageOptions, SendAndStreamOptions, UsageSnapshot } from "../session-types.js";
|
|
6
|
+
export type { SDKMessage, SDKUserMessage };
|
|
7
|
+
export type { SystemPromptConfig } from "../session-types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Used-token threshold (fraction of context window) at which we trigger
|
|
10
|
+
* compaction for **large** context windows (>= {@link COMPACT_MAX_CONTEXT_WINDOW}).
|
|
11
|
+
*
|
|
12
|
+
* Smaller windows scale up to {@link COMPACT_SMALL_WINDOW_USED_PCT_THRESHOLD}
|
|
13
|
+
* via {@link getCompactUsedPctThreshold}.
|
|
14
|
+
*/
|
|
15
|
+
export declare const COMPACT_LARGE_WINDOW_USED_PCT_THRESHOLD = 0.7;
|
|
16
|
+
export declare const MAX_BASE64_IMAGE_BLOCKS = 10;
|
|
17
|
+
/** When total image blocks (real + placeholder) exceed this, trigger compaction
|
|
18
|
+
* to avoid hitting Claude API's 100-image limit. */
|
|
19
|
+
export declare const IMAGE_COMPACT_THRESHOLD = 80;
|
|
20
|
+
/**
|
|
21
|
+
* Wraps the V1 query() API with a long-lived async generator for streaming
|
|
22
|
+
* input. This allows injecting additional user messages while the agent is
|
|
23
|
+
* processing (interrupt messages), instead of waiting for the query to
|
|
24
|
+
* finish before sending the next message.
|
|
25
|
+
*/
|
|
26
|
+
export declare class ClaudeAgentSession implements AgentSessionLike {
|
|
27
|
+
private config;
|
|
28
|
+
private buildSystemPrompt;
|
|
29
|
+
private runtimeSurface;
|
|
30
|
+
private getDualSessionEnabled;
|
|
31
|
+
private getAndroidUseEnabled;
|
|
32
|
+
/** Working directory for the SDK subprocess. Defaults to profile config dir. */
|
|
33
|
+
private readonly sessionCwd;
|
|
34
|
+
private nativeTools;
|
|
35
|
+
/** Resolved external MCP servers (playwright, serpapi, etc.) for this runtime. */
|
|
36
|
+
private externalMcpServers;
|
|
37
|
+
/** Dynamic MCP servers added at runtime via manage_mcp_servers tool. */
|
|
38
|
+
private dynamicMcpServers;
|
|
39
|
+
private currentQuery;
|
|
40
|
+
private warmQuery;
|
|
41
|
+
private warmUpPromise;
|
|
42
|
+
private warmReadyAtMs;
|
|
43
|
+
private sessionId;
|
|
44
|
+
private transcriptPath;
|
|
45
|
+
readonly mode: SessionMode;
|
|
46
|
+
/**
|
|
47
|
+
* Pending message queue for the long-lived generator.
|
|
48
|
+
* When a message is injected, it's pushed here and the waiting
|
|
49
|
+
* generator is woken up via the resolver.
|
|
50
|
+
*/
|
|
51
|
+
private pendingMessages;
|
|
52
|
+
private messageResolver;
|
|
53
|
+
private generatorClosed;
|
|
54
|
+
private _orphanedInjections;
|
|
55
|
+
private lastCompactRequestAtMs;
|
|
56
|
+
private compactInFlight;
|
|
57
|
+
private stopRequested;
|
|
58
|
+
/**
|
|
59
|
+
* Tracks whether we've seeded `<profile>/.claude/settings.local.json`
|
|
60
|
+
* with our auto-memory directory override this process-lifetime. The seed
|
|
61
|
+
* helper is itself idempotent, but this flag keeps hot paths (every
|
|
62
|
+
* `consume()` / `warmUp()`) from hitting the filesystem repeatedly.
|
|
63
|
+
*/
|
|
64
|
+
private autoMemorySeedDone;
|
|
65
|
+
/** Optional: auto-injects screenshots after Playwright tool calls. */
|
|
66
|
+
private _screenshotInjector;
|
|
67
|
+
/** AbortController passed to the SDK; aborting it cleanly stops the running query. */
|
|
68
|
+
private abortController;
|
|
69
|
+
private warmAbortController;
|
|
70
|
+
/**
|
|
71
|
+
* Timer that proactively replaces the pre-warmed subprocess at
|
|
72
|
+
* `WARM_REFRESH_TTL_MS` intervals. Cleared when the warm is consumed,
|
|
73
|
+
* on stop, or before arming a new one.
|
|
74
|
+
*/
|
|
75
|
+
private warmRefreshTimer;
|
|
76
|
+
private lastUsageSnapshot;
|
|
77
|
+
private playwriterConnectionsResetThisTurn;
|
|
78
|
+
constructor({ config, buildSystemPrompt, runtimeSurface, sessionContext, }: AgentSessionConstructorArgs);
|
|
79
|
+
set screenshotInjector(injector: ScreenshotInjector | null);
|
|
80
|
+
/**
|
|
81
|
+
* Seed `<profile>/.claude/settings.local.json` once per process so the
|
|
82
|
+
* CLI's auto-memory lands inside the active profile instead of the
|
|
83
|
+
* default `~/.claude/projects/...` tree. Called from every CLI-spawn
|
|
84
|
+
* path (warm + cold); the underlying helper is itself idempotent.
|
|
85
|
+
*
|
|
86
|
+
* Gated on the `autoMemorySeedDone` flag so the happy path doesn't
|
|
87
|
+
* re-read/re-write the file every turn. The flag is set **only on
|
|
88
|
+
* success** — if the first attempt fails (permissions, disk full, etc.)
|
|
89
|
+
* the next warm/cold spawn will retry until it lands, instead of
|
|
90
|
+
* silently leaving the CLI on its default memory path for the rest of
|
|
91
|
+
* the session.
|
|
92
|
+
*/
|
|
93
|
+
private ensureAutoMemorySeeded;
|
|
94
|
+
/**
|
|
95
|
+
* Build a fresh mcpServers record with a new visionclaw tool server instance.
|
|
96
|
+
* This avoids the "Already connected to a transport" error when the same
|
|
97
|
+
* McpServer instance is reused across concurrent/sequential query() calls.
|
|
98
|
+
*/
|
|
99
|
+
private buildMcpServers;
|
|
100
|
+
private buildNativeToolOptions;
|
|
101
|
+
private buildHooks;
|
|
102
|
+
/**
|
|
103
|
+
* Build the shared Claude query options used for both pre-warm and live runs.
|
|
104
|
+
*/
|
|
105
|
+
private buildQueryOptions;
|
|
106
|
+
/**
|
|
107
|
+
* Cancel any pending proactive-refresh timer. Idempotent.
|
|
108
|
+
*/
|
|
109
|
+
private clearWarmRefreshTimer;
|
|
110
|
+
/**
|
|
111
|
+
* Arm the proactive-refresh timer. Caller must ensure any previous timer
|
|
112
|
+
* is cleared. Uses `.unref()` so the process can exit while a warm
|
|
113
|
+
* subprocess is idle — the refresh is purely a background optimization
|
|
114
|
+
* and should never hold the event loop alive on its own.
|
|
115
|
+
*/
|
|
116
|
+
private scheduleWarmRefresh;
|
|
117
|
+
/**
|
|
118
|
+
* Handler for the refresh timer. Covers two cases:
|
|
119
|
+
* 1. Normal TTL refresh: `warmQuery` is non-null → close the stale
|
|
120
|
+
* subprocess and start a fresh one.
|
|
121
|
+
* 2. Retry after a previous failure: `warmQuery` is null → just
|
|
122
|
+
* attempt warmUp again (no teardown needed).
|
|
123
|
+
*
|
|
124
|
+
* If the warm was already consumed between timer arming and firing,
|
|
125
|
+
* the consume path would have cleared the timer, so we shouldn't
|
|
126
|
+
* reach here in that case.
|
|
127
|
+
*/
|
|
128
|
+
private performWarmRefresh;
|
|
129
|
+
/**
|
|
130
|
+
* Post-warmUp follow-up for performWarmRefresh. Kept in its own method so
|
|
131
|
+
* TS's control-flow narrowing in performWarmRefresh doesn't treat these
|
|
132
|
+
* fields as having their pre-await values.
|
|
133
|
+
*/
|
|
134
|
+
private handleWarmRefreshOutcome;
|
|
135
|
+
/**
|
|
136
|
+
* Send the initial user message and return the query generator to stream
|
|
137
|
+
* responses. The underlying async generator stays open so that additional
|
|
138
|
+
* messages can be injected via injectMessage().
|
|
139
|
+
*/
|
|
140
|
+
warmUp(): Promise<void>;
|
|
141
|
+
sendAndStream(content: MessageParam["content"], options?: SendAndStreamOptions): AsyncIterable<AgentStreamMessage>;
|
|
142
|
+
/**
|
|
143
|
+
* Inject a new user message into the running query stream.
|
|
144
|
+
* The agent will see this as a follow-up user message in the conversation.
|
|
145
|
+
* Only works while a query is active (between sendAndStream and closeInput).
|
|
146
|
+
*
|
|
147
|
+
* @returns true if the message was injected, false if the generator is closed.
|
|
148
|
+
*/
|
|
149
|
+
injectMessage(content: MessageParam["content"], options?: InjectMessageOptions): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Signal the input generator to close.
|
|
152
|
+
* Any injected messages still in the pending queue are counted as
|
|
153
|
+
* orphaned — they were accepted by injectMessage but never delivered
|
|
154
|
+
* to the SDK.
|
|
155
|
+
*/
|
|
156
|
+
closeInput(): void;
|
|
157
|
+
/**
|
|
158
|
+
* Returns true if injected messages were lost when the generator closed.
|
|
159
|
+
*/
|
|
160
|
+
get hasOrphanedInjections(): boolean;
|
|
161
|
+
get isInputClosed(): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Request that the current agent turn be stopped.
|
|
164
|
+
* Uses the SDK's AbortController to cleanly signal the running query to
|
|
165
|
+
* stop, then closes the input stream so the loop returns to idle.
|
|
166
|
+
*/
|
|
167
|
+
requestStop(): void;
|
|
168
|
+
isStopRequested(): boolean;
|
|
169
|
+
clearStop(): void;
|
|
170
|
+
/**
|
|
171
|
+
* Update the persisted session ID.
|
|
172
|
+
*
|
|
173
|
+
* Only saves on first capture (null → id). Once set, a different ID from
|
|
174
|
+
* the SDK is ignored — it means the resume failed and the SDK created a
|
|
175
|
+
* throwaway conversation. Persisting it would corrupt session.json and
|
|
176
|
+
* orphan the real transcript.
|
|
177
|
+
*/
|
|
178
|
+
captureSessionId(id: string | undefined): void;
|
|
179
|
+
captureTranscriptPath(p: string | undefined | null): void;
|
|
180
|
+
getSessionId(): string | null;
|
|
181
|
+
/**
|
|
182
|
+
* Proactively request compaction by running the /compact slash command as a
|
|
183
|
+
* separate one-turn query resumed from the current session.
|
|
184
|
+
*
|
|
185
|
+
* This is more reliable than injecting "/compact" into the streaming prompt
|
|
186
|
+
* because the running query may treat it as ordinary text.
|
|
187
|
+
*/
|
|
188
|
+
requestCompaction(): Promise<void>;
|
|
189
|
+
captureUsageSnapshot(snapshot: UsageSnapshot): void;
|
|
190
|
+
/**
|
|
191
|
+
* Update the usage snapshot after the SDK performs mid-query auto-compaction.
|
|
192
|
+
* Uses `pre_tokens` (the post-compaction token count) and the last known
|
|
193
|
+
* context window to compute an accurate `usedPct`, preventing the post-query
|
|
194
|
+
* `maybeCompact` from triggering a redundant compaction.
|
|
195
|
+
*/
|
|
196
|
+
capturePostCompactionSnapshot(postCompactionTokens: number): void;
|
|
197
|
+
/**
|
|
198
|
+
* Unified between-turn compaction gate. Triggers compaction when **either**:
|
|
199
|
+
* 1. Token usage exceeds the context-window threshold, OR
|
|
200
|
+
* 2. Total image blocks (real + placeholder) in the transcript reach
|
|
201
|
+
* `imageThreshold`, approaching Claude API's 100-image limit.
|
|
202
|
+
*
|
|
203
|
+
* Both triggers share the same cooldown and in-flight guard so we never
|
|
204
|
+
* run two back-to-back compactions in the same between-turn pass.
|
|
205
|
+
*/
|
|
206
|
+
maybeCompact(options?: {
|
|
207
|
+
/**
|
|
208
|
+
* Optional explicit override. When set, takes precedence over the
|
|
209
|
+
* context-window-aware auto-scaling done by
|
|
210
|
+
* {@link getCompactUsedPctThreshold}.
|
|
211
|
+
*/
|
|
212
|
+
usedPctThreshold?: number;
|
|
213
|
+
cooldownMs?: number;
|
|
214
|
+
imageThreshold?: number;
|
|
215
|
+
}): Promise<void>;
|
|
216
|
+
getTranscriptPath(): string | null;
|
|
217
|
+
/**
|
|
218
|
+
* Derive the transcript path from the session ID and config dir, even before
|
|
219
|
+
* the SDK subprocess has started and reported it via hooks.
|
|
220
|
+
*/
|
|
221
|
+
private deriveTranscriptPath;
|
|
222
|
+
getUsageSnapshot(): UsageSnapshot | null;
|
|
223
|
+
/** Returns the live Query object, or null if no query is active. */
|
|
224
|
+
getCurrentQuery(): Query | null;
|
|
225
|
+
/**
|
|
226
|
+
* Validation adapter around `Query.getContextUsage()` (SDK 0.2.114+).
|
|
227
|
+
*
|
|
228
|
+
* The SDK's `Query` interface exposes `getContextUsage()` as a control
|
|
229
|
+
* request over the streaming bridge. It returns a rich breakdown:
|
|
230
|
+
*
|
|
231
|
+
* ```ts
|
|
232
|
+
* {
|
|
233
|
+
* categories: { name: string; tokens: number; color: string; isDeferred?: boolean }[];
|
|
234
|
+
* totalTokens: number;
|
|
235
|
+
* maxTokens: number; // context window for the *active* model
|
|
236
|
+
* rawMaxTokens: number; // nominal max before any reductions
|
|
237
|
+
* percentage: number; // UI percentage (0..100)
|
|
238
|
+
* gridRows: ...[]; // TUI-only
|
|
239
|
+
* model: string;
|
|
240
|
+
* memoryFiles: { path: string; type: string; tokens: number }[];
|
|
241
|
+
* mcpTools: { name: string; serverName: string; tokens: number }[];
|
|
242
|
+
* }
|
|
243
|
+
* ```
|
|
244
|
+
*
|
|
245
|
+
* We map it onto our existing {@link UsageSnapshot} shape:
|
|
246
|
+
* - `usedInputTokens` ← `totalTokens`
|
|
247
|
+
* - `contextWindow` ← `maxTokens` (post-reduction; matches what the
|
|
248
|
+
* model actually has to work with right now)
|
|
249
|
+
* - `usedPct` ← computed from the two above, NOT from the
|
|
250
|
+
* SDK's `percentage` field. The SDK's value is
|
|
251
|
+
* UI-scale (0..100); our consumers expect 0..1.
|
|
252
|
+
* Computing locally keeps both fields internally
|
|
253
|
+
* consistent.
|
|
254
|
+
* - `allImageBlocks` — intentionally preserved from any existing
|
|
255
|
+
* snapshot; `getContextUsage` doesn't know about
|
|
256
|
+
* raw image-block counts in the transcript, so
|
|
257
|
+
* we don't clobber the value that
|
|
258
|
+
* {@link captureUsageSnapshot} maintains.
|
|
259
|
+
*
|
|
260
|
+
* This is purely a read adapter — no callers in compaction or the
|
|
261
|
+
* wake loop should depend on it yet. It exists to let us sanity-check
|
|
262
|
+
* the live SDK response before wiring any behavior changes around it.
|
|
263
|
+
* Returns null when no live query is active, when the method is absent
|
|
264
|
+
* on the active Query instance (e.g. an older CLI), or when the call
|
|
265
|
+
* errors transiently.
|
|
266
|
+
*
|
|
267
|
+
* TODO: we should update snapshot based on this.
|
|
268
|
+
*/
|
|
269
|
+
getLiveContextUsage(): Promise<UsageSnapshot | null>;
|
|
270
|
+
/** Replace the full set of dynamic MCP servers (used when loading from disk). */
|
|
271
|
+
setDynamicMcpServers(servers: Record<string, Record<string, unknown>>): void;
|
|
272
|
+
/** Get the current dynamic MCP servers record. */
|
|
273
|
+
getDynamicMcpServers(): Record<string, Record<string, unknown>>;
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../../src/agent/engines/claude/session.ts"],"names":[],"mappings":"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"}
|