@volter/supercode-teams 0.3.192 → 0.3.194
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/package.json +2 -2
- package/session-index-owner.mjs +22 -4
- package/terminal-mail.mjs +19 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.194",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"@volter/supercode-frontend": "^0.2.2",
|
|
85
85
|
"@volter/supercode-harness-sdk": "^0.3.58",
|
|
86
86
|
"@volter/supercode-health": "^0.1.0",
|
|
87
|
-
"@volter/supercode-interchange": "0.1.
|
|
87
|
+
"@volter/supercode-interchange": "0.1.10",
|
|
88
88
|
"@volter/supercode-terminal": "^0.2.54",
|
|
89
89
|
"@volter/supercode-trace": "^0.1.0",
|
|
90
90
|
"@volter/teams": "0.2.46",
|
package/session-index-owner.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import * as zlib from 'node:zlib';
|
|
|
3
3
|
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
4
4
|
import {
|
|
5
5
|
HARNESSES, resolveHarnessHomes, selectedHarnesses, FILE_HARNESSES, fileRoot,
|
|
6
|
-
normalizedSourcePath, pathUnder, sourceStat, sourceEntries, sameFingerprint,
|
|
6
|
+
normalizedSourcePath, pathUnder, sourceStat, sourceEntries, sameFingerprint, fileFingerprint,
|
|
7
7
|
listSessionParts, sessionPartFor, readFileMetadata, verifySources,
|
|
8
8
|
selectStoreFiles, storeRegistryDirectories, storeFingerprints,
|
|
9
9
|
readForeignStore, readHermesStore, readOrchestratorStore, readGeminiProjects,
|
|
@@ -29,7 +29,7 @@ export class SessionIndexOwner extends EventEmitter {
|
|
|
29
29
|
constructor({ database, env = process.env, log = () => {} }) {
|
|
30
30
|
super(); requireSessionIndexRuntime(); this.database = database; this.store = new SessionIndexStore(database);
|
|
31
31
|
this.env = env; this.log = log; this.sources = new Map(); this.pending = new Map();
|
|
32
|
-
this.fileCaches = new Map(); this.projections = new Map();
|
|
32
|
+
this.fileCaches = new Map(); this.projections = new Map(); this.notSessions = new Set();
|
|
33
33
|
this.metrics = {}; this.closed = false; this.running = null;
|
|
34
34
|
this.watches = new SessionIndexWatches({
|
|
35
35
|
changed: (id, event) => this.#event(id, event),
|
|
@@ -425,8 +425,9 @@ export class SessionIndexOwner extends EventEmitter {
|
|
|
425
425
|
|
|
426
426
|
async #file(source, path) {
|
|
427
427
|
const old = source.parts.get(path);
|
|
428
|
+
let info = null;
|
|
428
429
|
try {
|
|
429
|
-
|
|
430
|
+
info = await sourceStat(path);
|
|
430
431
|
if (this.closed) return;
|
|
431
432
|
// Saved Supercode can retain a metadata row after both content files
|
|
432
433
|
// disappear. Other formats require their native transcript file.
|
|
@@ -459,7 +460,24 @@ export class SessionIndexOwner extends EventEmitter {
|
|
|
459
460
|
source.parts.set(path, { path, kind: 'file', state, stamps });
|
|
460
461
|
this.metrics.changed_rows = (this.metrics.changed_rows ?? 0) + changed;
|
|
461
462
|
} catch (error) {
|
|
462
|
-
if (
|
|
463
|
+
if (this.closed) return;
|
|
464
|
+
if (error?.code === 'not_a_session' && info) return this.#notSession(source, path, old, info, error);
|
|
465
|
+
this.store.fail(source.id, path, 'file', error);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** A file in a harness's folder that is not one of its sessions (no session header) holds no rows and is no
|
|
470
|
+
* failure: it never makes the index, or the machine, unread. Its fingerprint, taken before the read, keeps it from
|
|
471
|
+
* being read again until it changes; the daemon log names it once. */
|
|
472
|
+
#notSession(source, path, old, info, error) {
|
|
473
|
+
const stamps = [{ path, fingerprint: fileFingerprint(info) }];
|
|
474
|
+
const state = { not_session: true, ...(source.harness === 'gemini' ? { projectsStamp: source.projectsStamp } : {}) };
|
|
475
|
+
this.store.commitPart(source.id, path, { kind: 'file', state, stamps, rows: [], invalidate: !sameStamps(stamps, old?.stamps) });
|
|
476
|
+
this.#apply(source, path, []);
|
|
477
|
+
source.parts.set(path, { path, kind: 'file', state, stamps });
|
|
478
|
+
if (!this.notSessions.has(path)) {
|
|
479
|
+
this.notSessions.add(path); this.metrics.not_sessions = this.notSessions.size;
|
|
480
|
+
this.log(`session index: left out ${path}: ${error.message} (${this.notSessions.size} file${this.notSessions.size === 1 ? '' : 's'} left out)`);
|
|
463
481
|
}
|
|
464
482
|
}
|
|
465
483
|
|
package/terminal-mail.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Per-recipient terminal mailbox. A retained paste is never repeated: retries
|
|
2
|
-
// press Enter and look for the exact
|
|
2
|
+
// press Enter and look for the exact message in the native transcript (a user row, or the queued_command
|
|
3
|
+
// attachment a running Claude Code turn absorbs it as).
|
|
3
4
|
import { statSync, openSync, readSync, closeSync } from 'node:fs';
|
|
4
5
|
import { createHash, randomUUID } from 'node:crypto';
|
|
5
6
|
import { spawnSync } from 'node:child_process';
|
|
@@ -52,7 +53,16 @@ function claudeHolds(batch, shown, owned = false) {
|
|
|
52
53
|
return batch.placeholder === placeholder[0];
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
/** Whether Claude Code recorded `text` as the prompt `wanted`. Claude records a large paste in a native
|
|
57
|
+
* `<pasted_content>` envelope; its whole body is compared, with line-ending and outer-whitespace normalization. */
|
|
58
|
+
function claudeSame(text, wanted) {
|
|
59
|
+
const native = normalized(text), want = normalized(wanted);
|
|
60
|
+
if (native === want) return true;
|
|
61
|
+
const pasted = /^<pasted_content id="([0-9a-f]{4})">\n([\s\S]*)\n<\/pasted_content id="\1">$/u.exec(native);
|
|
62
|
+
return Boolean(pasted) && normalized(pasted[2]) === want;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function confirmed(batch) {
|
|
56
66
|
let fd;
|
|
57
67
|
try { fd = openSync(batch.path, 'r'); } catch (error) { if (error.code === 'ENOENT') return false; throw error; }
|
|
58
68
|
try {
|
|
@@ -68,6 +78,12 @@ function confirmed(batch) {
|
|
|
68
78
|
while ((end = buffered.indexOf('\n')) >= 0) {
|
|
69
79
|
const line = buffered.slice(0, end); buffered = buffered.slice(end + 1);
|
|
70
80
|
let row; try { row = JSON.parse(line); } catch { continue; }
|
|
81
|
+
// A prompt submitted while a turn runs is queued, and reaches the model as a `queued_command` attachment when
|
|
82
|
+
// the turn absorbs it (queue-operation remove, reason absorbed_mid_turn); no `user` row is written for it.
|
|
83
|
+
// That attachment is its delivery. An enqueue alone is not: a queued prompt can still be withdrawn.
|
|
84
|
+
if (batch.kind === 'claude-code' && row.type === 'attachment' && row.attachment?.type === 'queued_command'
|
|
85
|
+
&& row.isSidechain !== true && (!batch.sessionId || row.sessionId === batch.sessionId)
|
|
86
|
+
&& typeof row.attachment.prompt === 'string' && claudeSame(row.attachment.prompt, batch.text)) return { fragments: 1 };
|
|
71
87
|
const content = batch.kind === 'codex' && row.type === 'response_item' && row.payload?.type === 'message' && row.payload.role === 'user'
|
|
72
88
|
? row.payload.content : batch.kind === 'claude-code' && row.type === 'user' && !row.isMeta && (!batch.sessionId || row.sessionId === batch.sessionId)
|
|
73
89
|
? row.message?.content : batch.kind === 'grok' && row.type === 'user' ? row.content : null;
|
|
@@ -75,13 +91,7 @@ function confirmed(batch) {
|
|
|
75
91
|
if (text === null || text.length === 0) continue;
|
|
76
92
|
if (text === batch.text) return { fragments: 1 };
|
|
77
93
|
if (batch.kind === 'claude-code') {
|
|
78
|
-
|
|
79
|
-
if (native === wanted) return { fragments: 1 };
|
|
80
|
-
// Claude records a large paste in this native envelope. Compare its
|
|
81
|
-
// whole body with matching identifiers, using the existing line-ending
|
|
82
|
-
// and outer-whitespace normalization.
|
|
83
|
-
const pasted = /^<pasted_content id="([0-9a-f]{4})">\n([\s\S]*)\n<\/pasted_content id="\1">$/u.exec(native);
|
|
84
|
-
if (pasted && normalized(pasted[2]) === wanted) return { fragments: 1 };
|
|
94
|
+
if (claudeSame(text, batch.text)) return { fragments: 1 };
|
|
85
95
|
continue;
|
|
86
96
|
}
|
|
87
97
|
// A Windows Codex is pasted its text with line ends as `\n` (codexWindowsPastes).
|