spexcode 0.7.0-next.13 → 0.7.0-next.14
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/node_modules/@spexcode/session-application/package.json +5 -5
- package/node_modules/@spexcode/session-events/package.json +2 -2
- package/node_modules/@spexcode/session-protocol/package.json +1 -1
- package/node_modules/@spexcode/session-runtime/package.json +2 -2
- package/node_modules/@spexcode/session-selflaunch/package.json +3 -3
- package/node_modules/@spexcode/session-topology/package.json +2 -2
- package/node_modules/@spexcode/spec-cli/package.json +7 -7
- package/node_modules/@spexcode/spec-core/package.json +1 -1
- package/node_modules/@spexcode/spec-eval/package.json +2 -2
- package/node_modules/@spexcode/spec-forge/package.json +2 -2
- package/node_modules/@spexcode/transcript/dist/parsers.d.ts +1 -0
- package/node_modules/@spexcode/transcript/dist/parsers.js +17 -2
- package/node_modules/@spexcode/transcript/dist/readers.js +13 -3
- package/node_modules/@spexcode/transcript/package.json +1 -1
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/session-application",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"publishConfig": { "access": "public" },
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Adopter-owned composition of session protocol and topology transactions.",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@spexcode/session-events": "0.7.0-next.
|
|
20
|
-
"@spexcode/session-protocol": "0.7.0-next.
|
|
21
|
-
"@spexcode/session-runtime": "0.7.0-next.
|
|
22
|
-
"@spexcode/session-topology": "0.7.0-next.
|
|
19
|
+
"@spexcode/session-events": "0.7.0-next.14",
|
|
20
|
+
"@spexcode/session-protocol": "0.7.0-next.14",
|
|
21
|
+
"@spexcode/session-runtime": "0.7.0-next.14",
|
|
22
|
+
"@spexcode/session-topology": "0.7.0-next.14"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^20.16.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/session-events",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"publishConfig": { "access": "public" },
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Append-only session facts and deterministic ordered replay inputs.",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"prepublishOnly": "node ../../scripts/release-publish.mjs --from-package-publish",
|
|
16
16
|
"test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts"
|
|
17
17
|
},
|
|
18
|
-
"dependencies": { "@spexcode/session-protocol": "0.7.0-next.
|
|
18
|
+
"dependencies": { "@spexcode/session-protocol": "0.7.0-next.14" },
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/node": "^20.16.0",
|
|
21
21
|
"tsx": "^4.19.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/session-runtime",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"publishConfig": { "access": "public" },
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Adopter-owned bindings between protocol addresses and native runtime identities.",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"prepublishOnly": "node ../../scripts/release-publish.mjs --from-package-publish",
|
|
16
16
|
"test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts"
|
|
17
17
|
},
|
|
18
|
-
"dependencies": { "@spexcode/session-protocol": "0.7.0-next.
|
|
18
|
+
"dependencies": { "@spexcode/session-protocol": "0.7.0-next.14" },
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/node": "^20.16.0",
|
|
21
21
|
"tsx": "^4.19.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/session-selflaunch",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"publishConfig": { "access": "public" },
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A fail-closed self-launch adopter for the SpexCode session protocol.",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@spexcode/session-protocol": "0.7.0-next.
|
|
28
|
-
"@spexcode/session-runtime": "0.7.0-next.
|
|
27
|
+
"@spexcode/session-protocol": "0.7.0-next.14",
|
|
28
|
+
"@spexcode/session-runtime": "0.7.0-next.14"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^20.16.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/session-topology",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"publishConfig": { "access": "public" },
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A neutral SQLite relation model for session addresses.",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"prepublishOnly": "node ../../scripts/release-publish.mjs --from-package-publish",
|
|
16
16
|
"test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts"
|
|
17
17
|
},
|
|
18
|
-
"dependencies": { "@spexcode/session-protocol": "0.7.0-next.
|
|
18
|
+
"dependencies": { "@spexcode/session-protocol": "0.7.0-next.14" },
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/node": "^20.16.0",
|
|
21
21
|
"tsx": "^4.19.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/spec-cli",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SpexCode CLI + server. The root spexcode package delegates to this compiled package; dashboard assets live in @spexcode/spec-dashboard.",
|
|
6
6
|
"bin": {
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"test": "tsx --import ../scripts/test-home.mjs --test src/*.test.ts"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@spexcode/session-application": "0.7.0-next.
|
|
39
|
-
"@spexcode/session-selflaunch": "0.7.0-next.
|
|
40
|
-
"@spexcode/spec-core": "0.7.0-next.
|
|
41
|
-
"@spexcode/spec-eval": "0.7.0-next.
|
|
42
|
-
"@spexcode/spec-forge": "0.7.0-next.
|
|
43
|
-
"@spexcode/transcript": "0.7.0-next.
|
|
38
|
+
"@spexcode/session-application": "0.7.0-next.14",
|
|
39
|
+
"@spexcode/session-selflaunch": "0.7.0-next.14",
|
|
40
|
+
"@spexcode/spec-core": "0.7.0-next.14",
|
|
41
|
+
"@spexcode/spec-eval": "0.7.0-next.14",
|
|
42
|
+
"@spexcode/spec-forge": "0.7.0-next.14",
|
|
43
|
+
"@spexcode/transcript": "0.7.0-next.14",
|
|
44
44
|
"smol-toml": "^1.8.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/spec-eval",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"test": "tsx --import ../scripts/test-home.mjs --test src/*.test.ts"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@spexcode/spec-core": "0.7.0-next.
|
|
29
|
+
"@spexcode/spec-core": "0.7.0-next.14"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^20.16.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/spec-forge",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test": "tsx --test src/*.test.ts"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@spexcode/spec-core": "0.7.0-next.
|
|
25
|
+
"@spexcode/spec-core": "0.7.0-next.14"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^20.16.0",
|
|
@@ -133,7 +133,14 @@ export function claudeEvent(value) {
|
|
|
133
133
|
return { at: eventAt, turn: { id: idOf(entry) ?? idOf(message), at: eventAt, role: 'user', text, tools: [] } };
|
|
134
134
|
}
|
|
135
135
|
if (entry.type === 'assistant' && message.role === 'assistant') {
|
|
136
|
-
|
|
136
|
+
// ONE API MESSAGE IS ONE TURN, however many lines Claude wrote it as. It writes one CONTENT BLOCK per
|
|
137
|
+
// line — prose on one, each tool call on its own — and every line of the same message repeats that
|
|
138
|
+
// message's `id` while carrying its own `uuid`. Keying on the line made one message up to six turns:
|
|
139
|
+
// measured over twelve recent threads, 5,077 assistant lines carry only 2,396 distinct message ids, so a
|
|
140
|
+
// turn count read off the lines is more than twice the truth. Keying on the message id lets the collector
|
|
141
|
+
// fold the fragments back together with the rule it already has for a re-emitted turn — text kept, calls
|
|
142
|
+
// merged by their own ids — so nothing here has to accumulate.
|
|
143
|
+
const turn = { id: idOf(message) ?? idOf(entry), at: eventAt, role: 'assistant', tools: [] };
|
|
137
144
|
for (const blockValue of items(message.content)) {
|
|
138
145
|
const block = object(blockValue);
|
|
139
146
|
if (block?.type === 'text')
|
|
@@ -517,6 +524,7 @@ export class IntervalCollector {
|
|
|
517
524
|
evicted = new Set();
|
|
518
525
|
synthesized = new Map(); // `<role>@<at>` → how many turns already wore it
|
|
519
526
|
sawTimestamp = false;
|
|
527
|
+
sawRecord = false; // a record this harness's parser RECOGNIZED arrived, with or without a clock
|
|
520
528
|
omittedTurns = 0;
|
|
521
529
|
omittedBytes = 0;
|
|
522
530
|
outOfOrderEvents = 0;
|
|
@@ -528,6 +536,7 @@ export class IntervalCollector {
|
|
|
528
536
|
this.range.to = to; }
|
|
529
537
|
// returns true once the source has moved past `to` (the caller may then bound its lookahead)
|
|
530
538
|
add(event) {
|
|
539
|
+
this.sawRecord = true;
|
|
531
540
|
const eventAt = event.at;
|
|
532
541
|
if (eventAt === null)
|
|
533
542
|
return this.pastRange;
|
|
@@ -600,7 +609,13 @@ export class IntervalCollector {
|
|
|
600
609
|
return this.pastRange;
|
|
601
610
|
}
|
|
602
611
|
finish(revision, harness) {
|
|
603
|
-
|
|
612
|
+
// THE CLOCK GATE IS ABOUT THE HARNESS, NOT THE MOMENT. It catches a source whose conversational records
|
|
613
|
+
// carry no usable time, which makes interval reads impossible. It must NOT catch a thread that has simply
|
|
614
|
+
// not spoken yet: every Claude transcript opens with clockless bookkeeping (`mode`, `permission-mode`,
|
|
615
|
+
// `file-history-snapshot`) before its first message — 40 of 40 real threads on this box — and those lines
|
|
616
|
+
// are not records this parser recognizes at all, so failing on them put an error frame on the page for the
|
|
617
|
+
// first moments of EVERY new session, which is exactly when someone is watching.
|
|
618
|
+
if (this.sawRecord && !this.sawTimestamp)
|
|
604
619
|
throw new TranscriptReadError('invalid', `${harness} transcript has no reliable timestamps; interval reads are unavailable`);
|
|
605
620
|
return {
|
|
606
621
|
revision,
|
|
@@ -205,8 +205,13 @@ class LineFileCursor {
|
|
|
205
205
|
catch (error) {
|
|
206
206
|
throw new TranscriptReadError('unreadable', `${this.harness} transcript is unreadable: ${error instanceof Error ? error.message : String(error)}`);
|
|
207
207
|
}
|
|
208
|
+
// AN EMPTY FILE IS A THREAD THAT HAS NOT SPOKEN YET, not a broken one. The harness creates the transcript
|
|
209
|
+
// before it writes the first record, so the moments right after a session starts — exactly when a person is
|
|
210
|
+
// watching — read as zero bytes. Failing there put an error on the page for a conversation that simply had
|
|
211
|
+
// not begun. Zero bytes is unambiguous in a way a garbled file is not: there is nothing to misread, so this
|
|
212
|
+
// is the one place the no-timestamp gate is skipped rather than tripped.
|
|
208
213
|
if (size <= 0)
|
|
209
|
-
|
|
214
|
+
return { revision: fileRevision(this.path) ?? '0', from: this.from, to, turns: [], truncated: false, omittedTurns: 0, omittedBytes: 0, outOfOrderEvents: 0 };
|
|
210
215
|
// a source that shrank was rewritten underneath the cursor: forget the position and read the interval afresh
|
|
211
216
|
if (!this.started || size < this.scan.position) {
|
|
212
217
|
this.restart(size);
|
|
@@ -216,8 +221,9 @@ class LineFileCursor {
|
|
|
216
221
|
let fd = null;
|
|
217
222
|
try {
|
|
218
223
|
fd = openSync(this.path, 'r');
|
|
219
|
-
let postRangeLines = 0;
|
|
224
|
+
let postRangeLines = 0, parsedLines = 0, unparsableLines = 0;
|
|
220
225
|
this.scan = scanLines(fd, this.scan, (value, offset) => {
|
|
226
|
+
parsedLines++;
|
|
221
227
|
const event = this.parse(value);
|
|
222
228
|
if (!event)
|
|
223
229
|
return false;
|
|
@@ -226,7 +232,11 @@ class LineFileCursor {
|
|
|
226
232
|
intervalOffsets.set(this.seekKey, offset);
|
|
227
233
|
const pastRange = this.collector.add(event);
|
|
228
234
|
return pastRange && ++postRangeLines >= lookahead;
|
|
229
|
-
}, (bytes) => { this.collector.omittedBytes += bytes; });
|
|
235
|
+
}, (bytes) => { unparsableLines++; this.collector.omittedBytes += bytes; });
|
|
236
|
+
// a file in which NOTHING is JSON is not this thread's transcript at all — that is the loud case, and it
|
|
237
|
+
// stays loud; a file whose lines parse but say nothing conversational yet is simply not started
|
|
238
|
+
if (!parsedLines && unparsableLines > 0)
|
|
239
|
+
throw new TranscriptReadError('invalid', `${this.harness} transcript cannot be parsed: no line is JSON`);
|
|
230
240
|
}
|
|
231
241
|
catch (error) {
|
|
232
242
|
if (error instanceof TranscriptReadError)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/transcript",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Normalized agent transcripts: one parser per harness, a bounded interval reader over a native thread file or an in-memory event stream, and the full/delta frame protocol every transport and renderer share.",
|
|
6
6
|
"files": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spexcode",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SpexCode — a spec-driven, self-developing dev tool. The `spex` CLI + spec server reads the .spec tree and its git history, and serves the dashboard.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"prepack": "node scripts/sync-init-plugins.mjs --check && node scripts/prepack.mjs"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@spexcode/spec-cli": "0.7.0-next.
|
|
47
|
+
"@spexcode/spec-cli": "0.7.0-next.14"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|