instar 1.3.1340 → 1.3.1341

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 (97) hide show
  1. package/dist/commands/init.d.ts.map +1 -1
  2. package/dist/commands/init.js +8 -0
  3. package/dist/commands/init.js.map +1 -1
  4. package/dist/commands/server.d.ts.map +1 -1
  5. package/dist/commands/server.js +204 -2
  6. package/dist/commands/server.js.map +1 -1
  7. package/dist/core/BackupManager.d.ts.map +1 -1
  8. package/dist/core/BackupManager.js +8 -0
  9. package/dist/core/BackupManager.js.map +1 -1
  10. package/dist/core/FileClassifier.d.ts.map +1 -1
  11. package/dist/core/FileClassifier.js +6 -0
  12. package/dist/core/FileClassifier.js.map +1 -1
  13. package/dist/core/MachineIdentity.d.ts.map +1 -1
  14. package/dist/core/MachineIdentity.js +3 -0
  15. package/dist/core/MachineIdentity.js.map +1 -1
  16. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  17. package/dist/core/PostUpdateMigrator.js +44 -2
  18. package/dist/core/PostUpdateMigrator.js.map +1 -1
  19. package/dist/core/keyMaterialPaths.d.ts +73 -0
  20. package/dist/core/keyMaterialPaths.d.ts.map +1 -0
  21. package/dist/core/keyMaterialPaths.js +139 -0
  22. package/dist/core/keyMaterialPaths.js.map +1 -0
  23. package/dist/data/standards-guard-index.json +1 -1
  24. package/dist/data/standards-guard-index.meta.json +2 -2
  25. package/dist/data/standards-registry.meta.json +1 -1
  26. package/dist/messaging/TelegramAdapter.d.ts +19 -0
  27. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  28. package/dist/messaging/TelegramAdapter.js +47 -9
  29. package/dist/messaging/TelegramAdapter.js.map +1 -1
  30. package/dist/messaging/telegram-origin/OriginAwareness.d.ts +2 -0
  31. package/dist/messaging/telegram-origin/OriginAwareness.d.ts.map +1 -1
  32. package/dist/messaging/telegram-origin/OriginAwareness.js +4 -0
  33. package/dist/messaging/telegram-origin/OriginAwareness.js.map +1 -1
  34. package/dist/messaging/telegram-origin/OriginConfig.d.ts +4 -0
  35. package/dist/messaging/telegram-origin/OriginConfig.d.ts.map +1 -1
  36. package/dist/messaging/telegram-origin/OriginConfig.js +26 -0
  37. package/dist/messaging/telegram-origin/OriginConfig.js.map +1 -1
  38. package/dist/messaging/telegram-origin/OriginForwardToHolder.d.ts +236 -0
  39. package/dist/messaging/telegram-origin/OriginForwardToHolder.d.ts.map +1 -0
  40. package/dist/messaging/telegram-origin/OriginForwardToHolder.js +337 -0
  41. package/dist/messaging/telegram-origin/OriginForwardToHolder.js.map +1 -0
  42. package/dist/messaging/telegram-origin/OriginMesh.d.ts.map +1 -1
  43. package/dist/messaging/telegram-origin/OriginMesh.js +17 -2
  44. package/dist/messaging/telegram-origin/OriginMesh.js.map +1 -1
  45. package/dist/messaging/telegram-origin/OriginMeshRelay.d.ts +7 -0
  46. package/dist/messaging/telegram-origin/OriginMeshRelay.d.ts.map +1 -1
  47. package/dist/messaging/telegram-origin/OriginMeshRelay.js +15 -15
  48. package/dist/messaging/telegram-origin/OriginMeshRelay.js.map +1 -1
  49. package/dist/messaging/telegram-origin/OriginStore.d.ts +38 -1
  50. package/dist/messaging/telegram-origin/OriginStore.d.ts.map +1 -1
  51. package/dist/messaging/telegram-origin/OriginStore.js +8 -1
  52. package/dist/messaging/telegram-origin/OriginStore.js.map +1 -1
  53. package/dist/messaging/telegram-origin/OriginStore.worker.js +1 -1
  54. package/dist/messaging/telegram-origin/OriginStore.worker.js.map +1 -1
  55. package/dist/messaging/telegram-origin/OriginStoreBackend.d.ts +58 -1
  56. package/dist/messaging/telegram-origin/OriginStoreBackend.d.ts.map +1 -1
  57. package/dist/messaging/telegram-origin/OriginStoreBackend.js +209 -23
  58. package/dist/messaging/telegram-origin/OriginStoreBackend.js.map +1 -1
  59. package/dist/messaging/telegram-origin/StoreTypes.d.ts +24 -0
  60. package/dist/messaging/telegram-origin/StoreTypes.d.ts.map +1 -1
  61. package/dist/messaging/telegram-origin/TelegramOriginBoot.d.ts.map +1 -1
  62. package/dist/messaging/telegram-origin/TelegramOriginBoot.js +7 -0
  63. package/dist/messaging/telegram-origin/TelegramOriginBoot.js.map +1 -1
  64. package/dist/messaging/telegram-origin/TelegramOriginRuntime.d.ts +49 -7
  65. package/dist/messaging/telegram-origin/TelegramOriginRuntime.d.ts.map +1 -1
  66. package/dist/messaging/telegram-origin/TelegramOriginRuntime.js +178 -2
  67. package/dist/messaging/telegram-origin/TelegramOriginRuntime.js.map +1 -1
  68. package/dist/messaging/telegram-origin/TelegramOriginService.d.ts +8 -0
  69. package/dist/messaging/telegram-origin/TelegramOriginService.d.ts.map +1 -1
  70. package/dist/messaging/telegram-origin/TelegramOriginService.js +19 -3
  71. package/dist/messaging/telegram-origin/TelegramOriginService.js.map +1 -1
  72. package/dist/messaging/telegram-origin/types.d.ts +12 -0
  73. package/dist/messaging/telegram-origin/types.d.ts.map +1 -1
  74. package/dist/messaging/telegram-origin/types.js +6 -0
  75. package/dist/messaging/telegram-origin/types.js.map +1 -1
  76. package/dist/scaffold/templates.d.ts.map +1 -1
  77. package/dist/scaffold/templates.js +3 -1
  78. package/dist/scaffold/templates.js.map +1 -1
  79. package/dist/server/fileRoutes.d.ts +26 -0
  80. package/dist/server/fileRoutes.d.ts.map +1 -1
  81. package/dist/server/fileRoutes.js +204 -56
  82. package/dist/server/fileRoutes.js.map +1 -1
  83. package/dist/server/routes.d.ts.map +1 -1
  84. package/dist/server/routes.js +38 -5
  85. package/dist/server/routes.js.map +1 -1
  86. package/dist/testing/selfActionRegistry.d.ts.map +1 -1
  87. package/dist/testing/selfActionRegistry.js +57 -0
  88. package/dist/testing/selfActionRegistry.js.map +1 -1
  89. package/package.json +1 -1
  90. package/src/data/builtin-manifest.json +64 -64
  91. package/src/data/standards-guard-index.json +1 -1
  92. package/src/data/standards-guard-index.meta.json +2 -2
  93. package/src/data/standards-registry.meta.json +1 -1
  94. package/src/scaffold/templates.ts +3 -1
  95. package/upgrades/1.3.1341.md +94 -0
  96. package/upgrades/side-effects/files-never-serve-key-material.md +399 -0
  97. package/upgrades/side-effects/telegram-origin-forward-to-holder.md +151 -0
@@ -25322,15 +25322,217 @@ export async function startServer(options) {
25322
25322
  const v = config.multiMachine?.relayTimeoutMs;
25323
25323
  return typeof v === 'number' && v > 0 ? v : 15_000;
25324
25324
  })();
25325
+ // ── a2a-single-agent-identity §4 (ACT-058): forward-to-holder ──
25326
+ // A token-holding machine that does NOT hold the serving lease (a
25327
+ // pool standby that legitimately owns a topic) forwards its reply to
25328
+ // the lease holder instead of holding it silently; a failed forward
25329
+ // becomes a DURABLE, reported hold. The forward itself is switchable
25330
+ // (`messaging[].config.messageOrigin.forwardToHolder.enabled`); the
25331
+ // durable held state, its reason and the notice are not.
25332
+ const originForwardMod = await import('../messaging/telegram-origin/OriginForwardToHolder.js');
25333
+ const originConfigMod = await import('../messaging/telegram-origin/OriginConfig.js');
25334
+ const hasUsableBotToken = typeof originBotToken === 'string' && originBotToken.length > 0;
25335
+ const forwardSwitchOn = () => {
25336
+ try {
25337
+ const messaging = liveConfig.get('messaging', config.messaging);
25338
+ const entry = Array.isArray(messaging) ? messaging.find(m => m?.type === 'telegram') : undefined;
25339
+ return originConfigMod.originForwardToHolderEnabled(entry?.config?.messageOrigin);
25340
+ }
25341
+ catch {
25342
+ return true;
25343
+ } // @silent-fallback-ok — an unreadable switch keeps the reachability floor on
25344
+ };
25345
+ const forwardLease = {
25346
+ selfMachineId: meshSelfId ?? '',
25347
+ leaseHolder: () => coordinator.getSyncStatus().leaseHolder ?? null,
25348
+ holdsLease: () => coordinator.holdsLease(),
25349
+ isHolderHealthy: (machineId) => { try {
25350
+ return leaseCoordinatorRef ? leaseCoordinatorRef.isHolderHealthy(machineId) : false;
25351
+ }
25352
+ catch {
25353
+ return false;
25354
+ } }, // @silent-fallback-ok — an unreadable lease reads as NOT healthy: settling, never a blind forward
25355
+ };
25356
+ const holderNickname = (machineId) => {
25357
+ try {
25358
+ return coordinator.managers.identityManager.loadRegistry().machines[machineId]?.nickname ?? `machine ${machineId.slice(0, 8)}`;
25359
+ }
25360
+ catch {
25361
+ return `machine ${machineId.slice(0, 8)}`;
25362
+ }
25363
+ };
25364
+ const heldAuditPath = path.join(config.stateDir, 'logs', 'telegram-origin-held.jsonl');
25365
+ const heldAudit = (row) => {
25366
+ try {
25367
+ fs.mkdirSync(path.dirname(heldAuditPath), { recursive: true });
25368
+ fs.appendFileSync(heldAuditPath, JSON.stringify({ machineId: meshSelfId, ...row }) + '\n');
25369
+ }
25370
+ catch { /* @silent-fallback-ok — audit is observability only */ }
25371
+ };
25372
+ const heldCollapser = new originForwardMod.HeldForwardItemCollapser({ now: Date.now });
25373
+ const raiseHeldItem = async (input) => {
25374
+ const verdict = heldCollapser.record(input.topicId);
25375
+ const holderName = input.holder ? holderNickname(input.holder) : 'no machine currently holds the serving lease';
25376
+ try {
25377
+ await telegram.createAttentionItem({ id: verdict.itemId, category: 'telegram-origin', priority: 'HIGH',
25378
+ title: verdict.aggregate ? `Replies held on ${holderNickname(meshSelfId ?? '')}: ${heldCollapser.topics().length} topics` : `Reply to topic ${input.topicId} is held`,
25379
+ summary: verdict.aggregate
25380
+ ? `Replies for topics ${heldCollapser.topics().join(', ')} could not be forwarded to the lease holder (${holderName}). They are held durably and retried; see GET /telegram/origins/status → held.`
25381
+ : `My reply to topic ${input.topicId} could not be forwarded to the lease holder (${holderName}): ${input.reason}. It is held durably (hold_reason: lease-not-held) and retried on the recovery schedule.`,
25382
+ sourceContext: 'telegram-origin-forward-to-holder' }, { hubOnly: true });
25383
+ }
25384
+ catch (error) {
25385
+ heldAudit({ phase: 'item-failed', topicId: input.topicId, error: error instanceof Error ? error.message : String(error) });
25386
+ }
25387
+ };
25388
+ const tgConfig = () => originTelegramConfig.config;
25389
+ const sendToHolder = (holder, timeoutMs = Math.min(relayTimeoutMs, originForwardMod.FORWARD_RPC_TIMEOUT_MS)) => {
25390
+ const url = peerUrl(holder);
25391
+ return (command) => {
25392
+ if (!url)
25393
+ return Promise.reject(new Error('origin-peer-unreachable'));
25394
+ // §4.2 — `capabilities` + `submit` share the 30 s attempt budget.
25395
+ return meshClient.send({ machineId: holder, url }, command, 0, { timeoutMs });
25396
+ };
25397
+ };
25398
+ const forwardDeps = (runtime) => ({
25399
+ lease: forwardLease,
25400
+ prepare: async (holder, input, operationId) => {
25401
+ const service = runtime.service;
25402
+ const { applyTelegramFormatter } = await import('../messaging/TelegramAdapter.js');
25403
+ const params = applyTelegramFormatter('sendMessage', { chat_id: input.chatId, text: input.text, parse_mode: 'Markdown',
25404
+ ...(input.topicId > 1 ? { message_thread_id: input.topicId } : {}), ...(input.silent ? { disable_notification: true } : {}) }, input.formatMode ?? tgConfig().formatMode).outgoingParams;
25405
+ const accountId = String(originBotToken).split(':')[0];
25406
+ const prepare = () => service.prepareBot({ method: 'sendMessage', accountId, params, executionOwnerMachineId: holder,
25407
+ ...(operationId === undefined ? {} : { operationId }), policyInput: service.currentSendPolicyInput(input.text, input.kindMetadata) });
25408
+ const operation = service.hasProducerContext() ? prepare() : service.runAsUnboundAutomation('telegram-server', prepare);
25409
+ service.authorizeSendPolicyDispatch(operation.record);
25410
+ await service.recordIntent(operation);
25411
+ return operation;
25412
+ },
25413
+ submit: (holder, operation) => originForwardMod.submitOriginToHolder({ operation, send: sendToHolder(holder), expectedOwner: holder }),
25414
+ hold: async (operation, detail) => {
25415
+ // Admit locally (the row + sealed payload) then name the reason
25416
+ // through the EXISTING operation-state writer; the recovery tick
25417
+ // re-forwards this same operation id.
25418
+ await runtime.service.admit(operation);
25419
+ const recorded = await runtime.store.recordOperationState({ operationId: operation.record.operationId, state: 'held', holdReason: originForwardMod.HOLD_REASON_LEASE_NOT_HELD, holdDetail: detail });
25420
+ await runtime.refreshHeldForward({ force: true });
25421
+ return recorded;
25422
+ },
25423
+ notify: async (holder, topicId) => {
25424
+ const text = originForwardMod.heldForwardNoticeText(holderNickname(holder));
25425
+ const { relayOriginBot } = await import('../messaging/telegram-origin/OriginMeshRelay.js');
25426
+ // Three short RPCs (5 s each): the notice must never push the reply
25427
+ // request past its route timeout — worst case settle 15 s + attempt 30 s + notice 15 s.
25428
+ const r = await runtime.service.runAsUnboundAutomation('telegram-server', () => relayOriginBot({ runtime, topicId, text, chatId: tgConfig().chatId,
25429
+ formatMode: tgConfig().formatMode, send: sendToHolder(holder, 5_000), executionOwnerMachineId: holder }));
25430
+ return Number.isSafeInteger(r.messageId);
25431
+ },
25432
+ audit: heldAudit, sleep: ms => new Promise(resolve => setTimeout(resolve, ms)), now: Date.now,
25433
+ });
25434
+ if (telegramOriginBoot && hasUsableBotToken && meshSelfId) {
25435
+ const runtime = telegramOriginBoot.runtime;
25436
+ telegram.holderForwardPolicy = {
25437
+ decide: () => {
25438
+ if (!forwardSwitchOn() || _sessionPoolStage() === 'dark')
25439
+ return 'local';
25440
+ const route = originForwardMod.classifyLeaseRoute(forwardLease);
25441
+ return route.kind === 'self' ? 'local' : route.kind === 'peer' ? 'forward' : 'settling';
25442
+ },
25443
+ };
25444
+ // §4.2 — recovery re-forwards the SAME operation; an outcome-unknown
25445
+ // attempt is resolved at the old holder's receipt, never re-sent blind.
25446
+ runtime.options.forwardRecovery = (operation, row) => originForwardMod.recoverForwardedHold({
25447
+ lease: forwardLease, prepare: forwardDeps(runtime).prepare, submit: forwardDeps(runtime).submit, audit: heldAudit, now: Date.now,
25448
+ receipt: async (machineId, operationId) => {
25449
+ try {
25450
+ const reply = await sendToHolder(machineId)({ type: 'telegram-origin', protocol: 'instar-telegram-origin-v1', action: 'receipt', operationId });
25451
+ const result = reply.result;
25452
+ if (reply.ok && result?.ok && result.operationId === operationId) {
25453
+ const state = String(result.state);
25454
+ if (state === 'accepted' && result.originReceiptConfirmed === true)
25455
+ return { state: 'accepted', receiptJson: JSON.stringify({ confirmedVia: 'receipt', deliveryMachineId: machineId, operationId }) };
25456
+ // Definite non-admission: never seen, evidence only, or terminal without delivery.
25457
+ if (['no-record', 'not-admitted', 'known-failed', 'expired', 'suppressed', 'superseded'].includes(state))
25458
+ return { state: 'not-accepted' };
25459
+ // The holder has custody (its own recovery delivers it): wait, never re-forward.
25460
+ if (['admitted', 'held', 'partial', 'accepted'].includes(state))
25461
+ return { state: 'owned-by-holder' };
25462
+ return { state: 'unreachable' };
25463
+ }
25464
+ // `origin-receipt-unavailable` / any refusal is an UNKNOWN (a store outage looks the same as absence to an old holder): keep waiting.
25465
+ return { state: 'unreachable' };
25466
+ }
25467
+ catch {
25468
+ return { state: 'unreachable' };
25469
+ }
25470
+ },
25471
+ resolve: (operationId, deliveryMachineId, receiptJson) => runtime.store.recordForwardedAcceptance({ operationId, deliveryMachineId, receiptJson }),
25472
+ rehold: async (operationId, detail) => { await runtime.store.recordOperationState({ operationId, state: 'held', holdReason: originForwardMod.HOLD_REASON_LEASE_NOT_HELD, holdDetail: detail }); },
25473
+ supersede: (operationId, next, detail) => runtime.store.supersedeWithAdmission({ operationId, admission: next.admission,
25474
+ holdReason: originForwardMod.HOLD_REASON_LEASE_NOT_HELD, holdDetail: detail }),
25475
+ sendLocal: async (detail) => {
25476
+ const sent = await telegram.sendToTopic(detail.topicId, detail.text, { silent: detail.silent, kindMetadata: detail.kindMetadata, provenance: 'automation' });
25477
+ return { messageId: sent.messageId };
25478
+ },
25479
+ }, row, operation);
25480
+ // §4.2 — a DIRECT-path hold (`authorize` refused while not holding the lease:
25481
+ // pool dark, or the lease moved between the adapter decision and authorize).
25482
+ // Its sealed record is owned by THIS machine, so it cannot be forwarded; it is
25483
+ // re-admitted and replayed the moment the lease returns (runtime.recoverHeld).
25484
+ // Report it exactly like a failed forward: the fixed notice through the
25485
+ // holder when one is reachable, otherwise the collapsed attention item.
25486
+ runtime.options.onLeaseHoldObserved = async (row) => {
25487
+ const topicId = Number(row.destination.topicId);
25488
+ heldAudit({ phase: 'direct-hold-observed', topicId: row.destination.topicId, operationId: row.operationId });
25489
+ if (!Number.isSafeInteger(topicId))
25490
+ return;
25491
+ const route = originForwardMod.classifyLeaseRoute(forwardLease);
25492
+ let noticeDelivered = false;
25493
+ if (route.kind === 'peer' && forwardSwitchOn()) {
25494
+ try {
25495
+ noticeDelivered = await forwardDeps(runtime).notify(route.holder, topicId);
25496
+ }
25497
+ catch {
25498
+ noticeDelivered = false;
25499
+ } // @silent-fallback-ok — an undelivered notice falls through to the attention item below
25500
+ }
25501
+ heldAudit({ phase: noticeDelivered ? 'notice-sent' : 'notice-not-sent', topicId, operationId: row.operationId, holder: route.kind === 'peer' ? route.holder : null });
25502
+ if (!noticeDelivered)
25503
+ await raiseHeldItem({ topicId, holder: route.kind === 'peer' ? route.holder : null, operationId: row.operationId, lastAttempt: 'refused', reason: 'lease-not-held (direct path)' });
25504
+ };
25505
+ runtime.options.onHeldForwardExpired = async (row) => {
25506
+ const topicId = Number(row.destination.topicId);
25507
+ heldAudit({ phase: 'expired', topicId: row.destination.topicId, operationId: row.operationId, lastAttempt: row.holdDetail?.lastAttempt ?? null });
25508
+ if (Number.isSafeInteger(topicId))
25509
+ heldCollapser.clear(topicId);
25510
+ await telegram.createAttentionItem({ id: `telegram-origin-held:${row.destination.topicId ?? row.operationId}:expired`, category: 'telegram-origin', priority: 'HIGH',
25511
+ title: `Held reply to topic ${row.destination.topicId ?? '?'} expired`, summary: originForwardMod.expiredForwardWording(row),
25512
+ sourceContext: 'telegram-origin-forward-to-holder' }, { hubOnly: true });
25513
+ };
25514
+ }
25325
25515
  telegram.outboundRelay = async (topicId, text, opts) => {
25326
25516
  if (telegramOriginBoot) {
25517
+ const { TelegramOriginHoldError, OriginForwardSettledLocallyError } = await import('../messaging/telegram-origin/types.js');
25518
+ const tg = tgConfig();
25519
+ if (hasUsableBotToken && meshSelfId) {
25520
+ // §4.1/§4.2 — settle, forward to the holder, ladder, then hold durably.
25521
+ const runtime = telegramOriginBoot.runtime;
25522
+ const outcome = await originForwardMod.forwardReplyToHolder(forwardDeps(runtime), { topicId, chatId: tg.chatId, text, silent: opts?.silent, formatMode: tg.formatMode, kindMetadata: opts?.kindMetadata });
25523
+ if (outcome.kind === 'local')
25524
+ throw new OriginForwardSettledLocallyError();
25525
+ if (outcome.kind === 'sent')
25526
+ return { messageId: outcome.messageId, topicId, destinationStoreConfirmed: true };
25527
+ if (!outcome.noticeDelivered)
25528
+ void raiseHeldItem({ topicId, holder: outcome.holder, operationId: outcome.operationId, lastAttempt: outcome.lastAttempt, reason: outcome.reason });
25529
+ throw new TelegramOriginHoldError(originForwardMod.HOLD_REASON_LEASE_NOT_HELD, outcome.operationId, 'held');
25530
+ }
25327
25531
  const holder = coordinator.getSyncStatus().leaseHolder;
25328
25532
  const url = holder ? peerUrl(holder) : null;
25329
- const { TelegramOriginHoldError } = await import('../messaging/telegram-origin/types.js');
25330
25533
  if (!holder || !url || holder === meshSelfId)
25331
25534
  throw new TelegramOriginHoldError('origin-credential-owner-unavailable');
25332
25535
  const { relayOriginBot } = await import('../messaging/telegram-origin/OriginMeshRelay.js');
25333
- const tg = originTelegramConfig.config;
25334
25536
  return relayOriginBot({ runtime: telegramOriginBoot.runtime, topicId, text, chatId: tg.chatId,
25335
25537
  silent: opts?.silent, formatMode: tg.formatMode, kindMetadata: opts?.kindMetadata,
25336
25538
  send: command => meshClient.send({ machineId: holder, url }, command, 0, { timeoutMs: relayTimeoutMs }) });