@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,132 @@
1
+ /**
2
+ * The OFFLINE engine LLM — a deterministic, in-process `Llm` seam implementation.
3
+ *
4
+ * Purpose: the TS host must be testable and replayable with ZERO network access
5
+ * (`禁真网`). This adapter answers every `generate()` from a local script, so a
6
+ * real turn (real `DefaultAgentLoop`, real `ToolRegistry`, real session log)
7
+ * runs end to end without a provider.
8
+ *
9
+ * It is not a stub that fakes the loop: the loop, the tool dispatch, the log and
10
+ * the SSE frames are the production ones. Only the model is local.
11
+ *
12
+ * Two behaviours:
13
+ * - a normal turn answers with the next scripted step (text / thinking /
14
+ * tool calls / usage), defaulting to a deterministic `echo:` reply;
15
+ * - a COMPACTION request (the four-section summarizer prompt) answers with a
16
+ * deterministic four-section summary derived from the transcript, so a
17
+ * compaction is reproducible byte for byte across runs.
18
+ *
19
+ * `deltaMs` interleaves real delays between frames: a test can abort mid-stream
20
+ * and observe the cooperative cancel path instead of a turn that already
21
+ * finished.
22
+ */
23
+ import { createHash } from "node:crypto";
24
+ import { assistantText, } from "@celestea/core";
25
+ import { COMPACT_SYSTEM_PROMPT } from "@celestea/runtime";
26
+ /** Text of the last user message (the echo source / summary input). */
27
+ function lastUserText(req) {
28
+ for (let i = req.messages.length - 1; i >= 0; i--) {
29
+ const m = req.messages[i];
30
+ if (m === undefined || m.role !== "user")
31
+ continue;
32
+ // W804: an image-only message still echoes something (never an empty string).
33
+ return m.content.map((c) => (c.type === "text" ? c.content : c.type === "image" ? `[图片:${c.content.media_type}]` : "")).join("");
34
+ }
35
+ return "";
36
+ }
37
+ /** Deterministic usage so `cache_hit_ratio` / `context_usage` are assertable. */
38
+ export function offlineUsage(req, answer) {
39
+ let chars = req.system?.length ?? 0;
40
+ for (const m of req.messages) {
41
+ for (const c of m.content) {
42
+ // W804: an image block must not estimate as 0 chars (context view accuracy).
43
+ if (c.type === "text")
44
+ chars += c.content.length;
45
+ else if (c.type === "image")
46
+ chars += 300;
47
+ }
48
+ }
49
+ const prompt = Math.ceil(chars / 4);
50
+ const completion = Math.max(1, Math.ceil(answer.length / 4));
51
+ return { prompt_tokens: prompt, completion_tokens: completion, total_tokens: prompt + completion, cache_read: Math.floor(prompt / 2), reasoning_tokens: 0 };
52
+ }
53
+ /** True when the request is the compaction summarizer (not a chat turn). */
54
+ export function isCompactionRequest(req) {
55
+ return req.system !== null && req.system.includes(COMPACT_SYSTEM_PROMPT.slice(0, 12));
56
+ }
57
+ /** The default offline summary: four sections + a stable digest of the input. */
58
+ export function offlineSummary(transcript) {
59
+ const digest = createHash("sha256").update(transcript, "utf8").digest("hex").slice(0, 12);
60
+ const last = transcript.trimEnd().split("\n").slice(-1)[0] ?? "";
61
+ return [
62
+ "1) 正在进行的任务:离线确定性摘要(mock LLM),输入摘要见下方 digest。",
63
+ "2) 已做的决策:结构由 compact 计划固定(摘要轮 + 最近 K 轮),摘要正文由本 mock 生成。",
64
+ `3) 关键事实与文件改动:transcript ${transcript.length} 字符,digest ${digest}。`,
65
+ `4) 待办:最近一行记录:${last.slice(0, 200)}`,
66
+ ].join("\n");
67
+ }
68
+ /** Split text into fixed-size chunks (never splitting a code point). */
69
+ export function chunkText(text, size) {
70
+ const chars = [...text];
71
+ const out = [];
72
+ for (let i = 0; i < chars.length; i += size)
73
+ out.push(chars.slice(i, i + size).join(""));
74
+ return out.length === 0 ? [""] : out;
75
+ }
76
+ /** Assistant message of a step (tool calls win over text, like the provider). */
77
+ export function stepMessage(step) {
78
+ const calls = step.tool_calls ?? [];
79
+ if (calls.length === 0)
80
+ return assistantText(step.text ?? "");
81
+ return { role: "assistant", content: calls.map((c) => ({ type: "tool_call", content: c })), tool_call_id: null };
82
+ }
83
+ /** The frame list of one scripted step (before timing is applied). */
84
+ export function stepFrames(step, req, chunk) {
85
+ const frames = [];
86
+ if (step.fail !== undefined)
87
+ return [{ kind: "failed", kindOf: "generate", message: step.fail }];
88
+ if (step.interrupted === true)
89
+ return [{ kind: "interrupted" }];
90
+ const thinking = step.thinking ?? "";
91
+ if (thinking !== "")
92
+ for (const piece of chunkText(thinking, chunk))
93
+ frames.push({ kind: "thinking", text: piece });
94
+ const text = step.text ?? "";
95
+ if (text !== "")
96
+ for (const piece of chunkText(text, chunk))
97
+ frames.push({ kind: "text", text: piece });
98
+ frames.push({ kind: "usage", usage: offlineUsage(req, text) });
99
+ frames.push({ kind: "done", message: stepMessage(step) });
100
+ return frames;
101
+ }
102
+ /** Yield frames, optionally with a delay between them. */
103
+ async function* emit(frames, delayMs) {
104
+ for (const frame of frames) {
105
+ if (delayMs > 0)
106
+ await new Promise((r) => setTimeout(r, delayMs));
107
+ yield frame;
108
+ }
109
+ }
110
+ /** Build the deterministic offline engine LLM. */
111
+ export function createOfflineLlm(opts = {}) {
112
+ const chunk = Math.max(1, opts.chunkChars ?? 24);
113
+ const delayMs = Math.max(0, opts.deltaMs ?? 0);
114
+ const summary = opts.summary ?? offlineSummary;
115
+ const script = opts.script ?? [];
116
+ let calls = 0;
117
+ const stepFor = (req) => {
118
+ if (isCompactionRequest(req))
119
+ return { text: summary(lastUserText(req)) };
120
+ return script.shift() ?? { text: `echo: ${lastUserText(req)}` };
121
+ };
122
+ return {
123
+ get calls() {
124
+ return calls;
125
+ },
126
+ generate(req) {
127
+ calls += 1;
128
+ opts.onRequest?.(req);
129
+ return Promise.resolve(emit(stepFrames(stepFor(req), req, chunk), delayMs));
130
+ },
131
+ };
132
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * W747 compat shim — this module moved to the runtime's host layer:
3
+ * `packages/runtime/src/host/provider-target.ts` (exported as `@celestea/runtime`).
4
+ *
5
+ * The rules, the export names and the error text are unchanged; a caller migrates
6
+ * by deleting its import, not by editing the engine. `ProfileSlot` (the minimal
7
+ * structural profile slice, added by the move) is deliberately NOT re-exported
8
+ * here: this path's export surface stays exactly what it was.
9
+ */
10
+ export { CHAT_COMPLETIONS_FORMAT, applyProviderTarget, listedModelIds, ownerOf, resolveBaseUrl, resolveModel, resolveProviderKey, resolveProviderTarget, type ProviderKeySource as KeySource, type ModelSource, type ProviderLookup, type ProviderRef, type ProviderTarget, } from "@celestea/runtime";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * W747 compat shim — this module moved to the runtime's host layer:
3
+ * `packages/runtime/src/host/provider-target.ts` (exported as `@celestea/runtime`).
4
+ *
5
+ * The rules, the export names and the error text are unchanged; a caller migrates
6
+ * by deleting its import, not by editing the engine. `ProfileSlot` (the minimal
7
+ * structural profile slice, added by the move) is deliberately NOT re-exported
8
+ * here: this path's export surface stays exactly what it was.
9
+ */
10
+ export { CHAT_COMPLETIONS_FORMAT, applyProviderTarget, listedModelIds, ownerOf, resolveBaseUrl, resolveModel, resolveProviderKey, resolveProviderTarget, } from "@celestea/runtime";
@@ -0,0 +1,33 @@
1
+ /**
2
+ * W783 — the host's user-question capability in ONE object.
3
+ *
4
+ * Three things have to exist together and must never drift apart:
5
+ * - the process-wide [QuestionRegistry] (so `POST /api/questions/{id}/answer`
6
+ * can find a request from its id alone, whichever session asked);
7
+ * - the [QuestionView] that publishes a parked question on the session's SSE
8
+ * bus and renders the §7 recovery list;
9
+ * - the wiring the composer hands to each session generation.
10
+ *
11
+ * Bundling them keeps `real-runtime-adapter.ts` — the engine seam, which has a
12
+ * hard size budget and should stay about the engine — down to a handle and three
13
+ * one-line delegations. The bus and the turn numbering are reached through
14
+ * callbacks, so the adapter stays the only thing that knows how to reach them.
15
+ */
16
+ import type { AskUserQuestionAnswerItem } from "@celestea/core";
17
+ import { type PendingQuestion, type QuestionRegistry } from "../question-registry.js";
18
+ import type { PendingQuestionView, QuestionAnswerOutcome } from "../runtime-adapter.js";
19
+ import { type QuestionPublisher } from "./question-view.js";
20
+ /** The host's question capability: table + view, created once per process. */
21
+ export declare class QuestionHost {
22
+ private readonly registry;
23
+ private readonly view;
24
+ constructor(publisher: QuestionPublisher);
25
+ /** The table (what the composer wires each session's service to). */
26
+ table(): QuestionRegistry;
27
+ /** Publish one parked question (§9 item 9). */
28
+ publish(sessionId: string | null, question: PendingQuestion): void;
29
+ /** Answer one pending question (`POST /api/questions/{id}/answer`). */
30
+ answer(requestId: string, answers: AskUserQuestionAnswerItem[], sessionId?: string): QuestionAnswerOutcome;
31
+ /** Every question still answerable (`GET /api/questions`, §7 recovery). */
32
+ list(sessionId?: string | null): PendingQuestionView[];
33
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * W783 — the host's user-question capability in ONE object.
3
+ *
4
+ * Three things have to exist together and must never drift apart:
5
+ * - the process-wide [QuestionRegistry] (so `POST /api/questions/{id}/answer`
6
+ * can find a request from its id alone, whichever session asked);
7
+ * - the [QuestionView] that publishes a parked question on the session's SSE
8
+ * bus and renders the §7 recovery list;
9
+ * - the wiring the composer hands to each session generation.
10
+ *
11
+ * Bundling them keeps `real-runtime-adapter.ts` — the engine seam, which has a
12
+ * hard size budget and should stay about the engine — down to a handle and three
13
+ * one-line delegations. The bus and the turn numbering are reached through
14
+ * callbacks, so the adapter stays the only thing that knows how to reach them.
15
+ */
16
+ import { createQuestionRegistry } from "../question-registry.js";
17
+ import { QuestionView } from "./question-view.js";
18
+ /** The host's question capability: table + view, created once per process. */
19
+ export class QuestionHost {
20
+ registry = createQuestionRegistry();
21
+ view;
22
+ constructor(publisher) {
23
+ this.view = new QuestionView(this.registry, publisher);
24
+ }
25
+ /** The table (what the composer wires each session's service to). */
26
+ table() {
27
+ return this.registry;
28
+ }
29
+ /** Publish one parked question (§9 item 9). */
30
+ publish(sessionId, question) {
31
+ this.view.publish(sessionId, question);
32
+ }
33
+ /** Answer one pending question (`POST /api/questions/{id}/answer`). */
34
+ answer(requestId, answers, sessionId) {
35
+ return this.view.answer(requestId, answers, sessionId);
36
+ }
37
+ /** Every question still answerable (`GET /api/questions`, §7 recovery). */
38
+ list(sessionId) {
39
+ return this.view.list(sessionId);
40
+ }
41
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * W783 — the host-side view of the pending-question table.
3
+ *
4
+ * This module is what the HTTP layer talks to: it publishes parked questions on
5
+ * the session's SSE bus, resolves them when the human answers, and renders the
6
+ * §7 recovery list. Keeping it out of `real-runtime-adapter.ts` is not cosmetic —
7
+ * that file is the engine seam and has a hard size budget; more importantly, none
8
+ * of this needs the engine, only the table plus a way to reach the bus.
9
+ *
10
+ * The one thing it deliberately does NOT do is write the `user_answer` log row:
11
+ * the session's own service owns that, because it is the single place that
12
+ * observes EVERY settlement (a real answer and a §6.3 timeout alike).
13
+ */
14
+ import { type TurnFrame } from "@celestea/runtime";
15
+ import type { AskUserQuestionAnswerItem } from "@celestea/core";
16
+ import type { PendingQuestion, QuestionRegistry } from "../question-registry.js";
17
+ import type { PendingQuestionView, QuestionAnswerOutcome } from "../runtime-adapter.js";
18
+ /** Where a question frame goes and which turn number it belongs to. */
19
+ export interface QuestionPublisher {
20
+ /** Emit one `question` SSE frame for `sessionId` (null = detached). */
21
+ emit: (sessionId: string | null, turn: number, frame: TurnFrame) => void;
22
+ /** The session-local turn number currently running, or 0 when idle. */
23
+ turnOf: (sessionId: string | null) => number;
24
+ }
25
+ /** The pending-question table as the host sees it. */
26
+ export declare class QuestionView {
27
+ private readonly registry;
28
+ private readonly publisher;
29
+ constructor(registry: QuestionRegistry, publisher: QuestionPublisher);
30
+ /** Publish one parked question (§9 item 9) — deadline and timeout included. */
31
+ publish(sessionId: string | null, question: PendingQuestion): void;
32
+ /**
33
+ * Answer one pending question (`POST /api/questions/{id}/answer`). Resolving
34
+ * the parked promise is what wakes the tool call — no message is injected
35
+ * (§4.2), because a message would only be steering the parked turn cannot drain.
36
+ *
37
+ * A refusal is a first-class answer, never a silent success: `unknown` and
38
+ * `mismatch` are caller errors, while `timed_out`/`settled` report the §6.2
39
+ * race in whichever direction it actually resolved.
40
+ */
41
+ answer(requestId: string, answers: AskUserQuestionAnswerItem[], sessionId?: string): QuestionAnswerOutcome;
42
+ /**
43
+ * Every question still answerable, with the deadline judged AT READ TIME
44
+ * (§6.1) so a reconnecting client rebuilds the card and its countdown without
45
+ * trusting its own clock.
46
+ */
47
+ list(sessionId?: string | null, now?: number): PendingQuestionView[];
48
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * W783 — the host-side view of the pending-question table.
3
+ *
4
+ * This module is what the HTTP layer talks to: it publishes parked questions on
5
+ * the session's SSE bus, resolves them when the human answers, and renders the
6
+ * §7 recovery list. Keeping it out of `real-runtime-adapter.ts` is not cosmetic —
7
+ * that file is the engine seam and has a hard size budget; more importantly, none
8
+ * of this needs the engine, only the table plus a way to reach the bus.
9
+ *
10
+ * The one thing it deliberately does NOT do is write the `user_answer` log row:
11
+ * the session's own service owns that, because it is the single place that
12
+ * observes EVERY settlement (a real answer and a §6.3 timeout alike).
13
+ */
14
+ import { questionFrame } from "@celestea/runtime";
15
+ /** The pending-question table as the host sees it. */
16
+ export class QuestionView {
17
+ registry;
18
+ publisher;
19
+ constructor(registry, publisher) {
20
+ this.registry = registry;
21
+ this.publisher = publisher;
22
+ }
23
+ /** Publish one parked question (§9 item 9) — deadline and timeout included. */
24
+ publish(sessionId, question) {
25
+ const frame = questionFrame({
26
+ session: sessionId,
27
+ id: question.requestId,
28
+ questions: question.questions,
29
+ expiresAt: question.expiresAt,
30
+ timeoutMs: question.timeoutMs,
31
+ });
32
+ this.publisher.emit(sessionId, this.publisher.turnOf(sessionId), frame);
33
+ }
34
+ /**
35
+ * Answer one pending question (`POST /api/questions/{id}/answer`). Resolving
36
+ * the parked promise is what wakes the tool call — no message is injected
37
+ * (§4.2), because a message would only be steering the parked turn cannot drain.
38
+ *
39
+ * A refusal is a first-class answer, never a silent success: `unknown` and
40
+ * `mismatch` are caller errors, while `timed_out`/`settled` report the §6.2
41
+ * race in whichever direction it actually resolved.
42
+ */
43
+ answer(requestId, answers, sessionId) {
44
+ const question = this.registry.get(requestId);
45
+ if (question === undefined)
46
+ return { ok: false, reason: "unknown" };
47
+ if (sessionId !== undefined && question.sessionId !== sessionId)
48
+ return { ok: false, reason: "mismatch" };
49
+ if (question.isSettled) {
50
+ return { ok: false, reason: question.settlement === "timed_out" ? "timed_out" : "settled" };
51
+ }
52
+ const session = question.sessionId;
53
+ question.answer(answers);
54
+ this.registry.remove(requestId);
55
+ return { ok: true, session };
56
+ }
57
+ /**
58
+ * Every question still answerable, with the deadline judged AT READ TIME
59
+ * (§6.1) so a reconnecting client rebuilds the card and its countdown without
60
+ * trusting its own clock.
61
+ */
62
+ list(sessionId, now = Date.now()) {
63
+ return this.registry
64
+ .all()
65
+ .filter((question) => sessionId === undefined || question.sessionId === sessionId)
66
+ .map((question) => ({
67
+ id: question.requestId,
68
+ session: question.sessionId,
69
+ questions: question.questions,
70
+ expires_at: question.expiresAt,
71
+ timeout_ms: question.timeoutMs,
72
+ remaining_ms: question.remainingMs(now),
73
+ expired: question.isExpired(now),
74
+ }));
75
+ }
76
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The REAL `RuntimeAdapter` — `packages/runtime` mounted behind the engine seam.
3
+ *
4
+ * W513 shape: this class is a HOST over a [SessionRuntimeRegistry], not a single
5
+ * engine. Every session gets its own composition (own agent loop, own tool
6
+ * registry, own status/usage trackers, own worker registry, own session log,
7
+ * own inbox) created lazily on first use, reused while the profile epoch is
8
+ * current, and reclaimed when idle. There is no "global main session":
9
+ * `workspaces.json.active_session` is only the view the UI should restore.
10
+ *
11
+ * W742 lifecycle (both were documented before but not wired):
12
+ * - an epoch bump (POST /api/config, POST /api/providers/default, a grant
13
+ * write) NEVER tears down an instance that is still driving workers: the
14
+ * registry only marks it and rebuilds it once those workers ended, so a
15
+ * model switch can no longer abort a background worker and erase its rows
16
+ * (the HTTP 409 guards of both endpoints close the same hole up front);
17
+ * - `CELESTEA_SESSION_IDLE_TTL_MS` is real: the registry's unref'ed reclaimer
18
+ * sweeps deferrable rebuilds + the idle TTL in the background, and
19
+ * `shutdown()` disarms it (no timer outlives the engine).
20
+ *
21
+ * Mapping (host HTTP surface -> composition / engine):
22
+ * POST /api/turn -> `startTurn` (idle) or `inject` (busy);
23
+ * GET /api/events -> `attach(bus)`, frames carry the session;
24
+ * POST /api/sessions/{id}/activate-> `ensureSession` (never 409);
25
+ * POST /api/cancel -> the target session's AbortController;
26
+ * POST /api/sessions/batch-delete -> `releaseSession` (W794: abort + drop the
27
+ * instance before the directory moves);
28
+ * POST /api/clear -> the target session log's `clear()`;
29
+ * POST /api/sessions/{id}/compact -> `runCompaction` + that instance rebuilt;
30
+ * GET /api/status -> the requested session's statusline;
31
+ * GET /api/tools -> the composed `ToolRegistry.schemas()`;
32
+ * GET+POST /api/config -> bump the profile epoch (lazy rebuild);
33
+ * worker endpoints -> per-session registries, merged for reads.
34
+ *
35
+ * The per-session turn/state machinery lives in the registry and the composition
36
+ * in `session-compose.ts`; this file is the seam implementation the handlers see.
37
+ *
38
+ * LLM: an injected seam wins (tests / replay inject the OFFLINE deterministic
39
+ * engine, so no test, no contract check and no replay reaches the network); with
40
+ * nothing injected each instance is assembled against the profile's provider
41
+ * (`llm-assembly.ts`), i.e. production is a real model.
42
+ */
43
+ import type { TurnOutcome } from "@celestea/core";
44
+ import type { Watchdog, WorkerRegistry } from "@celestea/workers";
45
+ import type { EngineProfile, RuntimeAdapter } from "../runtime-adapter.js";
46
+ import { type SessionComposerOptions } from "./session-compose.js";
47
+ export { SESSION_LOG_ID, SESSION_LOG_NAME, type SessionTarget } from "./engine-session.js";
48
+ export { MAX_CONCURRENT_TURNS, MAX_LIVE_SESSIONS, SESSION_IDLE_TTL_MS } from "./session-compose.js";
49
+ export { RELEASE_SETTLE_MS } from "./session-release.js";
50
+ /** Everything the composer needs, plus the resource caps. */
51
+ export interface RealRuntimeAdapterOptions extends Omit<SessionComposerOptions, "env" | "baseProfile"> {
52
+ /** Startup engine profile (see [defaultEngineProfile]). */
53
+ profile?: EngineProfile;
54
+ /** Process environment (provider keys, tool roots, resource caps). */
55
+ env?: NodeJS.ProcessEnv;
56
+ /** Live-instance cap (default [MAX_LIVE_SESSIONS] / `CELESTEA_MAX_LIVE_SESSIONS`). */
57
+ maxLiveSessions?: number;
58
+ /** Concurrent-turn cap (default [MAX_CONCURRENT_TURNS]). */
59
+ maxConcurrentTurns?: number;
60
+ /** Idle TTL for the reclaimer (default [SESSION_IDLE_TTL_MS]). */
61
+ idleTtlMs?: number;
62
+ /**
63
+ * `<data dir>` — where `fallbacks.json` / `fallbacks-audit.jsonl` live, and the
64
+ * default home of the worker table (`<data dir>/worker-registry.tsv`, §2.2.1).
65
+ * Defaults to the ledger's directory (all three are process-level data files).
66
+ */
67
+ dataDir?: string;
68
+ }
69
+ /** `RuntimeAdapter` + the lifecycle handles the host needs beyond the seam. */
70
+ export interface RealRuntimeAdapter extends RuntimeAdapter {
71
+ /** Epoch of the current profile generation (bumped by every configure). */
72
+ generationEpoch(): number;
73
+ /** Absolute path of the default (detached) session log (null = in memory). */
74
+ sessionLogPath(): string | null;
75
+ /** Set the engine system prompt used by the next composed generation. */
76
+ primeSystemPrompt(prompt: string): void;
77
+ /** The last terminal turn state over every session (diagnostics / tests). */
78
+ lastTurnOutcome(): TurnOutcome | null;
79
+ /**
80
+ * W740: the liveness watchdog of the session's instance (null when the
81
+ * watchdog is off). The timer is scheduled by the composition root; this
82
+ * handle is how the host inspects or hand-ticks it.
83
+ */
84
+ watchdog(session?: string | null): Watchdog | null;
85
+ /** W794: how many auto-wake loops are mounted (diagnostics / tests). */
86
+ autowakeLoops(): number;
87
+ /** Is this session's sweep timer running? (no instance = false.) */
88
+ watchdogRunning(session?: string | null): boolean;
89
+ /** The session's live worker registry, or null when it has no instance. */
90
+ workersOf(session?: string | null): WorkerRegistry | null;
91
+ /** Tear every live instance down (idempotent). */
92
+ shutdown(): Promise<void>;
93
+ }
94
+ /** Build the real adapter (the host's default engine). */
95
+ export declare function createRealRuntimeAdapter(opts?: RealRuntimeAdapterOptions): RealRuntimeAdapter;