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.
Files changed (91) hide show
  1. package/README.md +2 -4
  2. package/dashboard/favicon.png +0 -0
  3. package/dashboard/index.html +7 -3
  4. package/dashboard/logo.png +0 -0
  5. package/dist/commands/init.js +16 -0
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/commands/relay.d.ts +1 -0
  8. package/dist/commands/relay.d.ts.map +1 -1
  9. package/dist/commands/relay.js +2 -0
  10. package/dist/commands/relay.js.map +1 -1
  11. package/dist/commands/server.d.ts.map +1 -1
  12. package/dist/commands/server.js +177 -4
  13. package/dist/commands/server.js.map +1 -1
  14. package/dist/core/AutoUpdater.d.ts.map +1 -1
  15. package/dist/core/AutoUpdater.js +11 -2
  16. package/dist/core/AutoUpdater.js.map +1 -1
  17. package/dist/core/Config.d.ts.map +1 -1
  18. package/dist/core/Config.js +1 -0
  19. package/dist/core/Config.js.map +1 -1
  20. package/dist/core/MessageSentinel.d.ts +14 -0
  21. package/dist/core/MessageSentinel.d.ts.map +1 -1
  22. package/dist/core/MessageSentinel.js +42 -10
  23. package/dist/core/MessageSentinel.js.map +1 -1
  24. package/dist/core/SessionManager.d.ts +13 -1
  25. package/dist/core/SessionManager.d.ts.map +1 -1
  26. package/dist/core/SessionManager.js +67 -0
  27. package/dist/core/SessionManager.js.map +1 -1
  28. package/dist/core/types.d.ts +26 -0
  29. package/dist/core/types.d.ts.map +1 -1
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/scaffold/templates.d.ts.map +1 -1
  35. package/dist/scaffold/templates.js +40 -0
  36. package/dist/scaffold/templates.js.map +1 -1
  37. package/dist/server/AgentServer.d.ts +1 -0
  38. package/dist/server/AgentServer.d.ts.map +1 -1
  39. package/dist/server/AgentServer.js +1 -0
  40. package/dist/server/AgentServer.js.map +1 -1
  41. package/dist/server/routes.d.ts +1 -0
  42. package/dist/server/routes.d.ts.map +1 -1
  43. package/dist/server/routes.js +35 -0
  44. package/dist/server/routes.js.map +1 -1
  45. package/dist/threadline/AgentTrustManager.d.ts +40 -0
  46. package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
  47. package/dist/threadline/AgentTrustManager.js +115 -0
  48. package/dist/threadline/AgentTrustManager.js.map +1 -1
  49. package/dist/threadline/ContentClassifier.d.ts +83 -0
  50. package/dist/threadline/ContentClassifier.d.ts.map +1 -0
  51. package/dist/threadline/ContentClassifier.js +201 -0
  52. package/dist/threadline/ContentClassifier.js.map +1 -0
  53. package/dist/threadline/InboundMessageGate.d.ts +69 -0
  54. package/dist/threadline/InboundMessageGate.d.ts.map +1 -0
  55. package/dist/threadline/InboundMessageGate.js +196 -0
  56. package/dist/threadline/InboundMessageGate.js.map +1 -0
  57. package/dist/threadline/RelayGroundingPreamble.d.ts +48 -0
  58. package/dist/threadline/RelayGroundingPreamble.d.ts.map +1 -0
  59. package/dist/threadline/RelayGroundingPreamble.js +68 -0
  60. package/dist/threadline/RelayGroundingPreamble.js.map +1 -0
  61. package/dist/threadline/ThreadlineBootstrap.d.ts +19 -0
  62. package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
  63. package/dist/threadline/ThreadlineBootstrap.js +125 -2
  64. package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
  65. package/dist/threadline/ThreadlineMCPServer.d.ts +10 -0
  66. package/dist/threadline/ThreadlineMCPServer.d.ts.map +1 -1
  67. package/dist/threadline/ThreadlineMCPServer.js +289 -0
  68. package/dist/threadline/ThreadlineMCPServer.js.map +1 -1
  69. package/dist/threadline/ThreadlineRouter.d.ts +19 -1
  70. package/dist/threadline/ThreadlineRouter.d.ts.map +1 -1
  71. package/dist/threadline/ThreadlineRouter.js +42 -17
  72. package/dist/threadline/ThreadlineRouter.js.map +1 -1
  73. package/dist/threadline/client/ThreadlineClient.d.ts +13 -0
  74. package/dist/threadline/client/ThreadlineClient.d.ts.map +1 -1
  75. package/dist/threadline/client/ThreadlineClient.js +43 -0
  76. package/dist/threadline/client/ThreadlineClient.js.map +1 -1
  77. package/dist/threadline/index.d.ts +7 -1
  78. package/dist/threadline/index.d.ts.map +1 -1
  79. package/dist/threadline/index.js +6 -0
  80. package/dist/threadline/index.js.map +1 -1
  81. package/dist/threadline/mcp-stdio-entry.js +46 -0
  82. package/dist/threadline/mcp-stdio-entry.js.map +1 -1
  83. package/dist/threadline/relay/RelayRateLimiter.js +1 -1
  84. package/dist/threadline/relay/RelayRateLimiter.js.map +1 -1
  85. package/dist/threadline/relay/RelayServer.d.ts.map +1 -1
  86. package/dist/threadline/relay/RelayServer.js +4 -3
  87. package/dist/threadline/relay/RelayServer.js.map +1 -1
  88. package/package.json +14 -7
  89. package/src/data/builtin-manifest.json +48 -48
  90. package/upgrades/0.17.14.md +26 -0
  91. package/upgrades/0.18.1.md +34 -0
@@ -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: triageNurse
2109
+ triggerTriage: triageOrchestrator
2026
2110
  ? async (topicId, sessionName, reason) => {
2027
- const result = await triageNurse.triage(topicId, sessionName, reason, Date.now(), 'watchdog');
2111
+ const result = await triageOrchestrator.activate(topicId, sessionName, 'watchdog', reason, Date.now());
2028
2112
  return { resolved: result.resolved };
2029
2113
  }
2030
- : undefined,
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.