@celestea/core 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.
- package/LICENSE +21 -0
- package/README.md +95 -0
- package/contracts/data-files/checkpoint.schema.json +111 -0
- package/contracts/data-files/cli-main-jsonl-precompact.schema.json +27 -0
- package/contracts/data-files/cli-main-jsonl.schema.json +22 -0
- package/contracts/data-files/fallbacks.schema.json +71 -0
- package/contracts/data-files/index.json +124 -0
- package/contracts/data-files/pricing.schema.json +65 -0
- package/contracts/data-files/prompts.schema.json +130 -0
- package/contracts/data-files/providers.schema.json +177 -0
- package/contracts/data-files/registry-tsv.schema.json +74 -0
- package/contracts/data-files/session.schema.json +51 -0
- package/contracts/data-files/usage-ledger.schema.json +112 -0
- package/contracts/data-files/workspaces.schema.json +63 -0
- package/contracts/endpoints.json +4390 -0
- package/contracts/probe-evidence.json +219 -0
- package/contracts/route-table.snapshot.json +377 -0
- package/contracts/scope-hash-vectors.json +273 -0
- package/contracts/session-event.schema.json +441 -0
- package/contracts/sse-events.json +202 -0
- package/contracts/tools.json +730 -0
- package/dist/agent.d.ts +65 -0
- package/dist/agent.js +36 -0
- package/dist/celestea-home.d.ts +63 -0
- package/dist/celestea-home.js +96 -0
- package/dist/celestea-sources.d.ts +53 -0
- package/dist/celestea-sources.js +61 -0
- package/dist/context.d.ts +33 -0
- package/dist/context.js +55 -0
- package/dist/contracts/index.d.ts +234 -0
- package/dist/contracts/index.js +159 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.js +22 -0
- package/dist/event-bus.d.ts +60 -0
- package/dist/event-bus.js +100 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +66 -0
- package/dist/injection.d.ts +61 -0
- package/dist/injection.js +27 -0
- package/dist/json.d.ts +34 -0
- package/dist/json.js +127 -0
- package/dist/llm.d.ts +34 -0
- package/dist/llm.js +41 -0
- package/dist/memory.d.ts +72 -0
- package/dist/memory.js +123 -0
- package/dist/message.d.ts +189 -0
- package/dist/message.js +252 -0
- package/dist/plugin.d.ts +38 -0
- package/dist/plugin.js +49 -0
- package/dist/projection.d.ts +67 -0
- package/dist/projection.js +168 -0
- package/dist/question.d.ts +154 -0
- package/dist/question.js +82 -0
- package/dist/redact.d.ts +40 -0
- package/dist/redact.js +185 -0
- package/dist/repo.d.ts +14 -0
- package/dist/repo.js +87 -0
- package/dist/sandbox.d.ts +182 -0
- package/dist/sandbox.js +78 -0
- package/dist/session-event.d.ts +57 -0
- package/dist/session-event.js +425 -0
- package/dist/session-log.d.ts +71 -0
- package/dist/session-log.js +66 -0
- package/dist/skill-catalog.d.ts +29 -0
- package/dist/skill-catalog.js +52 -0
- package/dist/skills.d.ts +116 -0
- package/dist/skills.js +273 -0
- package/dist/sse-bus.d.ts +40 -0
- package/dist/sse-bus.js +105 -0
- package/dist/stream.d.ts +115 -0
- package/dist/stream.js +52 -0
- package/dist/tool-surface.d.ts +45 -0
- package/dist/tool-surface.js +98 -0
- package/dist/tool.d.ts +77 -0
- package/dist/tool.js +15 -0
- package/dist/turn-id.d.ts +37 -0
- package/dist/turn-id.js +76 -0
- package/dist/types.d.ts +396 -0
- package/dist/types.js +58 -0
- package/package.json +27 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://celestea.top/contracts/session-event.schema.json",
|
|
4
|
+
"title": "Celestea engine SessionEvent (cli-main.jsonl v1, one JSON object per line)",
|
|
5
|
+
"generatedAt": "2026-09-10T01:20:00Z",
|
|
6
|
+
"source": {
|
|
7
|
+
"legacy": "frozen from the retired implementation (original source file deleted 2026-09-15): /src/celestea_harness/crates/core/src/session_log.rs:44-85 (serde tag=\"type\", rename_all=snake_case)",
|
|
8
|
+
"doc": "docs/data-files.md §4"
|
|
9
|
+
},
|
|
10
|
+
"encoding": {
|
|
11
|
+
"file": "<session-dir>/cli-main.jsonl",
|
|
12
|
+
"format": "JSONL: one SessionEvent per line, UTF-8, no trailing comma",
|
|
13
|
+
"parse": "skip blank lines; STOP at the first unparsable line (a torn tail is never treated as content) - src/api.rs:141-153"
|
|
14
|
+
},
|
|
15
|
+
"turnId": {
|
|
16
|
+
"pattern": "^turn-\\d+$",
|
|
17
|
+
"monotonic": true,
|
|
18
|
+
"rule": "next_turn_number only recognises the 'turn-<n>' prefix and restores its counter from the max replayed id, so ids never repeat across restarts (session_log.rs:94-98)",
|
|
19
|
+
"compactRewrites": "/compact renumbers the kept turns as turn-1..turn-(K+1); the next live turn is turn-(K+2)"
|
|
20
|
+
},
|
|
21
|
+
"$defs": {
|
|
22
|
+
"AttachmentRef": {
|
|
23
|
+
"title": "AttachmentRef (W804 multimodal P0)",
|
|
24
|
+
"type": "object",
|
|
25
|
+
"required": [
|
|
26
|
+
"attachment_id",
|
|
27
|
+
"media_type",
|
|
28
|
+
"width",
|
|
29
|
+
"height"
|
|
30
|
+
],
|
|
31
|
+
"properties": {
|
|
32
|
+
"attachment_id": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
35
|
+
},
|
|
36
|
+
"media_type": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"enum": [
|
|
39
|
+
"image/png",
|
|
40
|
+
"image/jpeg",
|
|
41
|
+
"image/webp",
|
|
42
|
+
"image/gif"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"width": {
|
|
46
|
+
"type": "integer",
|
|
47
|
+
"minimum": 1
|
|
48
|
+
},
|
|
49
|
+
"height": {
|
|
50
|
+
"type": "integer",
|
|
51
|
+
"minimum": 1
|
|
52
|
+
},
|
|
53
|
+
"name": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
},
|
|
56
|
+
"original": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"description": "present only when the stored bytes are a downscaled/re-encoded variant (P0 never writes it)"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"additionalProperties": false,
|
|
62
|
+
"note": "A CONTENT-ADDRESSED REFERENCE, never bytes: the object lives at <session-dir>/attachments/<attachment_id>.<ext> and the log stores only this shape (no base64 is ever written to cli-main.jsonl)."
|
|
63
|
+
},
|
|
64
|
+
"TurnOutcome": {
|
|
65
|
+
"title": "TurnOutcome (5 terminal states)",
|
|
66
|
+
"oneOf": [
|
|
67
|
+
{
|
|
68
|
+
"const": "completed",
|
|
69
|
+
"description": "normal end: the model answered without further tool calls"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"const": "cancelled",
|
|
73
|
+
"description": "cooperative cancel fired; partial output/tool results stay in the log"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"type": "object",
|
|
77
|
+
"required": [
|
|
78
|
+
"error"
|
|
79
|
+
],
|
|
80
|
+
"properties": {
|
|
81
|
+
"error": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"required": [
|
|
84
|
+
"kind",
|
|
85
|
+
"message"
|
|
86
|
+
],
|
|
87
|
+
"properties": {
|
|
88
|
+
"kind": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"enum": [
|
|
91
|
+
"generate",
|
|
92
|
+
"stream"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"message": {
|
|
96
|
+
"type": "string"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"additionalProperties": false
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"additionalProperties": false,
|
|
103
|
+
"description": "LLM stream or generation failure"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"const": "step_limit",
|
|
107
|
+
"description": "max_steps exhausted without a final assistant answer (NOT completion)"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"const": "interrupted",
|
|
111
|
+
"description": "the stream ended without a terminal frame"
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"serdeShape": {
|
|
115
|
+
"completed": "\"completed\"",
|
|
116
|
+
"cancelled": "\"cancelled\"",
|
|
117
|
+
"error": "{\"error\":{\"kind\":\"generate\",\"message\":\"...\"}}",
|
|
118
|
+
"step_limit": "\"step_limit\"",
|
|
119
|
+
"interrupted": "\"interrupted\""
|
|
120
|
+
},
|
|
121
|
+
"legacyDefault": "a turn_end row WITHOUT an outcome field deserializes as \"completed\" (#[serde(default)])"
|
|
122
|
+
},
|
|
123
|
+
"SessionEvent": {
|
|
124
|
+
"oneOf": [
|
|
125
|
+
{
|
|
126
|
+
"title": "turn_start",
|
|
127
|
+
"type": "object",
|
|
128
|
+
"required": [
|
|
129
|
+
"type",
|
|
130
|
+
"id"
|
|
131
|
+
],
|
|
132
|
+
"properties": {
|
|
133
|
+
"type": {
|
|
134
|
+
"const": "turn_start"
|
|
135
|
+
},
|
|
136
|
+
"id": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"pattern": "^turn-\\d+$"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"additionalProperties": true
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"title": "turn_end",
|
|
145
|
+
"type": "object",
|
|
146
|
+
"required": [
|
|
147
|
+
"type",
|
|
148
|
+
"id"
|
|
149
|
+
],
|
|
150
|
+
"properties": {
|
|
151
|
+
"type": {
|
|
152
|
+
"const": "turn_end"
|
|
153
|
+
},
|
|
154
|
+
"id": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"pattern": "^turn-\\d+$"
|
|
157
|
+
},
|
|
158
|
+
"outcome": {
|
|
159
|
+
"$ref": "#/$defs/TurnOutcome"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"additionalProperties": true
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"title": "user_message",
|
|
166
|
+
"type": "object",
|
|
167
|
+
"required": [
|
|
168
|
+
"type",
|
|
169
|
+
"text"
|
|
170
|
+
],
|
|
171
|
+
"properties": {
|
|
172
|
+
"type": {
|
|
173
|
+
"const": "user_message"
|
|
174
|
+
},
|
|
175
|
+
"text": {
|
|
176
|
+
"type": "string"
|
|
177
|
+
},
|
|
178
|
+
"attachments": {
|
|
179
|
+
"type": "array",
|
|
180
|
+
"items": {
|
|
181
|
+
"$ref": "#/$defs/AttachmentRef"
|
|
182
|
+
},
|
|
183
|
+
"description": "W804: optional image references; omitted entirely when there are none (a legacy row is byte-identical)"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"additionalProperties": true
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"title": "assistant_message",
|
|
190
|
+
"type": "object",
|
|
191
|
+
"required": [
|
|
192
|
+
"type",
|
|
193
|
+
"text"
|
|
194
|
+
],
|
|
195
|
+
"properties": {
|
|
196
|
+
"type": {
|
|
197
|
+
"const": "assistant_message"
|
|
198
|
+
},
|
|
199
|
+
"text": {
|
|
200
|
+
"type": "string"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"additionalProperties": true
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"title": "thinking_delta",
|
|
207
|
+
"type": "object",
|
|
208
|
+
"required": [
|
|
209
|
+
"type",
|
|
210
|
+
"text"
|
|
211
|
+
],
|
|
212
|
+
"properties": {
|
|
213
|
+
"type": {
|
|
214
|
+
"const": "thinking_delta"
|
|
215
|
+
},
|
|
216
|
+
"text": {
|
|
217
|
+
"type": "string"
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
"additionalProperties": true
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"title": "tool_call",
|
|
224
|
+
"type": "object",
|
|
225
|
+
"required": [
|
|
226
|
+
"type",
|
|
227
|
+
"id",
|
|
228
|
+
"name",
|
|
229
|
+
"args"
|
|
230
|
+
],
|
|
231
|
+
"properties": {
|
|
232
|
+
"type": {
|
|
233
|
+
"const": "tool_call"
|
|
234
|
+
},
|
|
235
|
+
"id": {
|
|
236
|
+
"type": "string"
|
|
237
|
+
},
|
|
238
|
+
"name": {
|
|
239
|
+
"type": "string"
|
|
240
|
+
},
|
|
241
|
+
"args": {},
|
|
242
|
+
"parent_id": {
|
|
243
|
+
"type": "string",
|
|
244
|
+
"description": "W255 run_code sub-call: Some(<parent call id>); the row is logged for audit/replay but derive_messages skips it"
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"additionalProperties": true
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"title": "tool_result",
|
|
251
|
+
"type": "object",
|
|
252
|
+
"required": [
|
|
253
|
+
"type",
|
|
254
|
+
"id",
|
|
255
|
+
"value",
|
|
256
|
+
"error"
|
|
257
|
+
],
|
|
258
|
+
"properties": {
|
|
259
|
+
"type": {
|
|
260
|
+
"const": "tool_result"
|
|
261
|
+
},
|
|
262
|
+
"id": {
|
|
263
|
+
"type": "string"
|
|
264
|
+
},
|
|
265
|
+
"value": {},
|
|
266
|
+
"error": {
|
|
267
|
+
"type": [
|
|
268
|
+
"string",
|
|
269
|
+
"null"
|
|
270
|
+
]
|
|
271
|
+
},
|
|
272
|
+
"parent_id": {
|
|
273
|
+
"type": "string",
|
|
274
|
+
"description": "same contract as tool_call.parent_id"
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
"additionalProperties": true
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"title": "user_question",
|
|
281
|
+
"type": "object",
|
|
282
|
+
"required": [
|
|
283
|
+
"type",
|
|
284
|
+
"id",
|
|
285
|
+
"questions"
|
|
286
|
+
],
|
|
287
|
+
"properties": {
|
|
288
|
+
"type": {
|
|
289
|
+
"const": "user_question"
|
|
290
|
+
},
|
|
291
|
+
"id": {
|
|
292
|
+
"type": "string",
|
|
293
|
+
"description": "W783: the request id ('q-<n>'), echoed by the matching user_answer row."
|
|
294
|
+
},
|
|
295
|
+
"questions": {
|
|
296
|
+
"type": "array",
|
|
297
|
+
"description": "The question batch as the model asked it (the same shape the tool received).",
|
|
298
|
+
"items": {
|
|
299
|
+
"type": "object",
|
|
300
|
+
"additionalProperties": true
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
"expires_at": {
|
|
304
|
+
"type": "integer",
|
|
305
|
+
"description": "W783: absolute deadline in ms; judged at READ time so a replay still knows whether the question expired."
|
|
306
|
+
},
|
|
307
|
+
"timeout_ms": {
|
|
308
|
+
"type": "integer",
|
|
309
|
+
"description": "W783: the resolved maximum wait in ms."
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
"additionalProperties": true
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"title": "user_answer",
|
|
316
|
+
"type": "object",
|
|
317
|
+
"required": [
|
|
318
|
+
"type",
|
|
319
|
+
"id",
|
|
320
|
+
"answers"
|
|
321
|
+
],
|
|
322
|
+
"properties": {
|
|
323
|
+
"type": {
|
|
324
|
+
"const": "user_answer"
|
|
325
|
+
},
|
|
326
|
+
"id": {
|
|
327
|
+
"type": "string",
|
|
328
|
+
"description": "W783: the request id this row answers."
|
|
329
|
+
},
|
|
330
|
+
"answers": {
|
|
331
|
+
"type": "array",
|
|
332
|
+
"description": "selected holds option LABELS, never indices; custom is the optional free-text answer.",
|
|
333
|
+
"items": {
|
|
334
|
+
"type": "object",
|
|
335
|
+
"additionalProperties": true
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
"timed_out": {
|
|
339
|
+
"type": "boolean",
|
|
340
|
+
"description": "W783: true = the deadline expired and NO answer exists; the system decided nothing for the model."
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
"additionalProperties": true
|
|
344
|
+
}
|
|
345
|
+
],
|
|
346
|
+
"note": "W783 added user_question/user_answer (7 -> 9 variants). They are ADDITIVE rows: the engine never emits them, the host does, and a reader that does not know them treats the row as a torn tail exactly as before."
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
"type": "object",
|
|
350
|
+
"required": [
|
|
351
|
+
"type"
|
|
352
|
+
],
|
|
353
|
+
"oneOf": [
|
|
354
|
+
{
|
|
355
|
+
"$ref": "#/$defs/SessionEvent/oneOf/0"
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
"$ref": "#/$defs/SessionEvent/oneOf/1"
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"$ref": "#/$defs/SessionEvent/oneOf/2"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"$ref": "#/$defs/SessionEvent/oneOf/3"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"$ref": "#/$defs/SessionEvent/oneOf/4"
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
"$ref": "#/$defs/SessionEvent/oneOf/5"
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"$ref": "#/$defs/SessionEvent/oneOf/6"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"$ref": "#/$defs/SessionEvent/oneOf/7"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"$ref": "#/$defs/SessionEvent/oneOf/8"
|
|
380
|
+
}
|
|
381
|
+
],
|
|
382
|
+
"projections": {
|
|
383
|
+
"studioMessages": "GET /api/sessions/{id}/messages -> session_event_to_message (src/api.rs:94-135). Independent per event, NO pairing/dropping.",
|
|
384
|
+
"studioMapping": {
|
|
385
|
+
"turn_start": "(omitted)",
|
|
386
|
+
"turn_end": "(omitted)",
|
|
387
|
+
"user_message": {
|
|
388
|
+
"role": "user",
|
|
389
|
+
"content": "<text>",
|
|
390
|
+
"attachments": "[AttachmentRef] (omitted when absent; W804)"
|
|
391
|
+
},
|
|
392
|
+
"assistant_message": {
|
|
393
|
+
"role": "assistant",
|
|
394
|
+
"content": "<text>"
|
|
395
|
+
},
|
|
396
|
+
"thinking_delta": {
|
|
397
|
+
"role": "thinking",
|
|
398
|
+
"content": "<text>"
|
|
399
|
+
},
|
|
400
|
+
"tool_call": {
|
|
401
|
+
"role": "tool",
|
|
402
|
+
"kind": "call",
|
|
403
|
+
"tool_call_id": "<id>",
|
|
404
|
+
"tool_name": "<name>",
|
|
405
|
+
"tool_args": "<args>",
|
|
406
|
+
"tool_parent_id": "<parent_id> (only when present)",
|
|
407
|
+
"content": "ABSENT"
|
|
408
|
+
},
|
|
409
|
+
"tool_result": {
|
|
410
|
+
"role": "tool",
|
|
411
|
+
"kind": "result",
|
|
412
|
+
"tool_call_id": "<id>",
|
|
413
|
+
"tool_value": "<value>",
|
|
414
|
+
"tool_error": "<error>",
|
|
415
|
+
"tool_parent_id": "<parent_id> (only when present)",
|
|
416
|
+
"content": "ABSENT"
|
|
417
|
+
},
|
|
418
|
+
"user_question": {
|
|
419
|
+
"role": "question",
|
|
420
|
+
"kind": "question",
|
|
421
|
+
"content": "<questions>",
|
|
422
|
+
"question_id": "<id>",
|
|
423
|
+
"expires_at": "<expires_at> (when present)"
|
|
424
|
+
},
|
|
425
|
+
"user_answer": {
|
|
426
|
+
"role": "question",
|
|
427
|
+
"kind": "answer",
|
|
428
|
+
"content": "<answers>",
|
|
429
|
+
"question_id": "<id>",
|
|
430
|
+
"timed_out": "<timed_out> (when present)"
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
"engineDeriveMessages": "model-visible history: turn_start/turn_end/thinking_delta are SKIPPED; tool rows carrying parent_id are SKIPPED (run_code sub-calls); everything else is projected in log order. W783: user_question/user_answer are host-side audit rows about a PAUSED turn — they are SKIPPED too (the answer reaches the model as the ordinary tool_result of ask_user_question, never as invented history).",
|
|
434
|
+
"orphanRule": "an orphan tool_result (no preceding tool_call) is still emitted by the Studio projection; nothing is paired or filtered.",
|
|
435
|
+
"questionRecovery": "W783 §7: user_question/user_answer exist so a client that reconnects can rebuild the card and so a replay shows how a parked turn ended. The pending TABLE is in-memory: after a restart a question is unanswerable, and the row reads as an unanswered/expired question instead of blocking the model."
|
|
436
|
+
},
|
|
437
|
+
"serialization": {
|
|
438
|
+
"fieldOrder": "serde struct order",
|
|
439
|
+
"parentIdOmitted": "parent_id is omitted (skip_serializing_if=Option::is_none) when absent, so pre-W255 byte shape is unchanged"
|
|
440
|
+
}
|
|
441
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Celestea Studio SSE contract (GET /api/events)",
|
|
4
|
+
"generatedAt": "2026-09-10T01:20:00Z",
|
|
5
|
+
"source": {
|
|
6
|
+
"code": "frozen from the retired backend implementation (original source file deleted 2026-09-15): emit + loop_event_to_json, the SSE handler, and the bus capacity constant",
|
|
7
|
+
"doc": "docs/DEVELOPMENT.md §2.5",
|
|
8
|
+
"liveProbe": {
|
|
9
|
+
"endpoint": "http://127.0.0.1:3777/api/events",
|
|
10
|
+
"mode": "read-only passive connect",
|
|
11
|
+
"observedHeaders": {
|
|
12
|
+
"content-type": "text/event-stream",
|
|
13
|
+
"cache-control": "no-cache",
|
|
14
|
+
"transfer-encoding": "chunked"
|
|
15
|
+
},
|
|
16
|
+
"eventsObserved": 0,
|
|
17
|
+
"reason": "no turn was running; P0 forbids POST /api/turn because it appends to the production cli-main.jsonl"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"transport": {
|
|
21
|
+
"contentType": "text/event-stream",
|
|
22
|
+
"keepAlive": true,
|
|
23
|
+
"busCapacity": 512,
|
|
24
|
+
"envelope": {
|
|
25
|
+
"turn": "u64",
|
|
26
|
+
"seq": "u64 (global atomic counter, monotonic across the process)",
|
|
27
|
+
"payload": "object"
|
|
28
|
+
},
|
|
29
|
+
"envelopeExtensions": {
|
|
30
|
+
"note": "W513 per-session envelope: the TypeScript host adds these two keys to the frozen `{turn,seq,payload}` envelope (apps/studio/src/sse.ts: createStudioBus). The W744 parity test validates a real frame against `envelopeSchema` widened by exactly these keys, so an undeclared envelope key is a failure instead of an unnoticed drift.",
|
|
31
|
+
"v": {
|
|
32
|
+
"type": "integer",
|
|
33
|
+
"const": 2
|
|
34
|
+
},
|
|
35
|
+
"session": {
|
|
36
|
+
"type": [
|
|
37
|
+
"string",
|
|
38
|
+
"null"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"envelopeSchema": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"required": [
|
|
45
|
+
"turn",
|
|
46
|
+
"seq",
|
|
47
|
+
"payload"
|
|
48
|
+
],
|
|
49
|
+
"properties": {
|
|
50
|
+
"turn": {
|
|
51
|
+
"type": "integer",
|
|
52
|
+
"minimum": 0
|
|
53
|
+
},
|
|
54
|
+
"seq": {
|
|
55
|
+
"type": "integer",
|
|
56
|
+
"minimum": 0
|
|
57
|
+
},
|
|
58
|
+
"payload": {
|
|
59
|
+
"type": "object"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"additionalProperties": false
|
|
63
|
+
},
|
|
64
|
+
"frameFormat": "event: <name>\\ndata: <envelope JSON>\\n\\n"
|
|
65
|
+
},
|
|
66
|
+
"lagged": {
|
|
67
|
+
"trigger": "a slow client is dropped by the broadcast channel (capacity 512)",
|
|
68
|
+
"event": "status",
|
|
69
|
+
"payload": {
|
|
70
|
+
"phase": "lagged",
|
|
71
|
+
"hint": "slow client, skipped events",
|
|
72
|
+
"statusline": "<statusline object>"
|
|
73
|
+
},
|
|
74
|
+
"semantics": "the stream does NOT close; the client receives one lagged status and continues consuming. Skipped events are NOT replayed.",
|
|
75
|
+
"codeRef": "src/main.rs:883-892"
|
|
76
|
+
},
|
|
77
|
+
"payloadExtensions": {
|
|
78
|
+
"note": "Keys the TypeScript host adds to an event payload beyond the frozen legacy table above (W513 per-session frames + W515 injection placements). They are contract too: `payloadExtensions.<event>` is the ONLY place a host-only payload key may be declared, and the W744 parity test fails on any produced key that appears in neither the event's `payload` table nor here. W785 added the `phase:\"fallback\"` value (a payload VALUE, not a new event name) and its five companion keys under `payloadExtensions.status`; the 9 event names are untouched (K5/D8).",
|
|
79
|
+
"status": {
|
|
80
|
+
"session": "string|null (the session the frame belongs to; null = process-level)",
|
|
81
|
+
"dropped": "integer (per-session bucket overflow counter, lagged frames only)",
|
|
82
|
+
"hint": "string (the lagged hint, equal to `lagged.payload.hint`)",
|
|
83
|
+
"placement": "'queued'|'steering'|'context' (W515 injection placement)",
|
|
84
|
+
"boundary": "'turn-start'|'step'? (the boundary that consumed an injected message)",
|
|
85
|
+
"message": "object (the injected-message view {id,kind,from,lane,source,summary})",
|
|
86
|
+
"effective_model": "string (E §4.2.3 #4, W785: the model actually serving; present on a fallback frame and readable from /api/status.effective_model)",
|
|
87
|
+
"from": "string|null (W785: the target that just failed and is being left behind; `phase:\"fallback\"` frames only)",
|
|
88
|
+
"to": "string (W785: the target about to run; `phase:\"fallback\"` frames only)",
|
|
89
|
+
"reason": "string (W785: `http_503` / `timeout_idle` / `stream` / `network` / `generate`)",
|
|
90
|
+
"attempt": "integer (W785: index of the attempt ABOUT to run, §5.2 numbering)"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"count": 9,
|
|
94
|
+
"events": [
|
|
95
|
+
{
|
|
96
|
+
"name": "text",
|
|
97
|
+
"payload": {
|
|
98
|
+
"delta": "string"
|
|
99
|
+
},
|
|
100
|
+
"source": "LoopEvent::Text",
|
|
101
|
+
"codeRef": "src/main.rs:668",
|
|
102
|
+
"frontendListens": true
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "thinking",
|
|
106
|
+
"payload": {
|
|
107
|
+
"delta": "string"
|
|
108
|
+
},
|
|
109
|
+
"source": "LoopEvent::Thinking",
|
|
110
|
+
"codeRef": "src/main.rs:669",
|
|
111
|
+
"frontendListens": true
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "tool",
|
|
115
|
+
"payload": {
|
|
116
|
+
"id": "string",
|
|
117
|
+
"name": "string",
|
|
118
|
+
"args": "object"
|
|
119
|
+
},
|
|
120
|
+
"source": "LoopEvent::ToolCall",
|
|
121
|
+
"codeRef": "src/main.rs:670-672",
|
|
122
|
+
"frontendListens": true
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "tool_result",
|
|
126
|
+
"payload": {
|
|
127
|
+
"id": "string",
|
|
128
|
+
"ok": "boolean",
|
|
129
|
+
"value": "any|null",
|
|
130
|
+
"render": "any|null",
|
|
131
|
+
"error": "string|null",
|
|
132
|
+
"decision": "'allow'|'deny'|'ask'|null"
|
|
133
|
+
},
|
|
134
|
+
"source": "LoopEvent::ToolResult",
|
|
135
|
+
"codeRef": "src/main.rs:673-691",
|
|
136
|
+
"frontendListens": true
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "turn_end",
|
|
140
|
+
"payload": {
|
|
141
|
+
"outcome": "'completed'|'cancelled'|'error'|'step_limit'|'interrupted'",
|
|
142
|
+
"error": "string|null (only for outcome=error: '{kind}: {message}')"
|
|
143
|
+
},
|
|
144
|
+
"source": "LoopEvent::TurnEnd",
|
|
145
|
+
"codeRef": "src/main.rs:692-695,716-732",
|
|
146
|
+
"frontendListens": false
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "done",
|
|
150
|
+
"payload": {
|
|
151
|
+
"text": "string",
|
|
152
|
+
"tool_calls": "array<{id,name,args}>"
|
|
153
|
+
},
|
|
154
|
+
"source": "LoopEvent::Done",
|
|
155
|
+
"codeRef": "src/main.rs:696-710",
|
|
156
|
+
"frontendListens": true
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "status",
|
|
160
|
+
"payload": {
|
|
161
|
+
"phase": "'start'|'progress'|'completed'|'cancelled'|'error'|'step_limit'|'interrupted'|'lagged'|'fallback'",
|
|
162
|
+
"statusline": "object",
|
|
163
|
+
"error": "string?",
|
|
164
|
+
"source": "'autowake'?"
|
|
165
|
+
},
|
|
166
|
+
"source": "execute_turn / post_turn / slow-client degrade",
|
|
167
|
+
"codeRef": "src/main.rs:883-892,951-973,1005-1011,1172-1178 ; W785: phase 'fallback' (E §4.2.3 #2)",
|
|
168
|
+
"frontendListens": true
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "compact",
|
|
172
|
+
"payload": {
|
|
173
|
+
"session": "string",
|
|
174
|
+
"kept_turns": "integer",
|
|
175
|
+
"note": "string",
|
|
176
|
+
"rebound": "boolean"
|
|
177
|
+
},
|
|
178
|
+
"source": "compact handler",
|
|
179
|
+
"codeRef": "src/compact.rs:480-491",
|
|
180
|
+
"frontendListens": true,
|
|
181
|
+
"note": "envelope `turn` is ALWAYS 0 for compact events"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"name": "question",
|
|
185
|
+
"payload": {
|
|
186
|
+
"id": "string",
|
|
187
|
+
"questions": "array<{id,question}>",
|
|
188
|
+
"expires_at": "integer",
|
|
189
|
+
"timeout_ms": "integer",
|
|
190
|
+
"session": "string|null"
|
|
191
|
+
},
|
|
192
|
+
"source": "W783: the user-questions service, while a turn is parked (host-emitted)",
|
|
193
|
+
"codeRef": "apps/studio/src/user-questions.ts (publish)",
|
|
194
|
+
"frontendListens": true,
|
|
195
|
+
"note": "W783: emitted when the model calls ask_user_question, i.e. while the turn's slot is BUSY. The answer does NOT come back over POST /api/turn (that would be steering, and steering is only drained at step boundaries the parked call never reaches) -- it goes to POST /api/questions/{id}/answer. envelope `turn` is the SESSION-local turn number of the turn that parked."
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"frontendDeadListener": {
|
|
199
|
+
"name": "context",
|
|
200
|
+
"note": "frontend/src/sse.ts:34-43 subscribes to `context` but the backend never emits it"
|
|
201
|
+
}
|
|
202
|
+
}
|