@oharness/server 0.9.3 → 0.11.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 (93) hide show
  1. package/dist/analytics.d.ts +32 -29
  2. package/dist/analytics.js +225 -278
  3. package/dist/analytics.js.map +1 -1
  4. package/dist/i18n.js +500 -64
  5. package/dist/i18n.js.map +1 -1
  6. package/dist/icons.js +1 -0
  7. package/dist/icons.js.map +1 -1
  8. package/dist/main.js +4 -0
  9. package/dist/main.js.map +1 -1
  10. package/dist/model-order.js +1 -0
  11. package/dist/model-order.js.map +1 -1
  12. package/dist/page/client/account.js +570 -27
  13. package/dist/page/client/account.js.map +1 -1
  14. package/dist/page/client/base.js +55 -12
  15. package/dist/page/client/base.js.map +1 -1
  16. package/dist/page/client/browser.js +1 -1
  17. package/dist/page/client/composer.js +802 -59
  18. package/dist/page/client/composer.js.map +1 -1
  19. package/dist/page/client/computer.d.ts +27 -0
  20. package/dist/page/client/computer.js +358 -0
  21. package/dist/page/client/computer.js.map +1 -0
  22. package/dist/page/client/dialogs.js +168 -6
  23. package/dist/page/client/dialogs.js.map +1 -1
  24. package/dist/page/client/events.js +119 -9
  25. package/dist/page/client/events.js.map +1 -1
  26. package/dist/page/client/files.js +86 -1
  27. package/dist/page/client/files.js.map +1 -1
  28. package/dist/page/client/index.js +8 -0
  29. package/dist/page/client/index.js.map +1 -1
  30. package/dist/page/client/panels.js +14 -10
  31. package/dist/page/client/panels.js.map +1 -1
  32. package/dist/page/client/ripple.d.ts +13 -0
  33. package/dist/page/client/ripple.js +247 -0
  34. package/dist/page/client/ripple.js.map +1 -0
  35. package/dist/page/client/routines.js +432 -45
  36. package/dist/page/client/routines.js.map +1 -1
  37. package/dist/page/client/shell.js +386 -104
  38. package/dist/page/client/shell.js.map +1 -1
  39. package/dist/page/client/state.js +278 -19
  40. package/dist/page/client/state.js.map +1 -1
  41. package/dist/page/client/tasks.d.ts +8 -0
  42. package/dist/page/client/tasks.js +293 -0
  43. package/dist/page/client/tasks.js.map +1 -0
  44. package/dist/page/client/terminal.js +34 -7
  45. package/dist/page/client/terminal.js.map +1 -1
  46. package/dist/page/client/theme.d.ts +17 -0
  47. package/dist/page/client/theme.js +365 -0
  48. package/dist/page/client/theme.js.map +1 -0
  49. package/dist/page/client/transcript.js +196 -8
  50. package/dist/page/client/transcript.js.map +1 -1
  51. package/dist/page/client/usage.js +1 -11
  52. package/dist/page/client/usage.js.map +1 -1
  53. package/dist/page/style.js +745 -76
  54. package/dist/page/style.js.map +1 -1
  55. package/dist/page.js +94 -16
  56. package/dist/page.js.map +1 -1
  57. package/dist/prefs.d.ts +31 -0
  58. package/dist/prefs.js +13 -1
  59. package/dist/prefs.js.map +1 -1
  60. package/dist/remote-crypto.d.ts +78 -0
  61. package/dist/remote-crypto.js +216 -0
  62. package/dist/remote-crypto.js.map +1 -0
  63. package/dist/remote.d.ts +103 -0
  64. package/dist/remote.js +498 -0
  65. package/dist/remote.js.map +1 -0
  66. package/dist/server.d.ts +70 -0
  67. package/dist/server.js +1738 -73
  68. package/dist/server.js.map +1 -1
  69. package/dist/starters.d.ts +92 -0
  70. package/dist/starters.js +213 -0
  71. package/dist/starters.js.map +1 -0
  72. package/dist/suggest.d.ts +42 -0
  73. package/dist/suggest.js +215 -0
  74. package/dist/suggest.js.map +1 -0
  75. package/dist/terminal.d.ts +28 -2
  76. package/dist/terminal.js +45 -3
  77. package/dist/terminal.js.map +1 -1
  78. package/dist/theme-assets.d.ts +39 -0
  79. package/dist/theme-assets.js +109 -0
  80. package/dist/theme-assets.js.map +1 -0
  81. package/dist/theme-store.d.ts +50 -0
  82. package/dist/theme-store.js +124 -0
  83. package/dist/theme-store.js.map +1 -0
  84. package/dist/theme-tools.d.ts +25 -0
  85. package/dist/theme-tools.js +243 -0
  86. package/dist/theme-tools.js.map +1 -0
  87. package/dist/turn-shots.d.ts +43 -0
  88. package/dist/turn-shots.js +80 -0
  89. package/dist/turn-shots.js.map +1 -0
  90. package/dist/user-theme.d.ts +143 -0
  91. package/dist/user-theme.js +510 -0
  92. package/dist/user-theme.js.map +1 -0
  93. package/package.json +2 -2
package/dist/terminal.js CHANGED
@@ -71,6 +71,40 @@ function shell() {
71
71
  const file = process.env.SHELL || (process.platform === "darwin" ? "/bin/zsh" : "/bin/bash");
72
72
  return { file, args: ["-l"] };
73
73
  }
74
+ /**
75
+ * Programs a new shell may be opened running, by name. A fixed list rather
76
+ * than a command from the request: the page asks for "claude", and what that
77
+ * means is decided here.
78
+ *
79
+ * `claude` is the hand-off to Claude Code. It is the user's own install,
80
+ * signed in to their own account through Claude Code's own login — OHarness
81
+ * starts it and does nothing else, which is the one way of using a Claude
82
+ * plan from another app that Anthropic's terms leave plainly open. It is
83
+ * typed into the shell rather than spawned in its place, so PATH is the
84
+ * login shell's and the shell is still there when Claude Code exits.
85
+ */
86
+ export const LAUNCHERS = {
87
+ claude: (platform, resume) => {
88
+ // `--resume` opens a session `@claude` ran, in Claude Code itself.
89
+ const run = resume ? `claude --resume ${resume}` : "claude";
90
+ return platform === "win32"
91
+ ? `if (Get-Command claude -ErrorAction SilentlyContinue) { ${run} } ` +
92
+ "else { Write-Host 'Claude Code is not installed. Get it at https://claude.com/claude-code' -ForegroundColor Yellow }"
93
+ : `if command -v claude >/dev/null 2>&1; then ${run}; ` +
94
+ "else printf '\\033[33mClaude Code is not installed. Get it at https://claude.com/claude-code\\033[0m\\n'; fi";
95
+ },
96
+ };
97
+ export function isLauncher(value) {
98
+ return typeof value === "string" && Object.hasOwn(LAUNCHERS, value);
99
+ }
100
+ /**
101
+ * An agent session id fit to put on a command line: letters, digits and
102
+ * dashes, the shape Claude Code's ids have. Anything else is refused rather
103
+ * than escaped — there is no legitimate id that needs escaping.
104
+ */
105
+ export function isAgentSessionId(value) {
106
+ return typeof value === "string" && /^[A-Za-z0-9-]{8,64}$/.test(value);
107
+ }
74
108
  function clampSize(value, fallback, max) {
75
109
  const n = Math.floor(Number(value));
76
110
  return Number.isFinite(n) && n > 0 ? Math.min(n, max) : fallback;
@@ -84,8 +118,11 @@ export class TerminalHub {
84
118
  .filter((term) => term.cwd === cwd)
85
119
  .map((term) => ({ id: term.id, name: term.name, exited: term.exited }));
86
120
  }
87
- /** A new shell in `cwd`; `replace` names one it takes the place of. */
88
- async open(cwd, size, replace) {
121
+ /**
122
+ * A new shell in `cwd`; `replace` names one it takes the place of, and
123
+ * `run` a launcher typed into it as soon as it starts.
124
+ */
125
+ async open(cwd, size, replace, run, resume) {
89
126
  if (replace)
90
127
  this.close(replace);
91
128
  const pty = await loadPty();
@@ -108,7 +145,7 @@ export class TerminalHub {
108
145
  return { error: err.message };
109
146
  }
110
147
  const id = `t${++this.seq}`;
111
- const name = path.basename(file).replace(/\.exe$/i, "");
148
+ const name = run ?? path.basename(file).replace(/\.exe$/i, "");
112
149
  const term = { id, cwd, name, pty: proc, backlog: "", listeners: new Set(), exited: false };
113
150
  this.terminals.set(id, term);
114
151
  proc.onData((data) => {
@@ -119,6 +156,11 @@ export class TerminalHub {
119
156
  term.exited = true;
120
157
  this.emit(term, { type: "exit", code: exitCode });
121
158
  });
159
+ // Typed rather than passed as `-c`, so the shell reads its profile first
160
+ // and is still at its prompt afterwards. The pty buffers it until the
161
+ // shell is ready to read.
162
+ if (run)
163
+ proc.write(LAUNCHERS[run](process.platform, resume && isAgentSessionId(resume) ? resume : undefined) + "\r");
122
164
  return { id, name };
123
165
  }
124
166
  /** Attach a page: what has been written so far, then everything after. */
@@ -1 +1 @@
1
- {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Images a theme uses, kept where the theme is.
3
+ *
4
+ * A theme never points at a file in a project or at a URL: the image is copied
5
+ * here when the agent proposes it, named by the hash of its bytes, and the
6
+ * theme refers to that name. So a theme keeps working after the project it
7
+ * was made in is moved or deleted, the same picture used twice is stored once,
8
+ * and the name is the proof of the content — `GET /theme-asset/` can serve it
9
+ * without the session token, the way an unguessable capability URL works,
10
+ * because nobody can ask for a file they could not already have.
11
+ *
12
+ * Only still PNG, JPEG and WebP. SVG is a document that can carry script and
13
+ * fetch other things; an animation would run behind every message with no
14
+ * regard for reduced motion.
15
+ */
16
+ export declare const MAX_ASSET_BYTES: number;
17
+ export declare const assetPath: (id: string) => string;
18
+ export declare const ASSET_TYPES: Record<string, string>;
19
+ /** What the bytes are, from their header — never from the file's name. */
20
+ export declare function sniffImage(bytes: Uint8Array): {
21
+ ext: "png" | "jpg" | "webp";
22
+ } | {
23
+ error: string;
24
+ };
25
+ /** Copy an image file in as an asset; returns its id. */
26
+ export declare function importAsset(file: string): Promise<{
27
+ ok: true;
28
+ id: string;
29
+ } | {
30
+ ok: false;
31
+ error: string;
32
+ }>;
33
+ export declare function assetExists(id: string): Promise<boolean>;
34
+ /**
35
+ * Delete assets nothing refers to any more. Anything touched in the last day
36
+ * is kept: it may belong to a preview card nobody has acted on yet, which only
37
+ * lives in the server's memory.
38
+ */
39
+ export declare function pruneAssets(keep: Iterable<string>): Promise<void>;
@@ -0,0 +1,109 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { userPath } from "@oharness/engine/paths.ts";
5
+ import { ASSET_ID } from "./user-theme.js";
6
+ /**
7
+ * Images a theme uses, kept where the theme is.
8
+ *
9
+ * A theme never points at a file in a project or at a URL: the image is copied
10
+ * here when the agent proposes it, named by the hash of its bytes, and the
11
+ * theme refers to that name. So a theme keeps working after the project it
12
+ * was made in is moved or deleted, the same picture used twice is stored once,
13
+ * and the name is the proof of the content — `GET /theme-asset/` can serve it
14
+ * without the session token, the way an unguessable capability URL works,
15
+ * because nobody can ask for a file they could not already have.
16
+ *
17
+ * Only still PNG, JPEG and WebP. SVG is a document that can carry script and
18
+ * fetch other things; an animation would run behind every message with no
19
+ * regard for reduced motion.
20
+ */
21
+ export const MAX_ASSET_BYTES = 8 * 1024 * 1024;
22
+ const DIR = () => userPath("theme-assets");
23
+ export const assetPath = (id) => path.join(DIR(), id);
24
+ export const ASSET_TYPES = { png: "image/png", jpg: "image/jpeg", webp: "image/webp" };
25
+ /** What the bytes are, from their header — never from the file's name. */
26
+ export function sniffImage(bytes) {
27
+ const ascii = (from, to) => String.fromCharCode(...bytes.subarray(from, to));
28
+ if (bytes.length > 8 && bytes[0] === 0x89 && ascii(1, 4) === "PNG") {
29
+ // An animated PNG declares itself with an acTL chunk before the first IDAT.
30
+ const head = Buffer.from(bytes.subarray(0, Math.min(bytes.length, 65536)));
31
+ const actl = head.indexOf("acTL"), idat = head.indexOf("IDAT");
32
+ if (actl >= 0 && (idat < 0 || actl < idat))
33
+ return { error: "animated PNGs are not supported" };
34
+ return { ext: "png" };
35
+ }
36
+ if (bytes.length > 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
37
+ return { ext: "jpg" };
38
+ if (bytes.length > 30 && ascii(0, 4) === "RIFF" && ascii(8, 12) === "WEBP") {
39
+ // Extended WebP carries an animation flag in its VP8X header.
40
+ if (ascii(12, 16) === "VP8X" && (bytes[20] & 0x02) !== 0)
41
+ return { error: "animated WebP is not supported" };
42
+ return { ext: "webp" };
43
+ }
44
+ if (ascii(0, 5).toLowerCase().startsWith("<svg") || ascii(0, 5) === "<?xml")
45
+ return { error: "SVG is not supported; use PNG, JPEG or WebP" };
46
+ if (ascii(0, 3) === "GIF")
47
+ return { error: "GIF is not supported; use PNG, JPEG or WebP" };
48
+ return { error: "not a PNG, JPEG or WebP image" };
49
+ }
50
+ /** Copy an image file in as an asset; returns its id. */
51
+ export async function importAsset(file) {
52
+ let info;
53
+ try {
54
+ info = await stat(file);
55
+ }
56
+ catch {
57
+ return { ok: false, error: `no file at ${file}` };
58
+ }
59
+ if (!info.isFile())
60
+ return { ok: false, error: `${file} is not a file` };
61
+ if (info.size > MAX_ASSET_BYTES)
62
+ return { ok: false, error: `${path.basename(file)} is over ${MAX_ASSET_BYTES / 1024 / 1024} MB` };
63
+ const bytes = await readFile(file);
64
+ const kind = sniffImage(bytes);
65
+ if ("error" in kind)
66
+ return { ok: false, error: `${path.basename(file)}: ${kind.error}` };
67
+ const id = `${createHash("sha256").update(bytes).digest("hex")}.${kind.ext}`;
68
+ await mkdir(DIR(), { recursive: true });
69
+ await writeFile(assetPath(id), bytes);
70
+ return { ok: true, id };
71
+ }
72
+ export async function assetExists(id) {
73
+ if (!ASSET_ID.test(id))
74
+ return false;
75
+ try {
76
+ return (await stat(assetPath(id))).isFile();
77
+ }
78
+ catch {
79
+ return false;
80
+ }
81
+ }
82
+ /**
83
+ * Delete assets nothing refers to any more. Anything touched in the last day
84
+ * is kept: it may belong to a preview card nobody has acted on yet, which only
85
+ * lives in the server's memory.
86
+ */
87
+ export async function pruneAssets(keep) {
88
+ const wanted = new Set(keep);
89
+ let names;
90
+ try {
91
+ names = await readdir(DIR());
92
+ }
93
+ catch {
94
+ return;
95
+ }
96
+ const dayAgo = Date.now() - 24 * 60 * 60 * 1000;
97
+ for (const name of names) {
98
+ if (!ASSET_ID.test(name) || wanted.has(name))
99
+ continue;
100
+ try {
101
+ if ((await stat(assetPath(name))).mtimeMs < dayAgo)
102
+ await unlink(assetPath(name));
103
+ }
104
+ catch {
105
+ // Gone already, or not ours to remove: either way nothing to do.
106
+ }
107
+ }
108
+ }
109
+ //# sourceMappingURL=theme-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-assets.js","sourceRoot":"","sources":["../src/theme-assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { ThemeInput } from "./user-theme.ts";
2
+ /**
3
+ * The theme in use, the ones used before it, and the way back.
4
+ *
5
+ * One file in the user directory, beside prefs.json: a theme is a preference
6
+ * about how the app looks, not a property of a project. Stored as the input the
7
+ * model gave rather than as CSS, and resolved again on every load, so a stored
8
+ * theme always goes through today's validation and can never smuggle in
9
+ * anything the current rules would refuse.
10
+ *
11
+ * A file that cannot be read or does not validate is treated as no theme. The
12
+ * file itself is left where it is, for whoever wants to see what was in it:
13
+ * the one thing a broken theme must never do is stop the app from starting.
14
+ */
15
+ export interface StoredTheme {
16
+ id: string;
17
+ name: string;
18
+ appliedAt: number;
19
+ input: ThemeInput;
20
+ }
21
+ export interface ThemeState {
22
+ active: {
23
+ id: string;
24
+ name: string;
25
+ } | null;
26
+ css: string;
27
+ canUndo: boolean;
28
+ history: Array<{
29
+ id: string;
30
+ name: string;
31
+ appliedAt: number;
32
+ active: boolean;
33
+ }>;
34
+ }
35
+ export declare class ThemeStore {
36
+ private file;
37
+ private resolved;
38
+ static load(): Promise<ThemeStore>;
39
+ state(): ThemeState;
40
+ /** The active theme as given, for the model to start a revision from. */
41
+ activeInput(): ThemeInput | undefined;
42
+ apply(input: ThemeInput): Promise<ThemeState>;
43
+ applyHistory(id: string): Promise<ThemeState | undefined>;
44
+ reset(): Promise<ThemeState>;
45
+ undo(): Promise<ThemeState>;
46
+ private moveTo;
47
+ private activeEntry;
48
+ private resolveActive;
49
+ private save;
50
+ }
@@ -0,0 +1,124 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { userPath } from "@oharness/engine/paths.ts";
5
+ import { parseJson } from "@oharness/engine/util/json.ts";
6
+ import { renameWithRetry } from "@oharness/engine/util/fs.ts";
7
+ import { pruneAssets } from "./theme-assets.js";
8
+ import { resolveTheme, themeAssets, themeCss } from "./user-theme.js";
9
+ const MAX_HISTORY = 10;
10
+ const MAX_UNDO = 20;
11
+ const FILE = () => userPath("theme.json");
12
+ export class ThemeStore {
13
+ file = { version: 1, active: null, history: [], undo: [] };
14
+ resolved;
15
+ static async load() {
16
+ const store = new ThemeStore();
17
+ try {
18
+ const parsed = parseJson(await readFile(FILE(), "utf8"));
19
+ if (parsed.ok)
20
+ store.file = clean(parsed.value);
21
+ }
22
+ catch {
23
+ // No file yet, or one that cannot be read: the built-in theme.
24
+ }
25
+ store.resolveActive();
26
+ // Images no theme in the history uses any more. In the background: a slow
27
+ // disk must not hold up the page.
28
+ void pruneAssets(store.file.history.flatMap((h) => themeAssets(h.input)));
29
+ return store;
30
+ }
31
+ state() {
32
+ const entry = this.activeEntry();
33
+ return {
34
+ active: entry && this.resolved ? { id: entry.id, name: entry.name } : null,
35
+ css: this.resolved ? themeCss(this.resolved) : "",
36
+ canUndo: this.file.undo.length > 0,
37
+ history: this.file.history.map((h) => ({ id: h.id, name: h.name, appliedAt: h.appliedAt, active: h.id === this.file.active })),
38
+ };
39
+ }
40
+ /** The active theme as given, for the model to start a revision from. */
41
+ activeInput() {
42
+ return this.resolved ? this.activeEntry()?.input : undefined;
43
+ }
44
+ async apply(input) {
45
+ const entry = { id: randomUUID(), name: input.name, appliedAt: Date.now(), input };
46
+ this.file.history = [entry, ...this.file.history].slice(0, MAX_HISTORY);
47
+ return this.moveTo(entry.id);
48
+ }
49
+ async applyHistory(id) {
50
+ if (!this.file.history.some((h) => h.id === id))
51
+ return undefined;
52
+ return this.moveTo(id);
53
+ }
54
+ async reset() {
55
+ return this.moveTo(null);
56
+ }
57
+ async undo() {
58
+ const previous = this.file.undo.pop();
59
+ if (previous === undefined)
60
+ return this.state();
61
+ // An entry pushed out of history since cannot be gone back to; the
62
+ // built-in theme always can.
63
+ this.file.active = previous && this.file.history.some((h) => h.id === previous) ? previous : null;
64
+ this.resolveActive();
65
+ await this.save();
66
+ return this.state();
67
+ }
68
+ async moveTo(id) {
69
+ if (id === this.file.active)
70
+ return this.state();
71
+ this.file.undo = [...this.file.undo, this.file.active].slice(-MAX_UNDO);
72
+ this.file.active = id;
73
+ this.resolveActive();
74
+ await this.save();
75
+ return this.state();
76
+ }
77
+ activeEntry() {
78
+ return this.file.history.find((h) => h.id === this.file.active);
79
+ }
80
+ resolveActive() {
81
+ const entry = this.activeEntry();
82
+ const result = entry ? resolveTheme(entry.input) : undefined;
83
+ this.resolved = result?.ok ? result.theme : undefined;
84
+ }
85
+ async save() {
86
+ const file = FILE();
87
+ await mkdir(path.dirname(file), { recursive: true });
88
+ tempSeq += 1;
89
+ const temp = `${file}.${process.pid}.${tempSeq}.tmp`;
90
+ await writeFile(temp, `${JSON.stringify(this.file, null, 2)}\n`, "utf8");
91
+ await renameWithRetry(temp, file);
92
+ }
93
+ }
94
+ let tempSeq = 0;
95
+ /** Only the shape this module wrote; anything else is dropped, not trusted. */
96
+ function clean(raw) {
97
+ const out = { version: 1, active: null, history: [], undo: [] };
98
+ if (typeof raw !== "object" || raw === null)
99
+ return out;
100
+ const body = raw;
101
+ if (Array.isArray(body["history"])) {
102
+ for (const item of body["history"]) {
103
+ if (typeof item !== "object" || item === null)
104
+ continue;
105
+ const h = item;
106
+ if (typeof h["id"] !== "string" || typeof h["input"] !== "object" || h["input"] === null)
107
+ continue;
108
+ out.history.push({
109
+ id: h["id"],
110
+ name: typeof h["name"] === "string" ? h["name"] : "Custom",
111
+ appliedAt: typeof h["appliedAt"] === "number" ? h["appliedAt"] : 0,
112
+ input: h["input"],
113
+ });
114
+ }
115
+ }
116
+ const ids = new Set(out.history.map((h) => h.id));
117
+ if (typeof body["active"] === "string" && ids.has(body["active"]))
118
+ out.active = body["active"];
119
+ if (Array.isArray(body["undo"])) {
120
+ out.undo = body["undo"].filter((v) => v === null || (typeof v === "string" && ids.has(v))).slice(-MAX_UNDO);
121
+ }
122
+ return out;
123
+ }
124
+ //# sourceMappingURL=theme-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-store.js","sourceRoot":"","sources":["../src/theme-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Tool } from "@oharness/engine/tools/types.ts";
2
+ import type { ResolvedTheme, ThemeInput } from "./user-theme.ts";
3
+ /**
4
+ * The agent's side of theming: learn what can change, and propose a theme.
5
+ *
6
+ * Proposing is all it can do. `set_theme` makes a preview card in the
7
+ * conversation and returns; the theme is applied only when the person clicks
8
+ * Apply on it. That is why the tool is read-only and needs no approval: the
9
+ * card is the approval, and it comes with the result in front of them rather
10
+ * than a description of it.
11
+ *
12
+ * Registered by the web server only. A terminal has no page to theme.
13
+ */
14
+ export declare const GET_THEME_SCHEMA_TOOL = "get_theme_schema";
15
+ export declare const SET_THEME_TOOL = "set_theme";
16
+ export declare const RESET_THEME_TOOL = "reset_theme";
17
+ export interface ThemeToolHost {
18
+ /** The theme in use as it was given, or undefined for the built-in one. */
19
+ activeInput(): ThemeInput | undefined;
20
+ /** Put a preview card in front of the person; returns its id. */
21
+ propose(sessionId: string, theme: ResolvedTheme): string;
22
+ /** Go back to the built-in theme; returns the name of the one it replaced. */
23
+ reset(): Promise<string | undefined>;
24
+ }
25
+ export declare function createThemeTools(host: ThemeToolHost): Tool[];