instar 1.3.1346 → 1.3.1347
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +69 -20
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +14 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +71 -9
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +108 -20
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +4 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/routes.js +1 -1
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/ListenerSessionManager.d.ts +35 -2
- package/dist/threadline/ListenerSessionManager.d.ts.map +1 -1
- package/dist/threadline/ListenerSessionManager.js +48 -4
- package/dist/threadline/ListenerSessionManager.js.map +1 -1
- package/dist/threadline/ThreadlineRouter.d.ts +12 -0
- package/dist/threadline/ThreadlineRouter.d.ts.map +1 -1
- package/dist/threadline/ThreadlineRouter.js +18 -0
- package/dist/threadline/ThreadlineRouter.js.map +1 -1
- package/dist/threadline/TopicLinkageHandler.d.ts +40 -4
- package/dist/threadline/TopicLinkageHandler.d.ts.map +1 -1
- package/dist/threadline/TopicLinkageHandler.js +45 -2
- package/dist/threadline/TopicLinkageHandler.js.map +1 -1
- package/dist/threadline/autoAck.d.ts +87 -4
- package/dist/threadline/autoAck.d.ts.map +1 -1
- package/dist/threadline/autoAck.js +63 -0
- package/dist/threadline/autoAck.js.map +1 -1
- package/dist/threadline/topicRespawnHook.d.ts +17 -2
- package/dist/threadline/topicRespawnHook.d.ts.map +1 -1
- package/dist/threadline/topicRespawnHook.js +7 -2
- package/dist/threadline/topicRespawnHook.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/src/scaffold/templates.ts +4 -0
- package/upgrades/1.3.1347.md +38 -0
- package/upgrades/side-effects/relay-ack-waits-for-delivery.md +147 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAwGA,OAAO,EAAE,cAAc,EAAwD,MAAM,2BAA2B,CAAC;AAUjH,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AA4BjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/server.js
CHANGED
|
@@ -242,7 +242,13 @@ import { ConversationStore } from '../threadline/ConversationStore.js';
|
|
|
242
242
|
import { ThreadLog } from '../threadline/ThreadLog.js';
|
|
243
243
|
import { ThreadMessageRecorder } from '../threadline/recordThreadMessage.js';
|
|
244
244
|
import { recordInboundAck } from '../threadline/recordInboundAck.js';
|
|
245
|
-
import { isAutoAckInbound, autoAckRecognition, looksLikeAutoAckText, createAckRateLimiter, runRelayAckStage,
|
|
245
|
+
import { isAutoAckInbound, autoAckRecognition, looksLikeAutoAckText, createAckRateLimiter, runRelayAckStage, createRelayAckSettler, describeUndeliveredRouterResult } from '../threadline/autoAck.js';
|
|
246
|
+
/** ACT-091: the session manager's observed paste → the topic handler's inject outcome (starts the busy watch lazily). */
|
|
247
|
+
function liveInjectOutcomeFromObservation(r) {
|
|
248
|
+
if (r.state === 'queued-busy')
|
|
249
|
+
return { state: 'queued-busy', settled: r.settle() };
|
|
250
|
+
return { state: r.state };
|
|
251
|
+
}
|
|
246
252
|
import { createThreadlineTopicRespawn } from '../threadline/topicRespawnHook.js';
|
|
247
253
|
import { WarrantsReplyGate, evaluateAndRecordInbound } from '../threadline/WarrantsReplyGate.js';
|
|
248
254
|
import { CollaborationSurfacer } from '../threadline/CollaborationSurfacer.js';
|
|
@@ -3076,7 +3082,7 @@ getHubClassifierDeps) {
|
|
|
3076
3082
|
},
|
|
3077
3083
|
registeredSession: (topicId) => telegram.getSessionForTopic(topicId),
|
|
3078
3084
|
isSessionAlive: (name) => sessionManager.isSessionAlive(name),
|
|
3079
|
-
injectConfirmed: (name, text) => sessionManager.
|
|
3085
|
+
injectConfirmed: async (name, text) => liveInjectOutcomeFromObservation(await sessionManager.injectPasteNotificationObserved(name, text)),
|
|
3080
3086
|
leaseGate: (topicId) => g3ShouldSpawnLocally(topicId),
|
|
3081
3087
|
classifyDeath: (name) => {
|
|
3082
3088
|
try {
|
|
@@ -16907,9 +16913,11 @@ export async function startServer(options) {
|
|
|
16907
16913
|
// Confirm the inject actually submitted (not left stuck at the prompt
|
|
16908
16914
|
// by the paste-end race). A bare dispatch returning true is the A2 bug:
|
|
16909
16915
|
// a stalled inject must NOT count as delivered, so the Telegram fallback
|
|
16910
|
-
// surfaces the reply instead.
|
|
16916
|
+
// surfaces the reply instead. A paste into a MID-TURN session is
|
|
16917
|
+
// reported `queued-busy` (provisional) — the handler finishes the
|
|
16918
|
+
// route from the late verdict (ACT-091).
|
|
16911
16919
|
try {
|
|
16912
|
-
return await sessionManager.
|
|
16920
|
+
return liveInjectOutcomeFromObservation(await sessionManager.injectPasteNotificationObserved(sessionName, text));
|
|
16913
16921
|
}
|
|
16914
16922
|
catch {
|
|
16915
16923
|
return false;
|
|
@@ -17452,11 +17460,15 @@ export async function startServer(options) {
|
|
|
17452
17460
|
// the implicit delivery ack + the ledger disposition `no-reply` and the
|
|
17453
17461
|
// handler RETURNS here, before the auto-ack send, the inbox append, the
|
|
17454
17462
|
// Telegram mirror and the gate. Any other message gets our own ack
|
|
17455
|
-
// (post-trust-verification, never for status messages, rate-limited)
|
|
17463
|
+
// (post-trust-verification, never for status messages, rate-limited)
|
|
17464
|
+
// once routing has an outcome — see relayAck below.
|
|
17456
17465
|
const recordRelayDelivery = () => recordInboundAck({ a2aDeliveryTracker }, { threadId: msg.threadId, senderFingerprint, senderName: senderName ?? null });
|
|
17457
|
-
// ACT-091:
|
|
17458
|
-
// session
|
|
17459
|
-
// the routing outcome
|
|
17466
|
+
// ACT-091: any hand-off can reach no live consumer (a reaped topic
|
|
17467
|
+
// session, a refused or queued spawn, a listener gone stale), so
|
|
17468
|
+
// our ack — which promises a reply — waits for the routing outcome on
|
|
17469
|
+
// EVERY path. Each terminal exit below calls relayAck.settle(...) just
|
|
17470
|
+
// before it returns; an exit that settles nothing (a throw) sends no
|
|
17471
|
+
// ack, which is the honest result when nothing received the message.
|
|
17460
17472
|
const isTopicBoundReply = !!msg.threadId && threadResumeMap.get(msg.threadId)?.originTopicId !== undefined;
|
|
17461
17473
|
const relayAckDeps = {
|
|
17462
17474
|
autoAckEnabled: config.threadline?.autoAck !== false,
|
|
@@ -17467,8 +17479,9 @@ export async function startServer(options) {
|
|
|
17467
17479
|
};
|
|
17468
17480
|
const relayAckInput = {
|
|
17469
17481
|
isAutoAck, trustLevel, msgType, senderFingerprint, threadId: msg.threadId, ticket: ledgerTicket,
|
|
17470
|
-
deferSend:
|
|
17482
|
+
deferSend: true,
|
|
17471
17483
|
};
|
|
17484
|
+
const relayAck = createRelayAckSettler(relayAckDeps, relayAckInput);
|
|
17472
17485
|
const ackStage = runRelayAckStage(relayAckDeps, relayAckInput);
|
|
17473
17486
|
if (ackStage === 'ack-consumed') {
|
|
17474
17487
|
console.log(`[relay] delivery ack from ${senderName} (thread: ${msg.threadId ?? 'none'}, by ${autoAckRecognition({ type: msgType, text: textContent })}) recorded — not acked, not routed`);
|
|
@@ -17550,11 +17563,11 @@ export async function startServer(options) {
|
|
|
17550
17563
|
else
|
|
17551
17564
|
ledgerTicket?.recordNoReply();
|
|
17552
17565
|
console.log(`[relay] warrants-reply gate suppressed reply (${decision.verdict.signal}) for ${senderName} thread ${gateThreadId.slice(0, 8)}`);
|
|
17553
|
-
// ACT-091:
|
|
17554
|
-
// non-promising "Message
|
|
17555
|
-
//
|
|
17556
|
-
|
|
17557
|
-
|
|
17566
|
+
// ACT-091: the ack on a terminal no-reply exit (a no-reply verdict
|
|
17567
|
+
// or an exhausted loop budget) is the non-promising "Message
|
|
17568
|
+
// received." — received, deliberately not answered — so the
|
|
17569
|
+
// sender's tracker never reads a stuck channel.
|
|
17570
|
+
relayAck.settle('no-reply');
|
|
17558
17571
|
// On budget exhaustion, surface ONE status notice — never silently
|
|
17559
17572
|
// drop. CMT-519: route it to the SILENT Threadline hub (not a
|
|
17560
17573
|
// per-event attention topic, not the parent topic the operator is
|
|
@@ -17592,8 +17605,9 @@ export async function startServer(options) {
|
|
|
17592
17605
|
}
|
|
17593
17606
|
// Phase 2a: Pipe-mode session for simple queries (lightweight, auto-exit)
|
|
17594
17607
|
// Rapid-fire same-thread guard: if an active pipe session already exists for this
|
|
17595
|
-
// thread, fall through to the listener/
|
|
17596
|
-
//
|
|
17608
|
+
// thread, fall through to the listener/router path instead of killing the prior
|
|
17609
|
+
// tmux session. With no proven listener reader that is the router, which does not
|
|
17610
|
+
// know about the pipe session: a second session may answer on the same thread.
|
|
17597
17611
|
if (pipeSpawner &&
|
|
17598
17612
|
msg.threadId &&
|
|
17599
17613
|
!threadResumeMap.get(msg.threadId) &&
|
|
@@ -17626,6 +17640,9 @@ export async function startServer(options) {
|
|
|
17626
17640
|
if (result.spawned) {
|
|
17627
17641
|
ledgerTicket?.recordHandoff('pipe');
|
|
17628
17642
|
console.log(`[relay] Pipe session spawned for ${senderName} (thread: ${msg.threadId.slice(0, 8)})`);
|
|
17643
|
+
// ACT-091: the pipe session exists and the message is its
|
|
17644
|
+
// prompt — a live consumer has it, so the ack goes out now.
|
|
17645
|
+
relayAck.settle('pipe-spawned');
|
|
17629
17646
|
return;
|
|
17630
17647
|
}
|
|
17631
17648
|
}
|
|
@@ -17636,6 +17653,9 @@ export async function startServer(options) {
|
|
|
17636
17653
|
}
|
|
17637
17654
|
}
|
|
17638
17655
|
// Phase 2b: Route to warm listener if available and appropriate.
|
|
17656
|
+
// shouldUseListener is false unless a live inbox reader is PROVEN (a
|
|
17657
|
+
// fresh heartbeat); with none, the message falls through to the router
|
|
17658
|
+
// below instead of being written to a file nothing reads.
|
|
17639
17659
|
// EXCEPT topic-bound replies: a reply on a thread bound to a Telegram
|
|
17640
17660
|
// topic must reach handleInboundMessage → TopicLinkageHandler so it
|
|
17641
17661
|
// surfaces in the bound topic. The listener inbox is a side-channel
|
|
@@ -17648,6 +17668,15 @@ export async function startServer(options) {
|
|
|
17648
17668
|
listenerManager.writeToInbox({ from: senderFingerprint, senderName, trustLevel, threadId: msg.threadId ?? getSyntheticThreadId(senderFingerprint), text: ledgerNotice ? `[server notice: ${ledgerNotice}]\n\n${textContent}` : textContent });
|
|
17649
17669
|
ledgerTicket?.recordHandoff('listener');
|
|
17650
17670
|
console.log(`[relay] Routed to listener inbox from ${senderName} (trust: ${trustLevel})`);
|
|
17671
|
+
// ACT-091: shouldUseListener is true only while a reader's heartbeat
|
|
17672
|
+
// is fresh (ListenerSessionManager.hasLiveReader), so this hand-off
|
|
17673
|
+
// reaches a proven live reader and gets the promising ack. Re-read
|
|
17674
|
+
// after the write: if the heartbeat went stale in between, the write
|
|
17675
|
+
// is only a queue entry and the ack is withheld.
|
|
17676
|
+
const listenerAck = relayAck.settle(listenerManager.hasLiveReader() ? 'listener-delivered' : 'listener-queued');
|
|
17677
|
+
if (listenerAck === 'skipped-undelivered') {
|
|
17678
|
+
console.warn(`[relay] Auto-ack withheld for ${senderName} (thread: ${msg.threadId ?? 'none'}) — queued in the listener inbox; no live listener is proven`);
|
|
17679
|
+
}
|
|
17651
17680
|
return;
|
|
17652
17681
|
}
|
|
17653
17682
|
// Route through ThreadlineRouter (cold-spawn path)
|
|
@@ -17688,13 +17717,33 @@ export async function startServer(options) {
|
|
|
17688
17717
|
console.log(`[relay] Spawned session for ${senderName} (trust: ${trustLevel}, thread: ${result.threadId})`);
|
|
17689
17718
|
if (result.resumed)
|
|
17690
17719
|
console.log(`[relay] Resumed session for ${senderName} (thread: ${result.threadId})`);
|
|
17691
|
-
if (result.handled && result.delivered === false) {
|
|
17720
|
+
if (result.handled && result.delivered === false && !result.deliveryPending) {
|
|
17692
17721
|
console.warn(`[relay] Message from ${senderName} (thread: ${result.threadId}) accepted but not delivered to a live session${result.queued ? ' (queued)' : ''}`);
|
|
17693
17722
|
}
|
|
17694
|
-
|
|
17695
|
-
|
|
17723
|
+
// ACT-091: the last exit — the ack follows the router's own report of
|
|
17724
|
+
// a live hand-off (cold spawn, resumed/warm session, topic session).
|
|
17725
|
+
// A capacity refusal the spawn manager queued (`queued: true`) is
|
|
17726
|
+
// still undelivered: no ack, logged as queued — the decision and its
|
|
17727
|
+
// reasons are at describeUndeliveredRouterResult.
|
|
17728
|
+
if (result.deliveryPending) {
|
|
17729
|
+
// The reply was typed into a live session that is mid-turn, so it
|
|
17730
|
+
// sits as queued input: not delivered YET. The ack is settled from
|
|
17731
|
+
// the late verdict (bounded by the session manager's watch
|
|
17732
|
+
// ceiling), through the same one-shot settler — the promising ack
|
|
17733
|
+
// is sent only once the text has actually left the prompt.
|
|
17734
|
+
console.log(`[relay] Delivery to ${senderName} (thread: ${result.threadId}) pending — queued in a busy live session; the ack waits for the text to leave the prompt`);
|
|
17735
|
+
void result.deliveryPending.then(({ delivered, reason }) => {
|
|
17736
|
+
const late = relayAck.settle(delivered ? 'router-delivered' : 'router-undelivered');
|
|
17737
|
+
if (delivered)
|
|
17738
|
+
console.log(`[relay] Queued reply for ${senderName} (thread: ${result.threadId}) left the prompt (${reason}) — delivered${late === 'sent' ? ', ack sent' : ''}`);
|
|
17739
|
+
else
|
|
17740
|
+
console.warn(`[relay] Auto-ack withheld for ${senderName} (thread: ${result.threadId}) — queued text never left the prompt (${reason})`);
|
|
17741
|
+
});
|
|
17742
|
+
}
|
|
17743
|
+
else {
|
|
17744
|
+
const ackOutcome = relayAck.settle(result.delivered === true ? 'router-delivered' : 'router-undelivered');
|
|
17696
17745
|
if (ackOutcome === 'skipped-undelivered') {
|
|
17697
|
-
console.warn(`[relay] Auto-ack withheld for ${senderName} (thread: ${result.threadId}) —
|
|
17746
|
+
console.warn(`[relay] Auto-ack withheld for ${senderName} (thread: ${result.threadId}) — ${describeUndeliveredRouterResult(result)}`);
|
|
17698
17747
|
}
|
|
17699
17748
|
}
|
|
17700
17749
|
}
|