instar 1.3.1345 → 1.3.1346
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 +105 -4
- 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/SpawnAdmission.d.ts +1 -1
- package/dist/core/SpawnAdmission.d.ts.map +1 -1
- package/dist/core/SpawnAdmission.js.map +1 -1
- package/dist/core/SpawningTopicsRegistry.d.ts +13 -0
- package/dist/core/SpawningTopicsRegistry.d.ts.map +1 -1
- package/dist/core/SpawningTopicsRegistry.js +19 -0
- package/dist/core/SpawningTopicsRegistry.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/threadline/ThreadlineRouter.d.ts.map +1 -1
- package/dist/threadline/ThreadlineRouter.js +43 -16
- package/dist/threadline/ThreadlineRouter.js.map +1 -1
- package/dist/threadline/TopicLinkageHandler.d.ts +31 -1
- package/dist/threadline/TopicLinkageHandler.d.ts.map +1 -1
- package/dist/threadline/TopicLinkageHandler.js +34 -3
- package/dist/threadline/TopicLinkageHandler.js.map +1 -1
- package/dist/threadline/autoAck.d.ts +28 -0
- package/dist/threadline/autoAck.d.ts.map +1 -1
- package/dist/threadline/autoAck.js +35 -2
- package/dist/threadline/autoAck.js.map +1 -1
- package/dist/threadline/topicRespawnHook.d.ts +67 -0
- package/dist/threadline/topicRespawnHook.d.ts.map +1 -0
- package/dist/threadline/topicRespawnHook.js +98 -0
- package/dist/threadline/topicRespawnHook.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +19 -19
- 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.1346.md +27 -0
- package/upgrades/side-effects/threadline-reaped-topic-respawn.md +111 -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,8 @@ 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 } from '../threadline/autoAck.js';
|
|
245
|
+
import { isAutoAckInbound, autoAckRecognition, looksLikeAutoAckText, createAckRateLimiter, runRelayAckStage, settleDeferredRelayAck } from '../threadline/autoAck.js';
|
|
246
|
+
import { createThreadlineTopicRespawn } from '../threadline/topicRespawnHook.js';
|
|
246
247
|
import { WarrantsReplyGate, evaluateAndRecordInbound } from '../threadline/WarrantsReplyGate.js';
|
|
247
248
|
import { CollaborationSurfacer } from '../threadline/CollaborationSurfacer.js';
|
|
248
249
|
import { ListenerSessionManager } from '../threadline/ListenerSessionManager.js';
|
|
@@ -586,6 +587,12 @@ let _sweptInboundStore = null;
|
|
|
586
587
|
// The drain's local-delivery tail (§3.1 via:'drain') — assigned inside
|
|
587
588
|
// wireTelegramRouting where the session primitives live in scope.
|
|
588
589
|
let _drainLocalDeliver = null;
|
|
590
|
+
// ACT-091: wake a topic's dead/reaped session for a topic-bound Threadline
|
|
591
|
+
// reply — assigned inside wireTelegramRouting where the respawn primitives
|
|
592
|
+
// (and the per-topic spawn guard) live in scope. Null ⇒ not wired.
|
|
593
|
+
let _threadlineTopicRespawn = null;
|
|
594
|
+
/** ACT-091: "is a spawn for this topic in flight?" — the spawningTopics read, for the handler. */
|
|
595
|
+
let _threadlineTopicSpawning = null;
|
|
589
596
|
// Emergency-stop integration (§3.6): marks a topic stopped for the drain's
|
|
590
597
|
// pass/batch/chokepoint consults AND settles its custody (terminal
|
|
591
598
|
// operator-stop + PIS cleanup + loss report). Set with the engine.
|
|
@@ -3049,6 +3056,72 @@ getHubClassifierDeps) {
|
|
|
3049
3056
|
spawningTopics.clear(topicId, _spawnTokD);
|
|
3050
3057
|
}
|
|
3051
3058
|
};
|
|
3059
|
+
// ── ACT-091: topic-bound Threadline reply into a dead/reaped topic session ──
|
|
3060
|
+
// The decision lives in createThreadlineTopicRespawn (testable); this binds
|
|
3061
|
+
// it to the REAL primitives the Telegram dead-session path uses: ownership,
|
|
3062
|
+
// the G3 lease gate, the death classification, SpawnAdmission (its own
|
|
3063
|
+
// journal-only callsite), the spawningTopics guard, respawnSessionForTopic
|
|
3064
|
+
// (--resume) / spawnSessionForTopic, each awaited through the initial
|
|
3065
|
+
// injection. A spawn already in flight is WAITED for, then the reply is typed
|
|
3066
|
+
// into the live session — a second reply inside one respawn is never lost.
|
|
3067
|
+
{
|
|
3068
|
+
_threadlineTopicSpawning = (topicId) => spawningTopics.has(topicId);
|
|
3069
|
+
_threadlineTopicRespawn = createThreadlineTopicRespawn({
|
|
3070
|
+
spawning: spawningTopics,
|
|
3071
|
+
ownedElsewhere: (topicId) => {
|
|
3072
|
+
if (!_ownershipReadForDrain || !_meshSelfId)
|
|
3073
|
+
return false;
|
|
3074
|
+
const ownRec = _ownershipReadForDrain(String(topicId));
|
|
3075
|
+
return !!ownRec && ownRec.status === 'active' && ownRec.ownerMachineId !== _meshSelfId;
|
|
3076
|
+
},
|
|
3077
|
+
registeredSession: (topicId) => telegram.getSessionForTopic(topicId),
|
|
3078
|
+
isSessionAlive: (name) => sessionManager.isSessionAlive(name),
|
|
3079
|
+
injectConfirmed: (name, text) => sessionManager.injectPasteNotificationConfirmed(name, text),
|
|
3080
|
+
leaseGate: (topicId) => g3ShouldSpawnLocally(topicId),
|
|
3081
|
+
classifyDeath: (name) => {
|
|
3082
|
+
try {
|
|
3083
|
+
const output = sessionManager.captureOutput(name, 100);
|
|
3084
|
+
if (!output)
|
|
3085
|
+
return null;
|
|
3086
|
+
const death = classifySessionDeath(output, quotaTracker?.getState() ?? null);
|
|
3087
|
+
if (death.cause === 'quota_exhaustion' && death.confidence !== 'low')
|
|
3088
|
+
return 'quota';
|
|
3089
|
+
if (death.cause === 'context_exhausted')
|
|
3090
|
+
return 'context-exhausted';
|
|
3091
|
+
return 'other';
|
|
3092
|
+
}
|
|
3093
|
+
catch {
|
|
3094
|
+
return null; /* @silent-fallback-ok: classification failed — respawn as the Telegram dead path does */
|
|
3095
|
+
}
|
|
3096
|
+
},
|
|
3097
|
+
admit: (topicId) => {
|
|
3098
|
+
if (!_spawnAdmission)
|
|
3099
|
+
return { allow: true };
|
|
3100
|
+
try {
|
|
3101
|
+
const d = _spawnAdmission.admit({ sessionKey: String(topicId), callsite: 'threadline-topic-respawn' });
|
|
3102
|
+
if (d.allow)
|
|
3103
|
+
_ladderDryRunConsult(d, String(topicId), topicId);
|
|
3104
|
+
return { allow: d.allow, reason: d.reason };
|
|
3105
|
+
}
|
|
3106
|
+
catch (err) {
|
|
3107
|
+
// Same posture as the Telegram seam: an admission ERROR fails open.
|
|
3108
|
+
console.warn(`[spawn-admission] threadline-topic-respawn admit error (fail-open): ${err instanceof Error ? err.message : String(err)}`);
|
|
3109
|
+
return { allow: true };
|
|
3110
|
+
}
|
|
3111
|
+
},
|
|
3112
|
+
respawn: async (name, topicId, text, fresh) => {
|
|
3113
|
+
if (fresh)
|
|
3114
|
+
_topicResumeMap?.remove(topicId);
|
|
3115
|
+
await respawnSessionForTopic(sessionManager, telegram, name, topicId, text, topicMemory, undefined, undefined, { silent: true, awaitInitialInjection: true });
|
|
3116
|
+
},
|
|
3117
|
+
coldSpawn: async (topicId, text) => {
|
|
3118
|
+
const topicName = telegram.getTopicName(topicId) || `topic-${topicId}`;
|
|
3119
|
+
const newSessionName = await spawnSessionForTopic(sessionManager, telegram, topicName, topicId, text, topicMemory, undefined, undefined, undefined, { awaitInitialInjection: true });
|
|
3120
|
+
telegram.registerTopicSession(topicId, newSessionName, topicName);
|
|
3121
|
+
return newSessionName;
|
|
3122
|
+
},
|
|
3123
|
+
});
|
|
3124
|
+
}
|
|
3052
3125
|
}
|
|
3053
3126
|
/**
|
|
3054
3127
|
* Wire WhatsApp message routing: incoming messages → Claude sessions.
|
|
@@ -16856,6 +16929,12 @@ export async function startServer(options) {
|
|
|
16856
16929
|
getSessionForTopic: telegram
|
|
16857
16930
|
? (topicId) => telegram.getSessionForTopic(topicId)
|
|
16858
16931
|
: undefined,
|
|
16932
|
+
// ACT-091: a reply into a reaped topic session wakes it (late-bound —
|
|
16933
|
+
// Telegram routing is wired after this handler is constructed).
|
|
16934
|
+
respawnTopicSession: (args) => _threadlineTopicRespawn
|
|
16935
|
+
? _threadlineTopicRespawn(args)
|
|
16936
|
+
: Promise.resolve({ delivered: false, reason: 'telegram-routing-not-wired' }),
|
|
16937
|
+
isTopicSpawning: (topicId) => _threadlineTopicSpawning?.(topicId) === true,
|
|
16859
16938
|
// A2A cross-machine route §5: a topic-bound reply is typed into the
|
|
16860
16939
|
// topic's live session on whichever machine has it. Late-bound: the
|
|
16861
16940
|
// mesh client exists only after the multi-machine wiring below.
|
|
@@ -17375,13 +17454,22 @@ export async function startServer(options) {
|
|
|
17375
17454
|
// Telegram mirror and the gate. Any other message gets our own ack
|
|
17376
17455
|
// (post-trust-verification, never for status messages, rate-limited).
|
|
17377
17456
|
const recordRelayDelivery = () => recordInboundAck({ a2aDeliveryTracker }, { threadId: msg.threadId, senderFingerprint, senderName: senderName ?? null });
|
|
17378
|
-
|
|
17457
|
+
// ACT-091: a topic-bound reply can reach no live session (its topic
|
|
17458
|
+
// session was reaped), so its ack — which promises a reply — waits for
|
|
17459
|
+
// the routing outcome (settleDeferredRelayAck below).
|
|
17460
|
+
const isTopicBoundReply = !!msg.threadId && threadResumeMap.get(msg.threadId)?.originTopicId !== undefined;
|
|
17461
|
+
const relayAckDeps = {
|
|
17379
17462
|
autoAckEnabled: config.threadline?.autoAck !== false,
|
|
17380
17463
|
autoAckMessage: config.threadline?.autoAckMessage,
|
|
17381
17464
|
isAckRateLimited,
|
|
17382
17465
|
sendAck: (to, text, threadId) => { threadlineRelayClient.sendAck(to, text, threadId); },
|
|
17383
17466
|
recordDelivery: recordRelayDelivery,
|
|
17384
|
-
}
|
|
17467
|
+
};
|
|
17468
|
+
const relayAckInput = {
|
|
17469
|
+
isAutoAck, trustLevel, msgType, senderFingerprint, threadId: msg.threadId, ticket: ledgerTicket,
|
|
17470
|
+
deferSend: isTopicBoundReply,
|
|
17471
|
+
};
|
|
17472
|
+
const ackStage = runRelayAckStage(relayAckDeps, relayAckInput);
|
|
17385
17473
|
if (ackStage === 'ack-consumed') {
|
|
17386
17474
|
console.log(`[relay] delivery ack from ${senderName} (thread: ${msg.threadId ?? 'none'}, by ${autoAckRecognition({ type: msgType, text: textContent })}) recorded — not acked, not routed`);
|
|
17387
17475
|
return;
|
|
@@ -17462,6 +17550,11 @@ export async function startServer(options) {
|
|
|
17462
17550
|
else
|
|
17463
17551
|
ledgerTicket?.recordNoReply();
|
|
17464
17552
|
console.log(`[relay] warrants-reply gate suppressed reply (${decision.verdict.signal}) for ${senderName} thread ${gateThreadId.slice(0, 8)}`);
|
|
17553
|
+
// ACT-091: a deferred ack on a terminal no-reply exit becomes the
|
|
17554
|
+
// non-promising "Message received." — received, deliberately not
|
|
17555
|
+
// answered — so the sender's tracker never reads a stuck channel.
|
|
17556
|
+
if (relayAckInput.deferSend)
|
|
17557
|
+
settleDeferredRelayAck(relayAckDeps, relayAckInput, { delivered: false, noReply: true });
|
|
17465
17558
|
// On budget exhaustion, surface ONE status notice — never silently
|
|
17466
17559
|
// drop. CMT-519: route it to the SILENT Threadline hub (not a
|
|
17467
17560
|
// per-event attention topic, not the parent topic the operator is
|
|
@@ -17551,7 +17644,6 @@ export async function startServer(options) {
|
|
|
17551
17644
|
// for the user even though transport succeeded. (The pipe-spawn branch
|
|
17552
17645
|
// above is already excluded for topic-bound threads via its
|
|
17553
17646
|
// `!threadResumeMap.get(...)` guard, now that get() no longer nulls them.)
|
|
17554
|
-
const isTopicBoundReply = !!msg.threadId && threadResumeMap.get(msg.threadId)?.originTopicId !== undefined;
|
|
17555
17647
|
if (listenerManager && listenerManager.shouldUseListener(trustLevel, textContent.length) && !isTopicBoundReply) {
|
|
17556
17648
|
listenerManager.writeToInbox({ from: senderFingerprint, senderName, trustLevel, threadId: msg.threadId ?? getSyntheticThreadId(senderFingerprint), text: ledgerNotice ? `[server notice: ${ledgerNotice}]\n\n${textContent}` : textContent });
|
|
17557
17649
|
ledgerTicket?.recordHandoff('listener');
|
|
@@ -17596,6 +17688,15 @@ export async function startServer(options) {
|
|
|
17596
17688
|
console.log(`[relay] Spawned session for ${senderName} (trust: ${trustLevel}, thread: ${result.threadId})`);
|
|
17597
17689
|
if (result.resumed)
|
|
17598
17690
|
console.log(`[relay] Resumed session for ${senderName} (thread: ${result.threadId})`);
|
|
17691
|
+
if (result.handled && result.delivered === false) {
|
|
17692
|
+
console.warn(`[relay] Message from ${senderName} (thread: ${result.threadId}) accepted but not delivered to a live session${result.queued ? ' (queued)' : ''}`);
|
|
17693
|
+
}
|
|
17694
|
+
if (relayAckInput.deferSend) {
|
|
17695
|
+
const ackOutcome = settleDeferredRelayAck(relayAckDeps, relayAckInput, { delivered: result.delivered === true });
|
|
17696
|
+
if (ackOutcome === 'skipped-undelivered') {
|
|
17697
|
+
console.warn(`[relay] Auto-ack withheld for ${senderName} (thread: ${result.threadId}) — no live session received the message`);
|
|
17698
|
+
}
|
|
17699
|
+
}
|
|
17599
17700
|
}
|
|
17600
17701
|
// Relay client is passed to AgentServer → RouteContext for the /threadline/relay-send endpoint
|
|
17601
17702
|
// Report the client's ACTUAL state. This line used to say "connected"
|