indusagi-coding-agent 0.1.61 → 0.2.0

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 (73) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/entry.js +11589 -14719
  3. package/dist/types/boot/contract.d.ts +2 -0
  4. package/dist/types/boot/runners/addon-wiring.d.ts +103 -0
  5. package/dist/types/boot/runners/addon-wiring.test.d.ts +19 -0
  6. package/dist/types/boot/runners/checkpoint.d.ts +133 -0
  7. package/dist/types/boot/runners/checkpoint.test.d.ts +12 -0
  8. package/dist/types/boot/runners/delegate-runner.d.ts +83 -0
  9. package/dist/types/boot/runners/delegate-runner.test.d.ts +13 -0
  10. package/dist/types/boot/runners/memdir.d.ts +103 -0
  11. package/dist/types/boot/runners/memdir.test.d.ts +12 -0
  12. package/dist/types/boot/runners/read-state.d.ts +82 -0
  13. package/dist/types/boot/runners/read-state.test.d.ts +10 -0
  14. package/dist/types/boot/runners/session.d.ts +37 -2
  15. package/dist/types/boot/runners/session.test.d.ts +10 -0
  16. package/dist/types/briefing/context-docs.d.ts +38 -0
  17. package/dist/types/briefing/context-docs.test.d.ts +18 -0
  18. package/dist/types/briefing/index.d.ts +2 -0
  19. package/dist/types/capability-deck/cards/index.d.ts +6 -0
  20. package/dist/types/capability-deck/cards/memory-card.d.ts +9 -10
  21. package/dist/types/capability-deck/cards/plan-file.d.ts +56 -0
  22. package/dist/types/capability-deck/cards/plan-tools.d.ts +97 -0
  23. package/dist/types/capability-deck/cards/plan-tools.test.d.ts +9 -0
  24. package/dist/types/capability-deck/checkpoint.int.test.d.ts +25 -0
  25. package/dist/types/capability-deck/index.d.ts +1 -1
  26. package/dist/types/capability-deck/read-edit-gate.int.test.d.ts +21 -0
  27. package/dist/types/conductor/bash-guard.d.ts +106 -0
  28. package/dist/types/conductor/bash-guard.test.d.ts +17 -0
  29. package/dist/types/conductor/conductor.d.ts +38 -6
  30. package/dist/types/conductor/contract.d.ts +221 -2
  31. package/dist/types/conductor/diagnostics.d.ts +183 -0
  32. package/dist/types/conductor/diagnostics.test.d.ts +10 -0
  33. package/dist/types/conductor/index.d.ts +4 -1
  34. package/dist/types/conductor/permission-gate.integration.test.d.ts +22 -0
  35. package/dist/types/conductor/permission-wiring.test.d.ts +14 -0
  36. package/dist/types/conductor/permissions.d.ts +217 -0
  37. package/dist/types/conductor/permissions.test.d.ts +12 -0
  38. package/dist/types/conductor/plan-mode.integration.test.d.ts +23 -0
  39. package/dist/types/conductor/post-edit-diagnostics.test.d.ts +13 -0
  40. package/dist/types/conductor/transcript-store/serialize.test.d.ts +10 -0
  41. package/dist/types/conductor/transcript-store/store.d.ts +18 -0
  42. package/dist/types/console/components/Banner.d.ts +28 -6
  43. package/dist/types/console/components/Emblem.d.ts +49 -0
  44. package/dist/types/console/components/StatusBar.d.ts +14 -3
  45. package/dist/types/console/components/WorkingIndicator.d.ts +44 -0
  46. package/dist/types/console/components/WorkingIndicator.test.d.ts +9 -0
  47. package/dist/types/console/components/banner-sweep.d.ts +55 -0
  48. package/dist/types/console/components/banner.test.d.ts +9 -0
  49. package/dist/types/console/contract.d.ts +41 -7
  50. package/dist/types/console/input/keymap.d.ts +10 -1
  51. package/dist/types/console/overlays/approval-queue.d.ts +71 -0
  52. package/dist/types/console/overlays/approval.d.ts +104 -0
  53. package/dist/types/console/overlays/approval.test.d.ts +17 -0
  54. package/dist/types/console/overlays/host.d.ts +4 -3
  55. package/dist/types/console/overlays/index.d.ts +2 -0
  56. package/dist/types/console/theme/adapter.d.ts +19 -0
  57. package/dist/types/console/theme/index.d.ts +1 -1
  58. package/dist/types/console/theme/palette.d.ts +25 -0
  59. package/dist/types/console/theme/tokens.d.ts +23 -1
  60. package/dist/types/index.d.ts +1 -1
  61. package/dist/types/launch/contract.d.ts +2 -0
  62. package/dist/types/launch/index.d.ts +1 -1
  63. package/dist/types/launch/oauth.d.ts +13 -0
  64. package/dist/types/settings/contract.d.ts +59 -0
  65. package/dist/types/settings/index.d.ts +2 -2
  66. package/dist/types/window-budget/condenser.d.ts +15 -1
  67. package/dist/types/window-budget/index.d.ts +3 -1
  68. package/dist/types/window-budget/microcompact.d.ts +68 -0
  69. package/dist/types/window-budget/microcompact.test.d.ts +16 -0
  70. package/dist/types/window-budget/rehydrate.d.ts +56 -0
  71. package/dist/types/workspace/brand.d.ts +6 -0
  72. package/dist/types/workspace/index.d.ts +1 -1
  73. package/package.json +2 -2
@@ -84,6 +84,15 @@ export interface KeyChord {
84
84
  * Model / reasoning
85
85
  * - `model:cycleThinking` — advance the reasoning-effort ladder one step.
86
86
  *
87
+ * Permission mode
88
+ * - `mode:cyclePermission` — cycle through ALL permission modes (Shift+Tab),
89
+ * advancing one step per press: default → acceptEdits → plan → bypass →
90
+ * (wrap) default. In `plan` mode every mutating tool is blocked; in `bypass`
91
+ * every tool is auto-allowed.
92
+ * - `mode:togglePlan` — toggle read-only plan mode on/off. Drives `/plan`; no
93
+ * longer bound to Shift+Tab (which now cycles all modes). In plan mode every
94
+ * mutating tool is blocked at the permission gate.
95
+ *
87
96
  * Queue
88
97
  * - `queue:dequeue` — pull the most recently queued input back into the
89
98
  * composer so it can be edited or dropped.
@@ -106,7 +115,7 @@ export interface KeyChord {
106
115
  * Inert
107
116
  * - `none` — the keystroke carries no console meaning.
108
117
  */
109
- export type ConsoleVerb = "text:type" | "text:newline" | "edit:erasePrev" | "edit:eraseNext" | "edit:clearLine" | "nav:left" | "nav:right" | "nav:home" | "nav:end" | "nav:up" | "nav:down" | "flow:submit" | "flow:dismiss" | "flow:accept" | "flow:interrupt" | "flow:suspend" | "flow:cycleModel" | "model:cycleThinking" | "queue:dequeue" | "input:pasteImage" | "input:externalEditor" | "overlay:open" | "view:expandTools" | "view:toggleReasoning" | "none";
118
+ export type ConsoleVerb = "text:type" | "text:newline" | "edit:erasePrev" | "edit:eraseNext" | "edit:clearLine" | "nav:left" | "nav:right" | "nav:home" | "nav:end" | "nav:up" | "nav:down" | "flow:submit" | "flow:dismiss" | "flow:accept" | "flow:interrupt" | "flow:suspend" | "flow:cycleModel" | "model:cycleThinking" | "mode:cyclePermission" | "mode:togglePlan" | "queue:dequeue" | "input:pasteImage" | "input:externalEditor" | "overlay:open" | "view:expandTools" | "view:toggleReasoning" | "none";
110
119
  /**
111
120
  * A classified keystroke: a {@link ConsoleVerb} plus the small payload a verb
112
121
  * may carry — the literal text for a `text:type` insert, or the target overlay
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Approval queue — the headless, pure serializer behind the approval overlay.
3
+ *
4
+ * The permission gate may reach an `ask` decision while another approval prompt is
5
+ * already on screen (e.g. a turn that batches several tool calls). The UI shows ONE
6
+ * prompt at a time, so the pending requests are held in an ordered queue: each
7
+ * `enqueue` parks a request, the HEAD is the prompt currently shown, and `settle`
8
+ * resolves the head's parked promise then advances to the next. This module is a
9
+ * pure reducer over an immutable {@link ApprovalQueueState} plus a couple of tiny
10
+ * helpers — no React, no Ink, no timers — so the queue logic is unit-testable in
11
+ * isolation (the overlay component only renders the head and dispatches settles).
12
+ *
13
+ * Each queued entry carries the parked promise's `resolve` (the gate awaits it) and
14
+ * the request itself. Settling resolves with an {@link ApprovalChoice}; an
15
+ * abort/clear settles every outstanding entry with `"deny"` so a cancelled turn
16
+ * never hangs on a pending prompt.
17
+ */
18
+ import type { ApprovalChoice } from "../../conductor";
19
+ import type { ApprovalRequest } from "./approval";
20
+ /** One parked approval request awaiting the user's choice. */
21
+ export interface ApprovalEntry {
22
+ /** A stable id, unique within the queue's lifetime, used to settle the right one. */
23
+ readonly id: string;
24
+ /** The pending request the prompt renders. */
25
+ readonly request: ApprovalRequest;
26
+ /** Fulfil the gate's parked promise with the user's choice. Called exactly once. */
27
+ readonly resolve: (choice: ApprovalChoice) => void;
28
+ }
29
+ /**
30
+ * The immutable queue state. The array is ordered oldest-first; the head (index 0)
31
+ * is the request currently prompted. An empty queue means no prompt is showing.
32
+ */
33
+ export interface ApprovalQueueState {
34
+ /** The parked entries, oldest first; `[0]` is the active prompt. */
35
+ readonly entries: readonly ApprovalEntry[];
36
+ }
37
+ /** The empty queue — no approval prompt pending. */
38
+ export declare const EMPTY_APPROVAL_QUEUE: ApprovalQueueState;
39
+ /**
40
+ * The closed action union the queue reducer folds:
41
+ * - `enqueue` — park a new request at the tail.
42
+ * - `settle` — resolve the entry matching `id` with `choice` and drop it.
43
+ * - `clear` — drop EVERY entry (the caller resolves each with `"deny"`).
44
+ */
45
+ export type ApprovalQueueEvent = {
46
+ readonly type: "enqueue";
47
+ readonly entry: ApprovalEntry;
48
+ } | {
49
+ readonly type: "settle";
50
+ readonly id: string;
51
+ } | {
52
+ readonly type: "clear";
53
+ };
54
+ /**
55
+ * Fold one {@link ApprovalQueueEvent} over the queue, returning a fresh state.
56
+ *
57
+ * Pure and total: it never resolves a promise itself (the caller does that around
58
+ * a `settle`/`clear`), it only mutates the ordered entry list. `enqueue` appends;
59
+ * `settle` removes the entry with the matching `id` (a no-op when absent, e.g. a
60
+ * double-settle); `clear` empties the queue.
61
+ *
62
+ * @param state the prior queue state
63
+ * @param event the action to apply
64
+ */
65
+ export declare function approvalQueueReducer(state: ApprovalQueueState, event: ApprovalQueueEvent): ApprovalQueueState;
66
+ /**
67
+ * The active prompt — the head entry — or `undefined` when the queue is empty.
68
+ *
69
+ * @param state the queue state
70
+ */
71
+ export declare function activeApproval(state: ApprovalQueueState): ApprovalEntry | undefined;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Approval overlay — the interactive tool-permission prompt.
3
+ *
4
+ * This group owns the single modal kind raised when the permission gate reaches an
5
+ * `ask` decision for a tool call: it shows the pending request (tool name, a short
6
+ * rendering of the arguments, and any suggested allow-rule) and lets the user pick
7
+ * one of three outcomes — allow this one call, allow it for the rest of the session
8
+ * (which the gate turns into a session allow-rule), or deny it. It is mounted
9
+ * unconditionally by {@link import("./host").OverlayHost}, which routes by
10
+ * {@link ModalState.kind}; this component renders a dialog body only for its own
11
+ * `approval` kind and is otherwise inert.
12
+ *
13
+ * The wiring is a parked promise: when the React layer builds the conductor's
14
+ * approval resolver it returns a promise and stashes the request (plus the
15
+ * promise's `resolve`) under {@link ModalState.payload}. This overlay reads that
16
+ * payload, renders the choices, and on a pick calls `payload.resolve(choice)` then
17
+ * closes the modal — handing control back to the gate, which proceeds or denies. An
18
+ * Esc (the dialog's own close) resolves `"deny"` so a dismissed prompt never hangs
19
+ * the in-flight turn. The actual select UI reuses the framework `ThemeDialog`
20
+ * (a labelled, navigable pick-from-list with descriptions), so the overlay needs no
21
+ * bespoke key handling.
22
+ *
23
+ * React/Ink are obtained the same way the other overlays do (default React export
24
+ * from `indusagi/react-host`, `Box`/`Text` from `indusagi/react-host/ink`).
25
+ */
26
+ import { type ThemeDialogItem } from "indusagi/react-ink";
27
+ import type { ApprovalChoice } from "../../conductor";
28
+ import type { OverlayGroupProps } from "./pickers";
29
+ /**
30
+ * The pending tool-permission request a prompt is raised for. Mirrors the
31
+ * arguments the conductor's approval resolver is handed for an `ask` decision.
32
+ */
33
+ export interface ApprovalRequest {
34
+ /** The tool the model asked to run (e.g. `"Bash"`, `"Edit"`). */
35
+ readonly toolName: string;
36
+ /** The validated arguments the tool would run with. */
37
+ readonly input: unknown;
38
+ /**
39
+ * Optional human-readable allow-rule suggestions the prompt surfaces (e.g.
40
+ * `"Bash(npm run test:*)"`), so the user knows what an "allow always" remembers.
41
+ */
42
+ readonly suggestions?: readonly string[];
43
+ }
44
+ /**
45
+ * The opaque {@link ModalState.payload} the `approval` overlay narrows: the pending
46
+ * request plus the resolver of the parked approval promise. The overlay calls
47
+ * `resolve(choice)` exactly once, then closes the modal.
48
+ */
49
+ export interface ApprovalPayload {
50
+ /** The pending request to render. */
51
+ readonly request: ApprovalRequest;
52
+ /** Fulfil the parked approval promise with the user's choice. */
53
+ readonly resolve: (choice: ApprovalChoice) => void;
54
+ }
55
+ /**
56
+ * The three offerable outcomes, in listing order. The `id` is the
57
+ * {@link ApprovalChoice} the gate consumes; `label`/`description` are what the
58
+ * prompt shows. Kept as data (not an `if`-ladder) so the choice→resolution mapping
59
+ * is a pure table the headless test asserts against.
60
+ */
61
+ export declare const APPROVAL_CHOICES: readonly (ThemeDialogItem & {
62
+ id: ApprovalChoice;
63
+ })[];
64
+ /** The {@link ApprovalChoice} a dismissed (Esc) prompt resolves to. */
65
+ export declare const DISMISS_CHOICE: ApprovalChoice;
66
+ /**
67
+ * Map a selected dialog `id` back to the {@link ApprovalChoice} the gate consumes.
68
+ *
69
+ * Pure and total: a recognised id maps to itself; anything unrecognised falls back
70
+ * to {@link DISMISS_CHOICE} (`"deny"`) so a stray selection can never silently
71
+ * allow a tool. This is the unit the headless test pins.
72
+ *
73
+ * @param id the `id` the {@link ThemeDialog} reported on select
74
+ */
75
+ export declare function choiceFromId(id: string): ApprovalChoice;
76
+ /**
77
+ * Narrow the opaque modal payload into a typed {@link ApprovalPayload}, or
78
+ * `undefined` when it is not a well-formed approval request (a defensive guard —
79
+ * the overlay renders nothing rather than crash on a malformed payload).
80
+ *
81
+ * @param payload the opaque {@link ModalState.payload}
82
+ */
83
+ export declare function readApprovalPayload(payload: unknown): ApprovalPayload | undefined;
84
+ /**
85
+ * Render a tool call's arguments as a single short, human-readable line for the
86
+ * prompt body. A shell `command` (or any `command` field) shows verbatim;
87
+ * otherwise a compact JSON encoding is used. Long values are elided so the prompt
88
+ * never floods the terminal.
89
+ *
90
+ * @param input the validated tool arguments
91
+ */
92
+ export declare function summarizeInput(input: unknown): string;
93
+ /**
94
+ * The approval overlay group: the single `approval` modal kind.
95
+ *
96
+ * Renders the prompt body when the active modal is `approval` and its payload is a
97
+ * well-formed {@link ApprovalPayload}; every other kind (and a malformed payload)
98
+ * yields nothing. Unlike the session/auth groups this does NOT require the runtime
99
+ * services bundle — the payload carries everything the prompt needs — so it works
100
+ * on any mount path that wired a resolver.
101
+ *
102
+ * @param props the forwarded overlay props (modal, services, theme, dispatch, closer)
103
+ */
104
+ export declare function ApprovalOverlays(props: OverlayGroupProps): JSX.Element | null;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Approval overlay — headless unit tests for the pure logic behind the prompt.
3
+ *
4
+ * Rendering an Ink dialog headlessly is brittle, so these tests pin the two pure
5
+ * pieces the interactive overlay is built from — exactly the bar the task sets:
6
+ *
7
+ * 1. The choice→resolution mapping ({@link choiceFromId}) + the payload narrowing
8
+ * ({@link readApprovalPayload}) + the argument summary ({@link summarizeInput}):
9
+ * a selected dialog row maps to the {@link ApprovalChoice} the gate consumes, a
10
+ * malformed payload is rejected, and a stray id falls back to `deny`.
11
+ * 2. The queue reducer ({@link approvalQueueReducer}) + head selector
12
+ * ({@link activeApproval}): requests serialise oldest-first, a settle drops the
13
+ * matching entry (and a double-settle is inert), and a clear empties the queue.
14
+ *
15
+ * No React, no Ink, no conductor — just the exported functions.
16
+ */
17
+ export {};
@@ -10,9 +10,10 @@
10
10
  * owns a disjoint slice of the kind space and renders a body only for its own
11
11
  * kinds:
12
12
  *
13
- * - {@link PickerOverlays} — `models`, `scopedModels`, `theme`, `settings`.
14
- * - {@link SessionOverlays} — `sessions`, `tree`, `userTurns`.
15
- * - {@link AuthOverlays} — `signIn`, `signOut`, `oauth`, `plugin`.
13
+ * - {@link PickerOverlays} — `models`, `scopedModels`, `theme`, `settings`.
14
+ * - {@link SessionOverlays} — `sessions`, `tree`, `userTurns`.
15
+ * - {@link AuthOverlays} — `signIn`, `signOut`, `oauth`, `plugin`.
16
+ * - {@link ApprovalOverlays} — `approval` (the tool-permission prompt).
16
17
  *
17
18
  * When no overlay is raised (`kind === "none"`) the host renders nothing, so the
18
19
  * composer keeps focus and the groups are never mounted needlessly.
@@ -11,3 +11,5 @@ export { OverlayHost, type OverlayHostProps } from "./host";
11
11
  export { PickerOverlays, type OverlayGroupProps } from "./pickers";
12
12
  export { SessionOverlays } from "./sessions";
13
13
  export { AuthOverlays } from "./auth";
14
+ export { ApprovalOverlays, choiceFromId, readApprovalPayload, summarizeInput, APPROVAL_CHOICES, DISMISS_CHOICE, type ApprovalRequest, type ApprovalPayload, } from "./approval";
15
+ export { approvalQueueReducer, activeApproval, EMPTY_APPROVAL_QUEUE, type ApprovalEntry, type ApprovalQueueState, type ApprovalQueueEvent, } from "./approval-queue";
@@ -42,6 +42,25 @@ import type { InkThemeAdapter, ThemeTokens } from "../contract";
42
42
  * info ← notice (informational status tone)
43
43
  * highlight ← inkText (high-contrast on-accent text)
44
44
  *
45
+ * Plus the markdown / diff / syntax-highlight role keys the framework's rich
46
+ * render path (`theme.role(...)` / `theme.roleBackground(...)`) resolves. These
47
+ * key names match the framework adapter's default role → key map exactly, so the
48
+ * styled transcript, colored diffs, and fenced-code highlighting resolve their
49
+ * colours straight from the console's derived tokens:
50
+ *
51
+ * codeInline ← codeInline (inline `code` foreground)
52
+ * heading ← heading (markdown heading foreground)
53
+ * blockquoteBar ← blockquoteBar (dim quote bar)
54
+ * diffAddedBg ← diffAddedBg (added-line background tint)
55
+ * diffRemovedBg ← diffRemovedBg (removed-line background tint)
56
+ * diffAddedText ← diffAddedText (added foreground / `+`)
57
+ * diffRemovedText ← diffRemovedText (removed foreground / `-`)
58
+ * synKeyword ← synKeyword (syntax: keywords)
59
+ * synString ← synString (syntax: strings)
60
+ * synNumber ← synNumber (syntax: numbers)
61
+ * synComment ← synComment (syntax: comments)
62
+ * synType ← synType (syntax: types / classes)
63
+ *
45
64
  * @param tokens the derived semantic token map for a scheme
46
65
  * @returns a flat colour Record keyed by the framework's vocabulary
47
66
  */
@@ -12,7 +12,7 @@
12
12
  * Consumers import from `src/console/theme` and never reach into the individual
13
13
  * palette/tokens/adapter/resolve modules.
14
14
  */
15
- export { MIDNIGHT_PALETTE, DAYLIGHT_PALETTE, PALETTES } from "./palette";
15
+ export { MIDNIGHT_PALETTE, DAYLIGHT_PALETTE, MIDNIGHT_CB_PALETTE, DAYLIGHT_CB_PALETTE, PALETTES, } from "./palette";
16
16
  export { deriveTokens } from "./tokens";
17
17
  export { themeAdapter, frameworkColors } from "./adapter";
18
18
  export { THEMES, THEME_SCHEMES, resolveTheme } from "./resolve";
@@ -33,6 +33,31 @@ export declare const MIDNIGHT_PALETTE: ThemePalette;
33
33
  * to a dark-on-light slate.
34
34
  */
35
35
  export declare const DAYLIGHT_PALETTE: ThemePalette;
36
+ /**
37
+ * The dark-terminal color-blind-safe ramp.
38
+ *
39
+ * A clone of {@link MIDNIGHT_PALETTE} that re-derives the three *status* hues so
40
+ * a red-green color-blind user (deuteran/protan, ~8% of men) can tell success
41
+ * from failure without relying on the red-green axis. Success moves off green
42
+ * onto a vivid blue (`affirm → #4aa3ff`); failure stays red but is deepened to a
43
+ * darker tone (`alarm → #c83232`) so it sits well below the bright blue in
44
+ * lightness, and the amber warning is nudged brighter — so the three status
45
+ * tones separate by *lightness*, not hue alone. The three accent hues and the
46
+ * neutral gradient are carried over unchanged from the base midnight ramp — only
47
+ * the status stops move, so the overall look stays the midnight scheme.
48
+ */
49
+ export declare const MIDNIGHT_CB_PALETTE: ThemePalette;
50
+ /**
51
+ * The light-terminal color-blind-safe ramp.
52
+ *
53
+ * The daylight counterpart of {@link MIDNIGHT_CB_PALETTE}: clones
54
+ * {@link DAYLIGHT_PALETTE} and remaps success to a deeper blue tuned for a
55
+ * bright background (`affirm → #1f6fd6`), deepens the red alarm
56
+ * (`alarm → #b02622`) for lightness separation from that blue, and darkens the
57
+ * amber warning so the three status tones stay separable by lightness on a light
58
+ * terminal too.
59
+ */
60
+ export declare const DAYLIGHT_CB_PALETTE: ThemePalette;
36
61
  /**
37
62
  * The raw ramp for each scheme, keyed by {@link ThemeScheme}.
38
63
  *
@@ -25,6 +25,28 @@
25
25
  * - `caution` ← `caution` (warning tone)
26
26
  * - `alarm` ← `alarm` (error/fault tone)
27
27
  * - `pending` ← `primary` (busy/in-flight tone)
28
+ *
29
+ * Rich-render roles (markdown / diff / syntax highlighting) are derived from the
30
+ * same nine stops so the rebuild's new styled-transcript / colored-diff /
31
+ * fenced-code surfaces recolour with the scheme and need no extra palette stop:
32
+ *
33
+ * - `codeInline` ← `primary` (inline code accent)
34
+ * - `heading` ← `primary` (heading accent)
35
+ * - `blockquoteBar` ← `muted` (dim quote bar)
36
+ * - `diffAddedBg` ← `affirm` darkened toward ink (added-line tint)
37
+ * - `diffRemovedBg` ← `alarm` darkened toward ink (removed-line tint)
38
+ * - `diffAddedText` ← `affirm` (added foreground / `+`)
39
+ * - `diffRemovedText` ← `alarm` (removed foreground / `-`)
40
+ * - `synKeyword` ← `primary` (keywords)
41
+ * - `synString` ← `affirm` (strings)
42
+ * - `synNumber` ← `caution` (numbers)
43
+ * - `synComment` ← `muted` (comments)
44
+ * - `synType` ← `tertiary` (types / classes)
45
+ *
46
+ * Deriving the two diff backgrounds (rather than adding raw stops) keeps the
47
+ * {@link ThemePalette} a flat nine-stop ramp while guaranteeing every scheme —
48
+ * including the color-blind variants — gets a legible `+`/`-` tint that tracks
49
+ * its own success/alarm hue.
28
50
  */
29
51
  import type { ThemePalette, ThemeTokens } from "../contract";
30
52
  /**
@@ -35,6 +57,6 @@ import type { ThemePalette, ThemeTokens } from "../contract";
35
57
  * assignments are shared by both schemes — only the ramp handed in differs.
36
58
  *
37
59
  * @param palette the raw nine-stop ramp for a scheme
38
- * @returns the thirteen-role semantic token map
60
+ * @returns the full semantic token map (status + rich-render roles)
39
61
  */
40
62
  export declare function deriveTokens(palette: ThemePalette): ThemeTokens;
@@ -21,4 +21,4 @@ export * as insight from "./insight";
21
21
  export * as kit from "./kit";
22
22
  export * as settings from "./settings";
23
23
  export * as sessions from "./sessions";
24
- export declare const VERSION = "0.1.61";
24
+ export { VERSION } from "./workspace";
@@ -171,6 +171,8 @@ export interface Invocation {
171
171
  readonly attachments?: Attachments;
172
172
  /** Explicit model selector (`--model` / `-m`), provider-qualified or bare. */
173
173
  readonly model?: string;
174
+ /** Model to fall back to when the selected model is overloaded mid-turn (`--fallback-model`). */
175
+ readonly fallbackModel?: string;
174
176
  /** Named credential account to authenticate the run with (`--account`). */
175
177
  readonly account?: string;
176
178
  /** Working directory the run is scoped to (`--cwd`); absent means process cwd. */
@@ -20,7 +20,7 @@ export { runPackageCommand, defaultPackageIo, PACKAGE_COMMANDS, } from "./packag
20
20
  export type { PackageCommand, PackageIo, PackageCommandOptions, PackageResult, } from "./packages";
21
21
  export { runCredentialCommand, defaultCredentialIo, formatCredentialFault, validateApiKey, validateAccountName, findProvider, isOAuthCapable, asSigninMethod, PROVIDER_DIRECTORY, } from "./credentials";
22
22
  export type { CredentialIo, CredentialResult, SigninMethod } from "./credentials";
23
- export { registerBuiltInOAuthProviders, listLoginProviders, startOAuthLogin, openLoginUrl, } from "./oauth";
23
+ export { registerBuiltInOAuthProviders, listLoginProviders, startOAuthLogin, openLoginUrl, hasRegisteredOAuthClientId, } from "./oauth";
24
24
  export type { AuthKind, LoginProvider, OAuthLoginResult, } from "./oauth";
25
25
  export { printModelCatalog, defaultCatalogIo, registrySource } from "./catalog";
26
26
  export type { CatalogIo, CatalogModelSource } from "./catalog";
@@ -29,6 +29,19 @@ import type { AuthVault } from "./contract";
29
29
  * that are registered after the call, for callers that want to confirm the set.
30
30
  */
31
31
  export declare function registerBuiltInOAuthProviders(): string[];
32
+ /**
33
+ * Whether a registered sign-in provider has a *real* OAuth client id wired in
34
+ * (its env var is set to something other than the framework's sentinel), so its
35
+ * browser sign-in can actually complete.
36
+ *
37
+ * Reads the env var live (not at import time) so a deployer that exports the id
38
+ * before launching `/login` is honored. A provider with no known client-id
39
+ * wiring (i.e. not one of the three sentinel-shipped framework providers) is
40
+ * treated as registered — we only gate the providers we know ship a sentinel.
41
+ *
42
+ * @param providerId the registered sign-in provider id
43
+ */
44
+ export declare function hasRegisteredOAuthClientId(providerId: string): boolean;
32
45
  /** How a provider authenticates in the merged sign-in directory. */
33
46
  export type AuthKind = "oauth" | "apiKey";
34
47
  /** One row of the merged sign-in directory. */
@@ -48,6 +48,46 @@ export type DeliveryMode = "all" | "one-at-a-time";
48
48
  export declare const DELIVERY_MODES: readonly DeliveryMode[];
49
49
  /** Narrow an arbitrary value to a known {@link DeliveryMode}. */
50
50
  export declare function isDeliveryMode(value: unknown): value is DeliveryMode;
51
+ /**
52
+ * How aggressively the per-tool permission gate auto-allows tool calls before it
53
+ * falls back to asking (or denying):
54
+ *
55
+ * - `default` — consult the allow/ask/deny rules; read-only tools are
56
+ * auto-allowed, anything else with no matching allow rule
57
+ * falls through to `ask`.
58
+ * - `acceptEdits` — additionally auto-allow filesystem edits/writes (the
59
+ * mutating edit/write/multiedit tools), leaving everything
60
+ * else on the `default` path.
61
+ * - `bypass` — allow every tool with no prompt (the unsafe "trust all"
62
+ * mode; `bypassPermissions` is accepted as an alias).
63
+ * - `plan` — deny every mutating tool outright (read-only research only)
64
+ * so the agent can plan without touching the workspace.
65
+ */
66
+ export type PermissionMode = "default" | "acceptEdits" | "bypass" | "plan" | "bypassPermissions";
67
+ /** The canonical {@link PermissionMode} values, as a frozen tuple for menus/guards. */
68
+ export declare const PERMISSION_MODES: readonly PermissionMode[];
69
+ /** Narrow an arbitrary value to a known {@link PermissionMode} (folds the alias). */
70
+ export declare function isPermissionMode(value: unknown): value is PermissionMode;
71
+ /**
72
+ * The declarative permission policy a session reads at startup.
73
+ *
74
+ * `allow`/`ask`/`deny` are ordered lists of induscode-style rule strings — a bare
75
+ * tool name (`"Bash"`) or a tool name with an argument specifier
76
+ * (`"Bash(npm run test:*)"`). Across tiers the three lists are concatenated, and
77
+ * `deny` wins over `ask` wins over `allow`. `defaultMode` seeds the session's
78
+ * permission mode when no flag overrides it. Every field is optional so an empty
79
+ * `{}` (or an absent `permissions` key) keeps today's allow-all behavior.
80
+ */
81
+ export interface PermissionSettings {
82
+ /** Rule strings that auto-allow a matching tool call (lowest precedence). */
83
+ allow?: string[];
84
+ /** Rule strings that force a matching tool call to prompt for approval. */
85
+ ask?: string[];
86
+ /** Rule strings that hard-block a matching tool call (highest precedence). */
87
+ deny?: string[];
88
+ /** The permission mode the session opens in when no flag overrides it. */
89
+ defaultMode?: PermissionMode;
90
+ }
51
91
  /**
52
92
  * Every preference an interactive coding-agent session reads, as one flat,
53
93
  * fully-optional record.
@@ -93,12 +133,31 @@ export interface Preferences {
93
133
  defaultThinkingLevel?: ThinkingLevel;
94
134
  /** Suppress the banner / tips shown on a normal interactive launch. */
95
135
  quietStartup?: boolean;
136
+ /**
137
+ * The last product version whose full masthead the user has already seen. The
138
+ * banner auto-condenses to a one-line header when this equals the running
139
+ * version (and nothing else demands the full block); it is rewritten to the
140
+ * running version after the full masthead is shown, so a launch only shows the
141
+ * big wordmark on the first sight of a version or a version bump.
142
+ */
143
+ lastSeenVersion?: string;
144
+ /** Opt-in static colour-sweep flourish tinting the startup wordmark / emblem. */
145
+ logoSweep?: boolean;
146
+ /** When set, motion-flavoured flourishes (e.g. the logo sweep) are suppressed. */
147
+ reducedMotion?: boolean;
96
148
  /** Whether the window-budget manager compacts the transcript automatically. */
97
149
  autoCompact?: boolean;
98
150
  /** What a double-escape does in the console. */
99
151
  doubleEscapeAction?: EscapeAction;
100
152
  /** Extension-package sources the launcher installs and loads (npm / git / local). */
101
153
  extensionPackages?: string[];
154
+ /**
155
+ * The declarative tool-permission policy: ordered allow/ask/deny rule lists and
156
+ * the opening permission mode. Across the project/global tiers the rule lists
157
+ * are concatenated (not overridden) and `deny > ask > allow` decides; an absent
158
+ * value (or an empty object) preserves today's allow-all behavior.
159
+ */
160
+ permissions?: PermissionSettings;
102
161
  }
103
162
  /** The set of legal preference keys, derived from {@link Preferences}. */
104
163
  export type SettingKey = keyof Preferences;
@@ -7,7 +7,7 @@
7
7
  * {@link PreferenceStore} reader/writer. Console surfaces and boot stages depend
8
8
  * on this module rather than reaching into the individual files.
9
9
  */
10
- export type { Preferences, SettingKey, EscapeAction, DeliveryMode, ThinkingLevel, } from "./contract";
11
- export { DEFAULT_PREFERENCES, SETTING_KEYS, ESCAPE_ACTIONS, isEscapeAction, DELIVERY_MODES, isDeliveryMode, } from "./contract";
10
+ export type { Preferences, SettingKey, EscapeAction, DeliveryMode, ThinkingLevel, PermissionMode, PermissionSettings, } from "./contract";
11
+ export { DEFAULT_PREFERENCES, SETTING_KEYS, ESCAPE_ACTIONS, isEscapeAction, DELIVERY_MODES, isDeliveryMode, PERMISSION_MODES, isPermissionMode, } from "./contract";
12
12
  export { PreferenceStore } from "./manager";
13
13
  export type { PreferenceLocations } from "./manager";
@@ -28,8 +28,22 @@
28
28
  * matching session-scope entrypoint. Both are re-exported here so callers can
29
29
  * summarize a slice directly without building a full {@link Condenser}.
30
30
  */
31
- import type { AgentMessage, CondenserDeps, Condenser, Summary } from "./contract.js";
31
+ import type { AgentMessage, BudgetPolicy, CondenserDeps, Condenser, Summary } from "./contract.js";
32
32
  import { condenseScope, type SummarizeDeps } from "./summarize/index.js";
33
+ /**
34
+ * The fallback {@link BudgetPolicy} when the caller supplies none. These are
35
+ * ordinary configuration defaults — a window-relative ratio plus a token tail —
36
+ * computed from the model's own context window rather than fixed magic numbers.
37
+ * - `triggerRatio` 0.75 — condense once ~three-quarters of the window is used.
38
+ * - `keepRecent` 6000 — keep roughly the last 6k tokens of turns verbatim.
39
+ * - `reserveTokens` 2048 — carve a little headroom off the window first.
40
+ *
41
+ * Exported as {@link AUTO_CONDENSE_POLICY} so the conductor's token gate and the
42
+ * session runner's slice planner share **one** policy — the value the auto-path
43
+ * trigger ({@link isOverBudget}) compares against must equal the value the slice
44
+ * planner ({@link planSlice}) cuts with, or the gate and the cut disagree.
45
+ */
46
+ export declare const AUTO_CONDENSE_POLICY: BudgetPolicy;
33
47
  /**
34
48
  * Build a {@link Condenser} from a {@link CondenserDeps} bundle. The result plugs
35
49
  * straight into the conductor's `CondenseFn` seam.
@@ -12,4 +12,6 @@ export type { AgentMessage, BudgetPolicy, CompleteFn, CondensePlan, Condenser, C
12
12
  export { budgetLimit, estimateMessageTokens, estimateTokens, isOverBudget, planSlice, prefixTokens, } from "./budget/index.js";
13
13
  export { buildSummaryPrompt, CONDENSER_BRIEF, flattenTranscript, summarize, } from "./summarize/index.js";
14
14
  export type { SummarizeDeps } from "./summarize/index.js";
15
- export { condense, condenseScope, createCondenser } from "./condenser.js";
15
+ export { AUTO_CONDENSE_POLICY, condense, condenseScope, createCondenser } from "./condenser.js";
16
+ export { CLEARED_TOOL_RESULT, clearStaleToolResults, COMPACTABLE_TOOL_NAMES, } from "./microcompact.js";
17
+ export { rehydrateRecentReads, RESTORED_FILE_PREFIX } from "./rehydrate.js";
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Window-budget / microcompact — clear stale tool-result bodies in place.
3
+ *
4
+ * The cheapest token to reclaim is one that is already dead weight: the verbatim
5
+ * body of a tool result the model has long since acted on. Once an agent has
6
+ * read a file, run a search, or executed a command and moved several turns past
7
+ * it, the full body of that result rarely needs to stay in the window — the
8
+ * model's subsequent reasoning already captured what mattered. Yet those bodies
9
+ * (a 2,000-line `read`, a noisy `bash` log, a wide `grep`) dominate the token
10
+ * budget.
11
+ *
12
+ * **Microcompaction** runs *before* a full {@link condense}: it walks the
13
+ * transcript, keeps the last few tool results verbatim (the model may still be
14
+ * working with them), and replaces the body of every older compactable tool
15
+ * result with a short sentinel. This reclaims tokens for free — no model call,
16
+ * no summary round-trip — and often defers (or shrinks the input to) the real
17
+ * condense.
18
+ *
19
+ * Shape note (this rebuild): a tool result is its OWN top-level
20
+ * {@link ToolResultMessage} (`role: 'toolResult'`), not a block nested inside a
21
+ * user message. Clearing one therefore means replacing *that message's*
22
+ * `content` array with a single sentinel text block — never editing a sub-block
23
+ * of some other message. We only clear results whose owning assistant `toolCall`
24
+ * named a compactable tool, so structural/control results stay intact.
25
+ *
26
+ * Behavior-preserving by construction: when there is nothing to clear (no
27
+ * compactable results, or every one is within the keep window) the *same input
28
+ * reference* is returned, so a caller can cheaply detect a no-op.
29
+ */
30
+ import type { AgentMessage } from "./contract.js";
31
+ /**
32
+ * The placeholder spliced in where a stale tool-result body used to be. Kept
33
+ * short on purpose — the whole point is to reclaim the tokens the original body
34
+ * occupied — while still signalling to the model (and to a human reading the
35
+ * transcript) that real content was elided rather than lost.
36
+ */
37
+ export declare const CLEARED_TOOL_RESULT = "[Old tool result content cleared]";
38
+ /**
39
+ * The wire names of tools whose results are safe to clear once stale.
40
+ *
41
+ * These are the read-mostly / output-heavy tools whose bodies are reconstructable
42
+ * (re-read the file, re-run the search) and whose stale content the model has
43
+ * already digested. Control/state tools (todo, memory, task) are deliberately
44
+ * absent — their results can carry standing instructions the model still needs.
45
+ */
46
+ export declare const COMPACTABLE_TOOL_NAMES: ReadonlySet<string>;
47
+ /**
48
+ * Clear the bodies of stale compactable tool results, keeping the most recent
49
+ * `keepRecent` of them verbatim.
50
+ *
51
+ * Walks the transcript, identifies every `role:'toolResult'` message that
52
+ * belongs to a compactable tool call, keeps the last `keepRecent` (floored at 1
53
+ * — `slice(-0)` would keep everything), and replaces each older one's `content`
54
+ * with a single `{ type:'text', text: CLEARED_TOOL_RESULT }` block. Non-tool
55
+ * messages, control-tool results, and already-cleared results are left exactly
56
+ * as they were.
57
+ *
58
+ * Returns the **same input array reference** when nothing was cleared, so the
59
+ * caller can detect a no-op by identity. Otherwise returns a new array; cleared
60
+ * messages are fresh objects (the originals are not mutated).
61
+ *
62
+ * @param messages the active transcript, oldest-first
63
+ * @param opts.keepRecent how many most-recent compactable results to keep
64
+ * verbatim (default {@link DEFAULT_KEEP_RECENT}, floored at 1)
65
+ */
66
+ export declare function clearStaleToolResults(messages: AgentMessage[], opts?: {
67
+ keepRecent?: number;
68
+ }): AgentMessage[];
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Window-budget / microcompact + rehydrate — unit tests for item #18.
3
+ *
4
+ * Three layers ride the condense path:
5
+ * 1. microcompact ({@link clearStaleToolResults}) — blank older completed
6
+ * tool-result bodies to a sentinel, keeping the last N verbatim; no-op
7
+ * (same reference) otherwise.
8
+ * 2. rehydration ({@link rehydrateRecentReads}) — re-attach the most-recently
9
+ * dropped file reads as synthetic user messages, deduped against the kept
10
+ * tail, honoring maxFiles / tokenBudget.
11
+ * 3. circuit breaker — covered in the conductor suite (3 throwing condense
12
+ * calls stop further attempts, a success resets).
13
+ *
14
+ * Pure + network-free: no model is bound anywhere here.
15
+ */
16
+ export {};