@cargo-ai/cli 1.0.126 → 1.0.127

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.
Files changed (38) hide show
  1. package/build/commands/ask/harness/bridgeOutput.d.ts +19 -0
  2. package/build/commands/ask/harness/bridgeOutput.d.ts.map +1 -0
  3. package/build/commands/ask/harness/bridgeOutput.js +42 -0
  4. package/build/commands/ask/harness/index.d.ts +3 -2
  5. package/build/commands/ask/harness/index.d.ts.map +1 -1
  6. package/build/commands/ask/harness/index.js +2 -1
  7. package/build/commands/ask/harness/turn.d.ts +17 -0
  8. package/build/commands/ask/harness/turn.d.ts.map +1 -1
  9. package/build/commands/ask/harness/turn.js +85 -3
  10. package/build/commands/ask/harness/usage.d.ts +19 -0
  11. package/build/commands/ask/harness/usage.d.ts.map +1 -1
  12. package/build/commands/ask/harness/usage.js +46 -0
  13. package/build/commands/ask/input/completer.d.ts +1 -1
  14. package/build/commands/ask/input/completer.d.ts.map +1 -1
  15. package/build/commands/ask/input/completer.js +2 -0
  16. package/build/commands/ask/input/prompt.d.ts.map +1 -1
  17. package/build/commands/ask/input/prompt.js +54 -8
  18. package/build/commands/ask/input/slash.d.ts +7 -1
  19. package/build/commands/ask/input/slash.d.ts.map +1 -1
  20. package/build/commands/ask/input/slash.js +10 -0
  21. package/build/commands/ask/session/index.d.ts +2 -1
  22. package/build/commands/ask/session/index.d.ts.map +1 -1
  23. package/build/commands/ask/session/index.js +2 -1
  24. package/build/commands/ask/session/markdown.d.ts +20 -0
  25. package/build/commands/ask/session/markdown.d.ts.map +1 -0
  26. package/build/commands/ask/session/markdown.js +451 -0
  27. package/build/commands/ask/session/run.d.ts.map +1 -1
  28. package/build/commands/ask/session/run.js +194 -12
  29. package/build/commands/ask/session/tree.d.ts +81 -0
  30. package/build/commands/ask/session/tree.d.ts.map +1 -1
  31. package/build/commands/ask/session/tree.js +322 -51
  32. package/build/commands/runHandler.d.ts +1 -0
  33. package/build/commands/runHandler.d.ts.map +1 -1
  34. package/build/commands/runHandler.js +6 -3
  35. package/build/utils/textWidth.d.ts +25 -0
  36. package/build/utils/textWidth.d.ts.map +1 -0
  37. package/build/utils/textWidth.js +147 -0
  38. package/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `@ai-sdk/harness` copies every line the Claude Code bridge writes to its
3
+ * stderr onto ours, as `[harness:claude-code:stderr] …`, with no option to
4
+ * turn it off. In the middle of a session that lands on the spinner row and
5
+ * breaks the tree: an API retry warning is the usual one.
6
+ *
7
+ * So while a session runs, those lines are taken off stderr and handed to
8
+ * `onLine` instead. `CARGO_ASK_TIMING` leaves them alone, since that is when
9
+ * someone wants to read them. Returns the function that puts stderr back.
10
+ */
11
+ export declare function interceptBridgeOutput(onLine: (line: string) => void): () => void;
12
+ /**
13
+ * A spinner label for Claude Code retrying a failed API call, or undefined
14
+ * for any other bridge line.
15
+ *
16
+ * Exported for tests.
17
+ */
18
+ export declare function describeBridgeRetry(line: string): string | undefined;
19
+ //# sourceMappingURL=bridgeOutput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridgeOutput.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/bridgeOutput.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC7B,MAAM,IAAI,CAoBZ;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQpE"}
@@ -0,0 +1,42 @@
1
+ import { ASK_TIMING_ENABLED } from "./timing.js";
2
+ /**
3
+ * `@ai-sdk/harness` copies every line the Claude Code bridge writes to its
4
+ * stderr onto ours, as `[harness:claude-code:stderr] …`, with no option to
5
+ * turn it off. In the middle of a session that lands on the spinner row and
6
+ * breaks the tree: an API retry warning is the usual one.
7
+ *
8
+ * So while a session runs, those lines are taken off stderr and handed to
9
+ * `onLine` instead. `CARGO_ASK_TIMING` leaves them alone, since that is when
10
+ * someone wants to read them. Returns the function that puts stderr back.
11
+ */
12
+ export function interceptBridgeOutput(onLine) {
13
+ if (ASK_TIMING_ENABLED === true) {
14
+ return () => undefined;
15
+ }
16
+ const write = process.stderr.write;
17
+ // The harness writes each forwarded line in a single call, prefix first.
18
+ process.stderr.write = ((chunk, ...rest) => {
19
+ if (typeof chunk === "string" && chunk.startsWith(BRIDGE_PREFIX) === true) {
20
+ onLine(chunk.trimEnd());
21
+ return true;
22
+ }
23
+ return Reflect.apply(write, process.stderr, [chunk, ...rest]);
24
+ });
25
+ return () => {
26
+ process.stderr.write = write;
27
+ };
28
+ }
29
+ /**
30
+ * A spinner label for Claude Code retrying a failed API call, or undefined
31
+ * for any other bridge line.
32
+ *
33
+ * Exported for tests.
34
+ */
35
+ export function describeBridgeRetry(line) {
36
+ const match = line.match(/API retry: attempt (\d+)\/(\d+)/);
37
+ if (match === null || match[1] === undefined || match[2] === undefined) {
38
+ return undefined;
39
+ }
40
+ return `Retrying the API (${match[1]}/${match[2]})`;
41
+ }
42
+ const BRIDGE_PREFIX = "[harness:";
@@ -1,10 +1,11 @@
1
1
  export { isAlwaysDenied, isAutoApproved } from "./approvals.js";
2
2
  export { ASK_USER_QUESTION_DENIED_REASON, ASK_USER_QUESTION_MISSING_INPUT_REASON, ASK_USER_QUESTION_TOOL_NAME, buildAskUserQuestionHostTool, denyNativeAskUserQuestion, formatAskUserQuestionAnswers, readAskUserQuestions, summarizeAskUserQuestion, } from "./askUserQuestion.js";
3
+ export { describeBridgeRetry, interceptBridgeOutput } from "./bridgeOutput.js";
3
4
  export { createLocalHarness, type LocalHarness } from "./create.js";
4
5
  export { localHarnessInstructions, localHarnessSkills, } from "./instructions.js";
5
6
  export { resolveWorkspaceLayout } from "./layout.js";
6
7
  export { CARGO_MCP_SERVER_NAME } from "./mcp.js";
7
8
  export { replayKnownToolCalls, routeSessionEvents, withToolCallReplay, wrapSession, } from "./toolCallReplay.js";
8
- export { HarnessTurnStalled, HarnessTurnStuck, isBridgeClosedError, type LocalStreamHandlers, type LocalStreamOptions, type LocalToolActivity, type LocalToolApproval, runPromptTurn, settleWithin, type StreamLike, type TurnDriver, } from "./turn.js";
9
- export { createTurnAccumulator, type LocalTurnAccumulator, type LocalTurnUsage, readClaudeCodeCostUsd, } from "./usage.js";
9
+ export { HarnessTurnStalled, HarnessTurnStuck, isBridgeClosedError, type LocalStreamHandlers, type LocalStreamOptions, type LocalToolActivity, type LocalToolApproval, type LocalUsageUpdate, runPromptTurn, settleWithin, type StreamLike, type TurnDriver, } from "./turn.js";
10
+ export { createTurnAccumulator, creditsForStep, type LocalTurnAccumulator, type LocalTurnUsage, readClaudeCodeCostUsd, } from "./usage.js";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,+BAA+B,EAC/B,sCAAsC,EACtC,2BAA2B,EAC3B,4BAA4B,EAC5B,yBAAyB,EACzB,4BAA4B,EAC5B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,aAAa,EACb,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,+BAA+B,EAC/B,sCAAsC,EACtC,2BAA2B,EAC3B,4BAA4B,EAC5B,yBAAyB,EACzB,4BAA4B,EAC5B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,aAAa,EACb,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
@@ -1,9 +1,10 @@
1
1
  export { isAlwaysDenied, isAutoApproved } from "./approvals.js";
2
2
  export { ASK_USER_QUESTION_DENIED_REASON, ASK_USER_QUESTION_MISSING_INPUT_REASON, ASK_USER_QUESTION_TOOL_NAME, buildAskUserQuestionHostTool, denyNativeAskUserQuestion, formatAskUserQuestionAnswers, readAskUserQuestions, summarizeAskUserQuestion, } from "./askUserQuestion.js";
3
+ export { describeBridgeRetry, interceptBridgeOutput } from "./bridgeOutput.js";
3
4
  export { createLocalHarness } from "./create.js";
4
5
  export { localHarnessInstructions, localHarnessSkills, } from "./instructions.js";
5
6
  export { resolveWorkspaceLayout } from "./layout.js";
6
7
  export { CARGO_MCP_SERVER_NAME } from "./mcp.js";
7
8
  export { replayKnownToolCalls, routeSessionEvents, withToolCallReplay, wrapSession, } from "./toolCallReplay.js";
8
9
  export { HarnessTurnStalled, HarnessTurnStuck, isBridgeClosedError, runPromptTurn, settleWithin, } from "./turn.js";
9
- export { createTurnAccumulator, readClaudeCodeCostUsd, } from "./usage.js";
10
+ export { createTurnAccumulator, creditsForStep, readClaudeCodeCostUsd, } from "./usage.js";
@@ -23,6 +23,8 @@ export type LocalToolActivity = {
23
23
  toolCallId: string;
24
24
  toolName: string;
25
25
  isError: boolean;
26
+ /** What the tool returned, or its error, as text. Capped in length. */
27
+ output: string;
26
28
  }
27
29
  /**
28
30
  * Announced, then never run — a tool whose input stopped mid-stream, one
@@ -36,8 +38,23 @@ export type LocalToolActivity = {
36
38
  toolCallId: string;
37
39
  toolName: string;
38
40
  };
41
+ /**
42
+ * Tokens one model call used (`step`) or one stream used in total (`stream`).
43
+ * A step's input is the whole context it was sent, which is how full the
44
+ * window is.
45
+ */
46
+ export type LocalUsageUpdate = {
47
+ scope: "step" | "stream";
48
+ inputTokens: number;
49
+ /** The part of `inputTokens` served from the prompt cache. */
50
+ cacheReadTokens: number;
51
+ outputTokens: number;
52
+ };
39
53
  export type LocalStreamHandlers = {
40
54
  onTextDelta?: (delta: string) => void;
55
+ /** Claude Code's summarized thinking, as it streams. */
56
+ onReasoningDelta?: (delta: string) => void;
57
+ onUsage?: (usage: LocalUsageUpdate) => void;
41
58
  /**
42
59
  * Decide whether the tool may run. Omitted (`--print`, non-interactive)
43
60
  * means deny — the turn continues and the model reports what it could not
@@ -1 +1 @@
1
- {"version":3,"file":"turn.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/turn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"turn.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/turn.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { DENIED_TOOL_REASON, isAlwaysDenied, isAutoApproved, } from "./approvals.js";
2
2
  import { ASK_USER_QUESTION_DENIED_REASON, ASK_USER_QUESTION_TOOL_NAME, formatAskUserQuestionAnswers, summarizeAskUserQuestion, } from "./askUserQuestion.js";
3
- import { addTurnUsage, createTurnAccumulator, } from "./usage.js";
3
+ import { addTurnUsage, createTurnAccumulator, readUsageTokens, } from "./usage.js";
4
4
  /**
5
5
  * How many times one turn may suspend for approvals. Each round trip is a
6
6
  * decision the user made, so this is only a stop for a harness that keeps
@@ -464,6 +464,7 @@ async function consumeTurn(input) {
464
464
  toolCallId,
465
465
  toolName: recorded === undefined ? "tool" : recorded.toolName,
466
466
  isError,
467
+ output: readToolOutputText(part),
467
468
  });
468
469
  }
469
470
  if (typeof toolCallId === "string" && preliminary === false) {
@@ -487,9 +488,33 @@ async function consumeTurn(input) {
487
488
  }
488
489
  if (part["type"] === "finish") {
489
490
  // One suspended turn is several streams, each ending with its own
490
- // `finish` — all of them count. Per-step parts are ignored so the same
491
- // tokens are not counted twice.
491
+ // `finish` — all of them count. Per-step parts stay out of the turn's
492
+ // usage so the same tokens are not counted twice.
492
493
  addTurnUsage(input.accumulator, part);
494
+ const totals = readUsageTokens(part["totalUsage"]);
495
+ if (totals !== undefined && handlers.onUsage !== undefined) {
496
+ handlers.onUsage({ scope: "stream", ...totals });
497
+ }
498
+ continue;
499
+ }
500
+ if (part["type"] === "finish-step") {
501
+ const step = readUsageTokens(part["usage"]);
502
+ if (step !== undefined && handlers.onUsage !== undefined) {
503
+ handlers.onUsage({ scope: "step", ...step });
504
+ }
505
+ continue;
506
+ }
507
+ if (part["type"] === "reasoning-delta") {
508
+ const text = typeof part["text"] === "string"
509
+ ? part["text"]
510
+ : typeof part["delta"] === "string"
511
+ ? part["delta"]
512
+ : undefined;
513
+ if (text !== undefined &&
514
+ text.length > 0 &&
515
+ handlers.onReasoningDelta !== undefined) {
516
+ handlers.onReasoningDelta(text);
517
+ }
493
518
  continue;
494
519
  }
495
520
  if (part["type"] === "tool-approval-request") {
@@ -666,3 +691,60 @@ function isToolErrorPart(part) {
666
691
  part["type"] === "tool-output-error" ||
667
692
  part["type"] === "tool-output-denied");
668
693
  }
694
+ /** Where a finished tool part may carry its result, most specific first. */
695
+ const TOOL_OUTPUT_FIELDS = ["errorText", "error", "output", "result"];
696
+ /** Enough of a tool's output to preview; the rest is never shown. */
697
+ const TOOL_OUTPUT_MAX_LENGTH = 4_000;
698
+ /**
699
+ * A finished tool's result as text, for the transcript. Built-in tools return
700
+ * a string; MCP tools return content blocks; a failure carries an error.
701
+ */
702
+ function readToolOutputText(part) {
703
+ if (part["type"] === "tool-output-denied") {
704
+ return "Denied.";
705
+ }
706
+ // The first field with something in it. The UI stream spells the error
707
+ // `errorText`; older AI SDK parts used `result` for `output`. Presence is
708
+ // not enough: an empty `error` must not hide a real `output`.
709
+ const text = TOOL_OUTPUT_FIELDS.map((field) => toolValueText(part[field])).find((candidate) => candidate.trim() !== "");
710
+ if (text === undefined) {
711
+ return "";
712
+ }
713
+ return text.length > TOOL_OUTPUT_MAX_LENGTH
714
+ ? `${text.slice(0, TOOL_OUTPUT_MAX_LENGTH)}…`
715
+ : text;
716
+ }
717
+ function toolValueText(value) {
718
+ if (value === undefined || value === null) {
719
+ return "";
720
+ }
721
+ if (typeof value === "string") {
722
+ return value;
723
+ }
724
+ if (value instanceof Error) {
725
+ return value.message;
726
+ }
727
+ if (Array.isArray(value)) {
728
+ return value.map(toolValueText).join("\n");
729
+ }
730
+ if (typeof value === "object") {
731
+ const record = value;
732
+ // `{ type: "text", text }` content blocks, and the AI SDK's
733
+ // `{ type: "text", value }` tool output.
734
+ if (typeof record["text"] === "string") {
735
+ return record["text"];
736
+ }
737
+ if (record["type"] === "text" && typeof record["value"] === "string") {
738
+ return record["value"];
739
+ }
740
+ if (Array.isArray(record["content"])) {
741
+ return toolValueText(record["content"]);
742
+ }
743
+ }
744
+ try {
745
+ return JSON.stringify(value, null, 2);
746
+ }
747
+ catch {
748
+ return "";
749
+ }
750
+ }
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Credits the proxy bills for one model call, rounded up to the hundredth
3
+ * per call as it does. `inputTokens` is the whole prompt; cache writes are
4
+ * charged as plain input.
5
+ */
6
+ export declare function creditsForStep(tokens: {
7
+ inputTokens: number;
8
+ cacheReadTokens: number;
9
+ outputTokens: number;
10
+ }): number;
1
11
  /**
2
12
  * What one turn spent upstream, summed over the streams a suspended turn is
3
13
  * made of. `costUsd` is Claude Code's own accounting; the API stores it on
@@ -41,6 +51,15 @@ export declare function didReachAnthropic(usage: LocalTurnUsage): boolean;
41
51
  * cost is stored on the ingested message.
42
52
  */
43
53
  export declare function addTurnUsage(accumulator: LocalTurnAccumulator, part: Record<string, unknown>): void;
54
+ /**
55
+ * Input and output tokens off an AI SDK usage object, or undefined when the
56
+ * part carries none. Input is the whole prompt, cached or not.
57
+ */
58
+ export declare function readUsageTokens(value: unknown): {
59
+ inputTokens: number;
60
+ cacheReadTokens: number;
61
+ outputTokens: number;
62
+ } | undefined;
44
63
  /**
45
64
  * What Claude Code says the turn cost, off the `finish` part.
46
65
  *
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/usage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,cAAc,CAAC;IACtB;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,mBAAmB,EAAE,MAAM,GAC1B,oBAAoB,CActB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,oBAAoB,EACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,IAAI,CAwCN;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,SAAS,CA0BpB"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/usage.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAWT;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,cAAc,CAAC;IACtB;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,mBAAmB,EAAE,MAAM,GAC1B,oBAAoB,CActB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,oBAAoB,EACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,IAAI,CAwCN;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,GAEZ;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACtE,SAAS,CAkBZ;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,SAAS,CA0BpB"}
@@ -1,3 +1,31 @@
1
+ /**
2
+ * What one Cargo credit is worth, and what the sandbox proxy charges per
3
+ * million tokens. Mirrors `USD_PER_CREDIT` and
4
+ * `ANTHROPIC_FALLBACK_RATES_USD_PER_MILLION` in the backend's harness usage:
5
+ * the proxy prices each request from these, not from Claude Code's own cost,
6
+ * so the credits shown after a turn have to use them too.
7
+ */
8
+ const USD_PER_CREDIT = 0.04;
9
+ const PROXY_RATES_USD_PER_MILLION = {
10
+ input: 3,
11
+ output: 15,
12
+ cacheRead: 0.3,
13
+ };
14
+ /**
15
+ * Credits the proxy bills for one model call, rounded up to the hundredth
16
+ * per call as it does. `inputTokens` is the whole prompt; cache writes are
17
+ * charged as plain input.
18
+ */
19
+ export function creditsForStep(tokens) {
20
+ const usdPerMillion = Math.max(tokens.inputTokens - tokens.cacheReadTokens, 0) *
21
+ PROXY_RATES_USD_PER_MILLION.input +
22
+ tokens.cacheReadTokens * PROXY_RATES_USD_PER_MILLION.cacheRead +
23
+ tokens.outputTokens * PROXY_RATES_USD_PER_MILLION.output;
24
+ const credits = usdPerMillion / 1_000_000 / USD_PER_CREDIT;
25
+ // `toFixed` first, as the backend does, so float noise cannot round an
26
+ // exact hundredth up by one.
27
+ return Math.ceil(Number((credits * 100).toFixed(6))) / 100;
28
+ }
1
29
  export function createTurnAccumulator(lastReportedCostUsd) {
2
30
  return {
3
31
  chunks: [],
@@ -57,6 +85,24 @@ export function addTurnUsage(accumulator, part) {
57
85
  usage.reasoningTokens += readCount(details["reasoningTokens"]);
58
86
  }
59
87
  }
88
+ /**
89
+ * Input and output tokens off an AI SDK usage object, or undefined when the
90
+ * part carries none. Input is the whole prompt, cached or not.
91
+ */
92
+ export function readUsageTokens(value) {
93
+ if (typeof value !== "object" || value === null) {
94
+ return undefined;
95
+ }
96
+ const usage = value;
97
+ const inputDetails = usage["inputTokenDetails"];
98
+ return {
99
+ inputTokens: readCount(usage["inputTokens"]),
100
+ cacheReadTokens: typeof inputDetails === "object" && inputDetails !== null
101
+ ? readCount(inputDetails["cacheReadTokens"])
102
+ : 0,
103
+ outputTokens: readCount(usage["outputTokens"]),
104
+ };
105
+ }
60
106
  function readCount(value) {
61
107
  return typeof value === "number" && Number.isFinite(value) && value > 0
62
108
  ? value
@@ -1,5 +1,5 @@
1
1
  /** Chat slash commands offered on Tab when the line is `/…`. */
2
- export declare const ASK_SLASH_COMMANDS: readonly ["/help", "/clear", "/new", "/agent", "/cancel", "/exit"];
2
+ export declare const ASK_SLASH_COMMANDS: readonly ["/help", "/clear", "/new", "/agent", "/tools", "/reasoning", "/cancel", "/exit"];
3
3
  export type AskCompleter = (line: string) => [string[], string];
4
4
  /** What a `/…` token turned out to be. */
5
5
  export type SlashTokenKind = "command" | "path" | "unknown";
@@ -1 +1 @@
1
- {"version":3,"file":"completer.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/input/completer.ts"],"names":[],"mappings":"AAYA,gEAAgE;AAChE,eAAO,MAAM,kBAAkB,oEAOrB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;AAEhE,0CAA0C;AAC1C,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAgBhE;AAED,sDAAsD;AACtD,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAU5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CASnE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAYxE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,GAAG,SAAS,CAY1E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAwE1E"}
1
+ {"version":3,"file":"completer.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/input/completer.ts"],"names":[],"mappings":"AAYA,gEAAgE;AAChE,eAAO,MAAM,kBAAkB,4FASrB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;AAEhE,0CAA0C;AAC1C,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAgBhE;AAED,sDAAsD;AACtD,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAU5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CASnE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAYxE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,GAAG,SAAS,CAY1E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAwE1E"}
@@ -7,6 +7,8 @@ export const ASK_SLASH_COMMANDS = [
7
7
  "/clear",
8
8
  "/new",
9
9
  "/agent",
10
+ "/tools",
11
+ "/reasoning",
10
12
  "/cancel",
11
13
  "/exit",
12
14
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/input/prompt.ts"],"names":[],"mappings":";AAiBA;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,KAAK;;CAKxC;AASD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,CAAC,cAAc,EAC5B,GAAG,EAAE,MAAM;AACX;;;;GAIG;AACH,WAAW,SAAK,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiQ7B"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/input/prompt.ts"],"names":[],"mappings":";AAkBA;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,KAAK;;CAKxC;AASD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,CAAC,cAAc,EAC5B,GAAG,EAAE,MAAM;AACX;;;;GAIG;AACH,WAAW,SAAK,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA+R7B"}
@@ -1,5 +1,6 @@
1
1
  import * as readline from "node:readline";
2
- import { colors } from "../../runHandler.js";
2
+ import { cellWidth, textCells } from "../../../utils/textWidth.js";
3
+ import { clipToWidth, colors } from "../../runHandler.js";
3
4
  import { applyCompletion, ASK_COMPLETION_MENU_LIMIT, listCompletions, } from "./completer.js";
4
5
  /**
5
6
  * Ctrl-C on the Ask line.
@@ -37,6 +38,9 @@ initialLine = "") {
37
38
  let cursor = initialLine.length;
38
39
  let selected = 0;
39
40
  let painted = false;
41
+ // Rows between the ◆ row and the cursor. A line longer than the terminal
42
+ // wraps, and the next clear has to start from the ◆ row, not the cursor's.
43
+ let cursorRow = 0;
40
44
  let finished = false;
41
45
  const wasRaw = isTTYStream(input) ? input.isRaw === true : false;
42
46
  if (isTTYStream(input) === true) {
@@ -171,9 +175,13 @@ initialLine = "") {
171
175
  if (painted === false || process.stderr.isTTY !== true) {
172
176
  return;
173
177
  }
174
- // Cursor sits on the ◆ line; wipe from there through the menu below.
178
+ // Back up to the ◆ row; wipe from there through the menu below.
179
+ if (cursorRow > 0) {
180
+ process.stderr.write(`\x1B[${String(cursorRow)}A`);
181
+ }
175
182
  process.stderr.write("\r\x1B[J");
176
183
  painted = false;
184
+ cursorRow = 0;
177
185
  }
178
186
  function paint() {
179
187
  const matches = visibleMatches();
@@ -181,7 +189,16 @@ initialLine = "") {
181
189
  selected = matches.length === 0 ? 0 : matches.length - 1;
182
190
  }
183
191
  clearFrame();
192
+ const columns = process.stderr.columns === undefined ? 80 : process.stderr.columns;
193
+ // The terminal wraps the line itself; this says where it ends up.
194
+ const end = layoutCells(`◆ ${line}`, columns);
184
195
  process.stderr.write(`${colors.cyan("◆")} ${line}`);
196
+ // Text ending exactly at the edge leaves the cursor parked on that row
197
+ // until something is written. Step onto the next row so the count holds.
198
+ if (line.length > 0 && end.column === columns) {
199
+ process.stderr.write("\r\n");
200
+ }
201
+ let row = end.column === columns ? end.row + 1 : end.row;
185
202
  if (matches.length > 0) {
186
203
  process.stderr.write("\n");
187
204
  for (let index = 0; index < matches.length; index += 1) {
@@ -191,18 +208,25 @@ initialLine = "") {
191
208
  }
192
209
  const active = index === selected;
193
210
  const bullet = active === true ? colors.cyan("●") : colors.dim("○");
194
- const label = active === true ? colors.cyan(hit) : colors.dim(hit);
211
+ // One row each, or the count of rows to climb back is off.
212
+ const text = clipToWidth(hit, columns - 5);
213
+ const label = active === true ? colors.cyan(text) : colors.dim(text);
195
214
  process.stderr.write(`${colors.dim("│")} ${bullet} ${label}`);
196
215
  if (index < matches.length - 1) {
197
216
  process.stderr.write("\n");
198
217
  }
199
218
  }
200
- // Return to the ◆ line so the next clearFrame / keystroke is correct.
201
- process.stderr.write(`\x1B[${String(matches.length)}A`);
219
+ row += matches.length;
220
+ }
221
+ // Back to the cursor's cell inside the (possibly wrapped) line.
222
+ const at = layoutCells(`◆ ${line.slice(0, cursor)}`, columns);
223
+ const targetRow = at.column === columns ? at.row + 1 : at.row;
224
+ const targetColumn = (at.column === columns ? 0 : at.column) + 1;
225
+ if (row > targetRow) {
226
+ process.stderr.write(`\x1B[${String(row - targetRow)}A`);
202
227
  }
203
- // Column after `◆ ` (3 visible cells) plus the cursor in the line.
204
- const column = 3 + cursor + 1;
205
- process.stderr.write(`\r\x1B[${String(column)}G`);
228
+ process.stderr.write(`\r\x1B[${String(targetColumn)}G`);
229
+ cursorRow = targetRow;
206
230
  painted = true;
207
231
  }
208
232
  function cleanup() {
@@ -232,3 +256,25 @@ initialLine = "") {
232
256
  }
233
257
  });
234
258
  }
259
+ /**
260
+ * Where the terminal leaves the cursor after writing `text` from the start of
261
+ * a row: the row it is on, and how many columns of it are filled. A filled
262
+ * row stays the cursor's row until the next character, which is why `column`
263
+ * can equal `columns`.
264
+ *
265
+ * Walked character by character rather than divided out: an emoji or a CJK
266
+ * character takes two columns, and one that does not fit in the last column
267
+ * starts the next row and leaves that column empty.
268
+ */
269
+ function layoutCells(text, columns) {
270
+ const position = { row: 0, column: 0 };
271
+ for (const cell of textCells(text)) {
272
+ const width = cellWidth(cell);
273
+ if (position.column + width > columns) {
274
+ position.row += 1;
275
+ position.column = 0;
276
+ }
277
+ position.column += width;
278
+ }
279
+ return position;
280
+ }
@@ -6,7 +6,13 @@ export type SlashAction = {
6
6
  kind: "action";
7
7
  action: "exit" | "new" | "clear" | "pick-agent" | "cancel";
8
8
  };
9
- export type SlashResult = SlashCard | SlashAction;
9
+ /** `/tools` or `/reasoning`, with the display asked for if one was given. */
10
+ export type SlashDisplay = {
11
+ kind: "display";
12
+ setting: "tools" | "reasoning";
13
+ value: string | undefined;
14
+ };
15
+ export type SlashResult = SlashCard | SlashAction | SlashDisplay;
10
16
  /**
11
17
  * Parse a user line that starts with `/`. Anything else is prose for the agent.
12
18
  *
@@ -1 +1 @@
1
- {"version":3,"file":"slash.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/input/slash.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,YAAY,GAAG,QAAQ,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,CAAC;AAmBlD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAqDhE;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAE3B,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC;CAK1C"}
1
+ {"version":3,"file":"slash.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/input/slash.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,YAAY,GAAG,QAAQ,CAAC;CAC5D,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,OAAO,GAAG,WAAW,CAAC;IAC/B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,CAAC;AAqBjE;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CA+DhE;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAE3B,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC;CAK1C"}
@@ -6,6 +6,8 @@ const HELP_TEXT = [
6
6
  "/clear Start a fresh local session (no agent)",
7
7
  "/new Same as /clear",
8
8
  "/agent Pick a workspace agent, or Local workspace (default). Stays on this machine.",
9
+ "/tools How tool calls show: full, collapsed (default), hidden",
10
+ "/reasoning How thinking shows: full, auto-collapsed (default), collapsed, hidden",
9
11
  "/cancel Explain how to stop a turn (press Ctrl-C while it runs)",
10
12
  "/exit Leave without asking (same as an empty line)",
11
13
  "",
@@ -52,6 +54,14 @@ export function parseSlash(text) {
52
54
  if (command === "agent") {
53
55
  return { kind: "action", action: "pick-agent" };
54
56
  }
57
+ if (command === "tools" || command === "reasoning") {
58
+ const value = trimmed.split(/\s+/)[1];
59
+ return {
60
+ kind: "display",
61
+ setting: command,
62
+ value: value === undefined ? undefined : value.toLowerCase(),
63
+ };
64
+ }
55
65
  if (command === "cancel") {
56
66
  return { kind: "action", action: "cancel" };
57
67
  }
@@ -1,5 +1,6 @@
1
1
  export { type ChatTurnClaim, claimChatTurn, proxyTokenDeadlineMs, } from "./claim.js";
2
+ export { renderMarkdown } from "./markdown.js";
2
3
  export { type AskSessionOptions, doesChatExist, runAskPrint, runAskSession, } from "./run.js";
3
4
  export { ASK_THINKING_WORDS, pickThinkingWord } from "./thinking.js";
4
- export { displayPath, wrapLine } from "./tree.js";
5
+ export { displayPath, showTurnStats, wrapLine } from "./tree.js";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/session/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,aAAa,EACb,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,iBAAiB,EACtB,aAAa,EACb,WAAW,EACX,aAAa,GACd,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/session/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,aAAa,EACb,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,KAAK,iBAAiB,EACtB,aAAa,EACb,WAAW,EACX,aAAa,GACd,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { claimChatTurn, proxyTokenDeadlineMs, } from "./claim.js";
2
+ export { renderMarkdown } from "./markdown.js";
2
3
  export { doesChatExist, runAskPrint, runAskSession, } from "./run.js";
3
4
  export { ASK_THINKING_WORDS, pickThinkingWord } from "./thinking.js";
4
- export { displayPath, wrapLine } from "./tree.js";
5
+ export { displayPath, showTurnStats, wrapLine } from "./tree.js";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The model's markdown, drawn for the tree: headings, lists, quotes, code
3
+ * fences, rules and tables, with bold, italic, code and links inline.
4
+ *
5
+ * The block and table handling follows `@ai-sdk/tui`'s renderer
6
+ * (`src/tui/markdown.ts`). It is not exported from that package, and its
7
+ * output assumes it owns the whole width, so it is redone here around the
8
+ * rail: every line comes back already wrapped to `width`, because a line the
9
+ * terminal soft-wraps starts its next row at column zero, outside the tree.
10
+ *
11
+ * Styles are applied word by word rather than across a span. A colour code
12
+ * here ends with a full reset, so a bold phrase that wraps would lose its
13
+ * bold at the rail drawn in between.
14
+ *
15
+ * Exported for tests.
16
+ */
17
+ export declare function renderMarkdown(text: string, width: number, tone?: MarkdownTone): string[];
18
+ /** `dim` is for text that is not the answer, such as the model's reasoning. */
19
+ export type MarkdownTone = "normal" | "dim";
20
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/session/markdown.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,YAAuB,GAC5B,MAAM,EAAE,CA6CV;AAED,+EAA+E;AAC/E,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,KAAK,CAAC"}