archymedes-cli 2.3.0 → 2.6.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 (25) hide show
  1. package/dist/{archymedes-aj30app4.mjs → archymedes-06fh0z84.mjs} +370 -159
  2. package/dist/{archymedes-sf9t1hz9.mjs → archymedes-086xsz64.mjs} +176 -77
  3. package/dist/{archymedes-d5wqcmdr.mjs → archymedes-0jxzsh4z.mjs} +97 -97
  4. package/dist/{archymedes-h13ap0za.mjs → archymedes-1g1y6q74.mjs} +1 -1
  5. package/dist/{archymedes-4kx65anz.mjs → archymedes-1jcn326m.mjs} +43 -12
  6. package/dist/{archymedes-g8y3rt6t.mjs → archymedes-kn6wtq32.mjs} +332 -9
  7. package/dist/archymedes-main.mjs +31010 -27842
  8. package/dist/archymedes-s4qcy0q4.mjs +1250 -0
  9. package/dist/{archymedes-twbks7g3.mjs → archymedes-t1jtzkmp.mjs} +214 -5
  10. package/dist/{archymedes-tsne2m7p.mjs → archymedes-twanftqk.mjs} +2 -2
  11. package/dist/archymedes-w4bjfye7.mjs +280 -0
  12. package/dist/{archymedes-dz59nvw7.mjs → archymedes-zvcs68bv.mjs} +21 -11
  13. package/dist/{editor-screen-g08zzt70.mjs → editor-screen-pf252j2r.mjs} +49 -14
  14. package/dist/{factory-7vtm2fed.mjs → factory-hyv8agr0.mjs} +326 -326
  15. package/dist/{file-screen-ma9yxq2b.mjs → file-screen-1j65f7b4.mjs} +11 -9
  16. package/dist/{guide-screen-gd129txd.mjs → guide-screen-9vxsk83x.mjs} +10 -8
  17. package/dist/{index-qvgcwsvm.mjs → index-teq1cta4.mjs} +1 -1
  18. package/dist/{node-mdgzx68h.mjs → node-0fh63p8z.mjs} +7 -7
  19. package/dist/pager-screen-aamt6xv0.mjs +155 -0
  20. package/dist/projection-x6x6hm9t.mjs +248 -0
  21. package/dist/{sandbox-templates-9jcjws23.mjs → sandbox-templates-8sd1psez.mjs} +2 -2
  22. package/dist/workspace-screen-ntdcgfq9.mjs +70 -0
  23. package/package.json +2 -2
  24. package/dist/archymedes-a10k8yrk.mjs +0 -464
  25. package/dist/workspace-screen-8cwk6jz9.mjs +0 -241
@@ -1,24 +1,25 @@
1
1
  import {
2
2
  windowStart
3
- } from "./archymedes-4kx65anz.mjs";
3
+ } from "./archymedes-1jcn326m.mjs";
4
4
  import {
5
5
  joinHorizontal,
6
6
  padToWidth,
7
7
  paginator
8
- } from "./archymedes-g8y3rt6t.mjs";
8
+ } from "./archymedes-kn6wtq32.mjs";
9
9
  import {
10
10
  NO_COLOR_PALETTE,
11
- UNICODE_GLYPHS
12
- } from "./archymedes-a10k8yrk.mjs";
11
+ UNICODE_GLYPHS,
12
+ exitTopTermUIApp
13
+ } from "./archymedes-s4qcy0q4.mjs";
13
14
  import {
14
15
  __require
15
16
  } from "./archymedes-x67v3n58.mjs";
16
17
 
17
- // src/ui/file-screen.tsx
18
+ // packages/archymedes-cli/src/ui/file-screen.tsx
18
19
  import { useAsync, useInput, useState } from "@termuijs/jsx";
19
20
  import { Box, Text } from "@termuijs/widgets";
20
21
 
21
- // ../core/src/cli/file-tree.ts
22
+ // packages/core/src/cli/file-tree.ts
22
23
  function buildFileTree(paths) {
23
24
  const root = { name: "", path: "", kind: "dir", children: [] };
24
25
  for (const raw of paths) {
@@ -59,7 +60,7 @@ function directorySummary(node) {
59
60
  }
60
61
  return { files, dirs };
61
62
  }
62
- // src/ui/file-browser.ts
63
+ // packages/archymedes-cli/src/ui/file-browser.ts
63
64
  function flattenTree(tree, expanded, depth = 0, trunks = []) {
64
65
  const rows = [];
65
66
  for (const [index, node] of tree.entries()) {
@@ -228,7 +229,7 @@ function composeFileFrame(state, preview, glyphs = UNICODE_GLYPHS) {
228
229
  });
229
230
  }
230
231
  const overflow = rows.length > height ? ` ${paginator(selectedIndex < 0 ? 0 : selectedIndex, rows.length)}` : "";
231
- const footer = state.searching ? `search: ${state.query}▏ Enter open · Esc done` : `↑↓ move · →/l open · ←/h close · Enter pick a file · / search · q leave${state.query ? ` filter: ${state.query}` : ""}${overflow}`;
232
+ const footer = state.searching ? `search: ${state.query}▏ Enter open · Esc done` : `↑↓ move · →/l open · ←/h close · Enter pick a file · / search · Esc back${state.query ? ` filter: ${state.query}` : ""}${overflow}`;
232
233
  out.push({ text: padToWidth(` ${footer}`, columns), dim: true, color: theme.textMuted });
233
234
  return out;
234
235
  }
@@ -251,7 +252,7 @@ function previewToLines(preview, width) {
251
252
  }
252
253
  }
253
254
 
254
- // src/ui/file-screen.tsx
255
+ // packages/archymedes-cli/src/ui/file-screen.tsx
255
256
  import { jsxDEV } from "@termuijs/jsx/jsx-dev-runtime";
256
257
  var Panel = Box;
257
258
  var Line = Text;
@@ -301,6 +302,7 @@ async function runFileScreen(options) {
301
302
  if (settled)
302
303
  return;
303
304
  settled = true;
305
+ exitTopTermUIApp();
304
306
  resolve(picked);
305
307
  };
306
308
  renderApp(FileScreen, {
@@ -4,28 +4,29 @@ import {
4
4
  newViewport,
5
5
  visibleLines,
6
6
  wrapText
7
- } from "./archymedes-sf9t1hz9.mjs";
7
+ } from "./archymedes-086xsz64.mjs";
8
8
  import {
9
9
  joinHorizontal,
10
10
  padToWidth,
11
11
  scrollIndicator,
12
12
  scrollPercent,
13
13
  sliceToWidth
14
- } from "./archymedes-g8y3rt6t.mjs";
15
- import"./archymedes-tsne2m7p.mjs";
14
+ } from "./archymedes-kn6wtq32.mjs";
15
+ import"./archymedes-twanftqk.mjs";
16
16
  import {
17
17
  NO_COLOR_PALETTE,
18
+ exitTopTermUIApp,
18
19
  visibleWidth
19
- } from "./archymedes-a10k8yrk.mjs";
20
+ } from "./archymedes-s4qcy0q4.mjs";
20
21
  import {
21
22
  __require
22
23
  } from "./archymedes-x67v3n58.mjs";
23
24
 
24
- // src/ui/guide-screen.tsx
25
+ // packages/archymedes-cli/src/ui/guide-screen.tsx
25
26
  import { useInput, useState } from "@termuijs/jsx";
26
27
  import { Box, Text } from "@termuijs/widgets";
27
28
 
28
- // src/ui/guide-browser.ts
29
+ // packages/archymedes-cli/src/ui/guide-browser.ts
29
30
  function initialGuideState(columns, rows, palette = NO_COLOR_PALETTE) {
30
31
  return { topics: GUIDE_TOPICS, selected: 0, scroll: 0, query: "", searching: false, columns, rows, palette };
31
32
  }
@@ -208,12 +209,12 @@ function composeGuideFrame(state) {
208
209
  });
209
210
  }
210
211
  const position = lines.length > height ? ` ${scrollIndicator(scrollPercent(start, lines.length, height))}` : "";
211
- const footer = state.searching ? `search: ${state.query}▏ Enter done · Esc done` : `↑↓ topic · ←→ scroll · space page · / search · q leave${state.query ? ` filter: ${state.query}` : ""}${position}`;
212
+ const footer = state.searching ? `search: ${state.query}▏ Enter done · Esc done` : `↑↓ topic · ←→ scroll · space page · / search · Esc back${state.query ? ` filter: ${state.query}` : ""}${position}`;
212
213
  rows.push({ text: padToWidth(` ${footer}`, columns), dim: true, color: theme.textMuted });
213
214
  return rows;
214
215
  }
215
216
 
216
- // src/ui/guide-screen.tsx
217
+ // packages/archymedes-cli/src/ui/guide-screen.tsx
217
218
  import { jsxDEV } from "@termuijs/jsx/jsx-dev-runtime";
218
219
  var Panel = Box;
219
220
  var Line = Text;
@@ -253,6 +254,7 @@ async function runGuideScreen(options) {
253
254
  if (settled)
254
255
  return;
255
256
  settled = true;
257
+ exitTopTermUIApp();
256
258
  resolve();
257
259
  };
258
260
  renderApp(GuideScreen, {
@@ -22,7 +22,7 @@ import {
22
22
  UnprocessableEntityError,
23
23
  betaRefusalFallbackMiddleware,
24
24
  toFile
25
- } from "./archymedes-d5wqcmdr.mjs";
25
+ } from "./archymedes-0jxzsh4z.mjs";
26
26
  import"./archymedes-x67v3n58.mjs";
27
27
  export {
28
28
  toFile,
@@ -5,10 +5,10 @@ import {
5
5
  __classPrivateFieldSet,
6
6
  loggerFor,
7
7
  promiseWithResolvers
8
- } from "./archymedes-d5wqcmdr.mjs";
8
+ } from "./archymedes-0jxzsh4z.mjs";
9
9
  import"./archymedes-x67v3n58.mjs";
10
10
 
11
- // ../../node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/tools/agent-toolset/node.mjs
11
+ // node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/tools/agent-toolset/node.mjs
12
12
  import * as fs3 from "node:fs/promises";
13
13
  import * as fssync2 from "node:fs";
14
14
  import * as path3 from "node:path";
@@ -16,7 +16,7 @@ import * as cp from "node:child_process";
16
16
  import * as crypto from "node:crypto";
17
17
  import * as readline from "node:readline";
18
18
 
19
- // ../../node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/lib/transform-json-schema.mjs
19
+ // node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/lib/transform-json-schema.mjs
20
20
  var SUPPORTED_STRING_FORMATS = new Set([
21
21
  "date-time",
22
22
  "time",
@@ -30,7 +30,7 @@ var SUPPORTED_STRING_FORMATS = new Set([
30
30
  "uuid"
31
31
  ]);
32
32
 
33
- // ../../node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/helpers/beta/json-schema.mjs
33
+ // node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/helpers/beta/json-schema.mjs
34
34
  function betaTool(options) {
35
35
  if (options.inputSchema.type !== "object") {
36
36
  throw new Error(`JSON schema for tool "${options.name}" must be an object, but got ${options.inputSchema.type}`);
@@ -46,7 +46,7 @@ function betaTool(options) {
46
46
  };
47
47
  }
48
48
 
49
- // ../../node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/tools/agent-toolset/fs-util.mjs
49
+ // node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/tools/agent-toolset/fs-util.mjs
50
50
  import * as fs from "node:fs/promises";
51
51
  import * as path from "node:path";
52
52
  import { randomUUID } from "node:crypto";
@@ -145,7 +145,7 @@ function fsErrorMessage(err, file) {
145
145
  }
146
146
  }
147
147
 
148
- // ../../node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/tools/agent-toolset/skills.mjs
148
+ // node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/tools/agent-toolset/skills.mjs
149
149
  import * as fs2 from "node:fs/promises";
150
150
  import * as fssync from "node:fs";
151
151
  import * as path2 from "node:path";
@@ -304,7 +304,7 @@ async function readHead(file, n) {
304
304
  }
305
305
  }
306
306
 
307
- // ../../node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/tools/agent-toolset/node.mjs
307
+ // node_modules/.bun/@anthropic-ai+sdk@0.116.0+ab3ed8e9421bfd7b/node_modules/@anthropic-ai/sdk/tools/agent-toolset/node.mjs
308
308
  var _BashSession_instances;
309
309
  var _BashSession_proc;
310
310
  var _BashSession_buf;
@@ -0,0 +1,155 @@
1
+ import {
2
+ __require
3
+ } from "./archymedes-x67v3n58.mjs";
4
+
5
+ // packages/archymedes-cli/src/ui/pager-screen.tsx
6
+ import { useInput, useState } from "@termuijs/jsx";
7
+ import { Box, Text } from "@termuijs/widgets";
8
+
9
+ // packages/archymedes-cli/src/ui/text-pager.ts
10
+ var ANSI = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07]*\x07/g;
11
+ function initialPagerState(text, height) {
12
+ const lines = text.replace(ANSI, "").replace(/\r/g, "").split(`
13
+ `);
14
+ const rows = Math.max(1, height);
15
+ return { lines, top: Math.max(0, lines.length - rows), height: rows, search: { query: "", typing: false } };
16
+ }
17
+ function keyToPagerAction(key, character, state) {
18
+ const name = key.name ?? "";
19
+ if (key.ctrl && name === "c")
20
+ return { kind: "exit" };
21
+ if (state.search.typing) {
22
+ if (name === "escape")
23
+ return { kind: "searchCancel" };
24
+ if (name === "return" || name === "enter")
25
+ return { kind: "searchCommit" };
26
+ if (name === "backspace")
27
+ return { kind: "searchBackspace" };
28
+ if (character && character.length === 1 && character >= " ")
29
+ return { kind: "searchType", character };
30
+ return { kind: "none" };
31
+ }
32
+ if (name === "escape" || character === "q")
33
+ return { kind: "exit" };
34
+ if (name === "up" || character === "k")
35
+ return { kind: "scroll", rows: -1 };
36
+ if (name === "down" || character === "j" || name === "return" || name === "enter")
37
+ return { kind: "scroll", rows: 1 };
38
+ if (name === "pageup" || character === "b")
39
+ return { kind: "scroll", rows: -(state.height - 1) };
40
+ if (name === "pagedown" || name === "space" || character === " ")
41
+ return { kind: "scroll", rows: state.height - 1 };
42
+ if (name === "home" || character === "g")
43
+ return { kind: "home" };
44
+ if (name === "end" || character === "G")
45
+ return { kind: "end" };
46
+ if (character === "/")
47
+ return { kind: "search" };
48
+ if (character === "n")
49
+ return { kind: "searchNext" };
50
+ return { kind: "none" };
51
+ }
52
+ function clampTop(state, top) {
53
+ return Math.max(0, Math.min(top, Math.max(0, state.lines.length - state.height)));
54
+ }
55
+ function findFrom(state, from) {
56
+ const needle = state.search.query.toLowerCase();
57
+ if (!needle)
58
+ return -1;
59
+ for (let offset = 0;offset < state.lines.length; offset += 1) {
60
+ const row = (from + offset) % state.lines.length;
61
+ if (state.lines[row].toLowerCase().includes(needle))
62
+ return row;
63
+ }
64
+ return -1;
65
+ }
66
+ function applyPagerAction(state, action) {
67
+ switch (action.kind) {
68
+ case "exit":
69
+ return { state, exit: true };
70
+ case "scroll":
71
+ return { state: { ...state, top: clampTop(state, state.top + action.rows), message: undefined } };
72
+ case "home":
73
+ return { state: { ...state, top: 0, message: undefined } };
74
+ case "end":
75
+ return { state: { ...state, top: clampTop(state, state.lines.length), message: undefined } };
76
+ case "search":
77
+ return { state: { ...state, search: { query: "", typing: true }, message: undefined } };
78
+ case "searchType":
79
+ return { state: { ...state, search: { ...state.search, query: state.search.query + action.character } } };
80
+ case "searchBackspace":
81
+ return { state: { ...state, search: { ...state.search, query: state.search.query.slice(0, -1) } } };
82
+ case "searchCancel":
83
+ return { state: { ...state, search: { query: "", typing: false } } };
84
+ case "searchCommit":
85
+ case "searchNext": {
86
+ const settled = { ...state, search: { ...state.search, typing: false } };
87
+ const row = findFrom(settled, action.kind === "searchCommit" ? state.top : state.top + 1);
88
+ if (row < 0)
89
+ return { state: { ...settled, message: state.search.query ? `no match for "${state.search.query}"` : undefined } };
90
+ return { state: { ...settled, top: clampTop(settled, row), message: undefined } };
91
+ }
92
+ default:
93
+ return { state };
94
+ }
95
+ }
96
+ function composePagerFrame(state, columns, title = "transcript") {
97
+ const clip = (text) => text.length <= columns ? text : text.slice(0, Math.max(0, columns - 1));
98
+ const last = Math.min(state.lines.length, state.top + state.height);
99
+ const position = `${state.lines.length === 0 ? 0 : state.top + 1}-${last} of ${state.lines.length}`;
100
+ const body = state.lines.slice(state.top, state.top + state.height).map((text) => ({ text: clip(text) }));
101
+ while (body.length < state.height)
102
+ body.push({ text: "" });
103
+ const bar = state.search.typing ? `/${state.search.query} Enter find · Esc cancel` : state.message ?? "↑↓ scroll · PgUp/PgDn page · g/G ends · / find · Esc back";
104
+ return [
105
+ { text: clip(`${title} ${position}`), bold: true },
106
+ ...body,
107
+ { text: clip(bar), dim: true }
108
+ ];
109
+ }
110
+
111
+ // packages/archymedes-cli/src/ui/pager-screen.tsx
112
+ import { jsxDEV } from "@termuijs/jsx/jsx-dev-runtime";
113
+ var Panel = Box;
114
+ var Line = Text;
115
+ var CHROME_ROWS = 2;
116
+ function PagerScreen({ columns, rows, text, title, onExit }) {
117
+ const [state, setState] = useState(() => initialPagerState(text, rows - CHROME_ROWS));
118
+ useInput((input, key) => {
119
+ const action = keyToPagerAction({ name: key?.key, ctrl: key?.ctrl }, input, state);
120
+ const step = applyPagerAction(state, action);
121
+ if (step.exit) {
122
+ onExit();
123
+ return;
124
+ }
125
+ setState(step.state);
126
+ });
127
+ const frame = composePagerFrame(state, columns, title);
128
+ return /* @__PURE__ */ jsxDEV(Panel, {
129
+ flexDirection: "column",
130
+ width: columns,
131
+ height: rows,
132
+ children: frame.map((row, index) => /* @__PURE__ */ jsxDEV(Line, {
133
+ bold: row.bold,
134
+ dimColor: row.dim,
135
+ children: row.text
136
+ }, `row-${index}`, false, undefined, this))
137
+ }, undefined, false, undefined, this);
138
+ }
139
+ async function runPagerScreen(options) {
140
+ const { renderApp } = await import("@termuijs/jsx");
141
+ await new Promise((resolve) => {
142
+ let settled = false;
143
+ const finish = () => {
144
+ if (settled)
145
+ return;
146
+ settled = true;
147
+ resolve();
148
+ };
149
+ renderApp(PagerScreen, { ...options, onExit: finish, fullscreen: true }).catch(finish);
150
+ });
151
+ }
152
+ export {
153
+ runPagerScreen,
154
+ PagerScreen
155
+ };
@@ -0,0 +1,248 @@
1
+ import {
2
+ eventJournalPath,
3
+ readEventJournal
4
+ } from "./archymedes-w4bjfye7.mjs";
5
+ import {
6
+ __require
7
+ } from "./archymedes-x67v3n58.mjs";
8
+
9
+ // packages/core/src/cli/projection.ts
10
+ import { promises as fs } from "node:fs";
11
+ import path from "node:path";
12
+ var PROJECTION_SCHEMA_VERSION = 1;
13
+ async function loadSqlite() {
14
+ const original = process.emitWarning;
15
+ process.emitWarning = (warning, ...rest) => {
16
+ const text = typeof warning === "string" ? warning : warning?.message ?? "";
17
+ const type = typeof rest[0] === "string" ? rest[0] : rest[0]?.type;
18
+ if (type === "ExperimentalWarning" && /sqlite/i.test(text))
19
+ return;
20
+ return original.call(process, warning, ...rest);
21
+ };
22
+ try {
23
+ return await import("node:sqlite");
24
+ } finally {
25
+ process.emitWarning = original;
26
+ }
27
+ }
28
+ var SCHEMA = `
29
+ CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL) STRICT;
30
+ CREATE TABLE events (
31
+ session_id TEXT NOT NULL,
32
+ sequence INTEGER NOT NULL,
33
+ timestamp TEXT NOT NULL,
34
+ type TEXT NOT NULL,
35
+ turn_id TEXT,
36
+ hash TEXT NOT NULL,
37
+ payload TEXT NOT NULL,
38
+ PRIMARY KEY (session_id, sequence)
39
+ ) STRICT;
40
+ CREATE INDEX events_by_type ON events (session_id, type, sequence);
41
+ -- One row per approval request, updated in place when its decision arrives. Querying "still
42
+ -- waiting" is then an index lookup rather than a join of every request against every decision.
43
+ CREATE TABLE approvals (
44
+ session_id TEXT NOT NULL,
45
+ action_digest TEXT NOT NULL,
46
+ requested_seq INTEGER NOT NULL,
47
+ turn_id TEXT,
48
+ tool_name TEXT NOT NULL,
49
+ summary TEXT NOT NULL,
50
+ decided_seq INTEGER,
51
+ decision TEXT,
52
+ PRIMARY KEY (session_id, requested_seq)
53
+ ) STRICT;
54
+ CREATE INDEX approvals_pending ON approvals (session_id, decided_seq);
55
+ -- Searchable text, lowercased once at write time so a query is a scan of prepared values rather
56
+ -- than a per-row LOWER(). FTS5 is deliberately not used: it is a compile-time option, and a
57
+ -- projection that only works on some builds of Node is worse than one that is merely linear.
58
+ CREATE TABLE searchable (
59
+ session_id TEXT NOT NULL,
60
+ sequence INTEGER NOT NULL,
61
+ kind TEXT NOT NULL,
62
+ text TEXT NOT NULL,
63
+ PRIMARY KEY (session_id, sequence, kind)
64
+ ) STRICT;
65
+ `;
66
+ function textOf(payload) {
67
+ if (payload.type === "runtime") {
68
+ const event = payload.event;
69
+ if (event.type === "tool_call")
70
+ return [{ kind: "tool_call", text: `${event.toolName} ${JSON.stringify(event.arguments)}` }];
71
+ if (event.type === "tool_result")
72
+ return [{ kind: "tool_result", text: event.content }];
73
+ if (event.type === "runtime_stop")
74
+ return [{ kind: "summary", text: event.summary }];
75
+ return [];
76
+ }
77
+ if (payload.type === "approval_requested")
78
+ return [{ kind: "approval", text: payload.request.summary }];
79
+ return [];
80
+ }
81
+
82
+ class SessionProjection {
83
+ database;
84
+ root;
85
+ file;
86
+ constructor(database, root, file) {
87
+ this.database = database;
88
+ this.root = root;
89
+ this.file = file;
90
+ }
91
+ static async open(root, options = {}) {
92
+ const file = options.file ?? path.join(root, ".archymedes", "projection.db");
93
+ await fs.mkdir(path.dirname(file), { recursive: true });
94
+ const { DatabaseSync } = await loadSqlite();
95
+ const build = () => {
96
+ const database2 = new DatabaseSync(file);
97
+ database2.exec(SCHEMA);
98
+ database2.prepare("INSERT INTO meta (key, value) VALUES (?, ?)").run("schemaVersion", String(PROJECTION_SCHEMA_VERSION));
99
+ return database2;
100
+ };
101
+ let database;
102
+ try {
103
+ database = new DatabaseSync(file);
104
+ const row = database.prepare("SELECT value FROM meta WHERE key = 'schemaVersion'").get();
105
+ if (row?.value !== String(PROJECTION_SCHEMA_VERSION)) {
106
+ database.close();
107
+ database = undefined;
108
+ await fs.rm(file, { force: true });
109
+ database = build();
110
+ }
111
+ } catch {
112
+ try {
113
+ database?.close();
114
+ } catch {}
115
+ await fs.rm(file, { force: true });
116
+ database = build();
117
+ }
118
+ return new SessionProjection(database, root, file);
119
+ }
120
+ close() {
121
+ this.database.close();
122
+ }
123
+ async rebuild(sessionId) {
124
+ const events = await readEventJournal(this.root, sessionId);
125
+ this.database.exec("BEGIN");
126
+ try {
127
+ for (const table of ["events", "approvals", "searchable"]) {
128
+ this.database.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(sessionId);
129
+ }
130
+ const insertEvent = this.database.prepare("INSERT INTO events (session_id, sequence, timestamp, type, turn_id, hash, payload) VALUES (?, ?, ?, ?, ?, ?, ?)");
131
+ const insertApproval = this.database.prepare("INSERT INTO approvals (session_id, action_digest, requested_seq, turn_id, tool_name, summary) VALUES (?, ?, ?, ?, ?, ?)");
132
+ const decideApproval = this.database.prepare("UPDATE approvals SET decided_seq = ?, decision = ? WHERE session_id = ? AND action_digest = ? AND decided_seq IS NULL");
133
+ const insertText = this.database.prepare("INSERT INTO searchable (session_id, sequence, kind, text) VALUES (?, ?, ?, ?)");
134
+ for (const event of events) {
135
+ const payload = event.payload;
136
+ const turnId = "turnId" in payload ? payload.turnId : null;
137
+ insertEvent.run(sessionId, event.sequence, event.timestamp, payload.type, turnId, event.hash, JSON.stringify(payload));
138
+ if (payload.type === "approval_requested") {
139
+ insertApproval.run(sessionId, payload.request.actionDigest, event.sequence, turnId, payload.request.toolName, payload.request.summary);
140
+ }
141
+ if (payload.type === "approval_decided") {
142
+ decideApproval.run(event.sequence, payload.decision, sessionId, payload.actionDigest);
143
+ }
144
+ for (const entry of textOf(payload))
145
+ insertText.run(sessionId, event.sequence, entry.kind, entry.text.toLowerCase());
146
+ }
147
+ this.database.exec("COMMIT");
148
+ return { events: events.length };
149
+ } catch (error) {
150
+ this.database.exec("ROLLBACK");
151
+ throw error;
152
+ }
153
+ }
154
+ async rebuildAll() {
155
+ const directory = path.dirname(eventJournalPath(this.root, "placeholder"));
156
+ const files = await fs.readdir(directory).catch(() => []);
157
+ let events = 0;
158
+ let sessions = 0;
159
+ for (const file of files) {
160
+ if (!file.endsWith(".jsonl"))
161
+ continue;
162
+ const result = await this.rebuild(file.slice(0, -6));
163
+ events += result.events;
164
+ sessions += 1;
165
+ }
166
+ return { sessions, events };
167
+ }
168
+ eventsAfter(sessionId, cursor, limit = 500) {
169
+ const rows = this.database.prepare("SELECT session_id, sequence, timestamp, type, turn_id, hash, payload FROM events WHERE session_id = ? AND sequence > ? ORDER BY sequence LIMIT ?").all(sessionId, cursor, limit);
170
+ return rows.map((row) => ({
171
+ sessionId: row.session_id,
172
+ sequence: row.sequence,
173
+ timestamp: row.timestamp,
174
+ type: row.type,
175
+ turnId: row.turn_id ?? null,
176
+ hash: row.hash,
177
+ payload: JSON.parse(row.payload)
178
+ }));
179
+ }
180
+ cursor(sessionId) {
181
+ const row = this.database.prepare("SELECT MAX(sequence) AS last FROM events WHERE session_id = ?").get(sessionId);
182
+ return row?.last ?? 0;
183
+ }
184
+ pendingApprovals(sessionId) {
185
+ const rows = sessionId ? this.database.prepare("SELECT * FROM approvals WHERE decided_seq IS NULL AND session_id = ? ORDER BY requested_seq").all(sessionId) : this.database.prepare("SELECT * FROM approvals WHERE decided_seq IS NULL ORDER BY session_id, requested_seq").all();
186
+ return rows.map((row) => ({
187
+ sessionId: row.session_id,
188
+ actionDigest: row.action_digest,
189
+ requestedSeq: row.requested_seq,
190
+ turnId: row.turn_id ?? null,
191
+ toolName: row.tool_name,
192
+ summary: row.summary
193
+ }));
194
+ }
195
+ search(query, options = {}) {
196
+ const needle = query.trim().toLowerCase();
197
+ if (!needle)
198
+ return [];
199
+ const pattern = `%${needle.replace(/[\\%_]/g, (character) => `\\${character}`)}%`;
200
+ const limit = options.limit ?? 50;
201
+ const rows = options.sessionId ? this.database.prepare("SELECT * FROM searchable WHERE text LIKE ? ESCAPE '\\' AND session_id = ? ORDER BY sequence LIMIT ?").all(pattern, options.sessionId, limit) : this.database.prepare("SELECT * FROM searchable WHERE text LIKE ? ESCAPE '\\' ORDER BY session_id, sequence LIMIT ?").all(pattern, limit);
202
+ return rows.map((row) => ({ sessionId: row.session_id, sequence: row.sequence, kind: row.kind, text: row.text }));
203
+ }
204
+ sessions() {
205
+ const rows = this.database.prepare("SELECT session_id, COUNT(*) AS events, MIN(timestamp) AS first_at, MAX(timestamp) AS last_at, MAX(sequence) AS last_sequence FROM events GROUP BY session_id ORDER BY last_at DESC").all();
206
+ return rows.map((row) => ({
207
+ sessionId: row.session_id,
208
+ events: row.events,
209
+ firstAt: row.first_at,
210
+ lastAt: row.last_at,
211
+ lastSequence: row.last_sequence
212
+ }));
213
+ }
214
+ fork(sessionId, newSessionId, throughSequence) {
215
+ if (newSessionId === sessionId)
216
+ throw new Error("A fork needs a different session id");
217
+ this.database.exec("BEGIN");
218
+ try {
219
+ for (const table of ["events", "approvals", "searchable"]) {
220
+ this.database.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(newSessionId);
221
+ }
222
+ this.database.prepare("INSERT INTO events (session_id, sequence, timestamp, type, turn_id, hash, payload) SELECT ?, sequence, timestamp, type, turn_id, hash, payload FROM events WHERE session_id = ? AND sequence <= ?").run(newSessionId, sessionId, throughSequence);
223
+ this.database.prepare("INSERT INTO approvals (session_id, action_digest, requested_seq, turn_id, tool_name, summary, decided_seq, decision) SELECT ?, action_digest, requested_seq, turn_id, tool_name, summary, decided_seq, decision FROM approvals WHERE session_id = ? AND requested_seq <= ?").run(newSessionId, sessionId, throughSequence);
224
+ this.database.prepare("INSERT INTO searchable (session_id, sequence, kind, text) SELECT ?, sequence, kind, text FROM searchable WHERE session_id = ? AND sequence <= ?").run(newSessionId, sessionId, throughSequence);
225
+ this.database.exec("COMMIT");
226
+ return { events: this.cursor(newSessionId) === 0 ? 0 : this.eventsAfter(newSessionId, 0, Number.MAX_SAFE_INTEGER).length };
227
+ } catch (error) {
228
+ this.database.exec("ROLLBACK");
229
+ throw error;
230
+ }
231
+ }
232
+ archive(sessionId) {
233
+ this.database.exec("BEGIN");
234
+ try {
235
+ for (const table of ["events", "approvals", "searchable"]) {
236
+ this.database.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(sessionId);
237
+ }
238
+ this.database.exec("COMMIT");
239
+ } catch (error) {
240
+ this.database.exec("ROLLBACK");
241
+ throw error;
242
+ }
243
+ }
244
+ }
245
+ export {
246
+ SessionProjection,
247
+ PROJECTION_SCHEMA_VERSION
248
+ };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  ALLOWED_SANDBOX_PROGRAMS
3
- } from "./archymedes-h13ap0za.mjs";
3
+ } from "./archymedes-1g1y6q74.mjs";
4
4
  import"./archymedes-x67v3n58.mjs";
5
5
 
6
- // ../core/src/sandbox-templates.ts
6
+ // packages/core/src/sandbox-templates.ts
7
7
  var WORKSPACE_PRESETS = [
8
8
  {
9
9
  id: "general",
@@ -0,0 +1,70 @@
1
+ import {
2
+ applyAction,
3
+ composeFrame,
4
+ keyToAction
5
+ } from "./archymedes-t1jtzkmp.mjs";
6
+ import"./archymedes-kn6wtq32.mjs";
7
+ import"./archymedes-twanftqk.mjs";
8
+ import {
9
+ exitTopTermUIApp
10
+ } from "./archymedes-s4qcy0q4.mjs";
11
+ import {
12
+ __require
13
+ } from "./archymedes-x67v3n58.mjs";
14
+
15
+ // packages/archymedes-cli/src/ui/workspace-screen.tsx
16
+ import { useInput, useInterval, useState } from "@termuijs/jsx";
17
+ import { Box, Text } from "@termuijs/widgets";
18
+ import { jsxDEV } from "@termuijs/jsx/jsx-dev-runtime";
19
+ var Panel = Box;
20
+ var Line = Text;
21
+ function Workspace({ read, onExit, refreshMs = 200 }) {
22
+ const [snapshot, setSnapshot] = useState(() => read());
23
+ useInterval(() => {
24
+ setSnapshot((current) => ({ ...read(), selected: current.selected, scroll: current.scroll }));
25
+ }, refreshMs);
26
+ useInput((input, key) => {
27
+ const action = keyToAction({ name: key?.key, ctrl: key?.ctrl, shift: key?.shift }, input);
28
+ if (action.kind === "exit") {
29
+ onExit();
30
+ return;
31
+ }
32
+ setSnapshot((current) => applyAction(current, action));
33
+ });
34
+ const rows = composeFrame(snapshot);
35
+ return /* @__PURE__ */ jsxDEV(Panel, {
36
+ flexDirection: "column",
37
+ width: snapshot.columns,
38
+ height: snapshot.rows,
39
+ children: rows.map((row, index) => /* @__PURE__ */ jsxDEV(Line, {
40
+ bold: row.bold,
41
+ dimColor: row.dim,
42
+ inverse: row.inverse,
43
+ color: row.color,
44
+ children: row.text
45
+ }, `row-${index}`, false, undefined, this))
46
+ }, undefined, false, undefined, this);
47
+ }
48
+ async function runWorkspace(host) {
49
+ const { renderApp } = await import("@termuijs/jsx");
50
+ await new Promise((resolve) => {
51
+ let settled = false;
52
+ const finish = () => {
53
+ if (settled)
54
+ return;
55
+ settled = true;
56
+ exitTopTermUIApp();
57
+ host.onExit?.();
58
+ resolve();
59
+ };
60
+ renderApp(Workspace, {
61
+ read: host.read,
62
+ onExit: finish,
63
+ fullscreen: true
64
+ }).catch(finish);
65
+ });
66
+ }
67
+ export {
68
+ runWorkspace,
69
+ Workspace
70
+ };