instar 1.3.245 → 1.3.246
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 +108 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +13 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/SessionManager.d.ts +3 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +3 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/frameworkInjectionProcesses.d.ts +41 -0
- package/dist/core/frameworkInjectionProcesses.d.ts.map +1 -0
- package/dist/core/frameworkInjectionProcesses.js +53 -0
- package/dist/core/frameworkInjectionProcesses.js.map +1 -0
- package/dist/core/frameworkSessionLaunch.d.ts +9 -0
- package/dist/core/frameworkSessionLaunch.d.ts.map +1 -1
- package/dist/core/frameworkSessionLaunch.js +7 -0
- package/dist/core/frameworkSessionLaunch.js.map +1 -1
- package/dist/core/types.d.ts +22 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/messaging/SpawnRequestManager.d.ts +13 -0
- package/dist/messaging/SpawnRequestManager.d.ts.map +1 -1
- package/dist/messaging/SpawnRequestManager.js +3 -0
- package/dist/messaging/SpawnRequestManager.js.map +1 -1
- package/dist/messaging/types.d.ts.map +1 -1
- package/dist/messaging/types.js +8 -1
- package/dist/messaging/types.js.map +1 -1
- package/dist/threadline/ThreadlineRouter.d.ts +55 -1
- package/dist/threadline/ThreadlineRouter.d.ts.map +1 -1
- package/dist/threadline/ThreadlineRouter.js +246 -6
- package/dist/threadline/ThreadlineRouter.js.map +1 -1
- package/dist/threadline/WarmSessionPool.d.ts +22 -0
- package/dist/threadline/WarmSessionPool.d.ts.map +1 -1
- package/dist/threadline/WarmSessionPool.js +38 -0
- package/dist/threadline/WarmSessionPool.js.map +1 -1
- package/package.json +1 -1
- package/scripts/instar-dev-precommit.js +42 -0
- package/src/data/builtin-manifest.json +3 -3
- package/upgrades/1.3.246.md +97 -0
- package/upgrades/side-effects/threadline-warm-session-a2a.md +146 -0
- package/upgrades/threadline-warm-session-a2a.eli16.md +63 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2SH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAgxDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgxQtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
|
package/dist/commands/server.js
CHANGED
|
@@ -122,7 +122,8 @@ import { pickupDroppedMessages } from '../messaging/DropPickup.js';
|
|
|
122
122
|
import { pickupGitSyncMessages } from '../messaging/GitSyncTransport.js';
|
|
123
123
|
import { DeliveryRetryManager } from '../messaging/DeliveryRetryManager.js';
|
|
124
124
|
import { SpawnRequestManager } from '../messaging/SpawnRequestManager.js';
|
|
125
|
-
import { ThreadlineRouter } from '../threadline/ThreadlineRouter.js';
|
|
125
|
+
import { ThreadlineRouter, trustMeetsFloor } from '../threadline/ThreadlineRouter.js';
|
|
126
|
+
import { WarmSessionPool } from '../threadline/WarmSessionPool.js';
|
|
126
127
|
import { resolveThreadlineMcpEntry } from '../threadline/mcpEntry.js';
|
|
127
128
|
import { ThreadResumeMap } from '../threadline/ThreadResumeMap.js';
|
|
128
129
|
import { ConversationStore } from '../threadline/ConversationStore.js';
|
|
@@ -7116,6 +7117,42 @@ export async function startServer(options) {
|
|
|
7116
7117
|
?? 5,
|
|
7117
7118
|
getActiveSessions: () => sessionManager.listRunningSessions(),
|
|
7118
7119
|
spawnSession: async (prompt, opts) => {
|
|
7120
|
+
// Warm-session A2A (Arch Y): when the SpawnRequest is flagged interactive,
|
|
7121
|
+
// launch a PERSISTENT (keep-alive) interactive worker instead of the
|
|
7122
|
+
// headless one-shot `claude -p`. The grounded prompt is delivered as the
|
|
7123
|
+
// worker's FIRST turn (after-ready inject), and the worker stays alive so
|
|
7124
|
+
// follow-ups inject into the same session. The MCP/permission flag set
|
|
7125
|
+
// matches the `-p` path: claude interactive keeps the PROJECT MCP (where
|
|
7126
|
+
// threadline_send lives) under --dangerously-skip-permissions, and codex
|
|
7127
|
+
// interactive launches with --dangerously-bypass-approvals-and-sandbox +
|
|
7128
|
+
// the per-agent threadline MCP (the bypass the `-p` codexAllowMcpTools
|
|
7129
|
+
// selects) — so threadline_send is available either way.
|
|
7130
|
+
if (opts?.interactive) {
|
|
7131
|
+
const warmName = `msg-warm-${Date.now()}`;
|
|
7132
|
+
// Framework-GENERAL: the warm worker runs in the LOCAL agent's
|
|
7133
|
+
// framework (claude-code / codex-cli / gemini-cli), NOT hardcoded
|
|
7134
|
+
// Claude. spawnInteractiveSession + frameworkSessionLaunch compose the
|
|
7135
|
+
// right argv + MCP/permission flags per framework (claude keeps PROJECT
|
|
7136
|
+
// MCP under --dangerously-skip-permissions; codex launches under
|
|
7137
|
+
// --dangerously-bypass-approvals-and-sandbox with its per-agent
|
|
7138
|
+
// threadline MCP) so threadline_send works regardless of framework.
|
|
7139
|
+
// A2A reply threads have no topic, so the agent default framework
|
|
7140
|
+
// applies (mirrors resolveTopicFramework's own fallback).
|
|
7141
|
+
const tmuxSession = await sessionManager.spawnInteractiveSession(prompt, warmName, {
|
|
7142
|
+
framework: _defaultFramework,
|
|
7143
|
+
// Deterministic conversation id for lossless eviction→resume (#746).
|
|
7144
|
+
// frameworkSessionLaunch maps this per framework: claude-code pins
|
|
7145
|
+
// `--session-id`; codex/gemini ignore it (they resume by their own
|
|
7146
|
+
// mechanism). NOT a Claude assumption — it's the abstraction's job.
|
|
7147
|
+
sessionId: opts?.sessionId,
|
|
7148
|
+
});
|
|
7149
|
+
// spawnInteractiveSession returns the tmux name; resolve the instar
|
|
7150
|
+
// session id from the running set so the warm callback returns the
|
|
7151
|
+
// slice-2 {sessionId, tmuxSession} shape spawnWarmThread expects.
|
|
7152
|
+
const running = sessionManager.listRunningSessions();
|
|
7153
|
+
const rec = running.find(s => s.tmuxSession === tmuxSession);
|
|
7154
|
+
return { sessionId: rec?.id ?? tmuxSession, tmuxSession };
|
|
7155
|
+
}
|
|
7119
7156
|
const session = await sessionManager.spawnSession({
|
|
7120
7157
|
name: `msg-spawn-${Date.now()}`,
|
|
7121
7158
|
prompt,
|
|
@@ -7222,9 +7259,66 @@ export async function startServer(options) {
|
|
|
7222
7259
|
else {
|
|
7223
7260
|
console.log('[spawn-manager] drain loop disabled by config.threadline.spawn.drainEnabled=false');
|
|
7224
7261
|
}
|
|
7262
|
+
// Warm-Session A2A (Arch Y, dark-ship) — keep each A2A reply thread's session
|
|
7263
|
+
// alive between messages so follow-ups inject into the live worker instead of
|
|
7264
|
+
// cold-spawning. Resolved via the developmentAgent gate: `enabled ?? !!dev` →
|
|
7265
|
+
// LIVE on Echo, DARK on the fleet. The pool is null when disabled, so the
|
|
7266
|
+
// ThreadlineRouter falls back to the proven cold-spawn path byte-for-byte.
|
|
7267
|
+
const warmCfg = config.threadline?.warmSessionA2A;
|
|
7268
|
+
const warmEnabled = warmCfg?.enabled ?? !!config.developmentAgent;
|
|
7269
|
+
const warmSessionPool = warmEnabled
|
|
7270
|
+
? new WarmSessionPool({
|
|
7271
|
+
globalCap: warmCfg?.globalCap ?? 3,
|
|
7272
|
+
perPeerCap: warmCfg?.perPeerCap ?? 1,
|
|
7273
|
+
ttlMs: warmCfg?.ttlMs ?? 600000,
|
|
7274
|
+
})
|
|
7275
|
+
: null;
|
|
7276
|
+
const warmTrustFloor = warmCfg?.trustFloor ?? 'verified';
|
|
7277
|
+
// Server-owned kill primitive: resolve the tmux NAME → instar session id →
|
|
7278
|
+
// killSession (sessions are filed by id, not tmux name). Used for cap-eviction
|
|
7279
|
+
// on admit and the periodic reap tick. Never throws to the caller.
|
|
7280
|
+
const killWarmSessionByName = (sessionName) => {
|
|
7281
|
+
try {
|
|
7282
|
+
const rec = sessionManager.listRunningSessions().find(s => s.tmuxSession === sessionName);
|
|
7283
|
+
if (rec)
|
|
7284
|
+
sessionManager.killSession(rec.id);
|
|
7285
|
+
else
|
|
7286
|
+
console.log(`[warm-session] kill requested for ${sessionName} but no running session matched (already gone?)`);
|
|
7287
|
+
}
|
|
7288
|
+
catch (err) {
|
|
7289
|
+
console.warn(`[warm-session] kill ${sessionName} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7290
|
+
}
|
|
7291
|
+
};
|
|
7292
|
+
if (warmEnabled) {
|
|
7293
|
+
console.log(`[warm-session] A2A keep-alive ENABLED (globalCap=${warmCfg?.globalCap ?? 3}, perPeerCap=${warmCfg?.perPeerCap ?? 1}, ttlMs=${warmCfg?.ttlMs ?? 600000}, trustFloor=${warmTrustFloor})`);
|
|
7294
|
+
}
|
|
7225
7295
|
// Threadline Router — handles threaded cross-agent conversations via relay
|
|
7226
7296
|
const threadlineRouter = new ThreadlineRouter(messageRouter, spawnManager, threadResumeMap, messageStore, { localAgent: config.projectName, localMachine: os.hostname() }, null, // autonomyGate
|
|
7227
|
-
messageDelivery
|
|
7297
|
+
messageDelivery, // PR-4: live-session injection path
|
|
7298
|
+
undefined, // onLedgerEvent (wired below via registerLedgerEmitters if present)
|
|
7299
|
+
undefined, // nowFn
|
|
7300
|
+
warmSessionPool, // Warm-Session A2A (null when disabled)
|
|
7301
|
+
warmEnabled, warmTrustFloor, warmEnabled ? killWarmSessionByName : null);
|
|
7302
|
+
// Warm-Session A2A reap tick — kill warm sessions idle past their TTL so a
|
|
7303
|
+
// flood can't pin processes. Gated on warmEnabled; .unref() so it never holds
|
|
7304
|
+
// the process open; cleared on shutdown. Eviction is lossless (the next
|
|
7305
|
+
// message resumes via #746).
|
|
7306
|
+
let warmReapTimer = null;
|
|
7307
|
+
if (warmEnabled && warmSessionPool) {
|
|
7308
|
+
warmReapTimer = setInterval(() => {
|
|
7309
|
+
try {
|
|
7310
|
+
const expired = warmSessionPool.reapExpired();
|
|
7311
|
+
for (const rec of expired) {
|
|
7312
|
+
console.log(`[warm-session] reaping idle warm session ${rec.sessionName} (thread ${rec.threadId})`);
|
|
7313
|
+
killWarmSessionByName(rec.sessionName);
|
|
7314
|
+
}
|
|
7315
|
+
}
|
|
7316
|
+
catch (err) {
|
|
7317
|
+
console.warn(`[warm-session] reap tick failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7318
|
+
}
|
|
7319
|
+
}, 60_000);
|
|
7320
|
+
warmReapTimer.unref();
|
|
7321
|
+
}
|
|
7228
7322
|
sessionManager.on('sessionComplete', (session) => {
|
|
7229
7323
|
const sessionName = session.tmuxSession || session.name;
|
|
7230
7324
|
if (!sessionName)
|
|
@@ -7784,11 +7878,19 @@ export async function startServer(options) {
|
|
|
7784
7878
|
transport: { protocol: 'relay', origin: { agent: senderFingerprint, machine: 'relay' }, nonce: `${crypto.randomUUID()}:${new Date().toISOString()}`, timestamp: new Date().toISOString() },
|
|
7785
7879
|
delivery: { status: 'delivered', attempts: 1, lastAttempt: new Date().toISOString() },
|
|
7786
7880
|
};
|
|
7881
|
+
// Warm-Session A2A (Arch Y, dark-ship): mark this inbound as eligible for
|
|
7882
|
+
// a keep-alive interactive worker when the feature is on, it's NOT a
|
|
7883
|
+
// topic-bound reply (those go to TopicLinkageHandler), and the peer meets
|
|
7884
|
+
// the trust floor. Trust compare via the explicit ordering array
|
|
7885
|
+
// (trustMeetsFloor), NEVER string `>=`. When false (or feature off), the
|
|
7886
|
+
// router takes the proven cold-spawn path byte-for-byte.
|
|
7887
|
+
const preferWarmSession = warmEnabled && !isTopicBoundReply && trustMeetsFloor(trustLevel, warmTrustFloor);
|
|
7787
7888
|
const relayContext = {
|
|
7788
7889
|
trust: { kind: 'plaintext-tofu', senderFingerprint },
|
|
7789
7890
|
senderFingerprint,
|
|
7790
7891
|
senderName,
|
|
7791
7892
|
trustLevel,
|
|
7893
|
+
preferWarmSession,
|
|
7792
7894
|
};
|
|
7793
7895
|
let result = await threadlineRouter.handleInboundMessage(envelope, relayContext);
|
|
7794
7896
|
// Fallback for threadId-less messages
|
|
@@ -9738,6 +9840,10 @@ export async function startServer(options) {
|
|
|
9738
9840
|
await notificationBatcher.flushAll(); // Drain pending notifications before exit
|
|
9739
9841
|
notificationBatcher.stop();
|
|
9740
9842
|
retryManager.stop();
|
|
9843
|
+
if (warmReapTimer) {
|
|
9844
|
+
clearInterval(warmReapTimer);
|
|
9845
|
+
warmReapTimer = null;
|
|
9846
|
+
} // Warm-Session A2A reap tick
|
|
9741
9847
|
spawnManager.dispose(); // §4.4: stop drain loop + clear DRR state
|
|
9742
9848
|
summarySentinel.stop();
|
|
9743
9849
|
memoryMonitor.stop();
|