instar 0.28.2 → 0.28.3

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 (44) hide show
  1. package/dashboard/index.html +1 -1
  2. package/dist/commands/server.d.ts.map +1 -1
  3. package/dist/commands/server.js +89 -49
  4. package/dist/commands/server.js.map +1 -1
  5. package/dist/core/Config.js +2 -2
  6. package/dist/core/Config.js.map +1 -1
  7. package/dist/core/SessionManager.d.ts +1 -0
  8. package/dist/core/SessionManager.d.ts.map +1 -1
  9. package/dist/core/SessionManager.js +3 -0
  10. package/dist/core/SessionManager.js.map +1 -1
  11. package/dist/lifeline/ServerSupervisor.d.ts +1 -0
  12. package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
  13. package/dist/lifeline/ServerSupervisor.js +43 -10
  14. package/dist/lifeline/ServerSupervisor.js.map +1 -1
  15. package/dist/messaging/TelegramAdapter.d.ts +13 -1
  16. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  17. package/dist/messaging/TelegramAdapter.js +90 -7
  18. package/dist/messaging/TelegramAdapter.js.map +1 -1
  19. package/dist/messaging/imessage/IMessageAdapter.d.ts +4 -2
  20. package/dist/messaging/imessage/IMessageAdapter.d.ts.map +1 -1
  21. package/dist/messaging/imessage/IMessageAdapter.js +19 -5
  22. package/dist/messaging/imessage/IMessageAdapter.js.map +1 -1
  23. package/dist/messaging/slack/SlackAdapter.d.ts +6 -0
  24. package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
  25. package/dist/messaging/slack/SlackAdapter.js +27 -0
  26. package/dist/messaging/slack/SlackAdapter.js.map +1 -1
  27. package/dist/server/routes.d.ts.map +1 -1
  28. package/dist/server/routes.js +77 -19
  29. package/dist/server/routes.js.map +1 -1
  30. package/dist/threadline/AgentTrustManager.d.ts +15 -3
  31. package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
  32. package/dist/threadline/AgentTrustManager.js +40 -11
  33. package/dist/threadline/AgentTrustManager.js.map +1 -1
  34. package/dist/threadline/ThreadlineMCPServer.d.ts.map +1 -1
  35. package/dist/threadline/ThreadlineMCPServer.js +32 -10
  36. package/dist/threadline/ThreadlineMCPServer.js.map +1 -1
  37. package/dist/threadline/client/ThreadlineClient.d.ts +9 -1
  38. package/dist/threadline/client/ThreadlineClient.d.ts.map +1 -1
  39. package/dist/threadline/client/ThreadlineClient.js +64 -10
  40. package/dist/threadline/client/ThreadlineClient.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/data/builtin-manifest.json +48 -48
  43. package/src/templates/hooks/compaction-recovery.sh +82 -3
  44. package/upgrades/0.28.3.md +35 -0
@@ -2148,7 +2148,7 @@ export function createRoutes(ctx) {
2148
2148
  result.platform = 'telegram';
2149
2149
  result.platformId = topicId;
2150
2150
  const topicName = ctx.telegram.getTopicName?.(topicId);
2151
- if (topicName)
2151
+ if (topicName && !/^topic-\d+$/.test(topicName))
2152
2152
  result.platformName = topicName;
2153
2153
  }
2154
2154
  }
@@ -4330,7 +4330,7 @@ export function createRoutes(ctx) {
4330
4330
  res.send(html);
4331
4331
  });
4332
4332
  // Receive a secret submission (user-facing, NO auth — token + CSRF is the auth)
4333
- router.post('/secrets/drop/:token', (req, res) => {
4333
+ router.post('/secrets/drop/:token', async (req, res) => {
4334
4334
  const { _csrf, ...values } = req.body;
4335
4335
  if (!_csrf || typeof _csrf !== 'string') {
4336
4336
  res.status(400).json({ error: 'Missing CSRF token' });
@@ -4384,15 +4384,32 @@ export function createRoutes(ctx) {
4384
4384
  else {
4385
4385
  // No live session — spawn one to handle the secret receipt
4386
4386
  let topicName = `topic-${topicId}`;
4387
- try {
4388
- if (fs.existsSync(sdRegistryPath)) {
4389
- const reg = JSON.parse(fs.readFileSync(sdRegistryPath, 'utf-8'));
4390
- const stored = reg.topicToName?.[String(topicId)];
4391
- if (stored)
4392
- topicName = stored;
4387
+ // Try live adapter first, then disk registry, then active probe
4388
+ if (ctx.telegram) {
4389
+ const liveName = ctx.telegram.getTopicName(topicId);
4390
+ if (liveName && !/^topic-\d+$/.test(liveName)) {
4391
+ topicName = liveName;
4393
4392
  }
4394
4393
  }
4395
- catch { /* fall through to default */ }
4394
+ if (/^topic-\d+$/.test(topicName)) {
4395
+ try {
4396
+ if (fs.existsSync(sdRegistryPath)) {
4397
+ const reg = JSON.parse(fs.readFileSync(sdRegistryPath, 'utf-8'));
4398
+ const stored = reg.topicToName?.[String(topicId)];
4399
+ if (stored && !/^topic-\d+$/.test(stored))
4400
+ topicName = stored;
4401
+ }
4402
+ }
4403
+ catch { /* fall through */ }
4404
+ }
4405
+ if (/^topic-\d+$/.test(topicName) && ctx.telegram) {
4406
+ try {
4407
+ const resolved = await ctx.telegram.resolveTopicName(topicId);
4408
+ if (resolved)
4409
+ topicName = resolved;
4410
+ }
4411
+ catch { /* fall through to default */ }
4412
+ }
4396
4413
  // Build context with thread history
4397
4414
  const historyLines = [];
4398
4415
  if (ctx.telegram) {
@@ -4564,7 +4581,7 @@ export function createRoutes(ctx) {
4564
4581
  // ── Internal: Lifeline Telegram Forward ─────────────────────────
4565
4582
  // Receives messages from the Telegram Lifeline process and injects
4566
4583
  // them into the appropriate session, just like TelegramAdapter would.
4567
- router.post('/internal/telegram-forward', (req, res) => {
4584
+ router.post('/internal/telegram-forward', async (req, res) => {
4568
4585
  const { topicId, text, fromUserId, fromUsername, fromFirstName, messageId } = req.body;
4569
4586
  if (!topicId || !text) {
4570
4587
  res.status(400).json({ error: 'topicId and text required' });
@@ -4680,15 +4697,32 @@ export function createRoutes(ctx) {
4680
4697
  // tmux names include the project prefix (e.g., "ai-guy-lifeline"), and
4681
4698
  // spawnInteractiveSession prepends it again → cascading names.
4682
4699
  let topicName = `topic-${topicId}`;
4683
- try {
4684
- if (fs.existsSync(registryPath)) {
4685
- const reg = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
4686
- const stored = reg.topicToName?.[String(topicId)];
4687
- if (stored)
4688
- topicName = stored;
4700
+ // Try live adapter first, then disk registry, then active probe
4701
+ if (ctx.telegram) {
4702
+ const liveName = ctx.telegram.getTopicName(topicId);
4703
+ if (liveName && !/^topic-\d+$/.test(liveName)) {
4704
+ topicName = liveName;
4689
4705
  }
4690
4706
  }
4691
- catch { /* fall through to default */ }
4707
+ if (/^topic-\d+$/.test(topicName)) {
4708
+ try {
4709
+ if (fs.existsSync(registryPath)) {
4710
+ const reg = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
4711
+ const stored = reg.topicToName?.[String(topicId)];
4712
+ if (stored && !/^topic-\d+$/.test(stored))
4713
+ topicName = stored;
4714
+ }
4715
+ }
4716
+ catch { /* fall through */ }
4717
+ }
4718
+ if (/^topic-\d+$/.test(topicName) && ctx.telegram) {
4719
+ try {
4720
+ const resolved = await ctx.telegram.resolveTopicName(topicId);
4721
+ if (resolved)
4722
+ topicName = resolved;
4723
+ }
4724
+ catch { /* fall through to default */ }
4725
+ }
4692
4726
  console.log(`[telegram-forward] No live session for topic ${topicId}, spawning "${topicName}"...`);
4693
4727
  // Fetch thread history so auto-spawned sessions have full conversational context
4694
4728
  const historyLines = [];
@@ -7656,7 +7690,30 @@ export function createRoutes(ctx) {
7656
7690
  if (fs.existsSync(knownAgentsPath)) {
7657
7691
  const knownData = JSON.parse(fs.readFileSync(knownAgentsPath, 'utf-8'));
7658
7692
  const agents = knownData.agents ?? [];
7659
- const localTarget = agents.find(a => a.name === targetAgent || a.name?.toLowerCase() === targetAgent?.toLowerCase());
7693
+ // Support "name:fingerprintPrefix" disambiguation syntax
7694
+ let targetName = targetAgent;
7695
+ let targetFpPrefix;
7696
+ const colonIdx = targetAgent.lastIndexOf(':');
7697
+ if (colonIdx > 0 && colonIdx < targetAgent.length - 1) {
7698
+ const suffix = targetAgent.substring(colonIdx + 1);
7699
+ if (/^[0-9a-f]{4,32}$/i.test(suffix)) {
7700
+ targetName = targetAgent.substring(0, colonIdx);
7701
+ targetFpPrefix = suffix.toLowerCase();
7702
+ }
7703
+ }
7704
+ const nameMatches = agents.filter(a => a.name === targetName || a.name?.toLowerCase() === targetName?.toLowerCase());
7705
+ // If multiple same-named agents, disambiguate by fingerprint prefix
7706
+ let localTarget = nameMatches.length === 1 ? nameMatches[0] : undefined;
7707
+ if (nameMatches.length > 1 && targetFpPrefix) {
7708
+ localTarget = nameMatches.find(a => {
7709
+ const fp = a.fingerprint || a.publicKey?.substring(0, 32);
7710
+ return fp?.toLowerCase().startsWith(targetFpPrefix);
7711
+ });
7712
+ }
7713
+ else if (nameMatches.length > 1 && !targetFpPrefix) {
7714
+ // Ambiguous — skip local delivery, let relay handle it
7715
+ localTarget = undefined;
7716
+ }
7660
7717
  if (localTarget?.port && localTarget.name !== ctx.config.projectName) {
7661
7718
  // Check if the local agent is actually running
7662
7719
  try {
@@ -8409,7 +8466,7 @@ export function createRoutes(ctx) {
8409
8466
  res.status(501).json({ error: 'FeatureRegistry not initialized' });
8410
8467
  return;
8411
8468
  }
8412
- const { to, userId: bodyUserId, trigger, consentRecord, context } = req.body || {};
8469
+ const { to, userId: bodyUserId, trigger, consentRecord, context, activationChallenge } = req.body || {};
8413
8470
  if (!to) {
8414
8471
  res.status(400).json({ error: { code: 'MISSING_TARGET', message: 'Request body must include "to" (target state)' } });
8415
8472
  return;
@@ -8419,6 +8476,7 @@ export function createRoutes(ctx) {
8419
8476
  trigger,
8420
8477
  consentRecord,
8421
8478
  context,
8479
+ activationChallenge,
8422
8480
  });
8423
8481
  if (!result.success) {
8424
8482
  const status = result.error?.code === 'FEATURE_NOT_FOUND' ? 404