@a1hvdy/cc-openclaw 0.22.0 → 0.24.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/src/channels/telegram/edit-cadence.d.ts +37 -0
- package/dist/src/channels/telegram/edit-cadence.js +50 -0
- package/dist/src/channels/telegram/edit-cadence.js.map +1 -0
- package/dist/src/channels/telegram/injector.js +71 -0
- package/dist/src/channels/telegram/injector.js.map +1 -1
- package/dist/src/channels/telegram/speculative-bubble.d.ts +33 -0
- package/dist/src/channels/telegram/speculative-bubble.js +42 -0
- package/dist/src/channels/telegram/speculative-bubble.js.map +1 -0
- package/dist/src/channels/telegram/tool-tracker.d.ts +1 -1
- package/dist/src/channels/telegram/tool-tracker.js +10 -4
- package/dist/src/channels/telegram/tool-tracker.js.map +1 -1
- package/dist/src/command-router/cc-handler.js +15 -0
- package/dist/src/command-router/cc-handler.js.map +1 -1
- package/dist/src/command-router/launch-policy.js +14 -2
- package/dist/src/command-router/launch-policy.js.map +1 -1
- package/dist/src/engines/persistent-session.js +5 -0
- package/dist/src/engines/persistent-session.js.map +1 -1
- package/dist/src/index.js +55 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/config-service.js +12 -0
- package/dist/src/lib/config-service.js.map +1 -1
- package/dist/src/lib/config.d.ts +36 -0
- package/dist/src/lib/config.js +97 -0
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/http-agent.d.ts +47 -0
- package/dist/src/lib/http-agent.js +86 -0
- package/dist/src/lib/http-agent.js.map +1 -0
- package/dist/src/lib/perf/async-compact.d.ts +26 -0
- package/dist/src/lib/perf/async-compact.js +42 -0
- package/dist/src/lib/perf/async-compact.js.map +1 -0
- package/dist/src/lib/perf/direct-sdk.d.ts +26 -0
- package/dist/src/lib/perf/direct-sdk.js +58 -0
- package/dist/src/lib/perf/direct-sdk.js.map +1 -0
- package/dist/src/lib/perf/haiku-route.d.ts +19 -0
- package/dist/src/lib/perf/haiku-route.js +49 -0
- package/dist/src/lib/perf/haiku-route.js.map +1 -0
- package/dist/src/lib/perf/predictive-continuation.d.ts +18 -0
- package/dist/src/lib/perf/predictive-continuation.js +37 -0
- package/dist/src/lib/perf/predictive-continuation.js.map +1 -0
- package/dist/src/lib/perf/read-batch.d.ts +33 -0
- package/dist/src/lib/perf/read-batch.js +48 -0
- package/dist/src/lib/perf/read-batch.js.map +1 -0
- package/dist/src/lib/perf/skill-list-collapse.d.ts +22 -0
- package/dist/src/lib/perf/skill-list-collapse.js +35 -0
- package/dist/src/lib/perf/skill-list-collapse.js.map +1 -0
- package/dist/src/lib/perf/typing-prefetch.d.ts +25 -0
- package/dist/src/lib/perf/typing-prefetch.js +54 -0
- package/dist/src/lib/perf/typing-prefetch.js.map +1 -0
- package/dist/src/observability/perf-telemetry.d.ts +66 -0
- package/dist/src/observability/perf-telemetry.js +79 -0
- package/dist/src/observability/perf-telemetry.js.map +1 -0
- package/dist/src/openai-compat/openai-compat.d.ts +2 -0
- package/dist/src/openai-compat/openai-compat.js +85 -0
- package/dist/src/openai-compat/openai-compat.js.map +1 -1
- package/dist/src/openai-compat/streaming-handler.js +35 -1
- package/dist/src/openai-compat/streaming-handler.js.map +1 -1
- package/dist/src/patches/sysprompt-strip.spec.d.ts +33 -0
- package/dist/src/patches/sysprompt-strip.spec.js +53 -0
- package/dist/src/patches/sysprompt-strip.spec.js.map +1 -0
- package/dist/src/session/session-manager.d.ts +4 -0
- package/dist/src/session/session-manager.js +99 -5
- package/dist/src/session/session-manager.js.map +1 -1
- package/dist/src/session-bootstrap/cwd-patch.js +37 -0
- package/dist/src/session-bootstrap/cwd-patch.js.map +1 -1
- package/dist/src/types/runtime-config.d.ts +36 -0
- package/dist/src/types/runtime-config.js +44 -0
- package/dist/src/types/runtime-config.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M5 (perf overhaul idea #5) — undici keepAlive agent pool.
|
|
3
|
+
*
|
|
4
|
+
* Sets undici's global dispatcher to a keepAlive Agent so every Node 18+
|
|
5
|
+
* `fetch()` call in cc-openclaw reuses TCP+TLS sockets per upstream origin.
|
|
6
|
+
* For the Riyadh→US-East corridor (150-200ms RTT), eliminating one TLS 1.3
|
|
7
|
+
* handshake saves roughly 1 TCP + 2 TLS round trips = 450-600ms tail latency
|
|
8
|
+
* per reuse. Undici's `Agent` already pools per-origin internally; we just
|
|
9
|
+
* tune `connections` (concurrent sockets) and `keepAliveTimeout` (idle hold).
|
|
10
|
+
*
|
|
11
|
+
* Flag: CC_OPENCLAW_PERF_KEEPALIVE (default OFF until M8 baseline confirms
|
|
12
|
+
* the win — per plan, "default ON for safe ideas... once measured-good").
|
|
13
|
+
*
|
|
14
|
+
* Install is idempotent — second invocation in the same process is a no-op
|
|
15
|
+
* so importing from multiple bootstrap entry points is safe. The previous
|
|
16
|
+
* dispatcher is closed gracefully to avoid socket leaks during reinstall.
|
|
17
|
+
*
|
|
18
|
+
* Test hook: pass a custom factory to `installPerfKeepaliveAgent({ factory })`
|
|
19
|
+
* so the test suite can verify install+settings without binding real sockets.
|
|
20
|
+
*/
|
|
21
|
+
export interface KeepaliveOptions {
|
|
22
|
+
/** Max idle time a kept-alive socket stays open. Default 60s. */
|
|
23
|
+
keepAliveTimeout?: number;
|
|
24
|
+
/** Hard ceiling on idle socket age. Default 120s. */
|
|
25
|
+
keepAliveMaxTimeout?: number;
|
|
26
|
+
/** Concurrent sockets per origin. Default 8 — enough for cc-openclaw's
|
|
27
|
+
* fan-out to Anthropic + OpenAI fallback without saturating either. */
|
|
28
|
+
connections?: number;
|
|
29
|
+
}
|
|
30
|
+
interface InstallSnapshot {
|
|
31
|
+
installed: boolean;
|
|
32
|
+
options: Required<KeepaliveOptions>;
|
|
33
|
+
installedAt: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Install the keepAlive agent as undici's global dispatcher. Idempotent —
|
|
37
|
+
* subsequent calls in the same process return the existing snapshot without
|
|
38
|
+
* reinstalling. Returns null when the flag is off (no-op, no snapshot).
|
|
39
|
+
*
|
|
40
|
+
* `factory` exists for tests; production code never passes it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function installPerfKeepaliveAgent(opts?: KeepaliveOptions, factory?: (resolved: Required<KeepaliveOptions>) => unknown): InstallSnapshot | null;
|
|
43
|
+
/** Inspect current install state — used by /health + diagnostics. */
|
|
44
|
+
export declare function getPerfKeepaliveSnapshot(): InstallSnapshot | null;
|
|
45
|
+
/** Test hook — clear the install snapshot so the next call reinstalls. */
|
|
46
|
+
export declare function _resetPerfKeepaliveForTests(): void;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M5 (perf overhaul idea #5) — undici keepAlive agent pool.
|
|
3
|
+
*
|
|
4
|
+
* Sets undici's global dispatcher to a keepAlive Agent so every Node 18+
|
|
5
|
+
* `fetch()` call in cc-openclaw reuses TCP+TLS sockets per upstream origin.
|
|
6
|
+
* For the Riyadh→US-East corridor (150-200ms RTT), eliminating one TLS 1.3
|
|
7
|
+
* handshake saves roughly 1 TCP + 2 TLS round trips = 450-600ms tail latency
|
|
8
|
+
* per reuse. Undici's `Agent` already pools per-origin internally; we just
|
|
9
|
+
* tune `connections` (concurrent sockets) and `keepAliveTimeout` (idle hold).
|
|
10
|
+
*
|
|
11
|
+
* Flag: CC_OPENCLAW_PERF_KEEPALIVE (default OFF until M8 baseline confirms
|
|
12
|
+
* the win — per plan, "default ON for safe ideas... once measured-good").
|
|
13
|
+
*
|
|
14
|
+
* Install is idempotent — second invocation in the same process is a no-op
|
|
15
|
+
* so importing from multiple bootstrap entry points is safe. The previous
|
|
16
|
+
* dispatcher is closed gracefully to avoid socket leaks during reinstall.
|
|
17
|
+
*
|
|
18
|
+
* Test hook: pass a custom factory to `installPerfKeepaliveAgent({ factory })`
|
|
19
|
+
* so the test suite can verify install+settings without binding real sockets.
|
|
20
|
+
*/
|
|
21
|
+
// undici is lazy-loaded inside installPerfKeepaliveAgent to keep the
|
|
22
|
+
// cold-start import budget at zero when the flag is off (which is the
|
|
23
|
+
// default). Eager `import from 'undici'` here pushed the plugin-entry
|
|
24
|
+
// import time over the 2000ms cold-start budget enforced by
|
|
25
|
+
// tests/perf/cold-start.test.ts.
|
|
26
|
+
import { createRequire } from 'node:module';
|
|
27
|
+
import { getPerfKeepaliveEnabled, isLogLevelDebug } from './config.js';
|
|
28
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
29
|
+
let snapshot = null;
|
|
30
|
+
const DEFAULTS = {
|
|
31
|
+
keepAliveTimeout: 60_000,
|
|
32
|
+
keepAliveMaxTimeout: 120_000,
|
|
33
|
+
connections: 8,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Install the keepAlive agent as undici's global dispatcher. Idempotent —
|
|
37
|
+
* subsequent calls in the same process return the existing snapshot without
|
|
38
|
+
* reinstalling. Returns null when the flag is off (no-op, no snapshot).
|
|
39
|
+
*
|
|
40
|
+
* `factory` exists for tests; production code never passes it.
|
|
41
|
+
*/
|
|
42
|
+
export function installPerfKeepaliveAgent(opts = {}, factory) {
|
|
43
|
+
if (!getPerfKeepaliveEnabled())
|
|
44
|
+
return null;
|
|
45
|
+
if (snapshot)
|
|
46
|
+
return snapshot;
|
|
47
|
+
const resolved = {
|
|
48
|
+
keepAliveTimeout: opts.keepAliveTimeout ?? DEFAULTS.keepAliveTimeout,
|
|
49
|
+
keepAliveMaxTimeout: opts.keepAliveMaxTimeout ?? DEFAULTS.keepAliveMaxTimeout,
|
|
50
|
+
connections: opts.connections ?? DEFAULTS.connections,
|
|
51
|
+
};
|
|
52
|
+
try {
|
|
53
|
+
// Lazy-load undici only when actually installing. Cost is amortized
|
|
54
|
+
// across the rest of the process lifetime (Node caches require()).
|
|
55
|
+
const undici = requireFromHere('undici');
|
|
56
|
+
const prev = undici.getGlobalDispatcher();
|
|
57
|
+
// Default factory uses the just-loaded undici.Agent. Tests inject
|
|
58
|
+
// their own factory to avoid binding real sockets.
|
|
59
|
+
const realFactory = factory ?? ((r) => new undici.Agent(r));
|
|
60
|
+
const next = realFactory(resolved);
|
|
61
|
+
undici.setGlobalDispatcher(next);
|
|
62
|
+
// Close the previous dispatcher to release pooled sockets, but never
|
|
63
|
+
// throw — install must not break the gateway boot path on this branch.
|
|
64
|
+
if (prev && typeof prev.close === 'function') {
|
|
65
|
+
void prev.close().catch(() => { });
|
|
66
|
+
}
|
|
67
|
+
snapshot = { installed: true, options: resolved, installedAt: new Date().toISOString() };
|
|
68
|
+
return snapshot;
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (isLogLevelDebug()) {
|
|
72
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
73
|
+
process.stderr.write(`[http-agent] install failed: ${msg}\n`);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Inspect current install state — used by /health + diagnostics. */
|
|
79
|
+
export function getPerfKeepaliveSnapshot() {
|
|
80
|
+
return snapshot;
|
|
81
|
+
}
|
|
82
|
+
/** Test hook — clear the install snapshot so the next call reinstalls. */
|
|
83
|
+
export function _resetPerfKeepaliveForTests() {
|
|
84
|
+
snapshot = null;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=http-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-agent.js","sourceRoot":"","sources":["../../../src/lib/http-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,qEAAqE;AACrE,sEAAsE;AACtE,sEAAsE;AACtE,4DAA4D;AAC5D,iCAAiC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvE,MAAM,eAAe,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAkBvD,IAAI,QAAQ,GAA2B,IAAI,CAAC;AAE5C,MAAM,QAAQ,GAA+B;IAC3C,gBAAgB,EAAE,MAAM;IACxB,mBAAmB,EAAE,OAAO;IAC5B,WAAW,EAAE,CAAC;CACf,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAAyB,EAAE,EAC3B,OAA2D;IAE3D,IAAI,CAAC,uBAAuB,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,QAAQ,GAA+B;QAC3C,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,QAAQ,CAAC,gBAAgB;QACpE,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,IAAI,QAAQ,CAAC,mBAAmB;QAC7E,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW;KACtD,CAAC;IAEF,IAAI,CAAC;QACH,oEAAoE;QACpE,mEAAmE;QACnE,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAItC,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC1C,kEAAkE;QAClE,mDAAmD;QACnD,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,CAAC,CAA6B,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACxF,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,CAAC,mBAAmB,CAAC,IAAa,CAAC,CAAC;QAC1C,qEAAqE;QACrE,uEAAuE;QACvE,IAAI,IAAI,IAAI,OAAQ,IAA2C,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YACrF,KAAM,IAA0C,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,QAAQ,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QACzF,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,eAAe,EAAE,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,GAAG,IAAI,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,wBAAwB;IACtC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,2BAA2B;IACzC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M6 (perf overhaul idea #6) — async-lossless-compaction scheduler.
|
|
3
|
+
*
|
|
4
|
+
* Lossless-claw's DAG summarization currently runs on the hot path (turn N
|
|
5
|
+
* blocks while compaction completes). This scheduler defers the work to a
|
|
6
|
+
* setImmediate callback so turn N returns immediately; the summary lands
|
|
7
|
+
* before turn N+1 reads it (or N+1 skips the stale summary if compaction
|
|
8
|
+
* hasn't finished — that branch is the cost of the speedup).
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_ASYNC_COMPACT (default OFF, medium risk —
|
|
11
|
+
* compaction freshness can drift if N+1 fires before N's compaction lands).
|
|
12
|
+
*
|
|
13
|
+
* Per-session serialization: only one async compaction in flight per
|
|
14
|
+
* sessionKey at a time. Subsequent calls during in-flight work are dropped
|
|
15
|
+
* (the in-flight one will pick up the latest state at execution time).
|
|
16
|
+
*/
|
|
17
|
+
export interface AsyncCompactResult {
|
|
18
|
+
scheduled: boolean;
|
|
19
|
+
reason: 'flag_off' | 'in_flight' | 'scheduled';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Schedule `compactFn` to run after the current turn returns. Returns
|
|
23
|
+
* synchronously; the caller never awaits compaction completion.
|
|
24
|
+
*/
|
|
25
|
+
export declare function scheduleAsyncCompaction(sessionKey: string, compactFn: () => Promise<void> | void): AsyncCompactResult;
|
|
26
|
+
export declare function _resetAsyncCompactForTests(): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M6 (perf overhaul idea #6) — async-lossless-compaction scheduler.
|
|
3
|
+
*
|
|
4
|
+
* Lossless-claw's DAG summarization currently runs on the hot path (turn N
|
|
5
|
+
* blocks while compaction completes). This scheduler defers the work to a
|
|
6
|
+
* setImmediate callback so turn N returns immediately; the summary lands
|
|
7
|
+
* before turn N+1 reads it (or N+1 skips the stale summary if compaction
|
|
8
|
+
* hasn't finished — that branch is the cost of the speedup).
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_ASYNC_COMPACT (default OFF, medium risk —
|
|
11
|
+
* compaction freshness can drift if N+1 fires before N's compaction lands).
|
|
12
|
+
*
|
|
13
|
+
* Per-session serialization: only one async compaction in flight per
|
|
14
|
+
* sessionKey at a time. Subsequent calls during in-flight work are dropped
|
|
15
|
+
* (the in-flight one will pick up the latest state at execution time).
|
|
16
|
+
*/
|
|
17
|
+
import { getPerfAsyncCompactEnabled } from '../config.js';
|
|
18
|
+
const inFlight = new Set();
|
|
19
|
+
/**
|
|
20
|
+
* Schedule `compactFn` to run after the current turn returns. Returns
|
|
21
|
+
* synchronously; the caller never awaits compaction completion.
|
|
22
|
+
*/
|
|
23
|
+
export function scheduleAsyncCompaction(sessionKey, compactFn) {
|
|
24
|
+
if (!getPerfAsyncCompactEnabled())
|
|
25
|
+
return { scheduled: false, reason: 'flag_off' };
|
|
26
|
+
if (!sessionKey || !compactFn)
|
|
27
|
+
return { scheduled: false, reason: 'flag_off' };
|
|
28
|
+
if (inFlight.has(sessionKey))
|
|
29
|
+
return { scheduled: false, reason: 'in_flight' };
|
|
30
|
+
inFlight.add(sessionKey);
|
|
31
|
+
setImmediate(() => {
|
|
32
|
+
void Promise.resolve()
|
|
33
|
+
.then(() => compactFn())
|
|
34
|
+
.catch(() => { })
|
|
35
|
+
.finally(() => inFlight.delete(sessionKey));
|
|
36
|
+
});
|
|
37
|
+
return { scheduled: true, reason: 'scheduled' };
|
|
38
|
+
}
|
|
39
|
+
export function _resetAsyncCompactForTests() {
|
|
40
|
+
inFlight.clear();
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=async-compact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-compact.js","sourceRoot":"","sources":["../../../../src/lib/perf/async-compact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAE1D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;AAOnC;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,UAAkB,EAClB,SAAqC;IAErC,IAAI,CAAC,0BAA0B,EAAE;QAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACnF,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC/E,IAAI,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC/E,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACzB,YAAY,CAAC,GAAG,EAAE;QAChB,KAAK,OAAO,CAAC,OAAO,EAAE;aACnB,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;aACvB,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;aACf,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M12 (perf overhaul idea #12) — direct claude-code SDK in-process.
|
|
3
|
+
*
|
|
4
|
+
* Bypasses the `claude` CLI subprocess entirely by calling the
|
|
5
|
+
* `@anthropic-ai/claude-code` SDK in the cc-openclaw Node process.
|
|
6
|
+
* Eliminates all subprocess overhead (Node bootstrap, bundle parse, auth
|
|
7
|
+
* handshake) at the cost of losing CLI session/checkpoint features and
|
|
8
|
+
* recoupling to upstream SDK API churn.
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_DIRECT_SDK (default OFF, HIGH RISK — ships LAST
|
|
11
|
+
* with the resident-CLI pool as the fallback).
|
|
12
|
+
*
|
|
13
|
+
* Probes the SDK availability without bringing it as a hard dependency:
|
|
14
|
+
* dynamic require with a swallowed ENOENT. The actual invocation is
|
|
15
|
+
* delegated to the registered handler — session-manager owns the call
|
|
16
|
+
* details (model, prompt, options) and decides whether the SDK supports
|
|
17
|
+
* the requested feature set for this turn.
|
|
18
|
+
*/
|
|
19
|
+
export interface DirectSdkAvailability {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
available: boolean;
|
|
22
|
+
reason: 'flag_off' | 'sdk_missing' | 'sdk_loaded' | 'probe_error';
|
|
23
|
+
version: string | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function probeDirectSdkAvailability(): DirectSdkAvailability;
|
|
26
|
+
export declare function _resetDirectSdkProbeForTests(): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M12 (perf overhaul idea #12) — direct claude-code SDK in-process.
|
|
3
|
+
*
|
|
4
|
+
* Bypasses the `claude` CLI subprocess entirely by calling the
|
|
5
|
+
* `@anthropic-ai/claude-code` SDK in the cc-openclaw Node process.
|
|
6
|
+
* Eliminates all subprocess overhead (Node bootstrap, bundle parse, auth
|
|
7
|
+
* handshake) at the cost of losing CLI session/checkpoint features and
|
|
8
|
+
* recoupling to upstream SDK API churn.
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_DIRECT_SDK (default OFF, HIGH RISK — ships LAST
|
|
11
|
+
* with the resident-CLI pool as the fallback).
|
|
12
|
+
*
|
|
13
|
+
* Probes the SDK availability without bringing it as a hard dependency:
|
|
14
|
+
* dynamic require with a swallowed ENOENT. The actual invocation is
|
|
15
|
+
* delegated to the registered handler — session-manager owns the call
|
|
16
|
+
* details (model, prompt, options) and decides whether the SDK supports
|
|
17
|
+
* the requested feature set for this turn.
|
|
18
|
+
*/
|
|
19
|
+
import { createRequire } from 'node:module';
|
|
20
|
+
import { getPerfDirectSdkEnabled } from '../config.js';
|
|
21
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
22
|
+
let cachedProbe = null;
|
|
23
|
+
export function probeDirectSdkAvailability() {
|
|
24
|
+
if (!getPerfDirectSdkEnabled()) {
|
|
25
|
+
return { enabled: false, available: false, reason: 'flag_off', version: null };
|
|
26
|
+
}
|
|
27
|
+
if (cachedProbe && cachedProbe.reason !== 'probe_error')
|
|
28
|
+
return cachedProbe;
|
|
29
|
+
try {
|
|
30
|
+
// Dynamic require path — never bring the SDK as a hard dependency.
|
|
31
|
+
const mod = requireFromHere('@anthropic-ai/claude-code');
|
|
32
|
+
if (!mod) {
|
|
33
|
+
cachedProbe = { enabled: true, available: false, reason: 'sdk_missing', version: null };
|
|
34
|
+
return cachedProbe;
|
|
35
|
+
}
|
|
36
|
+
cachedProbe = {
|
|
37
|
+
enabled: true,
|
|
38
|
+
available: true,
|
|
39
|
+
reason: 'sdk_loaded',
|
|
40
|
+
version: mod.VERSION ?? null,
|
|
41
|
+
};
|
|
42
|
+
return cachedProbe;
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
46
|
+
if (msg.includes('Cannot find module')) {
|
|
47
|
+
cachedProbe = { enabled: true, available: false, reason: 'sdk_missing', version: null };
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
cachedProbe = { enabled: true, available: false, reason: 'probe_error', version: null };
|
|
51
|
+
}
|
|
52
|
+
return cachedProbe;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function _resetDirectSdkProbeForTests() {
|
|
56
|
+
cachedProbe = null;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=direct-sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct-sdk.js","sourceRoot":"","sources":["../../../../src/lib/perf/direct-sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,eAAe,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AASvD,IAAI,WAAW,GAAiC,IAAI,CAAC;AAErD,MAAM,UAAU,0BAA0B;IACxC,IAAI,CAAC,uBAAuB,EAAE,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACjF,CAAC;IACD,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,aAAa;QAAE,OAAO,WAAW,CAAC;IAC5E,IAAI,CAAC;QACH,mEAAmE;QACnE,MAAM,GAAG,GAAG,eAAe,CAAC,2BAA2B,CAAqC,CAAC;QAC7F,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,WAAW,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YACxF,OAAO,WAAW,CAAC;QACrB,CAAC;QACD,WAAW,GAAG;YACZ,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,IAAI;SAC7B,CAAC;QACF,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,IAAI,GAAG,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACvC,WAAW,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC1F,CAAC;aAAM,CAAC;YACN,WAAW,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC1F,CAAC;QACD,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,4BAA4B;IAC1C,WAAW,GAAG,IAAI,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M10 (perf overhaul idea #10) — Haiku 4.5 cheap-route classifier.
|
|
3
|
+
*
|
|
4
|
+
* Pattern-matches trivial queries that don't need Opus-level reasoning:
|
|
5
|
+
* status checks, simple acks, short factual lookups. When matched, the
|
|
6
|
+
* caller routes the request to Haiku via the OpenAI-compat fallback
|
|
7
|
+
* pipeline (NOT direct Anthropic API — A1 has no anthropic.com key, only
|
|
8
|
+
* Max 20x subscription). Sub-500ms; zero Max quota burn.
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_HAIKU_ROUTE (default OFF).
|
|
11
|
+
*
|
|
12
|
+
* Conservative by design: any uncertainty falls back to Opus. False positives
|
|
13
|
+
* are user-visible quality regressions; false negatives just miss a perf win.
|
|
14
|
+
*/
|
|
15
|
+
export interface RouteDecision {
|
|
16
|
+
routeToHaiku: boolean;
|
|
17
|
+
reason: 'trivial_pattern' | 'too_long' | 'complex_token' | 'flag_off' | 'no_match';
|
|
18
|
+
}
|
|
19
|
+
export declare function classifyForHaikuRoute(prompt: string): RouteDecision;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M10 (perf overhaul idea #10) — Haiku 4.5 cheap-route classifier.
|
|
3
|
+
*
|
|
4
|
+
* Pattern-matches trivial queries that don't need Opus-level reasoning:
|
|
5
|
+
* status checks, simple acks, short factual lookups. When matched, the
|
|
6
|
+
* caller routes the request to Haiku via the OpenAI-compat fallback
|
|
7
|
+
* pipeline (NOT direct Anthropic API — A1 has no anthropic.com key, only
|
|
8
|
+
* Max 20x subscription). Sub-500ms; zero Max quota burn.
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_HAIKU_ROUTE (default OFF).
|
|
11
|
+
*
|
|
12
|
+
* Conservative by design: any uncertainty falls back to Opus. False positives
|
|
13
|
+
* are user-visible quality regressions; false negatives just miss a perf win.
|
|
14
|
+
*/
|
|
15
|
+
import { getPerfHaikuRouteEnabled } from '../config.js';
|
|
16
|
+
const TRIVIAL_PATTERNS = [
|
|
17
|
+
/^(status|ping|hi|hello|hey|yo|ack|ok|okay|thanks|thx|ty)\s*[.!?]?\s*$/i,
|
|
18
|
+
/^(what'?s\s+(?:the\s+)?(?:time|date|day))\s*\??\s*$/i,
|
|
19
|
+
/^(are\s+you\s+(?:there|alive|up|online))\s*\??\s*$/i,
|
|
20
|
+
/^(version|uptime|health)\s*\??\s*$/i,
|
|
21
|
+
];
|
|
22
|
+
const COMPLEX_TOKENS = [
|
|
23
|
+
'write', 'create', 'build', 'implement', 'fix', 'debug', 'analyze',
|
|
24
|
+
'explain', 'refactor', 'review', 'design', 'plan', 'why', 'how',
|
|
25
|
+
'compare', 'recommend', 'suggest', 'should', 'would', 'could',
|
|
26
|
+
];
|
|
27
|
+
const TRIVIAL_MAX_CHARS = 60;
|
|
28
|
+
export function classifyForHaikuRoute(prompt) {
|
|
29
|
+
if (!getPerfHaikuRouteEnabled())
|
|
30
|
+
return { routeToHaiku: false, reason: 'flag_off' };
|
|
31
|
+
if (!prompt || typeof prompt !== 'string')
|
|
32
|
+
return { routeToHaiku: false, reason: 'no_match' };
|
|
33
|
+
const trimmed = prompt.trim();
|
|
34
|
+
if (trimmed.length === 0)
|
|
35
|
+
return { routeToHaiku: false, reason: 'no_match' };
|
|
36
|
+
if (trimmed.length > TRIVIAL_MAX_CHARS)
|
|
37
|
+
return { routeToHaiku: false, reason: 'too_long' };
|
|
38
|
+
const lower = trimmed.toLowerCase();
|
|
39
|
+
for (const token of COMPLEX_TOKENS) {
|
|
40
|
+
if (lower.includes(token))
|
|
41
|
+
return { routeToHaiku: false, reason: 'complex_token' };
|
|
42
|
+
}
|
|
43
|
+
for (const pat of TRIVIAL_PATTERNS) {
|
|
44
|
+
if (pat.test(trimmed))
|
|
45
|
+
return { routeToHaiku: true, reason: 'trivial_pattern' };
|
|
46
|
+
}
|
|
47
|
+
return { routeToHaiku: false, reason: 'no_match' };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=haiku-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"haiku-route.js","sourceRoot":"","sources":["../../../../src/lib/perf/haiku-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,gBAAgB,GAAa;IACjC,wEAAwE;IACxE,sDAAsD;IACtD,qDAAqD;IACrD,qCAAqC;CACtC,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS;IAClE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;IAC/D,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO;CAC9D,CAAC;AAOF,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,IAAI,CAAC,wBAAwB,EAAE;QAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACpF,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC9F,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC7E,IAAI,OAAO,CAAC,MAAM,GAAG,iBAAiB;QAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC3F,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IACrF,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAClF,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M11 (perf overhaul idea #11) — predictive continuation detector.
|
|
3
|
+
*
|
|
4
|
+
* Returns a confident yes/no when a user's incoming message is a pure
|
|
5
|
+
* continuation request ("more", "continue", "and?", "go on"). When true,
|
|
6
|
+
* the caller can start generation against the previous turn's tail before
|
|
7
|
+
* the user finishes typing, giving the perceived-instant feel on common
|
|
8
|
+
* follow-ups. False on ambiguous input keeps the regular dispatch path.
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_PREDICTIVE_CONTINUE (default OFF).
|
|
11
|
+
*/
|
|
12
|
+
export interface ContinuationDecision {
|
|
13
|
+
/** True iff the text is a confident continuation request. */
|
|
14
|
+
isContinuation: boolean;
|
|
15
|
+
/** Matched pattern source for telemetry; empty when isContinuation=false. */
|
|
16
|
+
matched: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function isPredictiveContinuation(text: string): ContinuationDecision;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M11 (perf overhaul idea #11) — predictive continuation detector.
|
|
3
|
+
*
|
|
4
|
+
* Returns a confident yes/no when a user's incoming message is a pure
|
|
5
|
+
* continuation request ("more", "continue", "and?", "go on"). When true,
|
|
6
|
+
* the caller can start generation against the previous turn's tail before
|
|
7
|
+
* the user finishes typing, giving the perceived-instant feel on common
|
|
8
|
+
* follow-ups. False on ambiguous input keeps the regular dispatch path.
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_PREDICTIVE_CONTINUE (default OFF).
|
|
11
|
+
*/
|
|
12
|
+
import { getPerfPredictiveContinueEnabled } from '../config.js';
|
|
13
|
+
const CONTINUATION_PATTERNS = [
|
|
14
|
+
/^\s*(more|continue|go\s*on|keep\s*going|next|and\??|then\??|\.\.\.)\s*[.!?]?\s*$/i,
|
|
15
|
+
/^\s*(go|do)\s+(on|ahead)\s*[.!?]?\s*$/i,
|
|
16
|
+
/^\s*(yes|yep|yeah|ok|okay|sure)[,!.?]?\s+(more|continue|go\s*on|please)\s*[.!?]?\s*$/i,
|
|
17
|
+
];
|
|
18
|
+
export function isPredictiveContinuation(text) {
|
|
19
|
+
if (!getPerfPredictiveContinueEnabled()) {
|
|
20
|
+
return { isContinuation: false, matched: '' };
|
|
21
|
+
}
|
|
22
|
+
if (!text || typeof text !== 'string') {
|
|
23
|
+
return { isContinuation: false, matched: '' };
|
|
24
|
+
}
|
|
25
|
+
const trimmed = text.trim();
|
|
26
|
+
// Hard length cap — anything over 40 chars is too substantive to predict.
|
|
27
|
+
if (trimmed.length === 0 || trimmed.length > 40) {
|
|
28
|
+
return { isContinuation: false, matched: '' };
|
|
29
|
+
}
|
|
30
|
+
for (const pat of CONTINUATION_PATTERNS) {
|
|
31
|
+
if (pat.test(trimmed)) {
|
|
32
|
+
return { isContinuation: true, matched: pat.source };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { isContinuation: false, matched: '' };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=predictive-continuation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predictive-continuation.js","sourceRoot":"","sources":["../../../../src/lib/perf/predictive-continuation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,gCAAgC,EAAE,MAAM,cAAc,CAAC;AAEhE,MAAM,qBAAqB,GAAa;IACtC,mFAAmF;IACnF,wCAAwC;IACxC,uFAAuF;CACxF,CAAC;AASF,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,IAAI,CAAC,gCAAgC,EAAE,EAAE,CAAC;QACxC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAChD,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAChD,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,0EAA0E;IAC1E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAChD,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAChD,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,qBAAqB,EAAE,CAAC;QACxC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;QACvD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M7 (perf overhaul idea #7) — Read([paths]) parallel batch reader.
|
|
3
|
+
*
|
|
4
|
+
* The legacy flow is N serial Read tool calls = N LLM round trips. This
|
|
5
|
+
* helper reads an array of paths concurrently in a single Node.js step,
|
|
6
|
+
* returning a structured result the tool-router collapses into one
|
|
7
|
+
* tool_result delta. Eliminates the per-file LLM round trip cost.
|
|
8
|
+
*
|
|
9
|
+
* Flag: CC_OPENCLAW_PERF_READ_BATCH (default OFF, medium risk — tool
|
|
10
|
+
* schema change requires sub-agent training to use the batched form).
|
|
11
|
+
*
|
|
12
|
+
* Per-file failures are isolated: an unreadable file produces an error
|
|
13
|
+
* entry in the result rather than failing the whole batch. Encoding is
|
|
14
|
+
* fixed to utf8 to match the existing Read tool semantics.
|
|
15
|
+
*/
|
|
16
|
+
export interface BatchReadEntry {
|
|
17
|
+
path: string;
|
|
18
|
+
content: string | null;
|
|
19
|
+
error: string | null;
|
|
20
|
+
bytes: number;
|
|
21
|
+
}
|
|
22
|
+
export interface BatchReadResult {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
entries: BatchReadEntry[];
|
|
25
|
+
totalBytes: number;
|
|
26
|
+
errorCount: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Read up to MAX_BATCH_SIZE paths in parallel. When the flag is off,
|
|
30
|
+
* returns `enabled: false` with an empty entry list — caller falls back
|
|
31
|
+
* to the per-file path. Per-file failures populate `.error`, not throw.
|
|
32
|
+
*/
|
|
33
|
+
export declare function batchReadPaths(paths: string[]): Promise<BatchReadResult>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M7 (perf overhaul idea #7) — Read([paths]) parallel batch reader.
|
|
3
|
+
*
|
|
4
|
+
* The legacy flow is N serial Read tool calls = N LLM round trips. This
|
|
5
|
+
* helper reads an array of paths concurrently in a single Node.js step,
|
|
6
|
+
* returning a structured result the tool-router collapses into one
|
|
7
|
+
* tool_result delta. Eliminates the per-file LLM round trip cost.
|
|
8
|
+
*
|
|
9
|
+
* Flag: CC_OPENCLAW_PERF_READ_BATCH (default OFF, medium risk — tool
|
|
10
|
+
* schema change requires sub-agent training to use the batched form).
|
|
11
|
+
*
|
|
12
|
+
* Per-file failures are isolated: an unreadable file produces an error
|
|
13
|
+
* entry in the result rather than failing the whole batch. Encoding is
|
|
14
|
+
* fixed to utf8 to match the existing Read tool semantics.
|
|
15
|
+
*/
|
|
16
|
+
import { readFile } from 'node:fs/promises';
|
|
17
|
+
import { getPerfReadBatchEnabled } from '../config.js';
|
|
18
|
+
const MAX_BATCH_SIZE = 16;
|
|
19
|
+
const PER_FILE_BYTE_CAP = 1_048_576;
|
|
20
|
+
/**
|
|
21
|
+
* Read up to MAX_BATCH_SIZE paths in parallel. When the flag is off,
|
|
22
|
+
* returns `enabled: false` with an empty entry list — caller falls back
|
|
23
|
+
* to the per-file path. Per-file failures populate `.error`, not throw.
|
|
24
|
+
*/
|
|
25
|
+
export async function batchReadPaths(paths) {
|
|
26
|
+
if (!getPerfReadBatchEnabled()) {
|
|
27
|
+
return { enabled: false, entries: [], totalBytes: 0, errorCount: 0 };
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
30
|
+
return { enabled: true, entries: [], totalBytes: 0, errorCount: 0 };
|
|
31
|
+
}
|
|
32
|
+
const slice = paths.slice(0, MAX_BATCH_SIZE);
|
|
33
|
+
const entries = await Promise.all(slice.map(async (p) => {
|
|
34
|
+
try {
|
|
35
|
+
const buf = await readFile(p, 'utf8');
|
|
36
|
+
const content = buf.length > PER_FILE_BYTE_CAP ? buf.slice(0, PER_FILE_BYTE_CAP) : buf;
|
|
37
|
+
return { path: p, content, error: null, bytes: content.length };
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41
|
+
return { path: p, content: null, error: msg, bytes: 0 };
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
const totalBytes = entries.reduce((s, e) => s + e.bytes, 0);
|
|
45
|
+
const errorCount = entries.filter((e) => e.error !== null).length;
|
|
46
|
+
return { enabled: true, entries, totalBytes, errorCount };
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=read-batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-batch.js","sourceRoot":"","sources":["../../../../src/lib/perf/read-batch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAgBvD,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,iBAAiB,GAAG,SAAS,CAAC;AAEpC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAe;IAClD,IAAI,CAAC,uBAAuB,EAAE,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IACvE,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IACtE,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAA2B,EAAE;QAC7C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YACtC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YACvF,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QAClE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IACF,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;IAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M4 (perf overhaul idea #4) — collapse <available_skills> sysprompt block.
|
|
3
|
+
*
|
|
4
|
+
* The block is ~3K tokens of skill names + descriptions. Almost every turn
|
|
5
|
+
* the user invokes 0 or 1 skill, so the full listing is dead weight in the
|
|
6
|
+
* cache-relevant prefix. This collapses it to a one-line hint that points
|
|
7
|
+
* the model at an on-demand `openclaw skills list` tool call.
|
|
8
|
+
*
|
|
9
|
+
* The collapse is text-level so it composes with sysprompt-strip.ts without
|
|
10
|
+
* coupling to its pipeline. Caller invokes after the strip pass.
|
|
11
|
+
*
|
|
12
|
+
* Flag: CC_OPENCLAW_PERF_SKILL_ONDEMAND (default OFF).
|
|
13
|
+
*
|
|
14
|
+
* Idempotent — replacing an already-collapsed sysprompt is a no-op.
|
|
15
|
+
*/
|
|
16
|
+
export interface CollapseResult {
|
|
17
|
+
content: string;
|
|
18
|
+
changed: boolean;
|
|
19
|
+
bytesRemoved: number;
|
|
20
|
+
}
|
|
21
|
+
/** Collapse the <available_skills> block; no-op when the flag is off. */
|
|
22
|
+
export declare function collapseSkillList(sysprompt: string): CollapseResult;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M4 (perf overhaul idea #4) — collapse <available_skills> sysprompt block.
|
|
3
|
+
*
|
|
4
|
+
* The block is ~3K tokens of skill names + descriptions. Almost every turn
|
|
5
|
+
* the user invokes 0 or 1 skill, so the full listing is dead weight in the
|
|
6
|
+
* cache-relevant prefix. This collapses it to a one-line hint that points
|
|
7
|
+
* the model at an on-demand `openclaw skills list` tool call.
|
|
8
|
+
*
|
|
9
|
+
* The collapse is text-level so it composes with sysprompt-strip.ts without
|
|
10
|
+
* coupling to its pipeline. Caller invokes after the strip pass.
|
|
11
|
+
*
|
|
12
|
+
* Flag: CC_OPENCLAW_PERF_SKILL_ONDEMAND (default OFF).
|
|
13
|
+
*
|
|
14
|
+
* Idempotent — replacing an already-collapsed sysprompt is a no-op.
|
|
15
|
+
*/
|
|
16
|
+
import { getPerfSkillOnDemandEnabled } from '../config.js';
|
|
17
|
+
const SKILL_BLOCK_RE = /<available_skills>[\s\S]*?<\/available_skills>/g;
|
|
18
|
+
const HINT_LINE = '<available_skills>Skills are available via the `openclaw skills list` tool. Invoke it when the user mentions a slash-command or asks "what skills are available?" — do not list them proactively.</available_skills>';
|
|
19
|
+
/** Collapse the <available_skills> block; no-op when the flag is off. */
|
|
20
|
+
export function collapseSkillList(sysprompt) {
|
|
21
|
+
if (!getPerfSkillOnDemandEnabled()) {
|
|
22
|
+
return { content: sysprompt, changed: false, bytesRemoved: 0 };
|
|
23
|
+
}
|
|
24
|
+
let bytesRemoved = 0;
|
|
25
|
+
let changed = false;
|
|
26
|
+
const content = sysprompt.replace(SKILL_BLOCK_RE, (match) => {
|
|
27
|
+
if (match === HINT_LINE)
|
|
28
|
+
return match;
|
|
29
|
+
bytesRemoved += match.length - HINT_LINE.length;
|
|
30
|
+
changed = true;
|
|
31
|
+
return HINT_LINE;
|
|
32
|
+
});
|
|
33
|
+
return { content, changed, bytesRemoved };
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=skill-list-collapse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-list-collapse.js","sourceRoot":"","sources":["../../../../src/lib/perf/skill-list-collapse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,cAAc,GAAG,iDAAiD,CAAC;AACzE,MAAM,SAAS,GACb,sNAAsN,CAAC;AAQzN,yEAAyE;AACzE,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,IAAI,CAAC,2BAA2B,EAAE,EAAE,CAAC;QACnC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IACjE,CAAC;IACD,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE;QAC1D,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACtC,YAAY,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QAChD,OAAO,GAAG,IAAI,CAAC;QACf,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M2 (perf overhaul idea #2) — typing-prefetch subprocess warmup.
|
|
3
|
+
*
|
|
4
|
+
* Telegram emits `sendChatAction` upstream events while the user is typing
|
|
5
|
+
* (the "typing…" indicator). This module debounces those signals into a
|
|
6
|
+
* single "warm the subprocess pool for this chatId" trigger, then invokes
|
|
7
|
+
* the registered warmup callback. The actual subprocess.spawn() is owned
|
|
8
|
+
* by the session-manager / cli pool — this module is just the signal layer.
|
|
9
|
+
*
|
|
10
|
+
* Flag: CC_OPENCLAW_PERF_TYPING_PREFETCH (default OFF).
|
|
11
|
+
*
|
|
12
|
+
* Coalescing: at most one warmup per chatId per WARMUP_COOLDOWN_MS. Prevents
|
|
13
|
+
* "typing… typing… typing…" hammer events from spawning N subprocesses.
|
|
14
|
+
*/
|
|
15
|
+
export declare const WARMUP_COOLDOWN_MS = 4000;
|
|
16
|
+
/** Register the actual warmup callback (called by session-manager bootstrap). */
|
|
17
|
+
export declare function setWarmupHandler(fn: ((chatId: string) => void) | null): void;
|
|
18
|
+
/**
|
|
19
|
+
* Notify that user is typing in chatId. Triggers warmup at most once per
|
|
20
|
+
* cooldown window. No-op when flag off, no handler registered, or in cooldown.
|
|
21
|
+
* Returns true iff a warmup was actually triggered.
|
|
22
|
+
*/
|
|
23
|
+
export declare function notifyTyping(chatId: string, now?: number): boolean;
|
|
24
|
+
/** Test hook — reset the cooldown map. */
|
|
25
|
+
export declare function _resetTypingPrefetchForTests(): void;
|