@kenkaiiii/ggcoder 5.82.0 → 5.83.1
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/README.md +1 -1
- package/dist/app-sidecar/app-settings.js +11 -5
- package/dist/app-sidecar/app-settings.js.map +1 -1
- package/dist/app-sidecar/auth-routes.js +8 -3
- package/dist/app-sidecar/auth-routes.js.map +1 -1
- package/dist/app-sidecar/goal-routes.js +7 -0
- package/dist/app-sidecar/goal-routes.js.map +1 -1
- package/dist/app-sidecar/run-control-routes.js +31 -3
- package/dist/app-sidecar/run-control-routes.js.map +1 -1
- package/dist/app-sidecar.js +65 -11
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli/auth.js +22 -1
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli.js +7 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/core/agent-notifications.d.ts +18 -0
- package/dist/core/agent-notifications.js +35 -2
- package/dist/core/agent-notifications.js.map +1 -1
- package/dist/core/agent-session.d.ts +71 -1
- package/dist/core/agent-session.js +175 -16
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/policy.d.ts +2 -2
- package/dist/core/compaction/policy.js +86 -43
- package/dist/core/compaction/policy.js.map +1 -1
- package/dist/core/compaction/token-estimator.d.ts +4 -1
- package/dist/core/compaction/token-estimator.js +5 -2
- package/dist/core/compaction/token-estimator.js.map +1 -1
- package/dist/core/idle-wake.d.ts +62 -0
- package/dist/core/idle-wake.js +103 -0
- package/dist/core/idle-wake.js.map +1 -0
- package/dist/core/process-manager.d.ts +51 -11
- package/dist/core/process-manager.js +229 -54
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts +5 -1
- package/dist/core/slash-commands.js +24 -9
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/steering.d.ts +7 -0
- package/dist/core/steering.js +13 -0
- package/dist/core/steering.js.map +1 -1
- package/dist/core/subagent-manager.js +5 -4
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/goals-agent/goals-agent.js +3 -2
- package/dist/goals-agent/goals-agent.js.map +1 -1
- package/dist/interactive.js +9 -3
- package/dist/interactive.js.map +1 -1
- package/dist/modes/acp-mode.js +8 -1
- package/dist/modes/acp-mode.js.map +1 -1
- package/dist/modes/agent-home-mode.js +13 -1
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/rpc-mode.d.ts +2 -1
- package/dist/modes/rpc-mode.js +15 -3
- package/dist/modes/rpc-mode.js.map +1 -1
- package/dist/modes/serve-mode.js +13 -1
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/tools/background-watch-lint.d.ts +18 -0
- package/dist/tools/background-watch-lint.js +102 -0
- package/dist/tools/background-watch-lint.js.map +1 -0
- package/dist/tools/bash.js +5 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/generate-image.d.ts +1 -5
- package/dist/tools/generate-image.js +188 -142
- package/dist/tools/generate-image.js.map +1 -1
- package/dist/tools/subagent-shared.js +4 -1
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/task-output.js +11 -1
- package/dist/tools/task-output.js.map +1 -1
- package/dist/ui/components/ModelSelector.d.ts +10 -0
- package/dist/ui/components/ModelSelector.js +51 -5
- package/dist/ui/components/ModelSelector.js.map +1 -1
- package/dist/utils/image.d.ts +6 -0
- package/dist/utils/image.js +9 -0
- package/dist/utils/image.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,63 +1,106 @@
|
|
|
1
1
|
export const DEFAULT_COMPACTION_THRESHOLD = 0.85;
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Caps on the context window used for compaction TRIGGERING, per provider.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* time-to-first-token at 213K tokens). A session sitting at 21% of a 1M
|
|
10
|
-
* window therefore paid a 45-70s tax on EVERY model call without ever
|
|
11
|
-
* approaching the 85% compaction trigger.
|
|
5
|
+
* The trigger is `threshold × min(contextWindow, cap)`. A cap moves only when
|
|
6
|
+
* compaction summarizes; the model's true window still governs hard limits
|
|
7
|
+
* (request size, output budgets), and min() keeps a smaller advertised window
|
|
8
|
+
* authoritative — a cap never raises a trigger.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
10
|
+
* Every entry below needs evidence of ONE of these, cited inline:
|
|
14
11
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* tokens, zero stalls in 2,000+ turns). Capping these would throw away
|
|
21
|
-
* working context for nothing.
|
|
22
|
-
* 3. UNKNOWN — the conservative default cap below. Any provider we have
|
|
23
|
-
* not measured gets it, so a user on ANY model is protected from the
|
|
24
|
-
* mega-window + cold-prefill pattern. The `cache_health` /
|
|
25
|
-
* `prompt_cache_miss` diag lines (agent-loop) turn tier 3 into tier 1 or
|
|
26
|
-
* 2 with one line here as evidence arrives — remove a provider's cap
|
|
27
|
-
* once its logs show healthy cache reads at large sizes, lower it when
|
|
28
|
-
* they show misses.
|
|
12
|
+
* - a hard input limit below the advertised window;
|
|
13
|
+
* - measured slow turns (re-prefill without prompt caching);
|
|
14
|
+
* - a price tier that reprices the WHOLE request once the prompt crosses a
|
|
15
|
+
* size, so every turn past it costs more;
|
|
16
|
+
* - the vendor's own agent compacting this model earlier on the same route.
|
|
29
17
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
18
|
+
* "uncapped" needs evidence too: automatic prompt caching with cheap cache
|
|
19
|
+
* hits and no long-context tier, or measured fast turns at large sizes.
|
|
20
|
+
* Providers with no evidence either way keep {@link DEFAULT_LATENCY_WINDOW_CAP}.
|
|
21
|
+
* Verified 2026-10-16 against the vendors' agents in source (deepseek-harness,
|
|
22
|
+
* gemini-cli, qwen-code, codex, CodeWhale's model/pricing tables) and docs.
|
|
34
23
|
*/
|
|
35
|
-
const
|
|
24
|
+
const PROVIDER_WINDOW_CAPS = {
|
|
25
|
+
// Measured: TTFT 1.1s <50K → 3.6s at 700K–1M, ~100% cache reads over
|
|
26
|
+
// 26,000+ turns (session logs, 2026-10). No long-context price tier. Claude
|
|
27
|
+
anthropic: "uncapped",
|
|
28
|
+
// Price line: once input passes 272K, OpenAI bills the WHOLE request at 2×
|
|
29
|
+
// input/cache and 1.5× output (OpenAI model pages, via CodeWhale pricing.rs
|
|
30
|
+
// OPENAI_LONG_CONTEXT_SURCHARGE_THRESHOLD). Codex itself stays under it,
|
|
31
|
+
// compacting at 90% of a 272K window (codex-rs/protocol/src/openai_models.rs
|
|
32
|
+
// auto_compact_token_limit). Trigger 231K on both routes: the API key's
|
|
33
|
+
// 1.05M window is capped here, the Codex OAuth route already passes 272K.
|
|
34
|
+
openai: 272_000,
|
|
35
|
+
// Measured slow: every turn re-prefilled the whole context on the Coding
|
|
36
|
+
// Plan endpoint at ~3–5K tok/s (2026-09-22 sidecar log: 44–68s TTFT at 213K).
|
|
36
37
|
glm: 176_000,
|
|
38
|
+
// DeepSeek's own agent compacts at 80% of a 1M window with no cap
|
|
39
|
+
// (deepseek-harness packages/compaction: ratio 0.8, DEFAULT_CONTEXT_WINDOW
|
|
40
|
+
// 1_000_000). Automatic prefix caching, hits ~1/50 of a miss (pi models:
|
|
41
|
+
// cacheRead 0.006 vs input 0.30), no long-context surcharge.
|
|
42
|
+
deepseek: "uncapped",
|
|
43
|
+
// Kimi K3: automatic caching, hit $0.30 vs miss $3.00, no long-context tier
|
|
44
|
+
// (platform.kimi.ai pricing, via CodeWhale pricing.rs). Kimi For Coding
|
|
45
|
+
// (K2.8 Preview) serves up to 1M (kimi.com/code/docs).
|
|
46
|
+
moonshot: "uncapped",
|
|
47
|
+
// Measured: mimo-v2.6-pro ~1,600 turns, TTFT 4–7s, 95–100% of input read
|
|
48
|
+
// from cache up to 250K (session logs). Cache hit $0.0036 vs miss $0.435
|
|
49
|
+
// (~1/120), and V2.6 has a single price tier (Xiaomi rate card).
|
|
50
|
+
xiaomi: "uncapped",
|
|
51
|
+
// MiniMax-M3 doubles its price once input passes 512K (0.30 → 0.60 in, cache
|
|
52
|
+
// 0.06 → 0.12: CodeWhale pricing.rs MINIMAX_M3_LONG_CONTEXT_THRESHOLD, from
|
|
53
|
+
// MiniMax's pay-as-you-go page), and 512K is the guaranteed window; "1M" is
|
|
54
|
+
// up to. Trigger 435K stays under the line.
|
|
55
|
+
minimax: 512_000,
|
|
56
|
+
// Google's own Gemini CLI compresses at 50% of the window on this same
|
|
57
|
+
// Code Assist route (packages/cli/src/ui/constants.ts
|
|
58
|
+
// DEFAULT_COMPRESSION_THRESHOLD = 0.5; tokenLimit 1_048_576 for every
|
|
59
|
+
// Gemini model), and implicit caching on Code Assist is unverified. 616K ×
|
|
60
|
+
// 0.85 = 524K ≈ 50% of 1,048,576. Flash and Flash Lite are flat-priced at any
|
|
61
|
+
// prompt size; 3.1 Pro has its own price-line cap in MODEL_WINDOW_CAPS.
|
|
62
|
+
gemini: 616_000,
|
|
63
|
+
// Price line: xAI bills the WHOLE request at 2× ($4 / $1 cached / $12) once a
|
|
64
|
+
// grok-4.7 prompt reaches 200K tokens (docs.x.ai/developers/pricing;
|
|
65
|
+
// CodeWhale pricing.rs GROK_4_6_LONG_CONTEXT_THRESHOLD = 200_000). Trigger
|
|
66
|
+
// 170K stays under the line.
|
|
67
|
+
xai: 200_000,
|
|
68
|
+
// Qwen's own agent (qwen-code) runs qwen3.8-max at a 1M window and compacts
|
|
69
|
+
// at 85% (chatCompressionService DEFAULT_PCT). OpenRouter lists cache reads
|
|
70
|
+
// at $0.25 vs $2.00 input and no long-context tier.
|
|
71
|
+
openrouter: "uncapped",
|
|
37
72
|
};
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Per-model caps, checked before the provider's rule: a model whose pricing
|
|
75
|
+
* differs from the rest of its provider.
|
|
76
|
+
*/
|
|
77
|
+
const MODEL_WINDOW_CAPS = {
|
|
78
|
+
// Price line: Gemini 3.1 Pro bills the WHOLE request at $4 / $18 instead of
|
|
79
|
+
// $2 / $12 once the prompt passes 200K tokens (Gemini API pricing); the
|
|
80
|
+
// Flash models stay flat. On a Code Assist licence this compacts earlier than
|
|
81
|
+
// Gemini CLI's 50%, which keeps API-key users off the higher rate.
|
|
82
|
+
"gemini-3.1-pro-preview": 200_000,
|
|
83
|
+
};
|
|
84
|
+
/** Conservative ceiling for providers with NO evidence either way (Sakana, the
|
|
85
|
+
* Hugging Face router — whose served window and caching vary by backend).
|
|
86
|
+
* Only bites windows above ~470K; 200–272K windows are unaffected. */
|
|
45
87
|
const DEFAULT_LATENCY_WINDOW_CAP = 400_000;
|
|
46
|
-
/** The
|
|
47
|
-
export function resolveLatencyWindowCap(provider) {
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
return
|
|
51
|
-
|
|
88
|
+
/** The effective trigger window cap for a provider (and model), if one applies. */
|
|
89
|
+
export function resolveLatencyWindowCap(provider, model) {
|
|
90
|
+
const modelCap = model === undefined ? undefined : MODEL_WINDOW_CAPS[model];
|
|
91
|
+
if (modelCap !== undefined)
|
|
92
|
+
return modelCap;
|
|
93
|
+
const rule = PROVIDER_WINDOW_CAPS[provider];
|
|
94
|
+
if (rule === "uncapped")
|
|
52
95
|
return undefined;
|
|
53
|
-
return DEFAULT_LATENCY_WINDOW_CAP;
|
|
96
|
+
return rule ?? DEFAULT_LATENCY_WINDOW_CAP;
|
|
54
97
|
}
|
|
55
98
|
/** One trigger policy shared by app sessions and CLI compaction. */
|
|
56
99
|
export function resolveCompactionPolicy(options) {
|
|
57
100
|
const threshold = Number.isFinite(options.threshold) && options.threshold > 0 && options.threshold < 1
|
|
58
101
|
? options.threshold
|
|
59
102
|
: DEFAULT_COMPACTION_THRESHOLD;
|
|
60
|
-
const latencyCap = resolveLatencyWindowCap(options.provider);
|
|
103
|
+
const latencyCap = resolveLatencyWindowCap(options.provider, options.model);
|
|
61
104
|
// The trigger fires against the latency-capped window when one applies —
|
|
62
105
|
// min() keeps a provider-declared window smaller than the cap authoritative.
|
|
63
106
|
const effectiveWindow = latencyCap
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/core/compaction/policy.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AAEjD
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/core/compaction/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,7 +27,10 @@ export declare function measureConversationChars(messages: Message[]): {
|
|
|
27
27
|
* its authoritative total input tokens (uncached + cache read/write). Skips
|
|
28
28
|
* histories containing image/video parts.
|
|
29
29
|
*/
|
|
30
|
-
export declare function calibrateEstimatorFromUsage(history: Message[], usage: Usage
|
|
30
|
+
export declare function calibrateEstimatorFromUsage(history: Message[], usage: Usage,
|
|
31
|
+
/** Characters the provider also counted but `history` lacks — the tool
|
|
32
|
+
* definitions sent with the request. Omitting them biases the ratio low. */
|
|
33
|
+
extraChars?: number): void;
|
|
31
34
|
export declare function estimateTokens(text: string): number;
|
|
32
35
|
export declare function estimateMessageTokens(message: Message): number;
|
|
33
36
|
export declare function estimateConversationTokens(messages: Message[]): number;
|
|
@@ -108,12 +108,15 @@ export function measureConversationChars(messages) {
|
|
|
108
108
|
* its authoritative total input tokens (uncached + cache read/write). Skips
|
|
109
109
|
* histories containing image/video parts.
|
|
110
110
|
*/
|
|
111
|
-
export function calibrateEstimatorFromUsage(history, usage
|
|
111
|
+
export function calibrateEstimatorFromUsage(history, usage,
|
|
112
|
+
/** Characters the provider also counted but `history` lacks — the tool
|
|
113
|
+
* definitions sent with the request. Omitting them biases the ratio low. */
|
|
114
|
+
extraChars = 0) {
|
|
112
115
|
const { chars, hasMedia } = measureConversationChars(history);
|
|
113
116
|
if (hasMedia)
|
|
114
117
|
return;
|
|
115
118
|
const totalInputTokens = usage.inputTokens + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
|
|
116
|
-
calibrateEstimator(chars, totalInputTokens);
|
|
119
|
+
calibrateEstimator(chars + Math.max(0, extraChars), totalInputTokens);
|
|
117
120
|
}
|
|
118
121
|
function getModelCharsPerToken() {
|
|
119
122
|
if (!activeModel)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-estimator.js","sourceRoot":"","sources":["../../../src/core/compaction/token-estimator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"token-estimator.js","sourceRoot":"","sources":["../../../src/core/compaction/token-estimator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wake an idle session when its own background work needs attention.
|
|
3
|
+
*
|
|
4
|
+
* Background alerts — a build or CI watcher that failed, a task that exited
|
|
5
|
+
* non-zero, a child agent that finished — ride the steering path, which only
|
|
6
|
+
* exists while a run is live. Once the agent has ended its turn, an alert sat
|
|
7
|
+
* in the queue until the user happened to send another message (measured: 8 of
|
|
8
|
+
* 483 runs began with a status update that had been waiting for exactly that).
|
|
9
|
+
*
|
|
10
|
+
* This starts a run for the alert instead, inside hard limits:
|
|
11
|
+
*
|
|
12
|
+
* - Never while anything else could deliver it: a live run, a run being
|
|
13
|
+
* claimed, an autopilot cycle. The alert waits for that run's next steering
|
|
14
|
+
* boundary instead, and is retried here if the run ends without draining it.
|
|
15
|
+
* - Never while the user is deciding something (a plan awaiting review).
|
|
16
|
+
* - Never after the user pressed Stop, until they send another message. Stop
|
|
17
|
+
* means "stop acting on your own", and a background exit is not a reason to
|
|
18
|
+
* override it.
|
|
19
|
+
* - At most {@link DEFAULT_MAX_WAKES_PER_PROMPT} wakes per user message, so a
|
|
20
|
+
* flapping task cannot keep the agent (and the bill) running unattended.
|
|
21
|
+
*
|
|
22
|
+
* Pure policy over injected callbacks; the host owns the run itself.
|
|
23
|
+
*/
|
|
24
|
+
/** Batch alerts that land together (a failure line, then the exit) into one run. */
|
|
25
|
+
export declare const DEFAULT_IDLE_WAKE_DEBOUNCE_MS = 2000;
|
|
26
|
+
/** Unattended wakes allowed per human prompt. */
|
|
27
|
+
export declare const DEFAULT_MAX_WAKES_PER_PROMPT = 2;
|
|
28
|
+
export type IdleWakeSkip = "stopped" | "budget" | "blocked" | "nothing-pending";
|
|
29
|
+
export interface IdleWakeDeps {
|
|
30
|
+
/** A run, a run claim, or an autopilot cycle is live and will deliver it. */
|
|
31
|
+
isBusy: () => boolean;
|
|
32
|
+
/** The user is deciding something (a plan awaiting review). */
|
|
33
|
+
isBlocked: () => boolean;
|
|
34
|
+
/** An urgent alert is still waiting to be delivered. */
|
|
35
|
+
hasPendingAlert: () => boolean;
|
|
36
|
+
/** Monotonic count of human prompts; budgets and Stop reset when it moves. */
|
|
37
|
+
humanPromptSeq: () => number;
|
|
38
|
+
/** Start the wake run; resolves when it has settled. */
|
|
39
|
+
start: () => Promise<void>;
|
|
40
|
+
/** Observability hook for skipped wakes. */
|
|
41
|
+
onSkip?: (reason: IdleWakeSkip) => void;
|
|
42
|
+
debounceMs?: number;
|
|
43
|
+
maxWakesPerPrompt?: number;
|
|
44
|
+
}
|
|
45
|
+
export declare class IdleWakeController {
|
|
46
|
+
private readonly deps;
|
|
47
|
+
private timer;
|
|
48
|
+
private budgetSeq;
|
|
49
|
+
private wakesUsed;
|
|
50
|
+
private stoppedAtSeq;
|
|
51
|
+
private starting;
|
|
52
|
+
private disposed;
|
|
53
|
+
constructor(deps: IdleWakeDeps);
|
|
54
|
+
/** An urgent alert was queued. Debounced; safe to call often. */
|
|
55
|
+
trigger(): void;
|
|
56
|
+
/** The user pressed Stop: no unattended wakes until their next message. */
|
|
57
|
+
noteStop(): void;
|
|
58
|
+
dispose(): void;
|
|
59
|
+
private cancelTimer;
|
|
60
|
+
private fire;
|
|
61
|
+
private skip;
|
|
62
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wake an idle session when its own background work needs attention.
|
|
3
|
+
*
|
|
4
|
+
* Background alerts — a build or CI watcher that failed, a task that exited
|
|
5
|
+
* non-zero, a child agent that finished — ride the steering path, which only
|
|
6
|
+
* exists while a run is live. Once the agent has ended its turn, an alert sat
|
|
7
|
+
* in the queue until the user happened to send another message (measured: 8 of
|
|
8
|
+
* 483 runs began with a status update that had been waiting for exactly that).
|
|
9
|
+
*
|
|
10
|
+
* This starts a run for the alert instead, inside hard limits:
|
|
11
|
+
*
|
|
12
|
+
* - Never while anything else could deliver it: a live run, a run being
|
|
13
|
+
* claimed, an autopilot cycle. The alert waits for that run's next steering
|
|
14
|
+
* boundary instead, and is retried here if the run ends without draining it.
|
|
15
|
+
* - Never while the user is deciding something (a plan awaiting review).
|
|
16
|
+
* - Never after the user pressed Stop, until they send another message. Stop
|
|
17
|
+
* means "stop acting on your own", and a background exit is not a reason to
|
|
18
|
+
* override it.
|
|
19
|
+
* - At most {@link DEFAULT_MAX_WAKES_PER_PROMPT} wakes per user message, so a
|
|
20
|
+
* flapping task cannot keep the agent (and the bill) running unattended.
|
|
21
|
+
*
|
|
22
|
+
* Pure policy over injected callbacks; the host owns the run itself.
|
|
23
|
+
*/
|
|
24
|
+
/** Batch alerts that land together (a failure line, then the exit) into one run. */
|
|
25
|
+
export const DEFAULT_IDLE_WAKE_DEBOUNCE_MS = 2_000;
|
|
26
|
+
/** Unattended wakes allowed per human prompt. */
|
|
27
|
+
export const DEFAULT_MAX_WAKES_PER_PROMPT = 2;
|
|
28
|
+
export class IdleWakeController {
|
|
29
|
+
deps;
|
|
30
|
+
timer = null;
|
|
31
|
+
budgetSeq = -1;
|
|
32
|
+
wakesUsed = 0;
|
|
33
|
+
stoppedAtSeq = null;
|
|
34
|
+
starting = false;
|
|
35
|
+
disposed = false;
|
|
36
|
+
constructor(deps) {
|
|
37
|
+
this.deps = deps;
|
|
38
|
+
}
|
|
39
|
+
/** An urgent alert was queued. Debounced; safe to call often. */
|
|
40
|
+
trigger() {
|
|
41
|
+
if (this.disposed || this.timer !== null)
|
|
42
|
+
return;
|
|
43
|
+
this.timer = setTimeout(() => {
|
|
44
|
+
this.timer = null;
|
|
45
|
+
void this.fire();
|
|
46
|
+
}, this.deps.debounceMs ?? DEFAULT_IDLE_WAKE_DEBOUNCE_MS);
|
|
47
|
+
this.timer.unref?.();
|
|
48
|
+
}
|
|
49
|
+
/** The user pressed Stop: no unattended wakes until their next message. */
|
|
50
|
+
noteStop() {
|
|
51
|
+
this.stoppedAtSeq = this.deps.humanPromptSeq();
|
|
52
|
+
this.cancelTimer();
|
|
53
|
+
}
|
|
54
|
+
dispose() {
|
|
55
|
+
this.disposed = true;
|
|
56
|
+
this.cancelTimer();
|
|
57
|
+
}
|
|
58
|
+
cancelTimer() {
|
|
59
|
+
if (this.timer !== null)
|
|
60
|
+
clearTimeout(this.timer);
|
|
61
|
+
this.timer = null;
|
|
62
|
+
}
|
|
63
|
+
async fire() {
|
|
64
|
+
if (this.disposed || this.starting)
|
|
65
|
+
return;
|
|
66
|
+
if (!this.deps.hasPendingAlert())
|
|
67
|
+
return this.skip("nothing-pending");
|
|
68
|
+
const seq = this.deps.humanPromptSeq();
|
|
69
|
+
if (this.stoppedAtSeq === seq)
|
|
70
|
+
return this.skip("stopped");
|
|
71
|
+
if (seq !== this.budgetSeq) {
|
|
72
|
+
this.budgetSeq = seq;
|
|
73
|
+
this.wakesUsed = 0;
|
|
74
|
+
}
|
|
75
|
+
if (this.wakesUsed >= (this.deps.maxWakesPerPrompt ?? DEFAULT_MAX_WAKES_PER_PROMPT)) {
|
|
76
|
+
return this.skip("budget");
|
|
77
|
+
}
|
|
78
|
+
if (this.deps.isBusy()) {
|
|
79
|
+
// The live run should drain it at its next steering boundary. If that
|
|
80
|
+
// run is already past its last boundary, the alert would strand — so
|
|
81
|
+
// look again shortly rather than trusting the run to have taken it.
|
|
82
|
+
this.trigger();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (this.deps.isBlocked())
|
|
86
|
+
return this.skip("blocked");
|
|
87
|
+
this.wakesUsed += 1;
|
|
88
|
+
this.starting = true;
|
|
89
|
+
try {
|
|
90
|
+
await this.deps.start();
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
this.starting = false;
|
|
94
|
+
}
|
|
95
|
+
// An alert that landed after the wake run's last steering boundary.
|
|
96
|
+
if (this.deps.hasPendingAlert())
|
|
97
|
+
this.trigger();
|
|
98
|
+
}
|
|
99
|
+
skip(reason) {
|
|
100
|
+
this.deps.onSkip?.(reason);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=idle-wake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idle-wake.js","sourceRoot":"","sources":["../../src/core/idle-wake.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,8 +10,11 @@ export interface BackgroundProcess {
|
|
|
10
10
|
startedAt: number;
|
|
11
11
|
exitCode: number | null;
|
|
12
12
|
lastReadOffset: number;
|
|
13
|
-
/** Unread
|
|
14
|
-
wakeReason?:
|
|
13
|
+
/** Unread wake, retained even if it fired before task_output waited. */
|
|
14
|
+
wakeReason?: WakeReason;
|
|
15
|
+
/** Log offset the wake is about. A read that reaches it consumes the wake;
|
|
16
|
+
* one that started earlier does not (the wake fired mid-read). */
|
|
17
|
+
wakeOffset?: number;
|
|
15
18
|
/**
|
|
16
19
|
* Last known size of `logFile` in bytes. Kept current by the progress
|
|
17
20
|
* watcher, the exit handler and every `readOutput`, so consumers (notably
|
|
@@ -37,6 +40,17 @@ export interface ReadOutputResult {
|
|
|
37
40
|
/** Ceiling on a single blocking `waitForExitOrWake`, so one wedged process cannot
|
|
38
41
|
* hold the agent loop indefinitely; callers re-wait if they still want to. */
|
|
39
42
|
export declare const MAX_PROCESS_WAIT_MS = 600000;
|
|
43
|
+
/**
|
|
44
|
+
* Remove terminal colour and cursor codes. Test runners and CLIs colour their
|
|
45
|
+
* output, and a code can sit inside the very word a wake looks for:
|
|
46
|
+
* `\e[31mFAIL\e[0mED` never matches /FAILED/ until it is stripped.
|
|
47
|
+
*/
|
|
48
|
+
export declare function stripAnsi(text: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* The first line in `text` that looks like a tool reporting a failure, ANSI
|
|
51
|
+
* codes stripped, or undefined. Exported for tests.
|
|
52
|
+
*/
|
|
53
|
+
export declare function findFailureLine(text: string): string | undefined;
|
|
40
54
|
/**
|
|
41
55
|
* Model-declared wake conditions for a background task. The agent states up
|
|
42
56
|
* front what output it cares about (or what silence means), and the watcher
|
|
@@ -45,12 +59,22 @@ export declare const MAX_PROCESS_WAIT_MS = 600000;
|
|
|
45
59
|
* or re-reading generic progress checkpoints hoping to spot the signal.
|
|
46
60
|
*/
|
|
47
61
|
export interface WakeRules {
|
|
48
|
-
/** Wake
|
|
62
|
+
/** Wake whenever new log output matches this regex — every new match, not
|
|
63
|
+
* just the first, so a dev server that breaks again after a fix is seen. */
|
|
49
64
|
pattern?: RegExp;
|
|
50
65
|
/** Wake when the process is still running but has logged nothing for this
|
|
51
66
|
* many milliseconds (a stalled build/hang detector). One-shot. */
|
|
52
67
|
silenceMs?: number;
|
|
53
68
|
}
|
|
69
|
+
/** Why a wake fired. `failure` is the built-in detector, armed on every task. */
|
|
70
|
+
export type WakeReason = "pattern" | "silence" | "failure";
|
|
71
|
+
/** How a blocking wait on one background task ended. */
|
|
72
|
+
export type WaitOutcome = "exited" | "timeout" | "unknown" | WakeReason | "other";
|
|
73
|
+
export interface WaitResult {
|
|
74
|
+
reason: WaitOutcome;
|
|
75
|
+
/** For `other`: the notification about a different task that ended the wait. */
|
|
76
|
+
detail?: string;
|
|
77
|
+
}
|
|
54
78
|
export interface ProcessManagerOps {
|
|
55
79
|
platform?: NodeJS.Platform;
|
|
56
80
|
kill?: typeof process.kill;
|
|
@@ -94,6 +118,9 @@ export declare class ProcessManager {
|
|
|
94
118
|
private wakeStates;
|
|
95
119
|
/** Log size at the last emitted checkpoint, so a quiet process stays quiet. */
|
|
96
120
|
private watchedSizes;
|
|
121
|
+
/** Processes the agent or host is deliberately stopping: their non-zero exit
|
|
122
|
+
* is the stop working, not news, so it must not raise an urgent alert. */
|
|
123
|
+
private stopping;
|
|
97
124
|
/** Timestamp of the last retention sweep; 0 means "never swept". */
|
|
98
125
|
private lastPruneAt;
|
|
99
126
|
constructor(ops?: ProcessManagerOps);
|
|
@@ -149,16 +176,22 @@ export declare class ProcessManager {
|
|
|
149
176
|
/** Read the trailing bytes of a log without loading the whole file. */
|
|
150
177
|
private readTail;
|
|
151
178
|
/** Read the bytes of a log in `[start, end)` without loading the file. */
|
|
152
|
-
private
|
|
179
|
+
private readBytes;
|
|
153
180
|
/**
|
|
154
|
-
* Wake
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
181
|
+
* Wake watcher, evaluated every {@link WAKE_INTERVAL_MS}. Always runs the
|
|
182
|
+
* built-in failure detector (one-shot), plus the model's declared rules when
|
|
183
|
+
* a notification queue can deliver them: `pattern` fires on every new match,
|
|
184
|
+
* `silenceMs` once. It retires on exit, or once nothing is left that could
|
|
185
|
+
* still fire. Unlike the progress watcher it never backs off — a late match
|
|
186
|
+
* on a quiet dev server is precisely the point.
|
|
187
|
+
*
|
|
188
|
+
* Returns whether the model's declared rules were armed. Without a queue they
|
|
189
|
+
* are not (nothing could deliver them), but failure detection still releases
|
|
190
|
+
* a blocking `task_output` wait directly.
|
|
160
191
|
*/
|
|
161
192
|
private armWakeWatcher;
|
|
193
|
+
/** Record a wake on the process and release anyone blocked waiting on it. */
|
|
194
|
+
private fireWake;
|
|
162
195
|
private evaluateWakeRules;
|
|
163
196
|
/** Stop and forget a process's wake watcher. */
|
|
164
197
|
private disposeWakeWatcher;
|
|
@@ -182,7 +215,14 @@ export declare class ProcessManager {
|
|
|
182
215
|
* loop step to be delivered, which is exactly what an agent with nothing
|
|
183
216
|
* else to do does not have.
|
|
184
217
|
*/
|
|
185
|
-
waitForExitOrWake(id: string, timeoutMs: number, signal?: AbortSignal): Promise<
|
|
218
|
+
waitForExitOrWake(id: string, timeoutMs: number, signal?: AbortSignal): Promise<WaitOutcome>;
|
|
219
|
+
/**
|
|
220
|
+
* {@link waitForExitOrWake}, plus what ended it. Besides this task exiting or
|
|
221
|
+
* waking, the wait also ends when ANOTHER task raises an urgent notification
|
|
222
|
+
* (a different build failed, a child agent finished): the agent blocked here
|
|
223
|
+
* has no other way to learn that before its wait runs out.
|
|
224
|
+
*/
|
|
225
|
+
waitForActivity(id: string, timeoutMs: number, signal?: AbortSignal): Promise<WaitResult>;
|
|
186
226
|
readOutput(id: string, fromStart?: boolean): Promise<ReadOutputResult>;
|
|
187
227
|
/**
|
|
188
228
|
* Write input to a running background process's stdin, enabling interactive
|