@celestea/studio 2.7.1

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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/dist/app.d.ts +111 -0
  3. package/dist/app.js +219 -0
  4. package/dist/auth/api-token.d.ts +76 -0
  5. package/dist/auth/api-token.js +172 -0
  6. package/dist/auth/htpasswd.d.ts +22 -0
  7. package/dist/auth/htpasswd.js +61 -0
  8. package/dist/auth/index.d.ts +14 -0
  9. package/dist/auth/index.js +14 -0
  10. package/dist/auth/page.d.ts +19 -0
  11. package/dist/auth/page.js +73 -0
  12. package/dist/auth/rate-limit.d.ts +27 -0
  13. package/dist/auth/rate-limit.js +47 -0
  14. package/dist/auth/token.d.ts +49 -0
  15. package/dist/auth/token.js +112 -0
  16. package/dist/config.d.ts +78 -0
  17. package/dist/config.js +72 -0
  18. package/dist/deployment.d.ts +92 -0
  19. package/dist/deployment.js +154 -0
  20. package/dist/fake-runtime-adapter.d.ts +37 -0
  21. package/dist/fake-runtime-adapter.js +276 -0
  22. package/dist/handlers/auth.d.ts +24 -0
  23. package/dist/handlers/auth.js +155 -0
  24. package/dist/handlers/common.d.ts +76 -0
  25. package/dist/handlers/common.js +127 -0
  26. package/dist/handlers/config-shape.d.ts +76 -0
  27. package/dist/handlers/config-shape.js +227 -0
  28. package/dist/handlers/config.d.ts +26 -0
  29. package/dist/handlers/config.js +161 -0
  30. package/dist/handlers/context-shape.d.ts +33 -0
  31. package/dist/handlers/context-shape.js +59 -0
  32. package/dist/handlers/dialog.d.ts +22 -0
  33. package/dist/handlers/dialog.js +292 -0
  34. package/dist/handlers/exec.d.ts +22 -0
  35. package/dist/handlers/exec.js +131 -0
  36. package/dist/handlers/fs-read.d.ts +63 -0
  37. package/dist/handlers/fs-read.js +159 -0
  38. package/dist/handlers/fs.d.ts +57 -0
  39. package/dist/handlers/fs.js +153 -0
  40. package/dist/handlers/grants-shape.d.ts +45 -0
  41. package/dist/handlers/grants-shape.js +130 -0
  42. package/dist/handlers/grants.d.ts +21 -0
  43. package/dist/handlers/grants.js +267 -0
  44. package/dist/handlers/health.d.ts +37 -0
  45. package/dist/handlers/health.js +139 -0
  46. package/dist/handlers/index.d.ts +40 -0
  47. package/dist/handlers/index.js +85 -0
  48. package/dist/handlers/permissions.d.ts +9 -0
  49. package/dist/handlers/permissions.js +183 -0
  50. package/dist/handlers/plugins.d.ts +18 -0
  51. package/dist/handlers/plugins.js +23 -0
  52. package/dist/handlers/prompts.d.ts +12 -0
  53. package/dist/handlers/prompts.js +120 -0
  54. package/dist/handlers/providers.d.ts +12 -0
  55. package/dist/handlers/providers.js +178 -0
  56. package/dist/handlers/questions.d.ts +23 -0
  57. package/dist/handlers/questions.js +115 -0
  58. package/dist/handlers/session-model.d.ts +38 -0
  59. package/dist/handlers/session-model.js +105 -0
  60. package/dist/handlers/session-move.d.ts +22 -0
  61. package/dist/handlers/session-move.js +188 -0
  62. package/dist/handlers/session-tools.d.ts +19 -0
  63. package/dist/handlers/session-tools.js +75 -0
  64. package/dist/handlers/sessions.d.ts +32 -0
  65. package/dist/handlers/sessions.js +242 -0
  66. package/dist/handlers/usage.d.ts +26 -0
  67. package/dist/handlers/usage.js +77 -0
  68. package/dist/handlers/worker.d.ts +12 -0
  69. package/dist/handlers/worker.js +82 -0
  70. package/dist/handlers/workspaces.d.ts +12 -0
  71. package/dist/handlers/workspaces.js +88 -0
  72. package/dist/harness.test-util.d.ts +101 -0
  73. package/dist/harness.test-util.js +176 -0
  74. package/dist/index.d.ts +33 -0
  75. package/dist/index.js +33 -0
  76. package/dist/main.d.ts +13 -0
  77. package/dist/main.js +35 -0
  78. package/dist/plugins.d.ts +105 -0
  79. package/dist/plugins.js +97 -0
  80. package/dist/question-registry.d.ts +124 -0
  81. package/dist/question-registry.js +212 -0
  82. package/dist/question-rows.d.ts +32 -0
  83. package/dist/question-rows.js +43 -0
  84. package/dist/replay/compare.d.ts +47 -0
  85. package/dist/replay/compare.js +89 -0
  86. package/dist/replay/e2e-replay.d.ts +46 -0
  87. package/dist/replay/e2e-replay.js +91 -0
  88. package/dist/replay/expect-compact.d.ts +31 -0
  89. package/dist/replay/expect-compact.js +92 -0
  90. package/dist/replay/fixtures.d.ts +52 -0
  91. package/dist/replay/fixtures.js +53 -0
  92. package/dist/replay/host.d.ts +32 -0
  93. package/dist/replay/host.js +62 -0
  94. package/dist/replay/index.d.ts +16 -0
  95. package/dist/replay/index.js +16 -0
  96. package/dist/replay/probes.d.ts +55 -0
  97. package/dist/replay/probes.js +225 -0
  98. package/dist/replay/report.d.ts +13 -0
  99. package/dist/replay/report.js +103 -0
  100. package/dist/replay/session-e2e.d.ts +44 -0
  101. package/dist/replay/session-e2e.js +91 -0
  102. package/dist/routes.d.ts +47 -0
  103. package/dist/routes.js +59 -0
  104. package/dist/runtime/attachments-llm.d.ts +13 -0
  105. package/dist/runtime/attachments-llm.js +37 -0
  106. package/dist/runtime/boot-recovery.d.ts +32 -0
  107. package/dist/runtime/boot-recovery.js +71 -0
  108. package/dist/runtime/context-snapshot.d.ts +59 -0
  109. package/dist/runtime/context-snapshot.js +113 -0
  110. package/dist/runtime/engine-grants.d.ts +121 -0
  111. package/dist/runtime/engine-grants.js +338 -0
  112. package/dist/runtime/engine-permissions.d.ts +29 -0
  113. package/dist/runtime/engine-permissions.js +92 -0
  114. package/dist/runtime/engine-plugins.d.ts +189 -0
  115. package/dist/runtime/engine-plugins.js +292 -0
  116. package/dist/runtime/engine-profile.d.ts +28 -0
  117. package/dist/runtime/engine-profile.js +88 -0
  118. package/dist/runtime/engine-session.d.ts +9 -0
  119. package/dist/runtime/engine-session.js +9 -0
  120. package/dist/runtime/fallback-contract.d.ts +32 -0
  121. package/dist/runtime/fallback-contract.js +10 -0
  122. package/dist/runtime/fallback-host.d.ts +134 -0
  123. package/dist/runtime/fallback-host.js +299 -0
  124. package/dist/runtime/host-autowake.d.ts +79 -0
  125. package/dist/runtime/host-autowake.js +86 -0
  126. package/dist/runtime/image-downgrade.d.ts +49 -0
  127. package/dist/runtime/image-downgrade.js +90 -0
  128. package/dist/runtime/inbox-message.d.ts +11 -0
  129. package/dist/runtime/inbox-message.js +20 -0
  130. package/dist/runtime/index.d.ts +17 -0
  131. package/dist/runtime/index.js +17 -0
  132. package/dist/runtime/ledger-view.d.ts +40 -0
  133. package/dist/runtime/ledger-view.js +56 -0
  134. package/dist/runtime/llm-assembly.d.ts +66 -0
  135. package/dist/runtime/llm-assembly.js +114 -0
  136. package/dist/runtime/offline-llm.d.ts +71 -0
  137. package/dist/runtime/offline-llm.js +132 -0
  138. package/dist/runtime/provider-target.d.ts +10 -0
  139. package/dist/runtime/provider-target.js +10 -0
  140. package/dist/runtime/question-host.d.ts +33 -0
  141. package/dist/runtime/question-host.js +41 -0
  142. package/dist/runtime/question-view.d.ts +48 -0
  143. package/dist/runtime/question-view.js +76 -0
  144. package/dist/runtime/real-runtime-adapter.d.ts +95 -0
  145. package/dist/runtime/real-runtime-adapter.js +553 -0
  146. package/dist/runtime/recovery-audit.d.ts +81 -0
  147. package/dist/runtime/recovery-audit.js +95 -0
  148. package/dist/runtime/recovery-view.d.ts +36 -0
  149. package/dist/runtime/recovery-view.js +63 -0
  150. package/dist/runtime/session-compose.d.ts +235 -0
  151. package/dist/runtime/session-compose.js +350 -0
  152. package/dist/runtime/session-grants.d.ts +36 -0
  153. package/dist/runtime/session-grants.js +69 -0
  154. package/dist/runtime/session-lifecycle.d.ts +40 -0
  155. package/dist/runtime/session-lifecycle.js +86 -0
  156. package/dist/runtime/session-publisher.d.ts +40 -0
  157. package/dist/runtime/session-publisher.js +48 -0
  158. package/dist/runtime/session-release.d.ts +53 -0
  159. package/dist/runtime/session-release.js +80 -0
  160. package/dist/runtime/test-util.d.ts +78 -0
  161. package/dist/runtime/test-util.js +162 -0
  162. package/dist/runtime/watchdog-view.d.ts +33 -0
  163. package/dist/runtime/watchdog-view.js +45 -0
  164. package/dist/runtime/worker-bridge.d.ts +76 -0
  165. package/dist/runtime/worker-bridge.js +156 -0
  166. package/dist/runtime/worker-live.d.ts +22 -0
  167. package/dist/runtime/worker-live.js +25 -0
  168. package/dist/runtime/worker-recovery.d.ts +35 -0
  169. package/dist/runtime/worker-recovery.js +64 -0
  170. package/dist/runtime/worker-table.d.ts +58 -0
  171. package/dist/runtime/worker-table.js +70 -0
  172. package/dist/runtime-adapter.d.ts +421 -0
  173. package/dist/runtime-adapter.js +76 -0
  174. package/dist/serial-queue.d.ts +16 -0
  175. package/dist/serial-queue.js +22 -0
  176. package/dist/server.d.ts +44 -0
  177. package/dist/server.js +119 -0
  178. package/dist/settings.d.ts +23 -0
  179. package/dist/settings.js +31 -0
  180. package/dist/sse.d.ts +67 -0
  181. package/dist/sse.js +186 -0
  182. package/dist/static.d.ts +20 -0
  183. package/dist/static.js +120 -0
  184. package/dist/store/builtin-sections.d.ts +52 -0
  185. package/dist/store/builtin-sections.js +118 -0
  186. package/dist/store/celestea-home.d.ts +12 -0
  187. package/dist/store/celestea-home.js +11 -0
  188. package/dist/store/fs-json.d.ts +51 -0
  189. package/dist/store/fs-json.js +119 -0
  190. package/dist/store/grants-audit.d.ts +84 -0
  191. package/dist/store/grants-audit.js +109 -0
  192. package/dist/store/grants-service.d.ts +44 -0
  193. package/dist/store/grants-service.js +41 -0
  194. package/dist/store/grants-tokens.d.ts +62 -0
  195. package/dist/store/grants-tokens.js +105 -0
  196. package/dist/store/grants.d.ts +113 -0
  197. package/dist/store/grants.js +238 -0
  198. package/dist/store/index.d.ts +36 -0
  199. package/dist/store/index.js +36 -0
  200. package/dist/store/mode.d.ts +35 -0
  201. package/dist/store/mode.js +41 -0
  202. package/dist/store/permissions.d.ts +61 -0
  203. package/dist/store/permissions.js +139 -0
  204. package/dist/store/prompts-compose.d.ts +56 -0
  205. package/dist/store/prompts-compose.js +62 -0
  206. package/dist/store/prompts-template.d.ts +26 -0
  207. package/dist/store/prompts-template.js +99 -0
  208. package/dist/store/prompts.d.ts +142 -0
  209. package/dist/store/prompts.js +273 -0
  210. package/dist/store/provider-probe.d.ts +81 -0
  211. package/dist/store/provider-probe.js +154 -0
  212. package/dist/store/providers.d.ts +97 -0
  213. package/dist/store/providers.js +219 -0
  214. package/dist/store/result.d.ts +29 -0
  215. package/dist/store/result.js +30 -0
  216. package/dist/store/session-id.d.ts +119 -0
  217. package/dist/store/session-id.js +220 -0
  218. package/dist/store/session-meta.d.ts +41 -0
  219. package/dist/store/session-meta.js +66 -0
  220. package/dist/store/session-ops.d.ts +109 -0
  221. package/dist/store/session-ops.js +264 -0
  222. package/dist/store/session-tools.d.ts +42 -0
  223. package/dist/store/session-tools.js +69 -0
  224. package/dist/store/sessions.d.ts +162 -0
  225. package/dist/store/sessions.js +307 -0
  226. package/dist/store/validate.d.ts +13 -0
  227. package/dist/store/validate.js +38 -0
  228. package/dist/store/workspaces.d.ts +67 -0
  229. package/dist/store/workspaces.js +228 -0
  230. package/dist/user-questions.d.ts +58 -0
  231. package/dist/user-questions.js +157 -0
  232. package/dist/version.d.ts +14 -0
  233. package/dist/version.js +52 -0
  234. package/package.json +38 -0
  235. package/webdist/assets/index-Bngs7gUm.js +98 -0
  236. package/webdist/assets/index-Bu6ci_rN.css +1 -0
  237. package/webdist/assets/katex-CCbS2qAY.js +257 -0
  238. package/webdist/assets/mhchem-CnUN8HwY.js +1 -0
  239. package/webdist/build-meta.json +7 -0
  240. package/webdist/index.html +261 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The iteration-E recovery audit channel (E §5.2③, P1).
3
+ *
4
+ * §5.2③: every automatic or observed recovery fact must appear in three places —
5
+ * an append-only local log, a structured field and an audit line. W730's P0
6
+ * deliberately deferred the audit half of the checkpoint capability because
7
+ * inventing a second audit FILE then had no owner; P1 is that owner, and this is
8
+ * the ONE channel both P1 capabilities write through:
9
+ *
10
+ * - `log_degraded` — a session log refused a write (`writeErrorCount>0`):
11
+ * disk and memory forked, and nobody would ever know;
12
+ * - `worker_stale` — a persisted worker row whose owning process is dead;
13
+ * - `worker_orphan` — a RUNNING row whose `host=` session is gone;
14
+ * - `worker_observed` — the boot sweep's summary line (always exactly one);
15
+ * - `session_repaired` — a crashed turn was closed by appending ONE
16
+ * `turn_end: interrupted` row (§1.2.3, R1-1).
17
+ *
18
+ * Discipline (identical to `grants-audit.jsonl` / `fallbacks-audit.jsonl`):
19
+ * local append-only `<data dir>/recovery-audit.jsonl`, mode 0600, rotated at
20
+ * 16 MiB; the platform `POST /api/audit` is BEST-EFFORT and only when
21
+ * `CELESTEA_AUDIT_URL` is set. A failed write is reported, never thrown — an
22
+ * audit channel must not be able to break the thing it observes. Nothing here
23
+ * ever records a prompt, a command line or a credential: wid / turn_id / count
24
+ * only (§4.4).
25
+ */
26
+ import { appendFileSync, chmodSync, mkdirSync, renameSync, statSync } from "node:fs";
27
+ import { dirname, join } from "node:path";
28
+ export const RECOVERY_AUDIT_FILE = "recovery-audit.jsonl";
29
+ /** Rotate at 16 MiB, keeping the previous chain (LTS ops audit rules). */
30
+ export const RECOVERY_AUDIT_MAX_BYTES = 16 * 1024 * 1024;
31
+ export const ENV_AUDIT_URL = "CELESTEA_AUDIT_URL";
32
+ export const ENV_CENTER_TOKEN = "CELESTEA_CENTER_TOKEN";
33
+ export class RecoveryAuditWriter {
34
+ path;
35
+ env;
36
+ now;
37
+ post;
38
+ /** Diagnostics of failed writes (never thrown): the caller reports them. */
39
+ errors = [];
40
+ constructor(opts) {
41
+ this.path = join(opts.dataDir, RECOVERY_AUDIT_FILE);
42
+ this.env = opts.env ?? process.env;
43
+ this.now = opts.now ?? Date.now;
44
+ this.post = opts.post ?? defaultPost;
45
+ }
46
+ get filePath() {
47
+ return this.path;
48
+ }
49
+ /** Append one line (local first, platform best-effort — §4.4). */
50
+ write(event) {
51
+ const line = { ts: event.ts ?? this.now(), ...event };
52
+ this.append(line);
53
+ this.deliver(line);
54
+ }
55
+ append(line) {
56
+ try {
57
+ mkdirSync(dirname(this.path), { recursive: true });
58
+ this.rotate();
59
+ appendFileSync(this.path, `${JSON.stringify(line)}\n`, { mode: 0o600 });
60
+ chmodSync(this.path, 0o600);
61
+ }
62
+ catch (e) {
63
+ this.errors.push(messageOf(e));
64
+ }
65
+ }
66
+ /** 16 MiB ceiling: the previous chain becomes `<path>.1` (replaced). */
67
+ rotate() {
68
+ try {
69
+ if (statSync(this.path).size < RECOVERY_AUDIT_MAX_BYTES)
70
+ return;
71
+ renameSync(this.path, `${this.path}.1`);
72
+ }
73
+ catch {
74
+ // No file yet (first write) or an unreadable one: nothing to rotate.
75
+ }
76
+ }
77
+ /** Best-effort platform channel; its own failure is recorded locally. */
78
+ deliver(line) {
79
+ const url = this.env[ENV_AUDIT_URL];
80
+ if (url === undefined || url.trim() === "")
81
+ return;
82
+ const token = this.env[ENV_CENTER_TOKEN] ?? "";
83
+ const body = JSON.stringify({ category: "audit", summary: `[recovery] ${line.event}`, detail: line });
84
+ void this.post(url, body, { "content-type": "application/json", "x-center-token": token }).catch((e) => {
85
+ this.errors.push(`platform audit failed: ${messageOf(e)}`);
86
+ });
87
+ }
88
+ }
89
+ async function defaultPost(url, body, headers) {
90
+ const response = await fetch(url, { method: "POST", body, headers });
91
+ return { ok: response.ok, status: response.status };
92
+ }
93
+ function messageOf(e) {
94
+ return e instanceof Error ? e.message : String(e);
95
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `GET /api/status.recovery` (E §1.3 P1 ②) — the LIVE view of one session's
3
+ * checkpoint, appended to an existing endpoint (P1 adds no endpoint).
4
+ *
5
+ * Every field answers a question the operator could otherwise only answer by
6
+ * reading two files by hand:
7
+ *
8
+ * - `recovered_turns` — which turns THIS engine had to close after a crash
9
+ * (the sidecar's `repaired[]`, the durable record of R1-1);
10
+ * - `dangling_turns` — how many turns the LOG leaves open right now. It is a
11
+ * judgement about the log (K4: the log is the truth), so it is counted from
12
+ * the events, using the same rule as `analyzeReplay().danglingTurns`;
13
+ * - `degraded` — the log and the disk forked (`writeErrorCount>0`, G1-6);
14
+ * - `last_outcome` — the phase of the LAST `turn_end` in the log, which is
15
+ * what "was the previous turn interrupted?" really asks.
16
+ *
17
+ * Nothing here composes an instance: a session with no live generation reports
18
+ * the empty block (zeros, no turns) rather than waking an engine for a poll.
19
+ */
20
+ import { type SessionLog } from "@celestea/core";
21
+ /** The `recovery` block of `/api/status` (a pure addition, always present). */
22
+ export interface RecoveryView {
23
+ session: string | null;
24
+ /** Turn ids this engine closed after a crash (`checkpoint.repaired[]`). */
25
+ recovered_turns: string[];
26
+ /** `turn_start` rows without a `turn_end` right now. */
27
+ dangling_turns: number;
28
+ /** True when the session log has refused a write (disk ≠ memory). */
29
+ degraded: boolean;
30
+ /** Phase of the log's last `turn_end` (null = the log has no closed turn). */
31
+ last_outcome: string | null;
32
+ }
33
+ /** The empty block: no live instance, no checkpoint, no log. */
34
+ export declare function emptyRecoveryView(session: string | null): RecoveryView;
35
+ /** Build the block from the session's LIVE log (a checkpointed one, normally). */
36
+ export declare function recoveryViewOf(log: SessionLog | null | undefined, session: string | null): RecoveryView;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `GET /api/status.recovery` (E §1.3 P1 ②) — the LIVE view of one session's
3
+ * checkpoint, appended to an existing endpoint (P1 adds no endpoint).
4
+ *
5
+ * Every field answers a question the operator could otherwise only answer by
6
+ * reading two files by hand:
7
+ *
8
+ * - `recovered_turns` — which turns THIS engine had to close after a crash
9
+ * (the sidecar's `repaired[]`, the durable record of R1-1);
10
+ * - `dangling_turns` — how many turns the LOG leaves open right now. It is a
11
+ * judgement about the log (K4: the log is the truth), so it is counted from
12
+ * the events, using the same rule as `analyzeReplay().danglingTurns`;
13
+ * - `degraded` — the log and the disk forked (`writeErrorCount>0`, G1-6);
14
+ * - `last_outcome` — the phase of the LAST `turn_end` in the log, which is
15
+ * what "was the previous turn interrupted?" really asks.
16
+ *
17
+ * Nothing here composes an instance: a session with no live generation reports
18
+ * the empty block (zeros, no turns) rather than waking an engine for a poll.
19
+ */
20
+ import { outcomePhase } from "@celestea/core";
21
+ import { checkpointStoreOf, writeErrorCountOf } from "@celestea/session";
22
+ /** The empty block: no live instance, no checkpoint, no log. */
23
+ export function emptyRecoveryView(session) {
24
+ return { session, recovered_turns: [], dangling_turns: 0, degraded: false, last_outcome: null };
25
+ }
26
+ /** Build the block from the session's LIVE log (a checkpointed one, normally). */
27
+ export function recoveryViewOf(log, session) {
28
+ if (log === null || log === undefined)
29
+ return emptyRecoveryView(session);
30
+ const events = log.events();
31
+ const store = checkpointStoreOf(log);
32
+ const checkpoint = store?.load() ?? null;
33
+ const repaired = checkpoint !== null && checkpoint.kind === "ok" ? checkpoint.value.repaired : [];
34
+ return {
35
+ session,
36
+ recovered_turns: repaired.map((r) => r.turn_id),
37
+ dangling_turns: danglingOf(events),
38
+ // The LIVE counter wins over the sampled one: it is the same number the log
39
+ // itself reports right now, and a sticky `>0` in the sidecar keeps a past
40
+ // fork visible even after a healthy restart (which is intended).
41
+ degraded: writeErrorCountOf(log) > 0 || (checkpoint !== null && checkpoint.kind === "ok" && checkpoint.value.degraded.log_write_errors > 0),
42
+ last_outcome: lastOutcomeOf(events),
43
+ };
44
+ }
45
+ /** `turn_start` minus `turn_end` — the rule of `analyzeReplay().danglingTurns`. */
46
+ function danglingOf(events) {
47
+ let open = 0;
48
+ for (const event of events) {
49
+ if (event.type === "turn_start")
50
+ open += 1;
51
+ else if (event.type === "turn_end")
52
+ open -= 1;
53
+ }
54
+ return Math.max(0, open);
55
+ }
56
+ function lastOutcomeOf(events) {
57
+ for (let i = events.length - 1; i >= 0; i--) {
58
+ const event = events[i];
59
+ if (event !== undefined && event.type === "turn_end")
60
+ return outcomePhase(event.outcome);
61
+ }
62
+ return null;
63
+ }
@@ -0,0 +1,235 @@
1
+ /**
2
+ * SessionComposer — `compose()` for ONE session (W513).
3
+ *
4
+ * Every session runtime is one self-consistent generation: its own profile
5
+ * snapshot (base profile + the session's `session.json` model override), its own
6
+ * session binding (`<dir>/cli-main.jsonl` or an in-memory log when detached),
7
+ * its own usage tracker, its own agent loop instance and its own worker
8
+ * registry. Nothing is shared but the process (and the LLM seam factory), which
9
+ * is exactly what makes two sessions unable to see each other's history.
10
+ *
11
+ * The module also owns the resource caps and the two error channels the host
12
+ * maps onto HTTP 503, so the adapter itself stays about the HTTP contract.
13
+ */
14
+ import { type Llm, type PendingInjection, type Sandbox, type Tool, type ToolGuard } from "@celestea/core";
15
+ import { type SessionInbox } from "@celestea/runtime";
16
+ import { type UsageLedgerFile } from "@celestea/runtime";
17
+ import { type Profile, type Runtime, type Summarizer, type WatchdogMountSettings } from "@celestea/runtime";
18
+ import { type CheckpointWiring, type SessionTarget } from "./engine-session.js";
19
+ import { type DisclosureOptions } from "./engine-plugins.js";
20
+ import type { PendingQuestion, QuestionRegistry } from "../question-registry.js";
21
+ import type { FallbackWiring } from "./fallback-host.js";
22
+ import type { RecoveryAuditWriter } from "./recovery-audit.js";
23
+ import type { SessionGrantsReader } from "./session-grants.js";
24
+ import { type ImageDowngradeInfo } from "@celestea/llm";
25
+ /** W510 resource caps (overridable through the adapter options or the env). */
26
+ export declare const MAX_LIVE_SESSIONS = 4;
27
+ export declare const MAX_CONCURRENT_TURNS = 2;
28
+ export declare const SESSION_IDLE_TTL_MS: number;
29
+ /**
30
+ * Per-session injection wiring the host supplies (placement over SSE, W515 §2).
31
+ * The HOST builds it in `session-publisher.ts`; the fields are optional here
32
+ * because a session with no observers gets a plain inbox and no callback.
33
+ */
34
+ export interface SessionInjectionHooks {
35
+ /** The session's inbox (default: a plain one with no observers). */
36
+ inbox?: SessionInbox;
37
+ /** Called when a message LEAVES a lane and becomes model-visible history. */
38
+ onInjected?: (messages: readonly PendingInjection[], boundary: "turn-start" | "step") => void;
39
+ }
40
+ export interface SessionComposerOptions {
41
+ env: NodeJS.ProcessEnv;
42
+ /** Build the injection hooks of one session instance (inbox + observer). */
43
+ sessionHooks?: (sessionId: string | null) => SessionInjectionHooks;
44
+ /** BASE profile (the `/api/config` one); sessions add their model override. */
45
+ baseProfile: () => Profile;
46
+ /** Host lookup: `<workspace>/<session>` -> directory (null = detached). */
47
+ resolveSession?: (id: string) => SessionTarget | null;
48
+ /** Session-level model override (`session.json`), applied per instance. */
49
+ sessionModel?: (id: string) => string | null;
50
+ /**
51
+ * W729: session-level mode (`session.json.mode`; null = the session never
52
+ * declared one). Consumed by the worker wiring, so a worker's row/receipt can
53
+ * record the mode of the session that spawned it (§2.3).
54
+ */
55
+ sessionMode?: (id: string) => string | null;
56
+ /**
57
+ * W729 (§5.1 #4, R3): the session's OWN system prompt. Without this hook the
58
+ * process would assemble ONE prompt at startup and every session would share
59
+ * it — the mode would then only hold for the focused session. `null` = "use
60
+ * the base profile prompt" (a session with no declared mode, K8).
61
+ */
62
+ sessionSystemPrompt?: (id: string) => string | null;
63
+ /**
64
+ * E §4 P1 (W785): the model-fallback wiring. `wrap()` returns null while the
65
+ * capability is off (`CELESTEA_LLM_FALLBACK` unset — the default), so the
66
+ * pre-P1 path stays byte-for-byte identical (D9); when armed it returns the
67
+ * decorated seam and the ledger is booked PER ATTEMPT by the decorator
68
+ * instead of once per call, which is what makes D6's rows possible.
69
+ */
70
+ fallback?: FallbackWiring | null;
71
+ /** LLM seam factory; default = the assembled engine LLM (live provider). */
72
+ llm?: (profile: Profile) => Llm;
73
+ /** Extra tools registered after the six builtins. */
74
+ tools?: readonly Tool[];
75
+ /**
76
+ * W806 (P0): dynamic tool disclosure for the composed sessions. Absent = the
77
+ * static mode baseline (byte-identical face). Present = a reduced initial set
78
+ * is offered and the rest is revealed one turn at a time. The DETACHED
79
+ * generation never takes it: it backs `{{tools}}` / `GET /api/tools`, which
80
+ * must keep announcing the static disclosable universe (S3).
81
+ */
82
+ disclosure?: DisclosureOptions;
83
+ sandbox?: Sandbox;
84
+ /** Guard override: `undefined` = production guard, `null` = no guard. */
85
+ guard?: ToolGuard | null;
86
+ /** Disable worker orchestration wiring entirely. */
87
+ workers?: false;
88
+ /**
89
+ * W740: the liveness watchdog over this session's worker registry. Omitted =
90
+ * the environment decides (`compose()` reads it); `false` never mounts it.
91
+ * The sweep timer dies with the instance (the runtime's shutdown hook), so a
92
+ * reclaimed session leaks nothing.
93
+ */
94
+ watchdog?: Partial<WatchdogMountSettings> | false;
95
+ /** Worker receipt/report directory (default `<cwd>/worker-results`). */
96
+ resultsDir?: string;
97
+ /**
98
+ * E §2.3 P0 ①: the worker table this process writes. `undefined` = derive it
99
+ * (`CELESTEA_WORKER_REGISTRY`, else `<data dir>/worker-registry.tsv`);
100
+ * `null` = IN-MEMORY only, which stays a first-class option for tests and
101
+ * embedded hosts (B6 is about the DEFAULT, not about removing the choice).
102
+ */
103
+ workerRegistryPath?: string | null;
104
+ /** `<data dir>` — the default home of that table (see `worker-table.ts`). */
105
+ dataDir?: string | null;
106
+ /** Compact summarizer override (default: the `Llm` seam). */
107
+ summarize?: (profile: Profile) => Summarizer;
108
+ /**
109
+ * Session grants reader (W516 §4.2): read at every compose, so a grant or a
110
+ * revocation is visible at the session's next turn boundary and never inside
111
+ * a running turn. Absent = no grants at all (tests, embedded use).
112
+ */
113
+ grants?: SessionGrantsReader;
114
+ /**
115
+ * W728 §3 P0: the process-shared append-only usage ledger. Absent/null = this
116
+ * generation books nothing (tests, embedded use); the studio host creates ONE
117
+ * file per process (`<data dir>/usage-ledger.jsonl`) and every session
118
+ * instance books its own rows into it.
119
+ */
120
+ ledgerFile?: UsageLedgerFile | null;
121
+ /** Provider row id of the startup target, recorded as the ledger's `provider`. */
122
+ providerLabel?: string | null;
123
+ /**
124
+ * E §1.3 P0 ②: checkpoint sidecar wiring. A persistent session log is always
125
+ * checkpointed; this only overrides the process identity (`boot_id`/`pid`) and
126
+ * the clock, which tests pin to keep the written file deterministic.
127
+ */
128
+ checkpoint?: CheckpointWiring;
129
+ /**
130
+ * W787 (§5.2③): the audit channel of the recovery facts this process observes
131
+ * (a degraded session log). Absent = no audit line is written.
132
+ */
133
+ recoveryAudit?: RecoveryAuditWriter | null;
134
+ now?: () => number;
135
+ /**
136
+ * W783: the process-wide pending-question table. Present = every composed
137
+ * session offers `ask_user_question`; absent = no session does (tests and
138
+ * embeddings that have no human answerer).
139
+ */
140
+ questionRegistry?: QuestionRegistry | null;
141
+ /**
142
+ * W783: publish one parked question as a `question` SSE frame. The composer
143
+ * supplies it (it knows the session id and can reach the bus); absent = the
144
+ * frame is not emitted, which no production host wants.
145
+ */
146
+ publishQuestion?: (sessionId: string | null, question: PendingQuestion) => void;
147
+ /**
148
+ * W804: the configured input_modalities of one model id, or null when the
149
+ * model is unknown/unconfigured (=> optimistic default: image input allowed).
150
+ */
151
+ modelInputModalities?: (modelId: string) => readonly string[] | null;
152
+ /**
153
+ * W804 (section 7.6): a model rejected image input and the turn was downgraded
154
+ * to text + placeholders. The host turns this into the three visible channels
155
+ * (info block / statusline / audit).
156
+ */
157
+ onModelDowngrade?: (sessionId: string | null, info: ImageDowngradeInfo) => void;
158
+ }
159
+ /** Non-negative integer from the environment, else the frozen default. */
160
+ export declare function limitFromEnv(env: NodeJS.ProcessEnv, name: string, fallback: number): number;
161
+ /** Shut one instance down and close its log descriptor (order matters). */
162
+ export declare function disposeRuntime(runtime: Runtime): Promise<void>;
163
+ /** Wrap the registry caps into the ONE error the HTTP layer maps to 503. */
164
+ export declare function capacityErrorOf(e: unknown): unknown;
165
+ export declare class SessionComposer {
166
+ private readonly opts;
167
+ private readonly memoryLogs;
168
+ constructor(opts: SessionComposerOptions);
169
+ /** Compose one session generation (the registry's build factory). */
170
+ compose(sessionId: string | null, dir: string | null): Runtime;
171
+ /**
172
+ * W783: the user-question wiring of ONE session generation, or null when the
173
+ * host mounted no table (the tool is then not offered to the model at all).
174
+ *
175
+ * The bus is the SESSION's own (`compose()` provides it), filled in by the
176
+ * plugin body: an answerer chain per generation is what stops a question asked
177
+ * in one session from being answered into another.
178
+ */
179
+ private questionWiring;
180
+ /**
181
+ * The session's ledger, or null when the host did not wire one. The session
182
+ * label is the file's self-description (`<workspace>/<session>`, §3.2.1).
183
+ *
184
+ * W878: `compose(sessionId, dir)` already carries the trusted id, so the label
185
+ * uses it directly and only falls back to `HOST_SESSION_ID` when the id is
186
+ * null (the detached generation). `dir` is kept in the signature for the
187
+ * caller but is deliberately no longer a source of the label.
188
+ */
189
+ private usageLedger;
190
+ /**
191
+ * E §4 P1 (W785): the ONE place a composed generation decides which `Llm` it
192
+ * runs on. Fallback OFF (or unwired) = the W728 path unchanged (ledger wrapper
193
+ * around the raw seam). Fallback ON = the decorator, which books one ledger
194
+ * row per ATTEMPT and hands the switch to the next target.
195
+ */
196
+ private engineLlm;
197
+ /**
198
+ * W728 §3 P0: wrap the engine `Llm` so every model step books one ledger row
199
+ * (success, failure and retry alike). The wrapper lives in the composed
200
+ * Context, so worker-driven calls go through it as well; the summarizer path
201
+ * is separate (`summarizer()`, a P1 concern).
202
+ */
203
+ private stepObservedLlm;
204
+ /**
205
+ * Base profile + the session's own `session.json` overrides (model AND, since
206
+ * W729, the mode-dependent system prompt). This is the ONE place a session's
207
+ * instance profile is decided, so two sessions in the same process can differ
208
+ * in prompt without either one seeing the other's.
209
+ */
210
+ profileFor(sessionId: string | null): Profile;
211
+ /** The session's profile overrides; `null` when it declares none. */
212
+ private sessionOverrides;
213
+ /** The compact summarizer of the CURRENT base profile. */
214
+ summarizer(): Summarizer;
215
+ /**
216
+ * Worker wiring: ONE registry per session instance (W513, design D7), so a
217
+ * receipt returns to the session that spawned the worker and `worker:<sid>`
218
+ * ids stay unique through the session-derived prefix. W787: the table is
219
+ * PERSISTED by default at `workerRegistryPath()` (`<data dir>/
220
+ * worker-registry.tsv`; `CELESTEA_WORKER_REGISTRY` overrides). Only an
221
+ * explicit `tsvPath: null` is an in-memory table, so the default no
222
+ * longer collides the host with the fleet's shared `/tmp/registry.tsv`.
223
+ */
224
+ private workerWiring;
225
+ /** E §2.3 P0 ①: the configured table path (see `worker-table.ts` for the rules). */
226
+ private workerRegistryPath;
227
+ private llmFactory;
228
+ private bindingTo;
229
+ /**
230
+ * E §1.3 P1 ③: a session log that refused a write is reported to the audit
231
+ * channel (the sidecar already carries the sticky counter, §1.2.2). One line
232
+ * per session instance — the store fires this at most once.
233
+ */
234
+ private noteDegraded;
235
+ }