@pikiloom/kernel 0.2.14 → 0.2.15
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/drivers/claude.d.ts +3 -0
- package/dist/drivers/claude.js +53 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/drivers/claude.d.ts
CHANGED
|
@@ -33,6 +33,9 @@ export declare function claudeContextWindowFromModel(model: unknown): number | n
|
|
|
33
33
|
export declare function claudeEffectiveContextWindow(advertised: number | null): number | null;
|
|
34
34
|
export declare function handleClaudeEvent(ev: any, s: any, emit: (e: DriverEvent) => void): void;
|
|
35
35
|
export declare function claudeBgHoldCapMs(): number;
|
|
36
|
+
export declare function claudeBgAgentHoldCapMs(): number;
|
|
37
|
+
export declare function claudeTurnHasAgentBackground(s: any): boolean;
|
|
38
|
+
export declare function claudeBgHoldRecheckMs(): number;
|
|
36
39
|
export declare function claudeBgSettleQuietMs(): number;
|
|
37
40
|
export declare function claudeModelStallMs(): number;
|
|
38
41
|
export declare const CLAUDE_TRUNCATED_RECOVERY_PROMPT: string;
|
package/dist/drivers/claude.js
CHANGED
|
@@ -59,6 +59,8 @@ export class ClaudeDriver {
|
|
|
59
59
|
// Dangling-tool-loop tracking: sawToolResult flips on the first tool_result; textSinceToolResult
|
|
60
60
|
// goes false at every tool_result and true again once the model streams visible text.
|
|
61
61
|
sawToolResult: false, textSinceToolResult: false,
|
|
62
|
+
// Wall-clock of the last parsed stream event — the hold cap defers while this is fresh.
|
|
63
|
+
lastEventAt: Date.now(),
|
|
62
64
|
sessionId: null, model: null,
|
|
63
65
|
stopReason: null, error: null,
|
|
64
66
|
input: null, output: null, cached: null,
|
|
@@ -138,13 +140,29 @@ export class ClaudeDriver {
|
|
|
138
140
|
ok: opts.ok ?? !state.error, text: state.text, reasoning: state.reasoning || undefined,
|
|
139
141
|
error: state.error, stopReason: opts.stopReason ?? state.stopReason, sessionId: state.sessionId, usage: usageOf(),
|
|
140
142
|
}, opts.kill ?? true);
|
|
141
|
-
//
|
|
142
|
-
// "still running in the background" so the
|
|
143
|
+
// Cap while holding for a still-running background task (stopReason marks it as
|
|
144
|
+
// "still running in the background" so the terminal presentation reads right). Idempotent —
|
|
145
|
+
// the countdown is absolute from the first arm. Sub-agent-backed holds use the longer
|
|
146
|
+
// agent cap, and a cap that fires while events are still flowing defers instead of
|
|
147
|
+
// cutting an actively-working turn mid-generation (the 2026-07-06 "停止不再继续生成":
|
|
148
|
+
// the 10-min cap yanked a live research turn 22s after its last tool_result and the
|
|
149
|
+
// graceful-close leak guard then killed the 4 still-running Explore agents).
|
|
143
150
|
const armHoldCap = () => {
|
|
144
151
|
if (holdCapTimer)
|
|
145
152
|
return;
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
const capMs = claudeTurnHasAgentBackground(state) ? claudeBgAgentHoldCapMs() : claudeBgHoldCapMs();
|
|
154
|
+
const fire = () => {
|
|
155
|
+
holdCapTimer = null;
|
|
156
|
+
if (settled)
|
|
157
|
+
return;
|
|
158
|
+
if (Date.now() - (state.lastEventAt ?? 0) < claudeBgSettleQuietMs()) {
|
|
159
|
+
holdCapTimer = setTimeout(fire, claudeBgHoldRecheckMs());
|
|
160
|
+
unref(holdCapTimer);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
settleResult({ stopReason: 'background', kill: false });
|
|
164
|
+
};
|
|
165
|
+
holdCapTimer = setTimeout(fire, capMs);
|
|
148
166
|
unref(holdCapTimer);
|
|
149
167
|
};
|
|
150
168
|
// Grace close once all background work is done: settle gracefully (no kill) if Claude stays
|
|
@@ -225,6 +243,7 @@ export class ClaudeDriver {
|
|
|
225
243
|
catch {
|
|
226
244
|
continue;
|
|
227
245
|
}
|
|
246
|
+
state.lastEventAt = Date.now();
|
|
228
247
|
handleClaudeEvent(ev, state, ctx.emit);
|
|
229
248
|
const pending = pendingClaudeBackgroundTasks(state);
|
|
230
249
|
// Any model output means the model is alive and streaming — cancel the post-tool stall watchdog.
|
|
@@ -689,6 +708,27 @@ export function claudeBgHoldCapMs() {
|
|
|
689
708
|
const raw = Number(process.env.PIKILOOM_CLAUDE_BG_HOLD_MS);
|
|
690
709
|
return Number.isFinite(raw) && raw > 0 ? raw : CLAUDE_BG_HOLD_CAP_DEFAULT_MS;
|
|
691
710
|
}
|
|
711
|
+
// Sub-agent-backed background work (Task/Agent tool launches) gets a much longer hold: these
|
|
712
|
+
// are finite model-driven jobs whose results the turn is genuinely waiting on — a research
|
|
713
|
+
// fleet mapping two repos legitimately runs past the 10-minute daemon cap, and capping it
|
|
714
|
+
// there discarded the agents' work and cut the turn mid-flight ("停止不再继续生成").
|
|
715
|
+
// Override with PIKILOOM_CLAUDE_BG_AGENT_HOLD_MS.
|
|
716
|
+
const CLAUDE_BG_AGENT_HOLD_CAP_DEFAULT_MS = 45 * 60_000;
|
|
717
|
+
export function claudeBgAgentHoldCapMs() {
|
|
718
|
+
const raw = Number(process.env.PIKILOOM_CLAUDE_BG_AGENT_HOLD_MS);
|
|
719
|
+
return Number.isFinite(raw) && raw > 0 ? raw : CLAUDE_BG_AGENT_HOLD_CAP_DEFAULT_MS;
|
|
720
|
+
}
|
|
721
|
+
export function claudeTurnHasAgentBackground(s) {
|
|
722
|
+
return !!s?.bgAgentTasks?.size;
|
|
723
|
+
}
|
|
724
|
+
// When the hold cap fires while events are still flowing, defer and re-check on this cadence
|
|
725
|
+
// instead of yanking an actively-working turn (the cap bounds SILENT stuck holds, nothing else).
|
|
726
|
+
// Override with PIKILOOM_CLAUDE_BG_HOLD_RECHECK_MS (tests need sub-second rechecks).
|
|
727
|
+
const CLAUDE_BG_HOLD_RECHECK_DEFAULT_MS = 30_000;
|
|
728
|
+
export function claudeBgHoldRecheckMs() {
|
|
729
|
+
const raw = Number(process.env.PIKILOOM_CLAUDE_BG_HOLD_RECHECK_MS);
|
|
730
|
+
return Number.isFinite(raw) && raw > 0 ? raw : CLAUDE_BG_HOLD_RECHECK_DEFAULT_MS;
|
|
731
|
+
}
|
|
692
732
|
// Once every KNOWN background task has finished, how long Claude must stay quiet (no further
|
|
693
733
|
// output at all) before we close a background turn. A completed task's status races AHEAD of the
|
|
694
734
|
// wake-up turn that reports it — with N parallel agents finishing together, the last agent's
|
|
@@ -743,6 +783,15 @@ export function trackClaudeBackgroundTask(ev, s) {
|
|
|
743
783
|
const subtype = ev?.subtype;
|
|
744
784
|
if (subtype !== 'task_started' && subtype !== 'task_updated' && subtype !== 'task_notification')
|
|
745
785
|
return;
|
|
786
|
+
// Sub-agent-backed background tasks (Task/Agent tool launches) are FINITE model-driven jobs,
|
|
787
|
+
// unlike a detached shell that may daemonize forever — they earn a much longer hold cap
|
|
788
|
+
// (see armHoldCap). The task_started's tool_use_id points at the launching tool call, which
|
|
789
|
+
// for sub-agents lives in s.subAgents.
|
|
790
|
+
if (subtype === 'task_started') {
|
|
791
|
+
const tui = String(ev?.tool_use_id ?? '').trim();
|
|
792
|
+
if (tui && s?.subAgents?.has?.(tui))
|
|
793
|
+
(s.bgAgentTasks ||= new Set()).add(String(ev?.task_id ?? ev?.id ?? tui));
|
|
794
|
+
}
|
|
746
795
|
const id = String(ev?.task_id ?? ev?.tool_use_id ?? '').trim();
|
|
747
796
|
if (!id)
|
|
748
797
|
return;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type { SessionStore, CoreSessionRecord, ModelResolver, ModelInjection, To
|
|
|
9
9
|
export type { LoomIO, PromptInput, Surface, SurfaceCapabilities, Plugin, SpawnContribution, } from './contracts/surface.js';
|
|
10
10
|
export { FsSessionStore, NullModelResolver, NoopToolProvider, PassthroughSystemPromptBuilder, AutoCancelInteractionHandler, DeferToTerminalInteractionHandler, NoopCatalog, defaultBaseDir, } from './ports/defaults.js';
|
|
11
11
|
export { EchoDriver } from './drivers/echo.js';
|
|
12
|
-
export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgSettleQuietMs, type ClaudeResultSettleDecision, claudeModelStallMs, claudeUserEventHasToolResult, handleClaudeEvent, claudeTurnEndedDangling, claudeTruncatedRecoveryEnabled, CLAUDE_TRUNCATED_RECOVERY_PROMPT, } from './drivers/claude.js';
|
|
12
|
+
export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgAgentHoldCapMs, claudeTurnHasAgentBackground, claudeBgHoldRecheckMs, claudeBgSettleQuietMs, type ClaudeResultSettleDecision, claudeModelStallMs, claudeUserEventHasToolResult, handleClaudeEvent, claudeTurnEndedDangling, claudeTruncatedRecoveryEnabled, CLAUDE_TRUNCATED_RECOVERY_PROMPT, } from './drivers/claude.js';
|
|
13
13
|
export { CodexDriver } from './drivers/codex.js';
|
|
14
14
|
export { GeminiDriver } from './drivers/gemini.js';
|
|
15
15
|
export { AcpDriver, type AcpDriverConfig } from './drivers/acp.js';
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ export { attachTui } from './runtime/tui.js';
|
|
|
19
19
|
export { FsSessionStore, NullModelResolver, NoopToolProvider, PassthroughSystemPromptBuilder, AutoCancelInteractionHandler, DeferToTerminalInteractionHandler, NoopCatalog, defaultBaseDir, } from './ports/defaults.js';
|
|
20
20
|
// Drivers & surfaces (also available via subpath exports)
|
|
21
21
|
export { EchoDriver } from './drivers/echo.js';
|
|
22
|
-
export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgSettleQuietMs, claudeModelStallMs, claudeUserEventHasToolResult, handleClaudeEvent, claudeTurnEndedDangling, claudeTruncatedRecoveryEnabled, CLAUDE_TRUNCATED_RECOVERY_PROMPT, } from './drivers/claude.js';
|
|
22
|
+
export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgAgentHoldCapMs, claudeTurnHasAgentBackground, claudeBgHoldRecheckMs, claudeBgSettleQuietMs, claudeModelStallMs, claudeUserEventHasToolResult, handleClaudeEvent, claudeTurnEndedDangling, claudeTruncatedRecoveryEnabled, CLAUDE_TRUNCATED_RECOVERY_PROMPT, } from './drivers/claude.js';
|
|
23
23
|
export { CodexDriver } from './drivers/codex.js';
|
|
24
24
|
export { GeminiDriver } from './drivers/gemini.js';
|
|
25
25
|
export { AcpDriver } from './drivers/acp.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikiloom/kernel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "Heterogeneous coding agents (Claude / Codex / Gemini / ACP) -> an interaction-friendly, accumulating session snapshot + control handle, over pluggable surfaces (IM, Web, tunnel, TUI). The reusable core pikiloom itself is built on.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|