@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,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "prompts.json + <CELESTEA_HOME>/workspaces/<ws>/prompts.json (section registry)",
|
|
4
|
+
"source": {
|
|
5
|
+
"legacy": "frozen from the retired implementation (original source file deleted 2026-09-15): src/prompts.rs:126-165",
|
|
6
|
+
"doc": "docs/data-files.md §3"
|
|
7
|
+
},
|
|
8
|
+
"path": {
|
|
9
|
+
"global": "<cwd>/prompts.json (CELESTEA_PROMPTS_FILE)",
|
|
10
|
+
"workspace": "<CELESTEA_HOME>/workspaces/<ws>/prompts.json (W880; legacy <workspace path>/.celestea/prompts.json and <workspace path>/.celestea-prompts.json readable)",
|
|
11
|
+
"mode": "0644 (no key)"
|
|
12
|
+
},
|
|
13
|
+
"notes": [
|
|
14
|
+
"New file, no migration. Missing/unreadable -> default empty; MALFORMED -> warn + default empty (the registry must never break compose/UI).",
|
|
15
|
+
"Persist: pretty JSON + .json.tmp + rename (no fsync, no 0600).",
|
|
16
|
+
"Four levels: builtin (10 code constants, order 100..1000) -> global.sections -> ws.sections -> bound prompt's section_overrides.",
|
|
17
|
+
"Existing sections keep their original order and only swap the template; new sections get ORDER_FALLBACK=2000; sort by (order,id); empty/whitespace templates dropped; join with '\\n\\n'; truncate to PROMPT_MAX_LEN=8192 bytes on a char boundary.",
|
|
18
|
+
"resolve_prompt: session-bound id (ws then global) -> if not found return None (NO default fallback); else ws.default_prompt -> global.default_prompt -> None.",
|
|
19
|
+
"is_default does NOT participate in the resolution chain; only default_prompt decides."
|
|
20
|
+
],
|
|
21
|
+
"schema": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"properties": {
|
|
24
|
+
"sections": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"required": [
|
|
29
|
+
"id",
|
|
30
|
+
"name",
|
|
31
|
+
"template",
|
|
32
|
+
"order"
|
|
33
|
+
],
|
|
34
|
+
"properties": {
|
|
35
|
+
"id": {
|
|
36
|
+
"type": "string"
|
|
37
|
+
},
|
|
38
|
+
"name": {
|
|
39
|
+
"type": "string"
|
|
40
|
+
},
|
|
41
|
+
"template": {
|
|
42
|
+
"type": "string"
|
|
43
|
+
},
|
|
44
|
+
"order": {
|
|
45
|
+
"type": "integer"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"additionalProperties": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"prompts": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"required": [
|
|
56
|
+
"id",
|
|
57
|
+
"name"
|
|
58
|
+
],
|
|
59
|
+
"properties": {
|
|
60
|
+
"id": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"pattern": "^[A-Za-z0-9._-]{1,128}$"
|
|
63
|
+
},
|
|
64
|
+
"name": {
|
|
65
|
+
"type": "string"
|
|
66
|
+
},
|
|
67
|
+
"section_overrides": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": {
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
"default": {}
|
|
73
|
+
},
|
|
74
|
+
"is_default": {
|
|
75
|
+
"type": "boolean",
|
|
76
|
+
"default": false
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"additionalProperties": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"default_prompt": {
|
|
83
|
+
"type": [
|
|
84
|
+
"string",
|
|
85
|
+
"null"
|
|
86
|
+
],
|
|
87
|
+
"default": null
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"additionalProperties": true
|
|
91
|
+
},
|
|
92
|
+
"validation": {
|
|
93
|
+
"promptId": "1-128 chars of [A-Za-z0-9._-]",
|
|
94
|
+
"template": [
|
|
95
|
+
"<=8192 bytes",
|
|
96
|
+
"every '{{' must close",
|
|
97
|
+
"variables must be in the whitelist"
|
|
98
|
+
],
|
|
99
|
+
"templateErrors": [
|
|
100
|
+
"template exceeds the 8192 byte cap ({n} bytes)",
|
|
101
|
+
"undefined prompt variable '{{name}}'",
|
|
102
|
+
"unclosed '{{' in template"
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
"interpolation": {
|
|
106
|
+
"syntax": "{{name}} (name trimmed; no aliases, no escaping)",
|
|
107
|
+
"vars": [
|
|
108
|
+
"model",
|
|
109
|
+
"provider",
|
|
110
|
+
"base_url",
|
|
111
|
+
"workspace",
|
|
112
|
+
"session",
|
|
113
|
+
"tools",
|
|
114
|
+
"context_window",
|
|
115
|
+
"max_output_tokens",
|
|
116
|
+
"date"
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"writeOrder": [
|
|
120
|
+
"409 check (no write)",
|
|
121
|
+
"persist",
|
|
122
|
+
"compose_and_swap",
|
|
123
|
+
"on compose failure write the old file back"
|
|
124
|
+
],
|
|
125
|
+
"memoryOverride": {
|
|
126
|
+
"slot": "USER_OVERRIDE via POST /api/config system_prompt",
|
|
127
|
+
"rule": "non-empty = fully bypass registry assembly; empty = clear and fall back to default_system_prompt(); assembled prompts are NEVER treated as an override"
|
|
128
|
+
},
|
|
129
|
+
"generatedAt": "2026-09-10T01:20:00Z"
|
|
130
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "providers.json (+ public_view)",
|
|
4
|
+
"source": {
|
|
5
|
+
"legacy": "frozen from the retired implementation (original source file deleted 2026-09-15): src/providers.rs:52-86 (Provider/ProviderModel), 219-241 (public_view)",
|
|
6
|
+
"doc": "docs/data-files.md §2"
|
|
7
|
+
},
|
|
8
|
+
"path": {
|
|
9
|
+
"default": "<cwd>/providers.json",
|
|
10
|
+
"envOverride": "CELESTEA_PROVIDERS_FILE",
|
|
11
|
+
"mode": "0600 (plaintext api_key)"
|
|
12
|
+
},
|
|
13
|
+
"notes": [
|
|
14
|
+
"NO version field and NO migration logic. id/name/base_url/request_format have no serde(default): a missing field is a deserialization failure.",
|
|
15
|
+
"ProviderModel deliberately does NOT derive Debug so a key can never be printed.",
|
|
16
|
+
"open(): missing file -> empty store; malformed JSON -> HARD error (never overwrite an unreadable file with an empty store).",
|
|
17
|
+
"save(): pretty JSON -> .tmp -> OpenOptions mode 0600 -> write_all + sync_all -> rename. Every save resets 0600.",
|
|
18
|
+
"W804 (multimodal P0 section 7): models[].input_modalities / output_modalities are ADDITIVE optional fields (additionalProperties is already true). Missing = the optimistic default input [\"text\",\"image\"], output [\"text\"]; publicView exposes both so the frontend can disable the image entry only for a model EXPLICITLY configured without \"image\"."
|
|
19
|
+
],
|
|
20
|
+
"schema": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"providers": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": [
|
|
28
|
+
"id",
|
|
29
|
+
"name",
|
|
30
|
+
"base_url",
|
|
31
|
+
"request_format"
|
|
32
|
+
],
|
|
33
|
+
"properties": {
|
|
34
|
+
"id": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "identity; same id = overwrite"
|
|
37
|
+
},
|
|
38
|
+
"name": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "display name only"
|
|
41
|
+
},
|
|
42
|
+
"note": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"default": ""
|
|
45
|
+
},
|
|
46
|
+
"base_url": {
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"request_format": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"enum": [
|
|
52
|
+
"chat_completions",
|
|
53
|
+
"responses",
|
|
54
|
+
"anthropic_messages"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"api_key": {
|
|
58
|
+
"type": [
|
|
59
|
+
"string",
|
|
60
|
+
"null"
|
|
61
|
+
],
|
|
62
|
+
"description": "PLAINTEXT secret; only this file may contain it"
|
|
63
|
+
},
|
|
64
|
+
"models": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"items": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"required": [
|
|
69
|
+
"id",
|
|
70
|
+
"name"
|
|
71
|
+
],
|
|
72
|
+
"properties": {
|
|
73
|
+
"id": {
|
|
74
|
+
"type": "string"
|
|
75
|
+
},
|
|
76
|
+
"name": {
|
|
77
|
+
"type": "string"
|
|
78
|
+
},
|
|
79
|
+
"reasoning_efforts": {
|
|
80
|
+
"type": "array",
|
|
81
|
+
"items": {
|
|
82
|
+
"type": "string"
|
|
83
|
+
},
|
|
84
|
+
"default": [],
|
|
85
|
+
"description": "free strings, no enum validation"
|
|
86
|
+
},
|
|
87
|
+
"context_window": {
|
|
88
|
+
"type": [
|
|
89
|
+
"integer",
|
|
90
|
+
"null"
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"max_output_tokens": {
|
|
94
|
+
"type": [
|
|
95
|
+
"integer",
|
|
96
|
+
"null"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"input_modalities": {
|
|
100
|
+
"type": "array",
|
|
101
|
+
"items": {
|
|
102
|
+
"type": "string"
|
|
103
|
+
},
|
|
104
|
+
"default": [
|
|
105
|
+
"text",
|
|
106
|
+
"image"
|
|
107
|
+
],
|
|
108
|
+
"description": "W804 OPTIMISTIC DEFAULT: absent = [\"text\",\"image\"]. A model is assumed vision-capable; configuring [\"text\"] is the ONLY way to disable the image entry points. The code never hard-codes a capability."
|
|
109
|
+
},
|
|
110
|
+
"output_modalities": {
|
|
111
|
+
"type": "array",
|
|
112
|
+
"items": {
|
|
113
|
+
"type": "string"
|
|
114
|
+
},
|
|
115
|
+
"default": [
|
|
116
|
+
"text"
|
|
117
|
+
],
|
|
118
|
+
"description": "W804 default: [\"text\"]. Models do not return images over chat-completions in P0."
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"additionalProperties": true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"additionalProperties": true
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"default_model": {
|
|
129
|
+
"type": [
|
|
130
|
+
"string",
|
|
131
|
+
"null"
|
|
132
|
+
],
|
|
133
|
+
"default": null
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"required": [
|
|
137
|
+
"providers"
|
|
138
|
+
],
|
|
139
|
+
"additionalProperties": true
|
|
140
|
+
},
|
|
141
|
+
"publicView": {
|
|
142
|
+
"note": "The api_key KEY IS ABSENT from every response (not null/empty). Tests assert the response text contains neither the key nor the string \"api_key\".",
|
|
143
|
+
"fields": [
|
|
144
|
+
"id",
|
|
145
|
+
"name",
|
|
146
|
+
"note",
|
|
147
|
+
"base_url",
|
|
148
|
+
"request_format",
|
|
149
|
+
"models[].id",
|
|
150
|
+
"models[].name",
|
|
151
|
+
"models[].reasoning_efforts",
|
|
152
|
+
"models[].context_window",
|
|
153
|
+
"models[].max_output_tokens",
|
|
154
|
+
"models[].input_modalities",
|
|
155
|
+
"models[].output_modalities",
|
|
156
|
+
"is_default",
|
|
157
|
+
"has_key"
|
|
158
|
+
],
|
|
159
|
+
"is_default": "default_model matches any of the provider's model ids (every provider listing that id gets true)",
|
|
160
|
+
"has_key": "api_key exists and is a non-empty string (UNtrimmed)",
|
|
161
|
+
"excluded": [
|
|
162
|
+
"api_key"
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
"updateSemantics": {
|
|
166
|
+
"api_key": "absent/null/blank = KEEP the stored key (the only keep-on-default field)",
|
|
167
|
+
"models": "absent = CLEAR to []",
|
|
168
|
+
"note": "absent = CLEAR to \"\"",
|
|
169
|
+
"request_format": "absent = reset to chat_completions (silently reverts a stored anthropic_messages)",
|
|
170
|
+
"name": "absent = fall back to id"
|
|
171
|
+
},
|
|
172
|
+
"keylessBorrow": {
|
|
173
|
+
"rule": "provider has no key AND normalized base_url (trim + strip trailing '/') equals the CURRENT generation's base_url -> borrow the engine's own key for that request only (never persisted, never echoed, never logged)",
|
|
174
|
+
"codeRef": "src/providers.rs:523-553"
|
|
175
|
+
},
|
|
176
|
+
"generatedAt": "2026-09-10T01:20:00Z"
|
|
177
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "/tmp/celestea-workers-registry.tsv (worker registry)",
|
|
4
|
+
"source": {
|
|
5
|
+
"legacy": "frozen from the retired implementation (original source file deleted 2026-09-15): /src/celestea_harness/crates/workers/src/types.rs:50-97, registry.rs:158-230",
|
|
6
|
+
"doc": "docs/DEVELOPMENT.md §2.6"
|
|
7
|
+
},
|
|
8
|
+
"path": {
|
|
9
|
+
"default": "/tmp/celestea-workers-registry.tsv",
|
|
10
|
+
"studio": "<data dir>/worker-registry.tsv (env `CELESTEA_WORKER_REGISTRY`; an EMPTY value = in-memory only, `tsvPath: null`)",
|
|
11
|
+
"ownershipRule": "E §2.2.1: the studio and the DSH-side plugin (`celestea-studio-ts`'s own table vs `celes-worker-spawn`'s `workerBase/registry.tsv`) are TWO tables and MUST NEVER write each other's (R2-1, asserted by B6). Inside the studio's table, `host=` tells the rows of its sessions apart."
|
|
12
|
+
},
|
|
13
|
+
"format": {
|
|
14
|
+
"type": "tsv",
|
|
15
|
+
"columns": [
|
|
16
|
+
"wid",
|
|
17
|
+
"started_at",
|
|
18
|
+
"status",
|
|
19
|
+
"extra"
|
|
20
|
+
],
|
|
21
|
+
"separator": "\\t",
|
|
22
|
+
"lineTerminator": "\\n",
|
|
23
|
+
"trailingNewline": true,
|
|
24
|
+
"parse": "split('\\t'); wid/started_at trimmed; empty wid or started_at -> row skipped; unknown status -> row skipped (bad rows never crash)",
|
|
25
|
+
"statusValues": [
|
|
26
|
+
"RUNNING",
|
|
27
|
+
"DONE",
|
|
28
|
+
"FAILED"
|
|
29
|
+
],
|
|
30
|
+
"extra": "free text; k=v tokens separated by whitespace (extra itself may contain spaces). state=<in-turn|idle> marks a RUNNING row's phase. report_to=<target> drives receipt wakeup. proc=<pid> marks the writing process (W234 row ownership).",
|
|
31
|
+
"atomicWrite": "tmp file in the same dir (<name>.tmp-<pid>-<seq>) then rename over the target",
|
|
32
|
+
"viewRule": "summarize() only counts rows written by THIS registry: `proc=<pid>` (W234) and, when the registry declares a host conversation, `host=<sid>` (W787). A row with NO `host=` token is kept when `proc` matches (a legacy / directly-upserted row). Stale cross-process rows are invisible to by_status/by_state/workers and are not matched by a wid filter",
|
|
33
|
+
"writeRule": "W787: one table carries the rows of EVERY studio session, so a write MERGES with the rows other session registries already wrote (`mergeTableRows`) before the atomic tmp+rename — writing the in-memory view alone would delete a sibling session's worker"
|
|
34
|
+
},
|
|
35
|
+
"extraTokens": {
|
|
36
|
+
"note": "W787 (E §2.2.2): the token vocabulary, appended WITHOUT changing the column count — an older parser only sees a longer `extra`, which is what makes the change backward compatible (B7 round-trip).",
|
|
37
|
+
"known": [
|
|
38
|
+
"sess=<session id>",
|
|
39
|
+
"title=<short title, whitespace folded to `-`>",
|
|
40
|
+
"driven=<yes|no>",
|
|
41
|
+
"workspace=<name>",
|
|
42
|
+
"provider=<id>",
|
|
43
|
+
"model=<id>",
|
|
44
|
+
"effort=<level>",
|
|
45
|
+
"report_to=<session id>",
|
|
46
|
+
"mode=<standard|execution>",
|
|
47
|
+
"brief=<folded brief, <=300 chars>",
|
|
48
|
+
"retries=<n>",
|
|
49
|
+
"fail=<reason>",
|
|
50
|
+
"ended_at=<utc stamp>",
|
|
51
|
+
"proc=<pid>",
|
|
52
|
+
"state=<in-turn|idle>",
|
|
53
|
+
"host=<session id>",
|
|
54
|
+
"attempt=<n>",
|
|
55
|
+
"lease=<pid>@<unix>",
|
|
56
|
+
"receipt=<wid>:<attempt>"
|
|
57
|
+
],
|
|
58
|
+
"whitelist": ["host", "attempt", "lease", "receipt"],
|
|
59
|
+
"added": {
|
|
60
|
+
"host": "the HOST conversation that dispatched the worker (E §2.2.2, closes G2-6); it is also the per-session ownership discriminator now that one process shares ONE table",
|
|
61
|
+
"attempt": "which try this row is: the FIRST spawn is 0 and a re-dispatch adds one (§5.2, the cross-capability convention that the usage ledger and the model fallback already use; §2.2.2's local `first = 1` wording is superseded by the ruling). It names the report file (`results/<wid>-<short>-a<attempt>.md`) and keys the receipt (`receipt:<wid>:<attempt>`); a row with no token at all is a pre-W787 first try, i.e. 0",
|
|
62
|
+
"lease": "`<pid>@<unix>` of the owning process and when it last touched the row; renewed on every driver state change. Absent (a legacy row) falls back to `proc=`",
|
|
63
|
+
"receipt": "the idempotency key `<wid>:<attempt>` of the receipt this row already DELIVERED — a second closeLoop for the same attempt is a no-op (E §2.2.3)"
|
|
64
|
+
},
|
|
65
|
+
"roundTrip": "tests/contracts.test.ts round-trips a row carrying ALL four new tokens through serializeRegistryTsv -> parseRegistryTsv byte-for-byte (B7)"
|
|
66
|
+
},
|
|
67
|
+
"columns": {
|
|
68
|
+
"wid": "worker id (e.g. W101)",
|
|
69
|
+
"started_at": "UTC timestamp (e.g. 2026-09-06_17:54:53Z)",
|
|
70
|
+
"status": "RUNNING|DONE|FAILED",
|
|
71
|
+
"extra": "key=value tokens"
|
|
72
|
+
},
|
|
73
|
+
"generatedAt": "2026-09-10T01:20:00Z"
|
|
74
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "<session-dir>/session.json (Studio session metadata)",
|
|
4
|
+
"source": {
|
|
5
|
+
"legacy": "frozen from the retired implementation (original source file deleted 2026-09-15): src/workspaces.rs:1001-1014, 1206-1218",
|
|
6
|
+
"doc": "docs/data-files.md §1.5"
|
|
7
|
+
},
|
|
8
|
+
"path": {
|
|
9
|
+
"default": "<session-dir>/session.json (optional; canonical <CELESTEA_HOME>/workspaces/<ws>/sessions/<session-dir>/, legacy <workspace path>/<session-dir>/ and <workspace path>/.celestea/sessions/<session-dir>/ readable)"
|
|
10
|
+
},
|
|
11
|
+
"notes": [
|
|
12
|
+
"The engine NEVER reads this file; cli-main.jsonl is the conversation history.",
|
|
13
|
+
"Writers: POST /api/sessions (always, from W779: `title` is mandatory at creation), /rename (updates `title` in place) and /branch (writes the new session's own `title` over the inherited keys). Nothing is written when every field is empty (pre-W779 sessions with no model/prompt/mode).",
|
|
14
|
+
"Written with a plain std::fs::write (NOT atomic, no fsync).",
|
|
15
|
+
"Missing/corrupt -> None (tolerated).",
|
|
16
|
+
"W779: `rename` moves the directory AND rewrites `title` (the directory name is the sanitized title, so the file is the only place the original survives); `branch` copies model/prompt/mode and writes its own `title` (default `<parent display name>-分支`) instead of copying the file verbatim; the whole directory still follows a rename.",
|
|
17
|
+
"W779: `title` is the ORIGINAL, un-sanitized session title (CJK, spaces preserved) — the display name the GUI shows. Absent -> the caller falls back to the directory name with its `-<secs>.<nanos>[-N]` creation suffix stripped (`stripCreationSuffix`); the directory name itself is unchanged (it stays sanitized + timestamped for uniqueness).",
|
|
18
|
+
"W779: a non-string `title` is ignored on read (like an unknown `mode`); an empty `title` is never written.",
|
|
19
|
+
"POST /api/config does NOT write it back: the session-level model is decided at creation time.",
|
|
20
|
+
"W729: `mode` (standard|execution) is fixed at creation; DEFAULT = standard and it is NOT written (a session without the key behaves exactly as before).",
|
|
21
|
+
"W729: an unknown `mode` value is ignored on read (behaves like a missing key); POST /api/sessions rejects it with 400 `invalid mode: <v>` before anything is created.",
|
|
22
|
+
"additionalProperties: true is kept: a hand-written unknown key never breaks a session."
|
|
23
|
+
],
|
|
24
|
+
"schema": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"model": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "model id override; invalid value -> 400 invalid session model: {e} on activate"
|
|
30
|
+
},
|
|
31
|
+
"prompt": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "bound prompt id (section_overrides of that prompt are applied last)"
|
|
34
|
+
},
|
|
35
|
+
"mode": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"enum": [
|
|
38
|
+
"standard",
|
|
39
|
+
"execution"
|
|
40
|
+
],
|
|
41
|
+
"description": "session working mode (W729): standard = call tools directly; execution = prefer one run_code program per dependent sequence. Absent = standard; the default is never written."
|
|
42
|
+
},
|
|
43
|
+
"title": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "W779: display name of the session, verbatim as created/renamed (NOT sanitized, may contain CJK/spaces). Absent -> the GUI strips the creation suffix from the directory name."
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"additionalProperties": true
|
|
49
|
+
},
|
|
50
|
+
"generatedAt": "2026-09-10T01:20:00Z"
|
|
51
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "usage-ledger.jsonl (append-only cost & usage ledger, one JSON object per line)",
|
|
4
|
+
"source": {
|
|
5
|
+
"design": "/src/celestea_studio-ts/docs/iteration-e-capabilities.md §3.2.1/§3.2.3 (W728 P0)",
|
|
6
|
+
"code": "packages/runtime/src/ledger.ts, packages/runtime/src/ledger-llm.ts"
|
|
7
|
+
},
|
|
8
|
+
"path": {
|
|
9
|
+
"default": "<data dir>/usage-ledger.jsonl (<data dir> = dirname(workspacesFile))",
|
|
10
|
+
"envOverride": "CELESTEA_USAGE_LEDGER_FILE",
|
|
11
|
+
"disableSwitch": "CELESTEA_USAGE_LEDGER=off|0|false|no",
|
|
12
|
+
"mode": "0600"
|
|
13
|
+
},
|
|
14
|
+
"format": {
|
|
15
|
+
"type": "jsonl",
|
|
16
|
+
"lineTerminator": "\n",
|
|
17
|
+
"appendOnly": true,
|
|
18
|
+
"rewrite": "never: a line is appended once and never edited, reordered or compacted (rotation would be a P1 concern and is NOT implemented)",
|
|
19
|
+
"write": "one writeSync per record on an O_APPEND descriptor, so two processes cannot interleave a line",
|
|
20
|
+
"idempotencyKey": "(session, turn_id, step, attempt) — a key already booked is skipped, not rewritten",
|
|
21
|
+
"grain": "one row per model STEP (kind ok|error) plus one turn_total row per turn",
|
|
22
|
+
"noBodyRule": "a row never contains prompt/message text, tool arguments or credentials; only counters, ids, names and prices"
|
|
23
|
+
},
|
|
24
|
+
"kinds": {
|
|
25
|
+
"ok": "a response arrived; usage is the provider's own counters (or null when the provider sent no usage frame, then billed_unknown=true)",
|
|
26
|
+
"error": "the step failed (non-2xx / timeout / torn stream); usage carries the counters observed before the failure, or null",
|
|
27
|
+
"turn_total": "the summary row of one turn: Σ of its step rows (token equality with the details is an assertion, §3.4 C2)"
|
|
28
|
+
},
|
|
29
|
+
"schema": {
|
|
30
|
+
"$defs": {
|
|
31
|
+
"usage": {
|
|
32
|
+
"type": ["object", "null"],
|
|
33
|
+
"required": ["prompt_tokens", "completion_tokens", "total_tokens"],
|
|
34
|
+
"properties": {
|
|
35
|
+
"prompt_tokens": { "type": "integer", "minimum": 0 },
|
|
36
|
+
"completion_tokens": { "type": "integer", "minimum": 0 },
|
|
37
|
+
"total_tokens": { "type": "integer", "minimum": 0 },
|
|
38
|
+
"cache_read": { "type": "integer", "minimum": 0 },
|
|
39
|
+
"reasoning_tokens": { "type": "integer", "minimum": 0 }
|
|
40
|
+
},
|
|
41
|
+
"additionalProperties": true
|
|
42
|
+
},
|
|
43
|
+
"cost": {
|
|
44
|
+
"type": ["object", "null"],
|
|
45
|
+
"required": ["in", "out", "cache", "total"],
|
|
46
|
+
"properties": {
|
|
47
|
+
"in": { "type": "number", "description": "cost of the UNCACHED input only: max(0, prompt_tokens - cache_read) x the `in` price. prompt_tokens already contains the cache-hit region, so this is NOT prompt_tokens x `in`" },
|
|
48
|
+
"out": { "type": "number", "description": "completion_tokens x the `out` price" },
|
|
49
|
+
"cache": { "type": "number", "description": "cache_read x the `cache_read` price; the hit region is charged HERE only, never also at the `in` price" },
|
|
50
|
+
"total": { "type": "number", "description": "in + out + cache (each component rounded to 6 decimals)" }
|
|
51
|
+
},
|
|
52
|
+
"additionalProperties": false
|
|
53
|
+
},
|
|
54
|
+
"price": {
|
|
55
|
+
"type": ["object", "null"],
|
|
56
|
+
"required": ["version", "currency", "unit", "in", "out", "cache_read"],
|
|
57
|
+
"description": "the snapshot THIS row was priced with (a frozen copy, not a reference)"
|
|
58
|
+
},
|
|
59
|
+
"step": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"required": ["v", "ts", "kind", "session", "turn", "turn_id", "step", "attempt", "usage", "billed_unknown", "priced_by"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"v": { "const": 1 },
|
|
64
|
+
"ts": { "type": "integer", "description": "unix seconds" },
|
|
65
|
+
"kind": { "enum": ["ok", "error"] },
|
|
66
|
+
"session": { "type": "string", "description": "\"<workspace>/<session>\" (data-file self-description)" },
|
|
67
|
+
"turn": { "type": ["integer", "null"], "description": "turn number parsed from turn_id; null = a call outside a driven turn (worker/summarizer)" },
|
|
68
|
+
"turn_id": { "type": ["string", "null"] },
|
|
69
|
+
"step": { "type": "integer", "minimum": 1, "description": "1-based step index within the turn; (turn, step) is strictly increasing" },
|
|
70
|
+
"attempt": { "type": "integer", "minimum": 0, "description": "0 = first attempt; retries/fallbacks increment (§5.2). P0 writes 0" },
|
|
71
|
+
"provider": { "type": ["string", "null"], "description": "providers.json row id of the target" },
|
|
72
|
+
"model": { "type": ["string", "null"] },
|
|
73
|
+
"base_url_host": { "type": ["string", "null"] },
|
|
74
|
+
"usage": { "$ref": "#/schema/$defs/usage" },
|
|
75
|
+
"billed_unknown": { "type": "boolean", "description": "true = no usage frame arrived: the cost is UNKNOWN, never 0" },
|
|
76
|
+
"error_kind": { "enum": ["generate", "stream", "timeout", null] },
|
|
77
|
+
"http_status": { "type": ["integer", "null"], "description": "W723 LlmError.httpStatus (null = no response arrived)" },
|
|
78
|
+
"retryable": { "type": ["boolean", "null"], "description": "W723 LlmError.retryable; null = not structurally known (mid-stream)" },
|
|
79
|
+
"price": { "$ref": "#/schema/$defs/price" },
|
|
80
|
+
"cost": { "$ref": "#/schema/$defs/cost", "description": "null when unpriced or unbilled — NOT a zero" },
|
|
81
|
+
"priced_by": { "enum": ["table", "record", "unpriced"] },
|
|
82
|
+
"fallback_from": { "type": ["string", "null"], "description": "target name this attempt fell over from (capability 4, P1)" }
|
|
83
|
+
},
|
|
84
|
+
"additionalProperties": false
|
|
85
|
+
},
|
|
86
|
+
"turn_total": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"required": ["v", "ts", "kind", "session", "turn", "turn_id", "steps", "attempts", "usage", "cost_complete", "priced_by", "unpriced_models", "billed_unknown_steps", "outcome"],
|
|
89
|
+
"properties": {
|
|
90
|
+
"v": { "const": 1 },
|
|
91
|
+
"ts": { "type": "integer" },
|
|
92
|
+
"kind": { "const": "turn_total" },
|
|
93
|
+
"session": { "type": "string" },
|
|
94
|
+
"turn": { "type": ["integer", "null"] },
|
|
95
|
+
"turn_id": { "type": ["string", "null"] },
|
|
96
|
+
"steps": { "type": "integer", "minimum": 1, "description": "step rows contributing to this turn" },
|
|
97
|
+
"attempts": { "type": "integer", "minimum": 1, "description": "Σ(attempt+1) over those rows" },
|
|
98
|
+
"usage": { "$ref": "#/schema/$defs/usage", "description": "Σ of the contributing rows' usage (token equality asserted)" },
|
|
99
|
+
"cost": { "$ref": "#/schema/$defs/cost", "description": "Σ over the PRICED rows; see cost_complete" },
|
|
100
|
+
"cost_complete": { "type": "boolean", "description": "false = at least one row was unpriced or unbilled, so total is a floor, not the whole cost" },
|
|
101
|
+
"priced_by": { "enum": ["table", "record", "unpriced"] },
|
|
102
|
+
"unpriced_models": { "type": "array", "items": { "type": "string" } },
|
|
103
|
+
"billed_unknown_steps": { "type": "integer", "minimum": 0 },
|
|
104
|
+
"outcome": { "enum": ["completed", "cancelled", "step_limit", "interrupted", "error"], "description": "the turn's terminal state (the log's own turn_end outcome)" }
|
|
105
|
+
},
|
|
106
|
+
"additionalProperties": false
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"oneOf": [{ "$ref": "#/schema/$defs/step" }, { "$ref": "#/schema/$defs/turn_total" }]
|
|
110
|
+
},
|
|
111
|
+
"generatedAt": "2026-09-11T00:00:00Z"
|
|
112
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "workspaces.json (v2)",
|
|
4
|
+
"source": {
|
|
5
|
+
"legacy": "frozen from the retired implementation (original source file deleted 2026-09-15): src/workspaces.rs:122-140 (RegistryData)",
|
|
6
|
+
"doc": "docs/data-files.md §1"
|
|
7
|
+
},
|
|
8
|
+
"path": {
|
|
9
|
+
"default": "<cwd>/workspaces.json",
|
|
10
|
+
"envOverride": "CELESTEA_WORKSPACES_FILE",
|
|
11
|
+
"mode": "0644 (no key)"
|
|
12
|
+
},
|
|
13
|
+
"notes": [
|
|
14
|
+
"NO version field. \"v2\" is a naming convention only: the workspace key is the registered path's folder basename and is NEVER stored.",
|
|
15
|
+
"Unknown fields are tolerated (no deny_unknown_fields).",
|
|
16
|
+
"There is no archive-state field: archiving is a pure filesystem move into the canonical <CELESTEA_HOME>/workspaces/<ws>/archive/ (W880; legacy .celestea-archived/ and .celestea/archive/ readable), and the default scanner skips dot-dirs.",
|
|
17
|
+
"Write: to_string_pretty -> <path>.json.tmp -> rename (atomic, NO fsync)."
|
|
18
|
+
],
|
|
19
|
+
"schema": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"workspaces": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": [
|
|
27
|
+
"path"
|
|
28
|
+
],
|
|
29
|
+
"properties": {
|
|
30
|
+
"path": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "absolute registered directory"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"additionalProperties": true
|
|
36
|
+
},
|
|
37
|
+
"default": []
|
|
38
|
+
},
|
|
39
|
+
"active_session": {
|
|
40
|
+
"type": [
|
|
41
|
+
"string",
|
|
42
|
+
"null"
|
|
43
|
+
],
|
|
44
|
+
"description": "\"<workspace-basename>/<session-dir>\"; null when unset",
|
|
45
|
+
"default": null
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"required": [],
|
|
49
|
+
"additionalProperties": true
|
|
50
|
+
},
|
|
51
|
+
"legacyV1": {
|
|
52
|
+
"name": "tolerated on load, used for the name->basename mapping, never written back"
|
|
53
|
+
},
|
|
54
|
+
"hardErrors": {
|
|
55
|
+
"malformed": "workspaces.json '<path>' is malformed: {e} (startup exits 1; the unreadable registry is never overwritten)",
|
|
56
|
+
"duplicateBasename": "two workspaces resolve to the same folder name '{base}' (workspace keys must be unique basenames; rename one folder)"
|
|
57
|
+
},
|
|
58
|
+
"sessionId": {
|
|
59
|
+
"shape": "<workspace-basename>/<session-dir>",
|
|
60
|
+
"rules": "exactly one '/', both sides non-empty, session segment has no '/' and is sanitized (separators/control/whitespace -> '_', CJK preserved); empty/'.'/'..'/leading-dot rejected; dir.parent() is one of the live-session roots (canonical <CELESTEA_HOME>/workspaces/<ws>/sessions, slice-A <ws>/.celestea/sessions, or the workspace path)"
|
|
61
|
+
},
|
|
62
|
+
"generatedAt": "2026-09-10T01:20:00Z"
|
|
63
|
+
}
|