@khorsheed/dsh-local-agent-claude-code 0.2.0-rc.3 → 0.2.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.
@@ -14,7 +14,7 @@
14
14
  * loud, by design).
15
15
  * @module @khorsheed/dsh-local-agent-claude-code/claude-cli-provider
16
16
  */
17
- import type { ContentBlock, TokenUsage } from '@deepseek-ai/dsh-llm';
17
+ import type { ContentBlock, ReasoningEffortId, TokenUsage } from '@deepseek-ai/dsh-llm';
18
18
  import type { TodoItem } from '@deepseek-ai/dsh-tool-todo';
19
19
  import { type ResolvedSubagentStartRequest, type SubagentCapabilities, type SubagentProvider, type SubagentRun, type SubagentStartRequest, type SubagentStopReason } from '@deepseek-ai/dsh-subagent';
20
20
  import type { Context } from '@deepseek-ai/cordis';
@@ -23,6 +23,14 @@ import type { SubprocessHandle, SubprocessSpawnSpec } from '@deepseek-ai/dsh-sub
23
23
  import type { DelegationExecTarget, LocalAgentToolCalls } from '@khorsheed/dsh-local-agent/types';
24
24
  import type { ClaudeLiveDriver } from './live-driver.ts';
25
25
  import type { ClaudeEndpointAuth } from './endpoint-auth.ts';
26
+ /**
27
+ * The synthesized result for a call whose result the stream never recorded
28
+ * (a settle-pass fold after a kill or a truncated stream). The released-log
29
+ * contract resolves every advertised call before its step closes, so an
30
+ * interrupted call folds with this error result. Shared verbatim with the
31
+ * kimi provider (`KIMI_INTERRUPTED_TOOL_RESULT`) so the family reads one text.
32
+ */
33
+ export declare const CLAUDE_INTERRUPTED_TOOL_RESULT = "\uFF08\u5DE5\u5177\u8C03\u7528\u88AB\u4E2D\u65AD\uFF0C\u672A\u80FD\u8BB0\u5F55\u8FD4\u56DE\u7ED3\u679C\uFF09";
26
34
  /** Default POSIX grace between subprocess termination tiers. */
27
35
  export declare const DEFAULT_DISPOSE_GRACE_MS = 3000;
28
36
  /**
@@ -34,6 +42,11 @@ export declare const DEFAULT_DISPOSE_GRACE_MS = 3000;
34
42
  * provider-controlled error strings only.
35
43
  */
36
44
  export declare const CLAUDE_AUTH_FAILURE: RegExp;
45
+ /**
46
+ * The MCP tool-call bound for a member's claude process (`MCP_TOOL_TIMEOUT`,
47
+ * ms): a bridge call may wait up to 30 minutes for the human's answer.
48
+ */
49
+ export declare const MEMBER_TOOL_TIMEOUT_MS: string;
37
50
  /**
38
51
  * Member channel registration for one claude process lifetime: mint the
39
52
  * per-run token and build the bridge MCP declaration for the spawn argv
@@ -211,16 +224,25 @@ export type ClaudeTranscriptLine = {
211
224
  kind: 'think';
212
225
  text: string;
213
226
  streamId?: string;
227
+ messageId?: string;
228
+ usage?: TokenUsage;
214
229
  } | {
215
230
  kind: 'text';
216
231
  text: string;
217
232
  streamId?: string;
233
+ messageId?: string;
234
+ usage?: TokenUsage;
218
235
  }
219
236
  /**
220
237
  * Tool activity: one `tool_use` with its (possibly still pending)
221
238
  * `tool_result`. `id` is the stream's tool_use id when present, else a
222
239
  * synthesized position-based id — stable within a run either way, so the
223
- * child session's `tool/call`/`tool/result` events pair by it.
240
+ * child session's `tool/call`/`tool/result` events pair by it. `usage` is
241
+ * the API call's own accounting (claude reports it inline on every
242
+ * assistant event): it rides the call's LAST folded line — a tool-only
243
+ * call's lands on the tool line's advertising message. `messageId` groups
244
+ * the per-block events of ONE API call (claude emits one assistant event
245
+ * per content block, each repeating the message's usage).
224
246
  */
225
247
  | {
226
248
  kind: 'tool';
@@ -228,12 +250,21 @@ export type ClaudeTranscriptLine = {
228
250
  name: string;
229
251
  args?: string;
230
252
  result?: string;
253
+ messageId?: string;
254
+ usage?: TokenUsage;
231
255
  };
256
+ /** Group the lines of one API call (consecutive lines sharing a message id).
257
+ * Lines without an id stay singleton units (the pre-grouping shape). */
258
+ export declare function groupClaudeTranscriptLines(lines: readonly ClaudeTranscriptLine[]): ClaudeTranscriptLine[][];
232
259
  /** Mutable fold state shared by the batch parse and the incremental parser. */
233
260
  interface ClaudeStreamFoldState {
234
261
  readonly lines: ClaudeTranscriptLine[];
235
262
  /** tool_use id → transcript line index, so a result pairs with its own call. */
236
263
  readonly callsById: Map<string, number>;
264
+ /** message id → the line index its usage currently rides (per-call dedupe:
265
+ * claude repeats one call's usage on EVERY per-block assistant event, and
266
+ * only the call's last line may keep it). */
267
+ readonly usageStamped: Map<string, number>;
237
268
  text: string | undefined;
238
269
  usage: TokenUsage | undefined;
239
270
  sessionId: string | undefined;
@@ -311,6 +342,7 @@ export declare class ClaudeStreamParser implements ClaudeStreamFoldState {
311
342
  private buffer;
312
343
  readonly lines: ClaudeTranscriptLine[];
313
344
  readonly callsById: Map<string, number>;
345
+ readonly usageStamped: Map<string, number>;
314
346
  text: string | undefined;
315
347
  usage: TokenUsage | undefined;
316
348
  sessionId: string | undefined;
@@ -356,6 +388,17 @@ export declare function parseClaudeStreamJson(output: string): {
356
388
  };
357
389
  /** The placeholder think line for a `redacted_thinking` block (content is server-encrypted by design). */
358
390
  export declare const REDACTED_THINKING_TEXT = "\uFF08\u601D\u8003\u5185\u5BB9\u5DF2\u7531\u670D\u52A1\u7AEF\u9690\u85CF\uFF1Aredacted_thinking\uFF0C\u5185\u5BB9\u6309\u8BBE\u8BA1\u4E0D\u53EF\u89C1\uFF0C\u6B64\u884C\u4EC5\u6807\u793A\u5B83\u5B58\u5728\uFF09";
391
+ /**
392
+ * Map claude's usage payload onto the shared usage contract. Claude's
393
+ * `input_tokens` is the uncached input; cache reads and cache creation are
394
+ * reported separately, so each maps to its own bucket with no subtraction.
395
+ * When the payload carries ANY cache field, `cacheReadTokens` is always
396
+ * written (0 when the read counter is absent or zero) — a reader can then
397
+ * tell "no hits" from "not reported".
398
+ * @param usage - the raw claude usage object.
399
+ * @returns the shared usage record.
400
+ */
401
+ export declare function usageFromClaude(usage: unknown): TokenUsage;
359
402
  /**
360
403
  * Start the real `claude -p` child and publish its run. The claude
361
404
  * result arrives as an NDJSON event stream on stdout (`--verbose
@@ -371,6 +414,17 @@ export declare const REDACTED_THINKING_TEXT = "\uFF08\u601D\u8003\u5185\u5BB9\u5
371
414
  */
372
415
  export declare function startClaudeCliRun(request: SubagentStartRequest, spec: ClaudeCliRunSpec): Promise<SubagentRun>;
373
416
  /** One assistant-role message event, attributed to the claude route. */
417
+ /**
418
+ * Write the round's request/header when the config differs from the child
419
+ * log's last one (the host logs changed snapshots, not per-call duplicates):
420
+ * the first write is `initial`, a differing one `change`, an identical one
421
+ * skipped. Context Lens reads the child log's config from these headers.
422
+ */
423
+ export declare function appendClaudeRequestHeader(childSession: Session, config: {
424
+ provider: string;
425
+ model: string;
426
+ reasoningEffort?: ReasoningEffortId;
427
+ }): void;
374
428
  export declare function assistantEvent(blocks: readonly ContentBlock[]): import("@deepseek-ai/dsh-llm").AssistantMessage;
375
429
  /**
376
430
  * Fold one transcript line into the child session as one assistant step,
@@ -380,10 +434,27 @@ export declare function assistantEvent(blocks: readonly ContentBlock[]): import(
380
434
  * the real-time view renders nothing (only a full rebuild, which mints step
381
435
  * drafts from the explicit coordinates, recovers it). Tool cards escape that
382
436
  * fate only because their definition does not consult the step location.
437
+ *
438
+ * A tool line folds the native lifecycle inside its one step: an assistant
439
+ * message advertising the tool-call block, the `tool/call`, and its
440
+ * `tool/result` — the released-log reader pairs call and result against the
441
+ * advertisement and resolves every advertised call before the step closes, so
442
+ * a line whose result the stream has not recorded yet must NOT reach this
443
+ * fold mid-round (the callers hold there; the result merges into the call's
444
+ * line in place, so a later pass sees it). A line still unresulted at settle
445
+ * folds with the interrupted placeholder ({@link CLAUDE_INTERRUPTED_TOOL_RESULT}).
383
446
  */
384
447
  export declare function appendClaudeTranscriptLine(childSession: Session, turn: number, step: number, line: ClaudeTranscriptLine, usage: TokenUsage | undefined): void;
385
448
  /** The delta-progress text for one transcript line. */
386
449
  export declare function claudeLineText(line: ClaudeTranscriptLine): string;
450
+ /**
451
+ * Fold one API call (a message-id group) into the child session as ONE
452
+ * assistant step: a single assistant/message carrying the call's reasoning,
453
+ * text and tool-call blocks (the host's own mixed-content shape), then each
454
+ * call's native lifecycle, then step/end. The call's usage rides the message
455
+ * once — the group's own per-call stamp wins over the round-total fallback.
456
+ */
457
+ export declare function appendClaudeTranscriptGroup(childSession: Session, turn: number, step: number, group: readonly ClaudeTranscriptLine[], usage: TokenUsage | undefined): void;
387
458
  /**
388
459
  * Persist the child session's mirrored events. Thin wrapper over the core's
389
460
  * {@link persistChildSession}: a live session syncs through the core's cached