indusagi-coding-agent 0.2.8 → 0.2.9

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 (219) hide show
  1. package/package.json +2 -1
  2. package/src/_decls/entry.ts +18 -0
  3. package/src/_decls/guardrails.ts +35 -0
  4. package/src/_decls/index.ts +26 -0
  5. package/src/addons/contract.ts +236 -0
  6. package/src/addons/dispatch/event-dispatcher.ts +164 -0
  7. package/src/addons/dispatch/index.ts +25 -0
  8. package/src/addons/dispatch/tool-interceptor.ts +208 -0
  9. package/src/addons/host.ts +225 -0
  10. package/src/addons/index.ts +112 -0
  11. package/src/addons/manifest.ts +158 -0
  12. package/src/addons/sandbox.ts +170 -0
  13. package/src/addons/surface.ts +78 -0
  14. package/src/boot/auth-vault.ts +195 -0
  15. package/src/boot/boot.ts +138 -0
  16. package/src/boot/contract.ts +238 -0
  17. package/src/boot/heap.ts +59 -0
  18. package/src/boot/index.ts +28 -0
  19. package/src/boot/invocation.ts +93 -0
  20. package/src/boot/runners/addon-wiring.ts +153 -0
  21. package/src/boot/runners/checkpoint.ts +169 -0
  22. package/src/boot/runners/delegate-runner.ts +294 -0
  23. package/src/boot/runners/index.ts +13 -0
  24. package/src/boot/runners/link-runner.ts +45 -0
  25. package/src/boot/runners/memdir.ts +168 -0
  26. package/src/boot/runners/oneshot-runner.ts +58 -0
  27. package/src/boot/runners/read-state.ts +90 -0
  28. package/src/boot/runners/registry.ts +42 -0
  29. package/src/boot/runners/repl-runner.ts +143 -0
  30. package/src/boot/runners/server-mode.ts +121 -0
  31. package/src/boot/runners/session.ts +641 -0
  32. package/src/boot/server-token.ts +148 -0
  33. package/src/boot/stages.ts +167 -0
  34. package/src/boot/upgrade/apply.ts +94 -0
  35. package/src/boot/upgrade/index.ts +13 -0
  36. package/src/boot/upgrade/upgrades.ts +289 -0
  37. package/src/briefing/compose.ts +150 -0
  38. package/src/briefing/context-docs.ts +19 -0
  39. package/src/briefing/contract.ts +717 -0
  40. package/src/briefing/index.ts +31 -0
  41. package/src/briefing/macros.ts +97 -0
  42. package/src/briefing/skills.ts +47 -0
  43. package/src/capability-deck/bridge-ledger/index.ts +27 -0
  44. package/src/capability-deck/bridge-ledger/key.ts +67 -0
  45. package/src/capability-deck/bridge-ledger/ledger.ts +131 -0
  46. package/src/capability-deck/bridge-ledger/network.ts +117 -0
  47. package/src/capability-deck/builtin-bridge.ts +312 -0
  48. package/src/capability-deck/cards/bg-process-card.ts +335 -0
  49. package/src/capability-deck/cards/index.ts +115 -0
  50. package/src/capability-deck/cards/memory-card.ts +146 -0
  51. package/src/capability-deck/cards/plan-file.ts +97 -0
  52. package/src/capability-deck/cards/plan-tools.ts +185 -0
  53. package/src/capability-deck/cards/saas-card.ts +183 -0
  54. package/src/capability-deck/cards/task-card.ts +207 -0
  55. package/src/capability-deck/cards/todo-card.ts +168 -0
  56. package/src/capability-deck/cards/workflow-card.ts +247 -0
  57. package/src/capability-deck/contract.ts +388 -0
  58. package/src/capability-deck/index.ts +48 -0
  59. package/src/capability-deck/manifest.ts +109 -0
  60. package/src/capability-deck/provision.ts +169 -0
  61. package/src/channels/contract.ts +191 -0
  62. package/src/channels/framer.ts +50 -0
  63. package/src/channels/index.ts +101 -0
  64. package/src/channels/link/dialog.ts +129 -0
  65. package/src/channels/link/driver.ts +190 -0
  66. package/src/channels/link/index.ts +34 -0
  67. package/src/channels/link/server.ts +134 -0
  68. package/src/channels/oneshot.ts +90 -0
  69. package/src/channels/ops.ts +65 -0
  70. package/src/channels/session-ops.ts +81 -0
  71. package/src/conductor/bash-guard.ts +599 -0
  72. package/src/conductor/catalog/catalog.ts +116 -0
  73. package/src/conductor/catalog/index.ts +8 -0
  74. package/src/conductor/catalog/matcher.ts +134 -0
  75. package/src/conductor/conductor.ts +234 -0
  76. package/src/conductor/contract.ts +842 -0
  77. package/src/conductor/diagnostics.ts +227 -0
  78. package/src/conductor/index.ts +33 -0
  79. package/src/conductor/permissions.ts +588 -0
  80. package/src/conductor/quota-error.ts +49 -0
  81. package/src/conductor/signal-hub/hub.ts +46 -0
  82. package/src/conductor/signal-hub/index.ts +2 -0
  83. package/src/conductor/signal-hub/translate.test.ts +81 -0
  84. package/src/conductor/signal-hub/translate.ts +74 -0
  85. package/src/conductor/skill-parse/index.ts +2 -0
  86. package/src/conductor/skill-parse/parse.ts +108 -0
  87. package/src/conductor/transcript-store/index.ts +22 -0
  88. package/src/conductor/transcript-store/serialize.ts +116 -0
  89. package/src/conductor/transcript-store/store.ts +205 -0
  90. package/src/console/auth-status.ts +56 -0
  91. package/src/console/components/AgentsView.ts +165 -0
  92. package/src/console/components/BackgroundAgents.ts +155 -0
  93. package/src/console/components/Banner.ts +334 -0
  94. package/src/console/components/Composer.ts +94 -0
  95. package/src/console/components/StatusBar.ts +49 -0
  96. package/src/console/components/TerminalConsole.ts +1090 -0
  97. package/src/console/components/WorkingIndicator.ts +98 -0
  98. package/src/console/components/banner-sweep.ts +24 -0
  99. package/src/console/components/welcome.ts +74 -0
  100. package/src/console/contract.ts +630 -0
  101. package/src/console/index.ts +34 -0
  102. package/src/console/input/complete.ts +127 -0
  103. package/src/console/input/dir-reader.ts +34 -0
  104. package/src/console/input/index.ts +23 -0
  105. package/src/console/input/keymap.ts +159 -0
  106. package/src/console/input/paste.ts +104 -0
  107. package/src/console/mount.ts +56 -0
  108. package/src/console/overlays/approval-queue.ts +57 -0
  109. package/src/console/overlays/approval.ts +130 -0
  110. package/src/console/overlays/auth.ts +342 -0
  111. package/src/console/overlays/boards.ts +308 -0
  112. package/src/console/overlays/host.ts +36 -0
  113. package/src/console/overlays/index.ts +26 -0
  114. package/src/console/overlays/pickers.ts +258 -0
  115. package/src/console/overlays/sessions.ts +190 -0
  116. package/src/console/reducer.ts +182 -0
  117. package/src/console/slash/builtins.ts +81 -0
  118. package/src/console/slash/commands/dynamic.ts +83 -0
  119. package/src/console/slash/commands/integrations.ts +695 -0
  120. package/src/console/slash/commands/shared.ts +75 -0
  121. package/src/console/slash/commands/transcript.ts +263 -0
  122. package/src/console/slash/commands/workbench.ts +246 -0
  123. package/src/console/slash/index.ts +15 -0
  124. package/src/console/slash/registry.ts +70 -0
  125. package/src/console/slash/resolve.ts +63 -0
  126. package/src/console/startup.ts +209 -0
  127. package/src/console/theme/adapter.ts +45 -0
  128. package/src/console/theme/index.ts +7 -0
  129. package/src/console/theme/palette.ts +68 -0
  130. package/src/console/theme/resolve.ts +39 -0
  131. package/src/console/theme/tokens.ts +71 -0
  132. package/src/entry.ts +55 -0
  133. package/src/guardrails.ts +37 -0
  134. package/src/index.ts +18 -0
  135. package/src/insight/channel.ts +88 -0
  136. package/src/insight/contract.ts +185 -0
  137. package/src/insight/index.ts +110 -0
  138. package/src/insight/recorder.ts +213 -0
  139. package/src/insight/redaction.ts +157 -0
  140. package/src/insight/replay.ts +158 -0
  141. package/src/insight/sampling.ts +70 -0
  142. package/src/insight/serialize.ts +50 -0
  143. package/src/insight/sinks/console.ts +64 -0
  144. package/src/insight/sinks/file.ts +40 -0
  145. package/src/insight/sinks/index.ts +24 -0
  146. package/src/insight/sinks/stream.ts +54 -0
  147. package/src/integrations/sarvam/attach.ts +239 -0
  148. package/src/integrations/sarvam/config.ts +156 -0
  149. package/src/integrations/sarvam/index.ts +25 -0
  150. package/src/integrations/sarvam/sarvam.test.ts +60 -0
  151. package/src/integrations/sarvam/types.ts +27 -0
  152. package/src/integrations/zoho/attach.ts +342 -0
  153. package/src/integrations/zoho/config.ts +125 -0
  154. package/src/integrations/zoho/index.ts +27 -0
  155. package/src/integrations/zoho/types.ts +21 -0
  156. package/src/integrations/zoho/zoho.test.ts +50 -0
  157. package/src/kit/clipboard-image.ts +107 -0
  158. package/src/kit/external-editor.ts +48 -0
  159. package/src/kit/image.ts +59 -0
  160. package/src/kit/index.ts +51 -0
  161. package/src/kit/shell.ts +19 -0
  162. package/src/kit/tool-fetch.ts +85 -0
  163. package/src/launch/catalog.ts +148 -0
  164. package/src/launch/contract.ts +187 -0
  165. package/src/launch/credentials.ts +625 -0
  166. package/src/launch/index.ts +98 -0
  167. package/src/launch/invocation/attachments.ts +179 -0
  168. package/src/launch/invocation/flags.ts +196 -0
  169. package/src/launch/invocation/index.ts +25 -0
  170. package/src/launch/invocation/read.ts +260 -0
  171. package/src/launch/invocation/usage.ts +67 -0
  172. package/src/launch/login.ts +324 -0
  173. package/src/launch/oauth.test.ts +18 -0
  174. package/src/launch/oauth.ts +203 -0
  175. package/src/launch/packages.ts +194 -0
  176. package/src/launch/pickers.ts +189 -0
  177. package/src/runtime-bridge/bridges/_drive.ts +96 -0
  178. package/src/runtime-bridge/bridges/builtins.ts +68 -0
  179. package/src/runtime-bridge/bridges/claude-cli.ts +123 -0
  180. package/src/runtime-bridge/bridges/codex-cli.ts +142 -0
  181. package/src/runtime-bridge/bridges/index.ts +33 -0
  182. package/src/runtime-bridge/bridges/indusagi-cli.ts +155 -0
  183. package/src/runtime-bridge/broker.ts +227 -0
  184. package/src/runtime-bridge/contract.ts +122 -0
  185. package/src/runtime-bridge/index.ts +79 -0
  186. package/src/runtime-bridge/sink.ts +180 -0
  187. package/src/sessions/contract.ts +81 -0
  188. package/src/sessions/index.ts +13 -0
  189. package/src/sessions/library.ts +229 -0
  190. package/src/settings/contract.ts +114 -0
  191. package/src/settings/index.ts +32 -0
  192. package/src/settings/manager.ts +117 -0
  193. package/src/transcript-export/index.ts +45 -0
  194. package/src/transcript-export/publish.ts +260 -0
  195. package/src/transcript-export/sgr.ts +315 -0
  196. package/src/transcript-export/template.ts +272 -0
  197. package/src/transcript-export/theme-bridge.ts +150 -0
  198. package/src/window-budget/budget/estimate.ts +135 -0
  199. package/src/window-budget/budget/gate.ts +33 -0
  200. package/src/window-budget/budget/index.ts +16 -0
  201. package/src/window-budget/budget/slice.ts +56 -0
  202. package/src/window-budget/condenser.ts +58 -0
  203. package/src/window-budget/contract.ts +184 -0
  204. package/src/window-budget/index.ts +19 -0
  205. package/src/window-budget/microcompact.ts +95 -0
  206. package/src/window-budget/rehydrate.ts +136 -0
  207. package/src/window-budget/summarize/condense.ts +103 -0
  208. package/src/window-budget/summarize/index.ts +14 -0
  209. package/src/window-budget/summarize/prompt.ts +149 -0
  210. package/src/workflow-engine/agent-runner.ts +181 -0
  211. package/src/workflow-engine/display.ts +224 -0
  212. package/src/workflow-engine/engine.ts +294 -0
  213. package/src/workflow-engine/index.ts +23 -0
  214. package/src/workflow-engine/parse.ts +172 -0
  215. package/src/workflow-engine/structured-output.ts +35 -0
  216. package/src/workspace/brand.ts +29 -0
  217. package/src/workspace/index.ts +18 -0
  218. package/src/workspace/locator.ts +103 -0
  219. package/src/workspace/runtime-detect.ts +64 -0
@@ -0,0 +1,229 @@
1
+ /**
2
+ * SessionLibrary — the catalog-and-navigation layer over persisted transcripts.
3
+ */
4
+ import {
5
+ mkdir,
6
+ readFile,
7
+ readdir,
8
+ rename,
9
+ rm,
10
+ stat,
11
+ } from "node:fs/promises";
12
+ import { basename, join } from "node:path";
13
+ import { TranscriptStore, fsBackend } from "../conductor/index.js";
14
+ import type { BranchNode, PriorTurn, SavedSession } from "./contract.js";
15
+
16
+ const SESSION_FILE_EXT = ".ndjson";
17
+ const PREVIEW_LIMIT = 72;
18
+
19
+ export interface SessionLibraryOptions {
20
+ readonly sessionsDir: string;
21
+ }
22
+
23
+ export class SessionLibrary {
24
+ readonly #dir: string;
25
+ readonly #backend: ReturnType<typeof fsBackend>;
26
+
27
+ constructor(options: SessionLibraryOptions) {
28
+ this.#dir = options.sessionsDir;
29
+ this.#backend = fsBackend(options.sessionsDir);
30
+ }
31
+
32
+ get directory(): string {
33
+ return this.#dir;
34
+ }
35
+
36
+ async list(options: { readonly deep?: boolean } = {}): Promise<SavedSession[]> {
37
+ const ids = await this.#sessionIds();
38
+ const rows = await Promise.all(
39
+ ids.map((id) => (options.deep ? this.#deepRow(id) : this.#shallowRow(id))),
40
+ );
41
+ return rows.sort(byMostRecent);
42
+ }
43
+
44
+ async open(id: string): Promise<TranscriptStore | null> {
45
+ return TranscriptStore.open(id, { backend: this.#backend });
46
+ }
47
+
48
+ async rename(fromId: string, toId: string): Promise<string> {
49
+ const fromPath = this.pathOf(fromId);
50
+ const toPath = this.pathOf(toId);
51
+ if (await this.#missing(fromPath)) {
52
+ throw new Error(`SessionLibrary.rename: no session "${fromId}"`);
53
+ }
54
+ if (!(await this.#missing(toPath))) {
55
+ throw new Error(`SessionLibrary.rename: session "${toId}" already exists`);
56
+ }
57
+ await mkdir(this.#dir, { recursive: true });
58
+ await rename(fromPath, toPath);
59
+ return toPath;
60
+ }
61
+
62
+ async remove(id: string): Promise<boolean> {
63
+ const path = this.pathOf(id);
64
+ if (await this.#missing(path)) return false;
65
+ await rm(path, { force: true });
66
+ return true;
67
+ }
68
+
69
+ pathOf(id: string): string {
70
+ return join(this.#dir, `${id}${SESSION_FILE_EXT}`);
71
+ }
72
+
73
+ tree(store: TranscriptStore): BranchNode[] {
74
+ const nodes = [...store.state().nodes.values()];
75
+ const currentLeaf = store.head.leaf;
76
+ const childrenOf = new Map<string | null, typeof nodes>();
77
+ for (const node of nodes) {
78
+ const bucket = childrenOf.get(node.parent) ?? [];
79
+ bucket.push(node);
80
+ childrenOf.set(node.parent, bucket);
81
+ }
82
+ const hasChildren = new Set<string>();
83
+ for (const node of nodes) {
84
+ if (node.parent !== null) hasChildren.add(node.parent);
85
+ }
86
+ const out: BranchNode[] = [];
87
+ const walk = (entry: (typeof nodes)[number], depth: number): void => {
88
+ out.push({
89
+ id: entry.id,
90
+ parent: entry.parent,
91
+ label: labelFor(entry, depth),
92
+ depth,
93
+ isLeaf: !hasChildren.has(entry.id),
94
+ isCurrent: entry.id === currentLeaf,
95
+ });
96
+ for (const child of childrenOf.get(entry.id) ?? []) {
97
+ walk(child, depth + 1);
98
+ }
99
+ };
100
+ for (const root of childrenOf.get(null) ?? []) {
101
+ walk(root, 0);
102
+ }
103
+ return out;
104
+ }
105
+
106
+ priorTurns(store: TranscriptStore): PriorTurn[] {
107
+ const out: PriorTurn[] = [];
108
+ for (const entry of store.pathTo()) {
109
+ if (entry.role !== "user") continue;
110
+ const text = messageText(entry.content).trim();
111
+ if (text.length === 0) continue;
112
+ out.push({ entryId: entry.id, text, preview: previewOf(text) });
113
+ }
114
+ return out;
115
+ }
116
+
117
+ async #sessionIds(): Promise<string[]> {
118
+ let names: string[];
119
+ try {
120
+ names = await readdir(this.#dir);
121
+ } catch {
122
+ return [];
123
+ }
124
+ return names
125
+ .filter((name) => name.endsWith(SESSION_FILE_EXT))
126
+ .map((name) => basename(name, SESSION_FILE_EXT));
127
+ }
128
+
129
+ async #shallowRow(id: string): Promise<SavedSession> {
130
+ const path = this.pathOf(id);
131
+ const meta = await this.#fileMeta(path);
132
+ return { id, path, ...meta };
133
+ }
134
+
135
+ async #deepRow(id: string): Promise<SavedSession> {
136
+ const path = this.pathOf(id);
137
+ const meta = await this.#fileMeta(path);
138
+ const store = await this.open(id);
139
+ if (store === null) return { id, path, ...meta };
140
+ const branch = store.pathTo();
141
+ const conversational = branch.filter(isConversational);
142
+ const opener = conversational.find((entry) => entry.role === "user");
143
+ const preview = opener ? previewOf(messageText(opener.content)) : undefined;
144
+ return {
145
+ id,
146
+ path,
147
+ ...meta,
148
+ messageCount: conversational.length,
149
+ ...(preview ? { preview, name: preview } : {}),
150
+ };
151
+ }
152
+
153
+ async #fileMeta(path: string): Promise<{ size?: number; lastModified?: number }> {
154
+ try {
155
+ const info = await stat(path);
156
+ return { size: info.size, lastModified: info.mtimeMs };
157
+ } catch {
158
+ return {};
159
+ }
160
+ }
161
+
162
+ async #missing(path: string): Promise<boolean> {
163
+ try {
164
+ await readFile(path);
165
+ return false;
166
+ } catch {
167
+ return true;
168
+ }
169
+ }
170
+ }
171
+
172
+ function isConversational(entry: { role: string }): boolean {
173
+ return entry.role === "user" || entry.role === "assistant" || entry.role === "tool";
174
+ }
175
+
176
+ function byMostRecent(a: SavedSession, b: SavedSession): number {
177
+ const delta = (b.lastModified ?? 0) - (a.lastModified ?? 0);
178
+ return delta !== 0 ? delta : a.id.localeCompare(b.id);
179
+ }
180
+
181
+ function labelFor(
182
+ entry: { id: string; role: string; content: unknown },
183
+ depth: number,
184
+ ): string {
185
+ const indent = " ".repeat(depth);
186
+ switch (entry.role) {
187
+ case "user":
188
+ return `${indent}user: ${previewOf(messageText(entry.content))}`;
189
+ case "assistant":
190
+ return `${indent}assistant`;
191
+ case "tool":
192
+ return `${indent}tool: ${toolNameOf(entry.content)}`;
193
+ case "condense":
194
+ return `${indent}condense`;
195
+ case "system":
196
+ return `${indent}system`;
197
+ default:
198
+ return `${indent}note`;
199
+ }
200
+ }
201
+
202
+ function toolNameOf(message: { toolName?: unknown }): string {
203
+ const candidate = message.toolName;
204
+ return typeof candidate === "string" && candidate.length > 0 ? candidate : "tool";
205
+ }
206
+
207
+ function messageText(message: unknown): string {
208
+ const content = (message as { content?: unknown }).content;
209
+ if (typeof content === "string") return content;
210
+ if (!Array.isArray(content)) return "";
211
+ const parts: string[] = [];
212
+ for (const block of content) {
213
+ if (
214
+ block &&
215
+ typeof block === "object" &&
216
+ (block as { type?: unknown }).type === "text"
217
+ ) {
218
+ const text = (block as { text?: unknown }).text;
219
+ if (typeof text === "string") parts.push(text);
220
+ }
221
+ }
222
+ return parts.join("");
223
+ }
224
+
225
+ function previewOf(text: string, limit: number = PREVIEW_LIMIT): string {
226
+ const flat = text.replace(/\s+/g, " ").trim();
227
+ if (flat.length <= limit) return flat;
228
+ return `${flat.slice(0, Math.max(0, limit - 1))}\u2026`;
229
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Settings subsystem — the typed surface of the user-tunable preferences an
3
+ * interactive coding-agent session reads at startup and while it runs.
4
+ */
5
+ import type { ThinkingLevel } from "indusagi/agent";
6
+
7
+ export type { ThinkingLevel };
8
+
9
+ export type EscapeAction = "tree" | "fork" | "clear" | "rewind" | "branch";
10
+
11
+ export const ESCAPE_ACTIONS: readonly EscapeAction[] = ["tree", "fork", "clear"];
12
+
13
+ export function isEscapeAction(value: unknown): value is EscapeAction {
14
+ return typeof value === "string" && (ESCAPE_ACTIONS as readonly string[]).includes(value);
15
+ }
16
+
17
+ export type DeliveryMode = "all" | "one-at-a-time";
18
+
19
+ export const DELIVERY_MODES: readonly DeliveryMode[] = ["all", "one-at-a-time"];
20
+
21
+ export function isDeliveryMode(value: unknown): value is DeliveryMode {
22
+ return typeof value === "string" && (DELIVERY_MODES as readonly string[]).includes(value);
23
+ }
24
+
25
+ export type PermissionMode =
26
+ | "default"
27
+ | "acceptEdits"
28
+ | "bypass"
29
+ | "plan"
30
+ | "bypassPermissions";
31
+
32
+ export const PERMISSION_MODES: readonly PermissionMode[] = [
33
+ "default",
34
+ "acceptEdits",
35
+ "bypass",
36
+ "plan",
37
+ ];
38
+
39
+ export function isPermissionMode(value: unknown): value is PermissionMode {
40
+ return (
41
+ value === "default" ||
42
+ value === "acceptEdits" ||
43
+ value === "bypass" ||
44
+ value === "plan" ||
45
+ value === "bypassPermissions"
46
+ );
47
+ }
48
+
49
+ export interface PermissionSettings {
50
+ allow?: string[];
51
+ ask?: string[];
52
+ deny?: string[];
53
+ defaultMode?: PermissionMode;
54
+ }
55
+
56
+ export interface Preferences {
57
+ colourScheme?: string;
58
+ showImages?: boolean;
59
+ showReasoning?: boolean;
60
+ hideThinking?: boolean;
61
+ imageAutoResize?: boolean;
62
+ blockImages?: boolean;
63
+ enableSkillCommands?: boolean;
64
+ steeringMode?: DeliveryMode;
65
+ followUpMode?: DeliveryMode;
66
+ collapseChangelog?: boolean;
67
+ showHardwareCursor?: boolean;
68
+ editorPaddingX?: number;
69
+ enabledModels?: string[];
70
+ defaultProvider?: string;
71
+ defaultModel?: string;
72
+ defaultThinkingLevel?: ThinkingLevel;
73
+ quietStartup?: boolean;
74
+ lastSeenVersion?: string;
75
+ logoSweep?: boolean;
76
+ reducedMotion?: boolean;
77
+ autoCompact?: boolean;
78
+ doubleEscapeAction?: EscapeAction;
79
+ extensionPackages?: string[];
80
+ permissions?: PermissionSettings;
81
+ }
82
+
83
+ export type SettingKey = keyof Preferences;
84
+
85
+ export const DEFAULT_PREFERENCES: Required<Preferences> = Object.freeze({
86
+ colourScheme: "default",
87
+ showImages: true,
88
+ showReasoning: true,
89
+ hideThinking: false,
90
+ imageAutoResize: true,
91
+ blockImages: false,
92
+ enableSkillCommands: true,
93
+ steeringMode: "all",
94
+ followUpMode: "all",
95
+ collapseChangelog: false,
96
+ showHardwareCursor: false,
97
+ editorPaddingX: 1,
98
+ enabledModels: [],
99
+ defaultProvider: "anthropic",
100
+ defaultModel: "",
101
+ defaultThinkingLevel: "medium",
102
+ quietStartup: false,
103
+ lastSeenVersion: "",
104
+ logoSweep: false,
105
+ reducedMotion: false,
106
+ autoCompact: true,
107
+ doubleEscapeAction: "clear",
108
+ extensionPackages: [],
109
+ permissions: { allow: [], ask: [], deny: [], defaultMode: "default" },
110
+ }) as Required<Preferences>;
111
+
112
+ export const SETTING_KEYS: readonly SettingKey[] = Object.freeze(
113
+ Object.keys(DEFAULT_PREFERENCES) as SettingKey[],
114
+ );
@@ -0,0 +1,32 @@
1
+ // @ts-nocheck
2
+ // Type declarations recovered from dist/types (no runtime body)
3
+ /**
4
+ * Settings subsystem — public barrel.
5
+ *
6
+ * One import site for the typed {@link Preferences} record and its
7
+ * {@link SettingKey} / {@link EscapeAction} / {@link ThinkingLevel}
8
+ * vocabularies, the frozen {@link DEFAULT_PREFERENCES}, and the two-tier
9
+ * {@link PreferenceStore} reader/writer. Console surfaces and boot stages depend
10
+ * on this module rather than reaching into the individual files.
11
+ */
12
+ export type {
13
+ Preferences,
14
+ SettingKey,
15
+ EscapeAction,
16
+ DeliveryMode,
17
+ ThinkingLevel,
18
+ PermissionMode,
19
+ PermissionSettings,
20
+ } from "./contract.js";
21
+ export {
22
+ DEFAULT_PREFERENCES,
23
+ SETTING_KEYS,
24
+ ESCAPE_ACTIONS,
25
+ isEscapeAction,
26
+ DELIVERY_MODES,
27
+ isDeliveryMode,
28
+ PERMISSION_MODES,
29
+ isPermissionMode,
30
+ } from "./contract.js";
31
+ export { PreferenceStore } from "./manager.js";
32
+ export type { PreferenceLocations } from "./manager.js";
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Settings manager — the two-tier reader/writer over the preference record.
3
+ */
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+ import type { Workspace } from "../boot/contract.js";
7
+ import { BRAND } from "../workspace/brand.js";
8
+ import { DEFAULT_PREFERENCES, type Preferences, type SettingKey } from "./contract.js";
9
+
10
+ export interface PreferenceLocations {
11
+ readonly global: string;
12
+ readonly project: string;
13
+ }
14
+
15
+ type PreferencesPartial = Partial<Preferences>;
16
+
17
+ function loadTier(filePath: string): PreferencesPartial {
18
+ if (!existsSync(filePath)) return {};
19
+ let raw: string;
20
+ try {
21
+ raw = readFileSync(filePath, "utf-8");
22
+ } catch {
23
+ return {};
24
+ }
25
+ let parsed: unknown;
26
+ try {
27
+ parsed = JSON.parse(raw);
28
+ } catch {
29
+ return {};
30
+ }
31
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
32
+ return {};
33
+ }
34
+ return { ...(parsed as Record<string, unknown>) } as PreferencesPartial;
35
+ }
36
+
37
+ function writeTier(filePath: string, tier: PreferencesPartial): void {
38
+ const dir = dirname(filePath);
39
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
40
+ writeFileSync(filePath, `${JSON.stringify(tier, null, 2)}\n`, "utf-8");
41
+ }
42
+
43
+ export class PreferenceStore {
44
+ readonly #locations: PreferenceLocations;
45
+ #globalTier: PreferencesPartial;
46
+ #projectTier: PreferencesPartial;
47
+
48
+ private constructor(locations: PreferenceLocations) {
49
+ this.#locations = locations;
50
+ this.#globalTier = loadTier(locations.global);
51
+ this.#projectTier = loadTier(locations.project);
52
+ }
53
+
54
+ static atPaths(locations: PreferenceLocations): PreferenceStore {
55
+ return new PreferenceStore(locations);
56
+ }
57
+
58
+ static fromWorkspace(workspace: Workspace, cwd: string = process.cwd()): PreferenceStore {
59
+ return new PreferenceStore({
60
+ global: workspace.settingsPath,
61
+ project: join(cwd, BRAND.profileDirName, "settings.json"),
62
+ });
63
+ }
64
+
65
+ paths(): PreferenceLocations {
66
+ return { ...this.#locations };
67
+ }
68
+
69
+ reload(): void {
70
+ this.#globalTier = loadTier(this.#locations.global);
71
+ this.#projectTier = loadTier(this.#locations.project);
72
+ }
73
+
74
+ get<K extends SettingKey>(key: K): Required<Preferences>[K] {
75
+ const fromProject = (this.#projectTier as Record<string, unknown>)[key];
76
+ if (fromProject !== undefined) return fromProject as Required<Preferences>[K];
77
+ const fromGlobal = (this.#globalTier as Record<string, unknown>)[key];
78
+ if (fromGlobal !== undefined) return fromGlobal as Required<Preferences>[K];
79
+ return DEFAULT_PREFERENCES[key];
80
+ }
81
+
82
+ set<K extends SettingKey>(key: K, value: Preferences[K]): void {
83
+ if (value === undefined) {
84
+ delete (this.#projectTier as Record<string, unknown>)[key];
85
+ return;
86
+ }
87
+ (this.#projectTier as Record<string, unknown>)[key] = value as unknown;
88
+ }
89
+
90
+ snapshot(): Required<Preferences> {
91
+ return {
92
+ ...DEFAULT_PREFERENCES,
93
+ ...stripUndefined(this.#globalTier),
94
+ ...stripUndefined(this.#projectTier),
95
+ } as Required<Preferences>;
96
+ }
97
+
98
+ projectOverrides(): Preferences {
99
+ return { ...this.#projectTier };
100
+ }
101
+
102
+ globalDefaults(): Preferences {
103
+ return { ...this.#globalTier };
104
+ }
105
+
106
+ save(): void {
107
+ writeTier(this.#locations.project, this.#projectTier);
108
+ }
109
+ }
110
+
111
+ function stripUndefined(tier: PreferencesPartial): Record<string, unknown> {
112
+ const out: Record<string, unknown> = {};
113
+ for (const [key, value] of Object.entries(tier)) {
114
+ if (value !== undefined) out[key] = value;
115
+ }
116
+ return out;
117
+ }
@@ -0,0 +1,45 @@
1
+ // @ts-nocheck
2
+ // Type declarations recovered from dist/types (no runtime body)
3
+ /**
4
+ * Transcript-export subsystem — public barrel.
5
+ *
6
+ * The HTML transcript publisher and its supporting machinery: the table-driven
7
+ * SGR painter ({@link paintSgr}) that turns terminal-styled output into safe HTML
8
+ * spans, the {@link ThemeBridge} that computes export colors from an
9
+ * {@link ExportTheme} via a WCAG luminance LUT, the regenerated page-shell
10
+ * {@link PAGE_SHELL} template with its `{{TOKEN}}` slots and {@link fill} helper,
11
+ * and {@link publishTranscript} which renders a session transcript to a
12
+ * standalone HTML document using `marked`, `highlight.js`, and the painter.
13
+ *
14
+ * Consumers import the publish surface from `src/transcript-export` rather than
15
+ * reaching into individual modules.
16
+ */
17
+ export { paintSgr, tokenizeSgr, foldSgr, SGR_CODE_TABLE } from "./sgr.js";
18
+ export {
19
+ DefaultThemeBridge,
20
+ createThemeBridge,
21
+ buildLuminanceLut,
22
+ parseColor,
23
+ formatColor,
24
+ } from "./theme-bridge.js";
25
+ export { PAGE_SHELL, PAGE_STYLES, CLIENT_SCRIPT, fill } from "./template.js";
26
+ export {
27
+ publishTranscript,
28
+ type TranscriptEntry,
29
+ type TranscriptWidget,
30
+ type PublishOptions,
31
+ } from "./publish.js";
32
+ export {
33
+ FALLBACK_EXPORT_THEME,
34
+ SHELL_SLOTS,
35
+ SGR_INITIAL_STATE,
36
+ briefingFault,
37
+ } from "../briefing/contract.js";
38
+ export type {
39
+ ExportTheme,
40
+ ThemeMode,
41
+ LuminanceLut,
42
+ SgrState,
43
+ SgrToken,
44
+ SgrMutation,
45
+ } from "../briefing/contract.js";