@volter/supercode-teams 0.2.157 → 0.2.159
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/connector/index.mjs +26 -6
- package/package.json +1 -1
- package/terminal-mail.mjs +22 -4
package/connector/index.mjs
CHANGED
|
@@ -514,16 +514,36 @@ export class TeamsConnector {
|
|
|
514
514
|
}
|
|
515
515
|
labelTurns(key,entries,messages){
|
|
516
516
|
const labels={...this.state.turn_labels?.[key]},notes=[...(this.state.turn_notes?.[key]??[])];let found=false;
|
|
517
|
+
const annotations=source=>Object.fromEntries([['source_via',source.via],['source_principal_id',source.from?.principalId],['source_name',source.from?.name]].filter(([,item])=>typeof item==='string'&&item));
|
|
518
|
+
const identities=messages.map(message=>{
|
|
519
|
+
if(message.value?.role!=='user')return null;
|
|
520
|
+
const stamp=message.value.metadata?.timestamp,native=typeof stamp==='string'?Date.parse(stamp):NaN;
|
|
521
|
+
const at=Number.isFinite(native)?native:message.recorded_at;
|
|
522
|
+
if(!Number.isFinite(at))return null;
|
|
523
|
+
return {at,key:`turn:${hash(canonicalJson([Number.isFinite(native)?stamp:at,messageText(message.value)]))}`};
|
|
524
|
+
});
|
|
525
|
+
// Positional native keys can be reused after compaction. Upgrade a legacy
|
|
526
|
+
// label only when its dated, fully annotated row matches the prior capture.
|
|
527
|
+
const legacy=new Set(Object.keys(labels).filter(id=>!id.startsWith('turn:'))),prior=new Map();
|
|
528
|
+
const origin=this.state.origins[key];
|
|
529
|
+
if(legacy.size)for(const [index,id]of(origin?.observed_keys??[]).entries())if(legacy.has(id))prior.set(id,origin.observed_digests?.[index]);
|
|
530
|
+
messages.forEach((message,index)=>{
|
|
531
|
+
const identity=identities[index],source=labels[message.native_key];
|
|
532
|
+
if(!identity||labels[identity.key]||!source||!Number.isFinite(Date.parse(message.value.metadata?.timestamp??'')))return;
|
|
533
|
+
const value={...message.value,metadata:{...message.value.metadata,...annotations(source)}};
|
|
534
|
+
if(prior.get(message.native_key)!==hash(canonicalJson(value)))return;
|
|
535
|
+
labels[identity.key]=source;delete labels[message.native_key];found=true;
|
|
536
|
+
});
|
|
517
537
|
for(const note of [...notes]){
|
|
518
|
-
const
|
|
519
|
-
if(
|
|
538
|
+
const index=messages.findIndex((message,index)=>identities[index]&&!labels[identities[index].key]&&identities[index].at>=note.at-60000&&messageText(message.value)===note.text);
|
|
539
|
+
if(index>=0){labels[identities[index].key]=note.source;notes.splice(notes.indexOf(note),1);found=true;}
|
|
520
540
|
}
|
|
521
541
|
if(found){(this.state.turn_labels??={})[key]=labels;if(notes.length)this.state.turn_notes[key]=notes;else delete this.state.turn_notes[key];this.save();}
|
|
522
542
|
messages.forEach((message,index)=>{
|
|
523
|
-
const source=labels[
|
|
524
|
-
const annotations
|
|
525
|
-
message.value={...message.value,metadata:{...message.value.metadata,...
|
|
526
|
-
const entry=entries[index];if(entry.value)entry.value={...entry.value,metadata:{...entry.value.metadata,...
|
|
543
|
+
const identity=identities[index],source=identity&&labels[identity.key];if(!source)return;
|
|
544
|
+
const value=annotations(source);
|
|
545
|
+
message.value={...message.value,metadata:{...message.value.metadata,...value}};
|
|
546
|
+
const entry=entries[index];if(entry.value)entry.value={...entry.value,metadata:{...entry.value.metadata,...value}};else entry.metadata={...entry.metadata,...value};
|
|
527
547
|
});
|
|
528
548
|
}
|
|
529
549
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.159",
|
|
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",
|
package/terminal-mail.mjs
CHANGED
|
@@ -14,6 +14,7 @@ function confirmed(batch) {
|
|
|
14
14
|
let offset = batch.offset, buffered = '';
|
|
15
15
|
const decoder = new StringDecoder('utf8');
|
|
16
16
|
const bytes = Buffer.alloc(64 * 1024);
|
|
17
|
+
let matched = 0, fragments = 0;
|
|
17
18
|
for (;;) {
|
|
18
19
|
const count = readSync(fd, bytes, 0, bytes.length, offset);
|
|
19
20
|
if (!count) return false;
|
|
@@ -24,7 +25,22 @@ function confirmed(batch) {
|
|
|
24
25
|
let row; try { row = JSON.parse(line); } catch { continue; }
|
|
25
26
|
const content = batch.kind === 'codex' && row.type === 'response_item' && row.payload?.type === 'message' && row.payload.role === 'user'
|
|
26
27
|
? row.payload.content : batch.kind === 'grok' && row.type === 'user' ? row.content : null;
|
|
27
|
-
|
|
28
|
+
const text = textOf(content);
|
|
29
|
+
if (text === null || text.length === 0) continue;
|
|
30
|
+
if (text === batch.text) return { fragments: 1 };
|
|
31
|
+
// Older mail delivery omitted the paste delimiter, so terminal newlines
|
|
32
|
+
// submitted one envelope as several native user messages. Recover only
|
|
33
|
+
// exact, ordered fragments of the retained bytes, separated by original
|
|
34
|
+
// newlines. Never infer delivery from an id, substring or empty composer.
|
|
35
|
+
let next = matched;
|
|
36
|
+
while (next < batch.text.length && batch.text[next] === '\n') next++;
|
|
37
|
+
if (matched && batch.text.startsWith(text, next)) {
|
|
38
|
+
matched = next + text.length; fragments++;
|
|
39
|
+
} else if (batch.text.startsWith(text)) {
|
|
40
|
+
matched = text.length; fragments = 1;
|
|
41
|
+
} else { matched = 0; fragments = 0; }
|
|
42
|
+
if (matched === batch.text.length) return { fragments };
|
|
43
|
+
|
|
28
44
|
}
|
|
29
45
|
}
|
|
30
46
|
} finally { closeSync(fd); }
|
|
@@ -50,7 +66,7 @@ export class TerminalMailbox {
|
|
|
50
66
|
const delivered = ids.every(id => this.state.receipts.some(r => r.message_ids.includes(id)));
|
|
51
67
|
const batch = this.state.active;
|
|
52
68
|
return { delivered, queued: !delivered, message_ids: ids,
|
|
53
|
-
...(delivered ? { receipt: 'native-user-message' } : { reason: batch?.error ?? 'queued', batch: batch?.id ?? null }) };
|
|
69
|
+
...(delivered ? { receipt: this.state.receipts.some(r => r.receipt === 'native-user-messages' && r.message_ids.some(id => ids.includes(id))) ? 'native-user-messages' : 'native-user-message' } : { reason: batch?.error ?? 'queued', batch: batch?.id ?? null }) };
|
|
54
70
|
}
|
|
55
71
|
enqueue(text, ids, identity, host, session) {
|
|
56
72
|
const messageIds = ids?.length ? ids : [`input-${randomUUID()}`];
|
|
@@ -95,7 +111,7 @@ export class TerminalMailbox {
|
|
|
95
111
|
if (!batch) return;
|
|
96
112
|
const seen = batch.offset !== null ? confirmed(batch) : (await this.load(batch.locator, { message_offset: batch.messageOffset, message_limit: 1000 })).session?.messages?.some(message => message.role === 'user' && textOf(message.content) === batch.text);
|
|
97
113
|
if (seen) {
|
|
98
|
-
const receipt = { batch: batch.id, message_ids: batch.message_ids, delivered: true, receipt: 'native-user-message', at: Date.now() };
|
|
114
|
+
const receipt = { batch: batch.id, message_ids: batch.message_ids, delivered: true, receipt: seen.fragments > 1 ? 'native-user-messages' : 'native-user-message', fragments: seen.fragments ?? 1, at: Date.now() };
|
|
99
115
|
appendFileSync(join(this.dir, 'receipts.jsonl'), JSON.stringify(receipt) + '\n', { mode: 0o600 });
|
|
100
116
|
this.state.receipts.push(receipt);
|
|
101
117
|
this.state.active = null; this.save(); this.log(receipt); return;
|
|
@@ -104,7 +120,9 @@ export class TerminalMailbox {
|
|
|
104
120
|
// Persist uncertainty before I/O. A daemon restart checks the transcript;
|
|
105
121
|
// it never guesses that an unacknowledged paste did not happen.
|
|
106
122
|
batch.phase = 'paste_started'; this.save();
|
|
107
|
-
|
|
123
|
+
// The terminal host's final newline selects one bracketed paste;
|
|
124
|
+
// submitMode:none holds the separate Enter until the paste completes.
|
|
125
|
+
await this.host.sendInput(this.session.id, `${batch.text}\n`, { submitMode: 'none' });
|
|
108
126
|
batch.phase = 'pasted'; this.save();
|
|
109
127
|
}
|
|
110
128
|
if (batch.phase === 'paste_started') {
|