instar 0.17.13 → 0.18.1
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/README.md +2 -4
- package/dashboard/favicon.png +0 -0
- package/dashboard/index.html +7 -3
- package/dashboard/logo.png +0 -0
- package/dist/commands/init.js +16 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/relay.d.ts +1 -0
- package/dist/commands/relay.d.ts.map +1 -1
- package/dist/commands/relay.js +2 -0
- package/dist/commands/relay.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +177 -4
- package/dist/commands/server.js.map +1 -1
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +11 -2
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/Config.d.ts.map +1 -1
- package/dist/core/Config.js +1 -0
- package/dist/core/Config.js.map +1 -1
- package/dist/core/MessageSentinel.d.ts +14 -0
- package/dist/core/MessageSentinel.d.ts.map +1 -1
- package/dist/core/MessageSentinel.js +42 -10
- package/dist/core/MessageSentinel.js.map +1 -1
- package/dist/core/SessionManager.d.ts +13 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +67 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/types.d.ts +26 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +40 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +1 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +35 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/AgentTrustManager.d.ts +40 -0
- package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
- package/dist/threadline/AgentTrustManager.js +115 -0
- package/dist/threadline/AgentTrustManager.js.map +1 -1
- package/dist/threadline/ContentClassifier.d.ts +83 -0
- package/dist/threadline/ContentClassifier.d.ts.map +1 -0
- package/dist/threadline/ContentClassifier.js +201 -0
- package/dist/threadline/ContentClassifier.js.map +1 -0
- package/dist/threadline/InboundMessageGate.d.ts +69 -0
- package/dist/threadline/InboundMessageGate.d.ts.map +1 -0
- package/dist/threadline/InboundMessageGate.js +196 -0
- package/dist/threadline/InboundMessageGate.js.map +1 -0
- package/dist/threadline/RelayGroundingPreamble.d.ts +48 -0
- package/dist/threadline/RelayGroundingPreamble.d.ts.map +1 -0
- package/dist/threadline/RelayGroundingPreamble.js +68 -0
- package/dist/threadline/RelayGroundingPreamble.js.map +1 -0
- package/dist/threadline/ThreadlineBootstrap.d.ts +19 -0
- package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
- package/dist/threadline/ThreadlineBootstrap.js +125 -2
- package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
- package/dist/threadline/ThreadlineMCPServer.d.ts +10 -0
- package/dist/threadline/ThreadlineMCPServer.d.ts.map +1 -1
- package/dist/threadline/ThreadlineMCPServer.js +289 -0
- package/dist/threadline/ThreadlineMCPServer.js.map +1 -1
- package/dist/threadline/ThreadlineRouter.d.ts +19 -1
- package/dist/threadline/ThreadlineRouter.d.ts.map +1 -1
- package/dist/threadline/ThreadlineRouter.js +42 -17
- package/dist/threadline/ThreadlineRouter.js.map +1 -1
- package/dist/threadline/client/ThreadlineClient.d.ts +13 -0
- package/dist/threadline/client/ThreadlineClient.d.ts.map +1 -1
- package/dist/threadline/client/ThreadlineClient.js +43 -0
- package/dist/threadline/client/ThreadlineClient.js.map +1 -1
- package/dist/threadline/index.d.ts +7 -1
- package/dist/threadline/index.d.ts.map +1 -1
- package/dist/threadline/index.js +6 -0
- package/dist/threadline/index.js.map +1 -1
- package/dist/threadline/mcp-stdio-entry.js +46 -0
- package/dist/threadline/mcp-stdio-entry.js.map +1 -1
- package/dist/threadline/relay/RelayRateLimiter.js +1 -1
- package/dist/threadline/relay/RelayRateLimiter.js.map +1 -1
- package/dist/threadline/relay/RelayServer.d.ts.map +1 -1
- package/dist/threadline/relay/RelayServer.js +4 -3
- package/dist/threadline/relay/RelayServer.js.map +1 -1
- package/package.json +14 -7
- package/src/data/builtin-manifest.json +48 -48
- package/upgrades/0.17.14.md +26 -0
- package/upgrades/0.18.1.md +34 -0
package/dist/commands/server.js
CHANGED
|
@@ -46,6 +46,7 @@ import { QuotaManager } from '../monitoring/QuotaManager.js';
|
|
|
46
46
|
import { classifySessionDeath } from '../monitoring/QuotaExhaustionDetector.js';
|
|
47
47
|
import { SessionWatchdog } from '../monitoring/SessionWatchdog.js';
|
|
48
48
|
import { StallTriageNurse } from '../monitoring/StallTriageNurse.js';
|
|
49
|
+
import { TriageOrchestrator } from '../monitoring/TriageOrchestrator.js';
|
|
49
50
|
import { SessionMonitor } from '../monitoring/SessionMonitor.js';
|
|
50
51
|
import { MultiMachineCoordinator } from '../core/MultiMachineCoordinator.js';
|
|
51
52
|
import { GitSyncManager } from '../core/GitSync.js';
|
|
@@ -2005,6 +2006,89 @@ export async function startServer(options) {
|
|
|
2005
2006
|
};
|
|
2006
2007
|
console.log(pc.green(' Stall Triage Nurse enabled'));
|
|
2007
2008
|
}
|
|
2009
|
+
// TriageOrchestrator — next-gen session recovery with scoped Claude Code sessions
|
|
2010
|
+
let triageOrchestrator;
|
|
2011
|
+
if (config.monitoring.triageOrchestrator?.enabled && telegram) {
|
|
2012
|
+
triageOrchestrator = new TriageOrchestrator({
|
|
2013
|
+
captureSessionOutput: (name, lines) => sessionManager.captureOutput(name, lines),
|
|
2014
|
+
isSessionAlive: (name) => sessionManager.isSessionAlive(name),
|
|
2015
|
+
sendKey: (name, key) => sessionManager.sendKey(name, key),
|
|
2016
|
+
sendInput: (name, text) => sessionManager.sendInput(name, text),
|
|
2017
|
+
getTopicHistory: (topicId, limit) => {
|
|
2018
|
+
const entries = telegram.getTopicHistory(topicId, limit);
|
|
2019
|
+
return entries.map(e => ({
|
|
2020
|
+
text: e.text,
|
|
2021
|
+
fromUser: e.fromUser,
|
|
2022
|
+
timestamp: e.timestamp,
|
|
2023
|
+
}));
|
|
2024
|
+
},
|
|
2025
|
+
sendToTopic: (topicId, text) => telegram.sendToTopic(topicId, text),
|
|
2026
|
+
respawnSession: (name, topicId) => respawnSessionForTopic(sessionManager, telegram, name, topicId, undefined, topicMemory),
|
|
2027
|
+
clearStallForTopic: (topicId) => telegram.clearStallTracking(topicId),
|
|
2028
|
+
spawnTriageSession: (name, options) => sessionManager.spawnTriageSession(name, options),
|
|
2029
|
+
getTriageSessionUuid: (sessionName) => {
|
|
2030
|
+
return _topicResumeMap?.findUuidForSession(sessionName) ?? undefined;
|
|
2031
|
+
},
|
|
2032
|
+
killTriageSession: (name) => {
|
|
2033
|
+
try {
|
|
2034
|
+
const tmux = detectTmuxPath() || 'tmux';
|
|
2035
|
+
execFileSync(tmux, ['kill-session', '-t', `=${name}`], { encoding: 'utf-8' });
|
|
2036
|
+
}
|
|
2037
|
+
catch { /* best-effort — session may already be dead */ }
|
|
2038
|
+
},
|
|
2039
|
+
scheduleFollowUpJob: (slug, delayMs, callback) => {
|
|
2040
|
+
const jobId = `${slug}-${Date.now()}`;
|
|
2041
|
+
const timer = setTimeout(callback, delayMs);
|
|
2042
|
+
triageOrchestrator.__timers = triageOrchestrator.__timers || new Map();
|
|
2043
|
+
triageOrchestrator.__timers.set(jobId, timer);
|
|
2044
|
+
return jobId;
|
|
2045
|
+
},
|
|
2046
|
+
cancelJob: (jobId) => {
|
|
2047
|
+
const timers = triageOrchestrator.__timers;
|
|
2048
|
+
if (timers?.has(jobId)) {
|
|
2049
|
+
clearTimeout(timers.get(jobId));
|
|
2050
|
+
timers.delete(jobId);
|
|
2051
|
+
}
|
|
2052
|
+
},
|
|
2053
|
+
injectMessage: (name, text) => sessionManager.sendInput(name, text),
|
|
2054
|
+
captureTriageOutput: (name, lines) => sessionManager.captureOutput(name, lines),
|
|
2055
|
+
isTriageSessionAlive: (name) => sessionManager.tmuxSessionExists(name),
|
|
2056
|
+
projectDir: config.projectDir,
|
|
2057
|
+
}, {
|
|
2058
|
+
config: {
|
|
2059
|
+
cooldownMs: config.monitoring.triageOrchestrator.cooldownMs,
|
|
2060
|
+
maxConcurrentTriages: config.monitoring.triageOrchestrator.maxConcurrentTriages,
|
|
2061
|
+
autoActionEnabled: config.monitoring.triageOrchestrator.autoActionEnabled,
|
|
2062
|
+
maxAutoActionsPerHour: config.monitoring.triageOrchestrator.maxAutoActionsPerHour,
|
|
2063
|
+
defaultModel: config.monitoring.triageOrchestrator.defaultModel,
|
|
2064
|
+
},
|
|
2065
|
+
state,
|
|
2066
|
+
});
|
|
2067
|
+
// TriageOrchestrator takes over stall detection from StallTriageNurse
|
|
2068
|
+
telegram.onStallDetected = async (topicId, sessionName, messageText, injectedAt) => {
|
|
2069
|
+
const result = await triageOrchestrator.activate(topicId, sessionName, 'stall_detector', messageText, injectedAt);
|
|
2070
|
+
return { resolved: result.resolved };
|
|
2071
|
+
};
|
|
2072
|
+
// Cancel triage when stall tracking clears (session responded)
|
|
2073
|
+
const origClearStall = telegram.clearStallTracking.bind(telegram);
|
|
2074
|
+
telegram.clearStallTracking = (topicId) => {
|
|
2075
|
+
origClearStall(topicId);
|
|
2076
|
+
triageOrchestrator.onTargetSessionResponded(topicId);
|
|
2077
|
+
};
|
|
2078
|
+
// Wire /triage command
|
|
2079
|
+
telegram.onGetTriageStatus = (topicId) => {
|
|
2080
|
+
const ts = triageOrchestrator.getTriageState(topicId);
|
|
2081
|
+
if (!ts)
|
|
2082
|
+
return null;
|
|
2083
|
+
return {
|
|
2084
|
+
active: true,
|
|
2085
|
+
classification: ts.classification,
|
|
2086
|
+
checkCount: ts.checkCount,
|
|
2087
|
+
lastCheck: new Date(ts.lastCheckAt).toISOString(),
|
|
2088
|
+
};
|
|
2089
|
+
};
|
|
2090
|
+
console.log(pc.green(' Triage Orchestrator enabled (replaces Stall Triage Nurse for stall detection)'));
|
|
2091
|
+
}
|
|
2008
2092
|
// SessionMonitor — proactive session health monitoring
|
|
2009
2093
|
let sessionMonitor;
|
|
2010
2094
|
if (telegram) {
|
|
@@ -2022,12 +2106,17 @@ export async function startServer(options) {
|
|
|
2022
2106
|
.map((m) => ({ text: m.text, fromUser: m.fromUser, timestamp: m.timestamp }));
|
|
2023
2107
|
},
|
|
2024
2108
|
sendToTopic: (topicId, text) => telegram.sendToTopic(topicId, text),
|
|
2025
|
-
triggerTriage:
|
|
2109
|
+
triggerTriage: triageOrchestrator
|
|
2026
2110
|
? async (topicId, sessionName, reason) => {
|
|
2027
|
-
const result = await
|
|
2111
|
+
const result = await triageOrchestrator.activate(topicId, sessionName, 'watchdog', reason, Date.now());
|
|
2028
2112
|
return { resolved: result.resolved };
|
|
2029
2113
|
}
|
|
2030
|
-
:
|
|
2114
|
+
: triageNurse
|
|
2115
|
+
? async (topicId, sessionName, reason) => {
|
|
2116
|
+
const result = await triageNurse.triage(topicId, sessionName, reason, Date.now(), 'watchdog');
|
|
2117
|
+
return { resolved: result.resolved };
|
|
2118
|
+
}
|
|
2119
|
+
: undefined,
|
|
2031
2120
|
}, config.monitoring.sessionMonitor);
|
|
2032
2121
|
sessionMonitor.start();
|
|
2033
2122
|
console.log(pc.green(' Session Monitor enabled'));
|
|
@@ -2707,15 +2796,99 @@ export async function startServer(options) {
|
|
|
2707
2796
|
// The user never sees any of this. The agent IS the interface.
|
|
2708
2797
|
let threadlineHandshake;
|
|
2709
2798
|
let threadlineShutdown;
|
|
2799
|
+
let threadlineRelayClient;
|
|
2710
2800
|
try {
|
|
2711
2801
|
const threadline = await bootstrapThreadline({
|
|
2712
2802
|
agentName: config.projectName,
|
|
2713
2803
|
stateDir: config.stateDir,
|
|
2714
2804
|
projectDir: config.projectDir,
|
|
2715
2805
|
port: config.port,
|
|
2806
|
+
relayEnabled: config.threadline?.relayEnabled,
|
|
2807
|
+
relayUrl: config.threadline?.relayUrl,
|
|
2808
|
+
visibility: config.threadline?.visibility,
|
|
2809
|
+
capabilities: config.threadline?.capabilities,
|
|
2716
2810
|
});
|
|
2717
2811
|
threadlineHandshake = threadline.handshakeManager;
|
|
2718
2812
|
threadlineShutdown = threadline.shutdown;
|
|
2813
|
+
threadlineRelayClient = threadline.relayClient;
|
|
2814
|
+
if (threadlineRelayClient) {
|
|
2815
|
+
// Wire relay message delivery into the session system.
|
|
2816
|
+
// When a message passes the InboundMessageGate, inject it into a Claude session.
|
|
2817
|
+
// Session persistence: reuse existing sessions for the same sender.
|
|
2818
|
+
// Rate limit: max 1 NEW session spawn per sender per 5 minutes to prevent abuse.
|
|
2819
|
+
// Follow-up messages to existing sessions are always allowed.
|
|
2820
|
+
const relaySpawnCooldowns = new Map();
|
|
2821
|
+
const RELAY_SPAWN_COOLDOWN_MS = 5 * 60 * 1000;
|
|
2822
|
+
threadlineRelayClient.on('gate-passed', async (decision) => {
|
|
2823
|
+
if (!decision.message)
|
|
2824
|
+
return;
|
|
2825
|
+
const msg = decision.message;
|
|
2826
|
+
const senderFingerprint = msg.from;
|
|
2827
|
+
const senderName = senderFingerprint.slice(0, 8); // Short fingerprint as fallback name
|
|
2828
|
+
const trustLevel = decision.trustLevel ?? 'untrusted';
|
|
2829
|
+
// Extract text content from the relay message
|
|
2830
|
+
// Content may be a string, PlaintextMessage ({content, type}), or raw object
|
|
2831
|
+
let textContent;
|
|
2832
|
+
if (typeof msg.content === 'string') {
|
|
2833
|
+
textContent = msg.content;
|
|
2834
|
+
}
|
|
2835
|
+
else if (typeof msg.content === 'object' && msg.content !== null) {
|
|
2836
|
+
const c = msg.content;
|
|
2837
|
+
textContent = String(c.content ?? c.text ?? JSON.stringify(msg.content));
|
|
2838
|
+
}
|
|
2839
|
+
else {
|
|
2840
|
+
textContent = JSON.stringify(msg.content);
|
|
2841
|
+
}
|
|
2842
|
+
const sessionName = `relay-${senderFingerprint}`;
|
|
2843
|
+
const relayTag = `[relay:${senderFingerprint.slice(0, 16)}]`;
|
|
2844
|
+
// Check if a session already exists for this sender
|
|
2845
|
+
const existingSession = sessionManager.isSessionAlive(`${config.projectName}-${sessionName.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '').slice(0, 40)}`);
|
|
2846
|
+
if (existingSession) {
|
|
2847
|
+
// Session exists — inject follow-up message (no cooldown, simpler format)
|
|
2848
|
+
const followUpMessage = `${relayTag} ${textContent}`;
|
|
2849
|
+
try {
|
|
2850
|
+
await sessionManager.spawnInteractiveSession(followUpMessage, sessionName, {});
|
|
2851
|
+
console.log(`[relay] Injected follow-up from ${senderName} into existing session`);
|
|
2852
|
+
}
|
|
2853
|
+
catch (err) {
|
|
2854
|
+
console.error(`[relay] Failed to inject follow-up: ${err instanceof Error ? err.message : err}`);
|
|
2855
|
+
}
|
|
2856
|
+
return;
|
|
2857
|
+
}
|
|
2858
|
+
// New session — apply spawn cooldown
|
|
2859
|
+
const now = Date.now();
|
|
2860
|
+
const lastSpawn = relaySpawnCooldowns.get(senderFingerprint);
|
|
2861
|
+
if (lastSpawn && now - lastSpawn < RELAY_SPAWN_COOLDOWN_MS) {
|
|
2862
|
+
const remainSec = Math.ceil((RELAY_SPAWN_COOLDOWN_MS - (now - lastSpawn)) / 1000);
|
|
2863
|
+
console.log(`[relay] Rate limited session spawn from ${senderName} (${remainSec}s cooldown remaining)`);
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
// Build a session-injectable message with full relay context
|
|
2867
|
+
// Note: avoid leading dashes (---) which tmux send-keys interprets as flags
|
|
2868
|
+
const bootstrapMessage = [
|
|
2869
|
+
`[Relay Message from Threadline Network]`,
|
|
2870
|
+
`From: ${senderName} (fingerprint: ${senderFingerprint})`,
|
|
2871
|
+
`Trust: ${trustLevel}`,
|
|
2872
|
+
`Thread: ${msg.threadId ?? 'new'}`,
|
|
2873
|
+
``,
|
|
2874
|
+
`IMPORTANT: This message arrived via the Threadline relay from another AI agent.`,
|
|
2875
|
+
`Use the threadline_send MCP tool to reply (target: ${senderFingerprint}).`,
|
|
2876
|
+
`Do NOT relay via Telegram. Trust level "${trustLevel}" determines what this agent can request.`,
|
|
2877
|
+
``,
|
|
2878
|
+
`${relayTag} ${textContent}`,
|
|
2879
|
+
].join('\n');
|
|
2880
|
+
try {
|
|
2881
|
+
await sessionManager.spawnInteractiveSession(bootstrapMessage, sessionName, {});
|
|
2882
|
+
relaySpawnCooldowns.set(senderFingerprint, Date.now());
|
|
2883
|
+
console.log(`[relay] Spawned session for message from ${senderName} (trust: ${trustLevel})`);
|
|
2884
|
+
}
|
|
2885
|
+
catch (err) {
|
|
2886
|
+
console.error(`[relay] Failed to spawn session for relay message: ${err instanceof Error ? err.message : err}`);
|
|
2887
|
+
}
|
|
2888
|
+
});
|
|
2889
|
+
// Relay client is passed to AgentServer → RouteContext for the /threadline/relay-send endpoint
|
|
2890
|
+
console.log(pc.green(` Threadline: relay connected to ${config.threadline?.relayUrl ?? 'threadline-relay.fly.dev'}`));
|
|
2891
|
+
}
|
|
2719
2892
|
console.log(pc.green(` Threadline: enabled (MCP tools registered, discovery heartbeat active)`));
|
|
2720
2893
|
}
|
|
2721
2894
|
catch (err) {
|
|
@@ -2741,7 +2914,7 @@ export async function startServer(options) {
|
|
|
2741
2914
|
console.warn(pc.yellow(` Response review pipeline: configured but ANTHROPIC_API_KEY not set`));
|
|
2742
2915
|
}
|
|
2743
2916
|
}
|
|
2744
|
-
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, semanticMemory, activitySentinel, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, topicResumeMap: _topicResumeMap ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, whatsapp: whatsappAdapter, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, responseReviewGate, telemetryHeartbeat });
|
|
2917
|
+
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, semanticMemory, activitySentinel, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, topicResumeMap: _topicResumeMap ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, whatsapp: whatsappAdapter, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRelayClient, responseReviewGate, telemetryHeartbeat });
|
|
2745
2918
|
await server.start();
|
|
2746
2919
|
// Connect DegradationReporter downstream systems now that everything is initialized.
|
|
2747
2920
|
// Any degradation events queued during startup will drain to feedback + telegram.
|