@pikiloom/kernel 0.2.20 → 0.2.21
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 +59 -0
- 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
|
@@ -40,6 +40,9 @@ export declare function claudeBgSettleQuietMs(): number;
|
|
|
40
40
|
export declare function claudeModelStallMs(): number;
|
|
41
41
|
export declare const CLAUDE_TRUNCATED_RECOVERY_PROMPT: string;
|
|
42
42
|
export declare function claudeTruncatedRecoveryEnabled(): boolean;
|
|
43
|
+
export declare function isClaudeSyntheticResumeNoise(text: string): boolean;
|
|
44
|
+
export declare function claudeProducedRealOutput(s: any): boolean;
|
|
45
|
+
export declare function claudeResumeNoopRetryLimit(): number;
|
|
43
46
|
export declare function claudeUserEventHasToolResult(ev: any): boolean;
|
|
44
47
|
export declare function isTerminalTaskStatus(status: unknown): boolean;
|
|
45
48
|
export declare function trackClaudeBackgroundTask(ev: any, s: any): void;
|
package/dist/drivers/claude.js
CHANGED
|
@@ -79,6 +79,10 @@ export class ClaudeDriver {
|
|
|
79
79
|
let settled = false;
|
|
80
80
|
// One-shot guard for the truncated-turn recovery injection (see the result handler).
|
|
81
81
|
let truncatedRecoveryAttempted = false;
|
|
82
|
+
// Bounded counter for the no-op-resume recovery re-injection (see the result handler): a
|
|
83
|
+
// resume of a session left incomplete by a prior turn can no-op several times before the
|
|
84
|
+
// CLI's repair clears and it runs our prompt for real.
|
|
85
|
+
let noopResumeRetries = 0;
|
|
82
86
|
// holdCap: hard backstop while a background task is still running (never-completing daemon).
|
|
83
87
|
// quiet: fires once all known background tasks finished AND Claude has gone quiet, so trailing
|
|
84
88
|
// wake-up turns can still land before we close (see claudeBgSettleQuietMs).
|
|
@@ -278,6 +282,29 @@ export class ClaudeDriver {
|
|
|
278
282
|
settleResult({ stopReason: 'truncated', kill: false });
|
|
279
283
|
return;
|
|
280
284
|
}
|
|
285
|
+
// No-op resume repair: resuming a session whose previous turn was left incomplete (a
|
|
286
|
+
// background hold that reclaimed its sub-agents/workflow — the ultra "no response"
|
|
287
|
+
// report — an interrupt, a stall) makes the CLI answer with a synthetic "No response
|
|
288
|
+
// requested." no-op that ran NONE of our prompt (no model turn at all), instead of
|
|
289
|
+
// processing the message. Settling here delivers a silent "(no textual response)" and
|
|
290
|
+
// drops the user's send. stdin is still open: re-issue the prompt so the CLI drives
|
|
291
|
+
// through its repair to a real answer within this one turn. Bounded (the repair can
|
|
292
|
+
// no-op more than once); the post-tool stall watchdog is the backstop if the CLI never
|
|
293
|
+
// engages. Scoped to resumes (input.sessionId) — a fresh session has no dangling turn.
|
|
294
|
+
if (!hasError && !!input.sessionId && !claudeProducedRealOutput(state)
|
|
295
|
+
&& noopResumeRetries < claudeResumeNoopRetryLimit()) {
|
|
296
|
+
noopResumeRetries++;
|
|
297
|
+
let injected = false;
|
|
298
|
+
try {
|
|
299
|
+
child.stdin.write(claudeUserMessage(input.prompt, input.attachments) + '\n');
|
|
300
|
+
injected = true;
|
|
301
|
+
}
|
|
302
|
+
catch { /* fall through to settle */ }
|
|
303
|
+
if (injected) {
|
|
304
|
+
armModelStall();
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
281
308
|
// kill=false: the CLI persists the turn into the session jsonl AFTER emitting
|
|
282
309
|
// `result`, and on a large session that flush (a whole-file rewrite) takes long
|
|
283
310
|
// enough that an immediate SIGTERM kills the process mid-write — the reply was
|
|
@@ -523,6 +550,13 @@ export function handleClaudeEvent(ev, s, emit) {
|
|
|
523
550
|
return;
|
|
524
551
|
}
|
|
525
552
|
if (t === 'assistant') {
|
|
553
|
+
// Synthetic resume-repair no-op: resuming a session whose previous turn was left incomplete
|
|
554
|
+
// makes the CLI emit an assistant message with model '<synthetic>' whose only text is
|
|
555
|
+
// "No response requested." (paired with an isMeta "Continue from where you left off." user
|
|
556
|
+
// record). It is NOT model output — mirror the legacy driver and drop it, so it neither shows
|
|
557
|
+
// as the reply nor counts as real output (the no-op-resume recovery keys off that emptiness).
|
|
558
|
+
if (ev.message?.model === '<synthetic>' && isClaudeSyntheticResumeNoise(claudeContentText(ev.message?.content)))
|
|
559
|
+
return;
|
|
526
560
|
const contents = ev.message?.content || [];
|
|
527
561
|
for (const b of contents) {
|
|
528
562
|
if (b?.type !== 'tool_use')
|
|
@@ -791,6 +825,31 @@ export function claudeTruncatedRecoveryEnabled() {
|
|
|
791
825
|
const v = String(process.env.PIKILOOM_CLAUDE_TRUNCATED_RECOVERY ?? '').trim().toLowerCase();
|
|
792
826
|
return v !== '0' && v !== 'false' && v !== 'off';
|
|
793
827
|
}
|
|
828
|
+
// The CLI's resume-repair placeholder for a turn that never concluded: an assistant message with
|
|
829
|
+
// model '<synthetic>' whose only text is "No response requested." (paired with an isMeta "Continue
|
|
830
|
+
// from where you left off." user record). It is NOT model output. Mirrors the legacy driver.
|
|
831
|
+
export function isClaudeSyntheticResumeNoise(text) {
|
|
832
|
+
const t = (text || '').trim().toLowerCase();
|
|
833
|
+
return t === 'no response requested.' || t === 'no response requested';
|
|
834
|
+
}
|
|
835
|
+
// True once the turn produced ANY real model output — streamed or whole-message text/reasoning, a
|
|
836
|
+
// tool use, or a spawned sub-agent. False for a pure no-op (a synthetic resume-repair result that
|
|
837
|
+
// ran none of the prompt), which is exactly what the no-op-resume recovery keys off. Pure +
|
|
838
|
+
// exported for hermetic testing.
|
|
839
|
+
export function claudeProducedRealOutput(s) {
|
|
840
|
+
return !!(s?.streamedText || s?.streamedReasoning
|
|
841
|
+
|| (s?.tools?.size ?? 0) > 0 || (s?.subAgents?.size ?? 0) > 0
|
|
842
|
+
|| (typeof s?.text === 'string' && s.text.trim().length > 0)
|
|
843
|
+
|| (typeof s?.reasoning === 'string' && s.reasoning.trim().length > 0));
|
|
844
|
+
}
|
|
845
|
+
// How many times to re-issue the prompt when a resume comes back a pure no-op before giving up and
|
|
846
|
+
// settling (see the result handler). Bounded so a genuinely dead session can't loop forever; 0
|
|
847
|
+
// disables the recovery. Override with PIKILOOM_CLAUDE_RESUME_NOOP_RETRIES.
|
|
848
|
+
const CLAUDE_RESUME_NOOP_RETRY_DEFAULT = 3;
|
|
849
|
+
export function claudeResumeNoopRetryLimit() {
|
|
850
|
+
const raw = Number(process.env.PIKILOOM_CLAUDE_RESUME_NOOP_RETRIES);
|
|
851
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : CLAUDE_RESUME_NOOP_RETRY_DEFAULT;
|
|
852
|
+
}
|
|
794
853
|
// True when a claude `type:'user'` stream event carries at least one tool_result block — i.e. the
|
|
795
854
|
// tool loop just handed control back to the model. Pure + exported for hermetic testing.
|
|
796
855
|
export function claudeUserEventHasToolResult(ev) {
|
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, claudeBgAgentHoldCapMs, claudeTurnHasAgentBackground, claudeBgHoldRecheckMs, 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, isClaudeSyntheticResumeNoise, claudeProducedRealOutput, claudeResumeNoopRetryLimit, } 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, claudeBgAgentHoldCapMs, claudeTurnHasAgentBackground, claudeBgHoldRecheckMs, 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, isClaudeSyntheticResumeNoise, claudeProducedRealOutput, claudeResumeNoopRetryLimit, } 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.21",
|
|
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",
|