@humwick/runner 0.130.4 → 0.130.6
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/LICENSE +661 -167
- package/agent-bin/humwick +2 -2
- package/agent-bin/humwick-browser +4 -0
- package/hooks/fff-search-redirect/fff-search-redirect.cjs +1 -1
- package/hooks/web-tools-redirect/web-tools-redirect.cjs +2 -2
- package/package.json +8 -11
- package/plugins/browser/skills/playwright-cli/SKILL.md +8 -6
- package/plugins/claude/.claude-plugin/plugin.json +3 -3
- package/plugins/claude/claude-statusline-probe.cjs +26 -16
- package/plugins/claude/hooks/gate-run-guard.provisioned.mjs +47 -17
- package/plugins/claude/hooks/gate-run-guard.sh +3 -3
- package/plugins/claude/hooks/gate-run-guard.ts +5 -5
- package/plugins/claude/hooks/kill-guard.provisioned.mjs +1 -1
- package/plugins/claude/hooks/kill-guard.sh +4 -4
- package/plugins/claude/hooks/kill-guard.ts +2 -2
- package/plugins/claude/hooks/permission-request.sh +1 -1
- package/plugins/claude/hooks/post-compact.sh +1 -1
- package/plugins/claude/hooks/post-tool-use.sh +3 -3
- package/plugins/claude/hooks/pre-compact.sh +2 -2
- package/plugins/claude/hooks/read-only-bash-guard.sh +4 -4
- package/plugins/claude/hooks/relay-status.sh +32 -32
- package/plugins/claude/hooks/session-end.sh +2 -2
- package/plugins/claude/hooks/session-start.sh +3 -3
- package/plugins/claude/hooks/shell-trust.ts +1 -1
- package/plugins/claude/hooks/stop-failure.sh +2 -2
- package/plugins/claude/hooks/stop.sh +6 -6
- package/plugins/claude/hooks/subagent-skill-policy.cjs +8 -6
- package/plugins/claude/hooks/subagent-stop.sh +3 -3
- package/plugins/claude/hooks/user-prompt-submit.sh +1 -1
- package/plugins/claude/hooks/wait-loop-guard.ts +3 -3
- package/plugins/claude/monitors/monitors.json +2 -2
- package/plugins/claude/monitors/relay-monitor.provisioned.mjs +96 -62
- package/plugins/claude/monitors/relay-monitor.ts +5 -5
- package/plugins/claude/skills/label/SKILL.md +3 -3
- package/plugins/claude/skills/message/SKILL.md +5 -5
- package/plugins/claude/skills/react/SKILL.md +1 -1
- package/plugins/claude/skills/read-message/SKILL.md +4 -4
- package/plugins/claude/skills/reply/SKILL.md +5 -5
- package/plugins/claude/skills/send-claimable/SKILL.md +5 -5
- package/plugins/claude/skills/slice-first/SKILL.md +5 -3
- package/plugins/claude/skills/slice-first/references/plan-recipe.md +30 -29
- package/plugins/claude/skills/status/SKILL.md +3 -3
- package/plugins/claude/skills/tags/SKILL.md +5 -5
- package/plugins/claude/skills/task/SKILL.md +10 -10
- package/plugins/claude-output-style/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-output-style/output-styles/{agent-relay.md → humwick.md} +1 -1
- package/plugins/codex/skills/guide/SKILL.md +3 -3
- package/plugins/codex/skills/label/SKILL.md +3 -3
- package/plugins/codex/skills/message/SKILL.md +5 -5
- package/plugins/codex/skills/react/SKILL.md +1 -1
- package/plugins/codex/skills/read-message/SKILL.md +4 -4
- package/plugins/codex/skills/reply/SKILL.md +5 -5
- package/plugins/codex/skills/send-claimable/SKILL.md +5 -5
- package/plugins/codex/skills/slice-first/SKILL.md +5 -3
- package/plugins/codex/skills/slice-first/references/plan-recipe.md +30 -29
- package/plugins/codex/skills/status/SKILL.md +3 -3
- package/plugins/codex/skills/tags/SKILL.md +5 -5
- package/plugins/codex/skills/task/SKILL.md +11 -10
- package/src/adapter.ts +95 -92
- package/src/adapters/claude-api.ts +8 -11
- package/src/adapters/claude-context-command.ts +1 -1
- package/src/adapters/claude-credentials.ts +7 -3
- package/src/adapters/claude-delivery-lanes.ts +2 -2
- package/src/adapters/claude-delivery.ts +16 -16
- package/src/adapters/claude-effort.ts +2 -2
- package/src/adapters/claude-exit.ts +2 -2
- package/src/adapters/claude-headless-mirror.ts +18 -2
- package/src/adapters/claude-headless-refusal.ts +3 -3
- package/src/adapters/claude-headless.ts +11 -10
- package/src/adapters/claude-launch-exit.ts +1 -1
- package/src/adapters/claude-monitor-delivery-plan.ts +36 -16
- package/src/adapters/claude-permission.ts +5 -5
- package/src/adapters/claude-prompt-gates.ts +9 -9
- package/src/adapters/claude-prompt-hook.ts +2 -2
- package/src/adapters/claude-session-capture.ts +6 -6
- package/src/adapters/claude-session-probe.ts +25 -25
- package/src/adapters/claude-shutdown.ts +11 -11
- package/src/adapters/claude-status-detectors.ts +31 -31
- package/src/adapters/claude-subagents.ts +7 -7
- package/src/adapters/claude-submit-confirmation.ts +6 -6
- package/src/adapters/claude-tmux.ts +43 -43
- package/src/adapters/claude-token-usage-capture.ts +5 -5
- package/src/adapters/claude-tool-policy.ts +7 -7
- package/src/adapters/claude-transcript.ts +24 -24
- package/src/adapters/claude.ts +100 -100
- package/src/adapters/codex-agent-message-capture.ts +26 -63
- package/src/adapters/codex-app-server.ts +1 -1
- package/src/adapters/codex-client.ts +9 -9
- package/src/adapters/codex-effective-config.ts +1 -1
- package/src/adapters/codex-launch-config.ts +5 -5
- package/src/adapters/codex-rollout-context.ts +2 -2
- package/src/adapters/codex-skill-discovery.ts +1 -1
- package/src/adapters/codex-token-usage-capture.ts +16 -16
- package/src/adapters/codex-tool-output.ts +4 -4
- package/src/adapters/codex-version.ts +3 -3
- package/src/adapters/codex.ts +64 -64
- package/src/adapters/opencode-api.ts +1 -1
- package/src/adapters/opencode-context.ts +20 -22
- package/src/adapters/opencode-model-registry.ts +7 -7
- package/src/adapters/opencode-provider.ts +8 -8
- package/src/adapters/opencode-provisioning.ts +30 -0
- package/src/adapters/opencode-server-retry.ts +1 -1
- package/src/adapters/opencode-soft-steer.ts +5 -5
- package/src/adapters/opencode-spawn.ts +18 -14
- package/src/adapters/opencode-token-usage-capture.ts +12 -12
- package/src/adapters/opencode-tool-events.ts +15 -15
- package/src/adapters/opencode.ts +232 -232
- package/src/adapters/project-shell.ts +11 -11
- package/src/adapters/service-access.ts +6 -6
- package/src/adapters/tmux-offload.ts +9 -9
- package/src/adapters/tool-summary.ts +5 -5
- package/src/agent-redact.ts +1 -1
- package/src/agent-runner/commands/dispatch.ts +10 -10
- package/src/agent-runner/commands/lifecycle.ts +2 -2
- package/src/agent-runner/commands/prompt.ts +19 -19
- package/src/agent-runner/credentials.ts +5 -5
- package/src/agent-runner/inbox.ts +1 -1
- package/src/agent-runner/lifecycle-milestones.ts +3 -3
- package/src/agent-runner/lifecycle-phase.ts +2 -2
- package/src/agent-runner/liveness-exchange.ts +4 -10
- package/src/agent-runner/registration.ts +8 -8
- package/src/agent-runner/runner-context.ts +9 -14
- package/src/agent-runner/session-mirror.ts +12 -12
- package/src/agent-runner/task-claims.ts +2 -2
- package/src/agent-runner/token-usage-coverage.ts +1 -1
- package/src/agent-runner/turn-report.ts +12 -12
- package/src/api-budget-accounting.ts +3 -3
- package/src/area-memory.ts +6 -6
- package/src/artifact-path-materialize.ts +1 -1
- package/src/async-guard.ts +5 -5
- package/src/attachment-cache.ts +17 -22
- package/src/background-script-reconcile.ts +6 -6
- package/src/bounded-log.ts +1 -1
- package/src/busy-reconciler.ts +22 -22
- package/src/claim-tracker.ts +3 -3
- package/src/claude-settings.ts +8 -8
- package/src/claude-system-prompt.ts +1 -1
- package/src/claude-workspace-trust.ts +1 -1
- package/src/config.ts +62 -61
- package/src/context-boundary.ts +5 -5
- package/src/context-command.ts +3 -3
- package/src/control-server.ts +32 -32
- package/src/delivery-result.ts +2 -2
- package/src/effort-command.ts +2 -2
- package/src/empty-turn.ts +22 -14
- package/src/free-port.ts +1 -1
- package/src/gate-run-config.ts +3 -3
- package/src/host-asset-plan.ts +24 -17
- package/src/host-claude-config-worker.ts +2 -2
- package/src/host-sources.ts +2 -2
- package/src/index.ts +16 -16
- package/src/launch-assembly.ts +94 -96
- package/src/liveness.ts +15 -15
- package/src/logger.ts +3 -3
- package/src/mcp-outbox.ts +3 -3
- package/src/mcp-preflight.ts +2 -2
- package/src/mcp-source-selection.ts +2 -2
- package/src/mcp-tool-deferral.ts +1 -1
- package/src/mcp-vault-env.ts +2 -2
- package/src/message-body-config/config.ts +4 -4
- package/src/mid-turn-delivery.ts +2 -2
- package/src/native-memory-lever.ts +8 -8
- package/src/outbox.ts +15 -15
- package/src/pending-permission-store.ts +1 -1
- package/src/profile-home.ts +19 -19
- package/src/profile-projection.ts +26 -24
- package/src/project-instructions.ts +5 -5
- package/src/provider-auth-warning.ts +17 -0
- package/src/provider-double.ts +1 -1
- package/src/provider-hold.ts +3 -3
- package/src/provider-home-marker.ts +1 -1
- package/src/provider-home-tombstone.ts +1 -1
- package/src/provider-input-queue.ts +2 -2
- package/src/provider-integration.ts +15 -13
- package/src/provider-setup.ts +15 -3
- package/src/providers.ts +1 -1
- package/src/provisioning.ts +37 -40
- package/src/question-decision.ts +2 -2
- package/src/quota.ts +0 -6
- package/src/rate-limit.ts +24 -13
- package/src/refused-prompt-store.ts +2 -2
- package/src/relay-injection-events.ts +2 -2
- package/src/relay-instructions.ts +5 -5
- package/src/relay-mcp-proxy.long-turn-e2e.ts +13 -13
- package/src/relay-mcp-proxy.ts +57 -58
- package/src/relay-mcp.ts +55 -69
- package/src/reply-obligation-cache.ts +9 -9
- package/src/response-capture-report.ts +9 -9
- package/src/response-routing.ts +1 -1
- package/src/runner-core.ts +209 -222
- package/src/runner-exit.ts +1 -1
- package/src/runner-helpers.ts +10 -10
- package/src/runner-insights.ts +4 -2
- package/src/runner-mcp-proxy.ts +3 -3
- package/src/runner-spawn-config.ts +3 -3
- package/src/runtime-token-file.ts +1 -1
- package/src/runtime-token-remint.ts +1 -1
- package/src/service-access.ts +2 -2
- package/src/session-destroy.ts +1 -1
- package/src/session-insights.ts +1 -1
- package/src/session-scratch.ts +24 -37
- package/src/spawn-input.ts +6 -6
- package/src/startup-config-fetch.ts +4 -4
- package/src/subreaper.ts +3 -5
- package/src/test-run-dedup.ts +13 -13
- package/src/transcript-context.ts +2 -2
- package/src/transport-errors.ts +1 -1
- package/src/turn-opener.ts +1 -1
- package/src/turn-reconcile-event.ts +2 -2
- package/src/vault-proxy-hop.ts +2 -2
- package/src/web-tools.ts +2 -2
- package/agent-bin/agent-relay-browser +0 -10
- package/plugins/claude/skills/disconnect/SKILL.md +0 -16
- package/plugins/claude/skills/pair/SKILL.md +0 -30
- package/plugins/codex/skills/disconnect/SKILL.md +0 -15
- package/plugins/codex/skills/pair/SKILL.md +0 -29
- package/src/adapters/provider-quota-harvest.ts +0 -76
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { readdir } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, join } from "node:path";
|
|
3
|
-
//
|
|
3
|
+
// per-turn Claude token-usage capture: the runner-side half of the token-usage
|
|
4
4
|
// spine's first capture seam. On every Stop-hook session turn (runner-core's
|
|
5
5
|
// publishSessionTurn) the transcript's APPENDED tail is read and every NEW root-session
|
|
6
|
-
// assistant entry's API-reported usage is extracted (the
|
|
6
|
+
// assistant entry's API-reported usage is extracted (the sibling parser beside
|
|
7
7
|
// extractLatestContextUsage in adapters/claude-transcript.ts) and one spine record is
|
|
8
8
|
// POSTed per entry, keyed dedupeKey=entry-uuid so the spine's dedupe_key upsert makes
|
|
9
9
|
// replays and retries idempotent by construction.
|
|
@@ -33,7 +33,7 @@ import { claudeSessionIdFromTranscriptPath, TranscriptTokenUsageExtractor, type
|
|
|
33
33
|
import { incrementalFileNeedsReset, readIncrementalFileRange, splitIncrementalFileLines, statIncrementalFile, type IncrementalFileInfo, type IncrementalFileLineBuffer, type IncrementalFileLinePolicy, type IncrementalFilePosition } from "./incremental-file";
|
|
34
34
|
import type { ProviderTokenUsageRecord } from "../adapter";
|
|
35
35
|
|
|
36
|
-
/** The spine's turn-less report shape
|
|
36
|
+
/** The spine's turn-less report shape. */
|
|
37
37
|
export type TranscriptTokenUsageSpineRecord = ProviderTokenUsageRecord & { provider: "claude" };
|
|
38
38
|
|
|
39
39
|
// Claude assistant transcript entries carry both the full model output and usage. Eight
|
|
@@ -142,13 +142,13 @@ export class ClaudeTranscriptTokenUsageCapture {
|
|
|
142
142
|
|
|
143
143
|
private async settleTranscript(transcriptPath: string, options: { pollIntervalMs: number; timeoutMs: number; now?: () => number; sleep?: (ms: number) => Promise<unknown> }): Promise<number> {
|
|
144
144
|
// now/sleep are injectable so tests can drive the settle window on a virtual
|
|
145
|
-
// clock instead of racing real timers against host load
|
|
145
|
+
// clock instead of racing real timers against host load.
|
|
146
146
|
const now = options.now ?? Date.now;
|
|
147
147
|
const sleep = options.sleep ?? Bun.sleep;
|
|
148
148
|
const deadline = now() + options.timeoutMs;
|
|
149
149
|
const pollIntervalMs = Math.max(1, options.pollIntervalMs);
|
|
150
150
|
const quietWindowMs = Math.min(options.timeoutMs, Math.max(pollIntervalMs * 2, 25));
|
|
151
|
-
//
|
|
151
|
+
// review round 3 (reverts round 2's watermark bail, which was WRONG — it exited
|
|
152
152
|
// exactly when the watermark hadn't moved yet, which is precisely the state a still-
|
|
153
153
|
// flushing final entry looks like before it lands). Seed the quiet-window baseline at
|
|
154
154
|
// settle START rather than leaving it undefined until something is first found: without
|
|
@@ -5,13 +5,13 @@ import type { AgentProfileProviderOptions } from "@humwick/sdk";
|
|
|
5
5
|
import { isRecord } from "@humwick/sdk/types/guards";
|
|
6
6
|
import { claudeDisallowedWebTools } from "../web-tools";
|
|
7
7
|
|
|
8
|
-
// ──
|
|
8
|
+
// ── — eager "System tools" context trim for the coding/review profiles ─────────────────
|
|
9
9
|
// A BARE tool name on Claude Code's --disallowedTools removes the tool's schema from the
|
|
10
10
|
// model's eager context (the /context "System tools" bucket) WITHOUT touching MCP (mcp__*),
|
|
11
11
|
// skills, or core coding tools — which an allowlist (--tools) would put at risk. The Artifact
|
|
12
12
|
// family + Workflow carry the heaviest eager docstrings and coding/review agents never publish
|
|
13
13
|
// artifacts or run workflows; SendFeedback is misused by autonomous agents (also env-suppressed
|
|
14
|
-
// fleet-wide since
|
|
14
|
+
// fleet-wide since — this is the per-profile context-trim path); ScheduleWakeup is
|
|
15
15
|
// loop-only; ReportFindings duplicates relay report-up.
|
|
16
16
|
export const CLAUDE_CONTEXT_TRIM_DISALLOWED_TOOLS = ["Artifact", "ArtifactComments", "ArtifactData", "ArtifactCheck", "Workflow", "SendFeedback", "ScheduleWakeup", "ReportFindings"];
|
|
17
17
|
|
|
@@ -23,7 +23,7 @@ const CLAUDE_CONTEXT_TRIM_DEFAULT_PROFILES = new Set(["worker", "debugger", "rev
|
|
|
23
23
|
|
|
24
24
|
const CLAUDE_DISALLOWED_TOOLS_ARG = /^--(?:disallowedTools|disallowed-tools)(?:=(.*))?$/;
|
|
25
25
|
|
|
26
|
-
/** Effective
|
|
26
|
+
/** Effective denylist for this launch: a profile-configured array REPLACES the default,
|
|
27
27
|
* `null` opts the profile out entirely, absent falls back to the default — and only for the
|
|
28
28
|
* coding/review profiles. Resolves the profile via the established
|
|
29
29
|
* `agentProfile?.name || profile` pattern. */
|
|
@@ -41,12 +41,12 @@ export function claudeProfileDisallowedTools(config: Pick<RunnerSpawnConfig, "pr
|
|
|
41
41
|
}
|
|
42
42
|
const profileName = config.agentProfile?.name || config.profile || "";
|
|
43
43
|
if (!CLAUDE_CONTEXT_TRIM_DEFAULT_PROFILES.has(profileName)) return [];
|
|
44
|
-
// A profile that switches the Artifact or Workflow tool back on
|
|
44
|
+
// A profile that switches the Artifact or Workflow tool back on gets it.
|
|
45
45
|
const harness = config.agentProfile ? claudeHarnessOptions(config) : CLAUDE_HARNESS_DEFAULTS;
|
|
46
46
|
return CLAUDE_CONTEXT_TRIM_DISALLOWED_TOOLS.filter((tool) => tool === "Workflow" ? !harness.workflows : !tool.startsWith("Artifact") || !harness.artifacts);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// ──
|
|
49
|
+
// ── — harness context trim: Claude Code built-ins Relay agents never use ──────────────────
|
|
50
50
|
export type ClaudeAgentTypes = NonNullable<NonNullable<AgentProfileProviderOptions["claude"]>["agentTypes"]>;
|
|
51
51
|
const CLAUDE_AGENT_TYPES: readonly ClaudeAgentTypes[] = ["all", "general-purpose", "none"];
|
|
52
52
|
export interface ClaudeHarnessOptions {
|
|
@@ -146,7 +146,7 @@ function splitDisallowedToolsList(value: string): string[] {
|
|
|
146
146
|
return value.split(",").map((name) => name.trim()).filter(Boolean);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
/** Appends the profile's
|
|
149
|
+
/** Appends the profile's denylist to the launch args, merging with any inherited
|
|
150
150
|
* --disallowedTools entries into ONE canonical trailing occurrence (union, never clobber).
|
|
151
151
|
* No-op (args returned as-is) when the profile resolves no denylist. The single launch seam
|
|
152
152
|
* for this trim is ClaudeAdapter.buildSpawnArgs.
|
|
@@ -156,7 +156,7 @@ function splitDisallowedToolsList(value: string): string[] {
|
|
|
156
156
|
* launch would eat the prompt as a deny value (silent no-prompt launch). The `=` form
|
|
157
157
|
* terminates the flag so following positionals survive. */
|
|
158
158
|
export function claudeProfileDisallowedToolsArgs(args: string[], config: Pick<RunnerSpawnConfig, "harnessSubagents" | "profile" | "agentProfile">): string[] {
|
|
159
|
-
//
|
|
159
|
+
// a profile's switched-off web tools are hidden whatever its denylist says.
|
|
160
160
|
const deny = [...(harnessSubagentsEnabled(config) ? [] : ["Agent", "Task", "TeamCreate", "TeamDelete", "SendMessage"]), ...claudeProfileDisallowedTools(config), ...claudeHarnessTrim(config).deny, ...claudeDisallowedWebTools(config)];
|
|
161
161
|
if (!deny.length) return args;
|
|
162
162
|
const { kept, inherited } = partitionClaudeDisallowedToolsArgs(args);
|
|
@@ -32,11 +32,11 @@ export interface TurnStep {
|
|
|
32
32
|
label?: string;
|
|
33
33
|
category?: ToolCallCategory;
|
|
34
34
|
occurredAt?: number;
|
|
35
|
-
/**
|
|
35
|
+
/** redacted, size-capped tool input, only set for type "tool". */
|
|
36
36
|
params?: unknown;
|
|
37
37
|
/** The native tool_use id, only set for type "tool"; pairs the call with its tool_result. */
|
|
38
38
|
toolUseId?: string;
|
|
39
|
-
/**
|
|
39
|
+
/** absolute file paths the tool reads or edits, only set for type "tool". */
|
|
40
40
|
paths?: string[];
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -47,7 +47,7 @@ export interface TranscriptMessage {
|
|
|
47
47
|
// Assistant entries carry the model id and the API-reported token usage for
|
|
48
48
|
// the turn. `usage.{input,cache_creation_input,cache_read_input}_tokens` sum to
|
|
49
49
|
// the same context-size figure the status-line probe reports — read here so the
|
|
50
|
-
// runner can keep context stats advancing every turn without the probe
|
|
50
|
+
// runner can keep context stats advancing every turn without the probe.
|
|
51
51
|
model?: string;
|
|
52
52
|
usage?: TranscriptUsage;
|
|
53
53
|
}
|
|
@@ -59,7 +59,7 @@ export interface TranscriptUsage {
|
|
|
59
59
|
output_tokens?: number;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/** Fresh, per-turn context usage read straight from the transcript
|
|
62
|
+
/** Fresh, per-turn context usage read straight from the transcript. */
|
|
63
63
|
export interface TranscriptContextUsage {
|
|
64
64
|
/** input + cache-creation + cache-read tokens — the context size sent this turn. */
|
|
65
65
|
tokensUsed: number;
|
|
@@ -80,7 +80,7 @@ interface TranscriptEntry {
|
|
|
80
80
|
// sidechain entries to keep a subagent's reasoning/tools/responses from
|
|
81
81
|
// leaking into the parent agent's chat. Insights parsers (collectClaudeSession-
|
|
82
82
|
// Events/countSubstantiveTurns) intentionally do NOT filter — changing them
|
|
83
|
-
// would shift the
|
|
83
|
+
// would shift the baselines, a separate concern.
|
|
84
84
|
isSidechain?: boolean;
|
|
85
85
|
// Every entry carries its own `uuid` and its `parentUuid` (the entry it was
|
|
86
86
|
// appended after), forming a causal chain for the whole session. A `user`-type
|
|
@@ -113,7 +113,7 @@ function isRealUserPrompt(entry: TranscriptEntry): boolean {
|
|
|
113
113
|
return blocks(entry.message).some((b) => b.type === "text" && Boolean(b.text?.trim()));
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
//
|
|
116
|
+
// `closingOnly` keeps just the text after the turn's last tool call (the closing message,
|
|
117
117
|
// without narration the reasoning tail already mirrored); a turn ending on a tool keeps its last text.
|
|
118
118
|
class AssistantTurnText {
|
|
119
119
|
private texts: string[] = [];
|
|
@@ -282,7 +282,7 @@ interface QueueOperationEntry extends TranscriptEntry {
|
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
/**
|
|
285
|
-
*
|
|
285
|
+
* prompts left in Claude's input queue at the end of `jsonl`: a mid-turn prompt is `enqueue`d,
|
|
286
286
|
* then `remove`d when a tool boundary absorbs it, or `dequeue`d (every queued prompt at once) to run as
|
|
287
287
|
* the next turn after the Stop. So a dequeue or a new promptId empties the queue.
|
|
288
288
|
*/
|
|
@@ -323,13 +323,13 @@ function canonicalJson(value: unknown): string {
|
|
|
323
323
|
|
|
324
324
|
/**
|
|
325
325
|
* True when the transcript already contains the exact assistant tool_use block that
|
|
326
|
-
* triggered a PreToolUse hook — the settle-poll anchor
|
|
326
|
+
* triggered a PreToolUse hook — the settle-poll anchor. The hook payload's own
|
|
327
327
|
* `tool_name`/`tool_input` are the precise call that fired it, so matching them against
|
|
328
328
|
* the transcript's tool_use blocks (name + structural input equality) is exact, unlike
|
|
329
329
|
* guessing from transcript shape alone. Claude's JSONL flush is async/bursty and can
|
|
330
330
|
* lag well behind the hook firing, so the entry backing the hook's own call may still be
|
|
331
331
|
* missing from disk the instant the hook runs — this is what the settle-poll waits for.
|
|
332
|
-
*
|
|
332
|
+
* should adopt this same anchor for its own turn-final settle marker rather than
|
|
333
333
|
* inventing a second one.
|
|
334
334
|
*/
|
|
335
335
|
export function transcriptHasToolUseAnchor(jsonl: string, toolName: string, toolInput: unknown): boolean {
|
|
@@ -357,7 +357,7 @@ export function transcriptHasToolUseAnchor(jsonl: string, toolName: string, tool
|
|
|
357
357
|
* transcriptHasToolUseAnchor above). Used to stamp a resolved permission prompt with the
|
|
358
358
|
* tool call's real event time — a hook's own wall-clock receipt time can end up earlier than
|
|
359
359
|
* the entry the provider eventually flushes for that same call, which otherwise sorts the
|
|
360
|
-
* resolved prompt ahead of the narration that led up to it
|
|
360
|
+
* resolved prompt ahead of the narration that led up to it.
|
|
361
361
|
*/
|
|
362
362
|
export function transcriptToolUseOccurredAt(jsonl: string, toolName: string, toolInput: unknown): number | undefined {
|
|
363
363
|
const wantInput = canonicalJson(toolInput ?? {});
|
|
@@ -461,7 +461,7 @@ export class LatestTurnStepAccumulator {
|
|
|
461
461
|
}
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
-
/** Narration, reasoning and tool steps of one assistant message, shared by the transcript tail and the headless stream
|
|
464
|
+
/** Narration, reasoning and tool steps of one assistant message, shared by the transcript tail and the headless stream. */
|
|
465
465
|
export function claudeAssistantSteps(message: TranscriptMessage | undefined, occurredAt?: number): TurnStep[] {
|
|
466
466
|
const steps: TurnStep[] = [];
|
|
467
467
|
for (const b of blocks(message)) {
|
|
@@ -477,7 +477,7 @@ export function claudeAssistantSteps(message: TranscriptMessage | undefined, occ
|
|
|
477
477
|
return steps;
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
-
//
|
|
480
|
+
// Claude's file tools and the input field naming their file. Keyed on the tool name, not the
|
|
481
481
|
// category: MultiEdit is "generic". Claude's file tools take absolute paths only.
|
|
482
482
|
const CLAUDE_TOOL_PATH_FIELDS: Record<string, string> = {
|
|
483
483
|
Read: "file_path",
|
|
@@ -493,7 +493,7 @@ export function claudeToolPaths(name: string, input: Record<string, unknown> | u
|
|
|
493
493
|
return field && input ? absoluteToolPaths([input[field]]) : undefined;
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
-
/** The raw PostToolUse payload uses the same path contract as transcript tools
|
|
496
|
+
/** The raw PostToolUse payload uses the same path contract as transcript tools. */
|
|
497
497
|
export function claudeToolHookPaths(input: unknown): string[] | undefined {
|
|
498
498
|
return isRecord(input) && typeof input.tool_name === "string"
|
|
499
499
|
? claudeToolPaths(input.tool_name, isRecord(input.tool_input) ? input.tool_input : undefined) : undefined;
|
|
@@ -529,7 +529,7 @@ function transcriptEntryOccurredAt(entry: TranscriptEntry): number | undefined {
|
|
|
529
529
|
* Stable dedup keys for a turn's steps, in order. Each key is salted with how many
|
|
530
530
|
* identical (type,label,text) steps preceded it in the same window — so running the
|
|
531
531
|
* same tool twice with identical input within a turn yields two distinct keys and
|
|
532
|
-
* both show in the activity trace
|
|
532
|
+
* both show in the activity trace. Keying on occurrence-within-window rather
|
|
533
533
|
* than raw transcript index keeps the reasoning tailer idempotent when the "latest
|
|
534
534
|
* turn" window shrinks/resets mid-poll: a surviving step recomputes to the same or a
|
|
535
535
|
* lower occurrence, so an already-emitted step never re-fires.
|
|
@@ -544,7 +544,7 @@ export function stepDedupKeys(steps: TurnStep[]): string[] {
|
|
|
544
544
|
});
|
|
545
545
|
}
|
|
546
546
|
|
|
547
|
-
//
|
|
547
|
+
// Claude's native tool names → the provider-independent category taxonomy. Tools with
|
|
548
548
|
// no dedicated category (Grep, Glob, LS, WebFetch, WebSearch, ...) fall to "generic" rather than
|
|
549
549
|
// being force-fit into a category that doesn't really describe them — the fallback exists
|
|
550
550
|
// precisely so nothing is hidden from the dashboard's activity trace.
|
|
@@ -560,7 +560,7 @@ const CLAUDE_TOOL_CATEGORIES: Record<string, ToolCallCategory> = {
|
|
|
560
560
|
ToolSearch: "mcp-tool-search",
|
|
561
561
|
};
|
|
562
562
|
|
|
563
|
-
/** Map a Claude tool_use name onto the provider-independent tool-call category
|
|
563
|
+
/** Map a Claude tool_use name onto the provider-independent tool-call category. */
|
|
564
564
|
export function claudeToolCategory(name: string): ToolCallCategory {
|
|
565
565
|
if (name.startsWith("mcp__")) return "mcp-tool-call";
|
|
566
566
|
return CLAUDE_TOOL_CATEGORIES[name] ?? "generic";
|
|
@@ -575,7 +575,7 @@ export function summarizeToolUse(name: string, input: Record<string, unknown> |
|
|
|
575
575
|
return clipToolSummary(summary);
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
-
// --- Insights
|
|
578
|
+
// --- Insights: context-gathering ratio ---
|
|
579
579
|
//
|
|
580
580
|
// Computed mechanically from the whole-session transcript at session end — no model
|
|
581
581
|
// involvement, so it costs zero agent tokens and the agent can't game it. The ratio is
|
|
@@ -584,7 +584,7 @@ export function summarizeToolUse(name: string, input: Record<string, unknown> |
|
|
|
584
584
|
|
|
585
585
|
/**
|
|
586
586
|
* Normalize a full Claude transcript into the provider-agnostic `SessionEvent` stream
|
|
587
|
-
*
|
|
587
|
+
* The classifier and ratio math live in `session-insights.ts` and are shared
|
|
588
588
|
* across providers; this only knows the Claude JSONL shape. Events are emitted in
|
|
589
589
|
* transcript order so `leadingGather` is meaningful.
|
|
590
590
|
*/
|
|
@@ -628,7 +628,7 @@ export function analyzeSession(jsonl: string): SessionAnalysis | null {
|
|
|
628
628
|
return computeContextRatio(collectClaudeSessionEvents(jsonl));
|
|
629
629
|
}
|
|
630
630
|
|
|
631
|
-
/** Count substantive assistant turns — used by the
|
|
631
|
+
/** Count substantive assistant turns — used by the introspection gate. */
|
|
632
632
|
export function countSubstantiveTurns(jsonl: string): number {
|
|
633
633
|
let turns = 0;
|
|
634
634
|
for (const line of jsonl.split("\n")) {
|
|
@@ -650,7 +650,7 @@ export function countSubstantiveTurns(jsonl: string): number {
|
|
|
650
650
|
}
|
|
651
651
|
|
|
652
652
|
// Walk the transcript from the end and return the most recent root-session
|
|
653
|
-
// assistant turn's token usage
|
|
653
|
+
// assistant turn's token usage. This is the SAME input+cache token sum
|
|
654
654
|
// the Claude status-line probe reports, but read directly from the transcript —
|
|
655
655
|
// which the Stop hook hands the runner every turn — so context stats keep
|
|
656
656
|
// advancing across compact/clear/native-resume even when the status-line probe
|
|
@@ -697,7 +697,7 @@ export function sumUsageTokens(usage: TranscriptUsage): number | undefined {
|
|
|
697
697
|
return seen ? total : undefined;
|
|
698
698
|
}
|
|
699
699
|
|
|
700
|
-
//
|
|
700
|
+
// the token-usage spine's capture parser. Where extractLatestContextUsage
|
|
701
701
|
// collapses a turn's usage into one context-size sum, this sibling keeps the RAW
|
|
702
702
|
// per-entry breakdown the spine records, one record per assistant transcript entry:
|
|
703
703
|
// dedupeKey = the entry `uuid` (entries never move once written, so the uuid is the
|
|
@@ -716,7 +716,7 @@ export interface TranscriptTokenUsageEntry {
|
|
|
716
716
|
cacheReadTokens: number;
|
|
717
717
|
cacheWriteTokens: number;
|
|
718
718
|
occurredAt: number;
|
|
719
|
-
/**
|
|
719
|
+
/** an API assistant entry without a usage block: spend happened, counts are unknown. */
|
|
720
720
|
usageStatus?: "unknown";
|
|
721
721
|
}
|
|
722
722
|
|
|
@@ -758,7 +758,7 @@ function usageEntryFromTranscriptEntry(entry: TranscriptEntry, byUuid: ReadonlyM
|
|
|
758
758
|
}
|
|
759
759
|
|
|
760
760
|
/**
|
|
761
|
-
* Incremental
|
|
761
|
+
* Incremental extractor: feeds transcript lines one at a time so a caller can
|
|
762
762
|
* consume only newly-appended data without re-walking the file. promptId is resolved
|
|
763
763
|
* against the entries THIS instance has seen (the uuid→entry map) — feed a whole file
|
|
764
764
|
* for whole-transcript resolution, or feed only an appended tail and accept that an
|
|
@@ -794,7 +794,7 @@ function usageCount(value: number | undefined): number {
|
|
|
794
794
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
795
795
|
}
|
|
796
796
|
|
|
797
|
-
/** Claude names its transcript file `<sessionId>.jsonl
|
|
797
|
+
/** Claude names its transcript file `<sessionId>.jsonl`, so the provider's own
|
|
798
798
|
* session uuid is recoverable from the path even when the status probe is silent. */
|
|
799
799
|
export function claudeSessionIdFromTranscriptPath(transcriptPath: string): string {
|
|
800
800
|
const file = transcriptPath.split(/[\\/]/).pop() ?? "";
|