instar 1.3.1582 → 1.3.1583

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 (94) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +98 -2
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/AppConversationService.d.ts +15 -0
  5. package/dist/core/AppConversationService.d.ts.map +1 -1
  6. package/dist/core/AppConversationService.js +20 -0
  7. package/dist/core/AppConversationService.js.map +1 -1
  8. package/dist/core/AppConversationStore.d.ts +21 -3
  9. package/dist/core/AppConversationStore.d.ts.map +1 -1
  10. package/dist/core/AppConversationStore.js +35 -5
  11. package/dist/core/AppConversationStore.js.map +1 -1
  12. package/dist/core/AppConversationsReplicatedStore.js +1 -1
  13. package/dist/core/AppConversationsReplicatedStore.js.map +1 -1
  14. package/dist/core/AppRecordSignatures.d.ts +102 -0
  15. package/dist/core/AppRecordSignatures.d.ts.map +1 -0
  16. package/dist/core/AppRecordSignatures.js +247 -0
  17. package/dist/core/AppRecordSignatures.js.map +1 -0
  18. package/dist/core/CoherenceJournal.d.ts +1 -1
  19. package/dist/core/CoherenceJournal.d.ts.map +1 -1
  20. package/dist/core/CoherenceJournal.js +10 -2
  21. package/dist/core/CoherenceJournal.js.map +1 -1
  22. package/dist/core/MobileDeviceKeyLedger.d.ts +93 -0
  23. package/dist/core/MobileDeviceKeyLedger.d.ts.map +1 -0
  24. package/dist/core/MobileDeviceKeyLedger.js +323 -0
  25. package/dist/core/MobileDeviceKeyLedger.js.map +1 -0
  26. package/dist/core/MobileDeviceKeyLedgerReplicatedStore.d.ts +19 -0
  27. package/dist/core/MobileDeviceKeyLedgerReplicatedStore.d.ts.map +1 -0
  28. package/dist/core/MobileDeviceKeyLedgerReplicatedStore.js +53 -0
  29. package/dist/core/MobileDeviceKeyLedgerReplicatedStore.js.map +1 -0
  30. package/dist/core/MobileDeviceKeys.d.ts +9 -2
  31. package/dist/core/MobileDeviceKeys.d.ts.map +1 -1
  32. package/dist/core/MobileDeviceKeys.js +8 -4
  33. package/dist/core/MobileDeviceKeys.js.map +1 -1
  34. package/dist/core/PostUpdateMigrator.d.ts +3 -0
  35. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  36. package/dist/core/PostUpdateMigrator.js +22 -2
  37. package/dist/core/PostUpdateMigrator.js.map +1 -1
  38. package/dist/core/ReplicatedStoreReconcilerWiring.d.ts.map +1 -1
  39. package/dist/core/ReplicatedStoreReconcilerWiring.js +1 -0
  40. package/dist/core/ReplicatedStoreReconcilerWiring.js.map +1 -1
  41. package/dist/core/devGatedFeatures.js +1 -1
  42. package/dist/core/devGatedFeatures.js.map +1 -1
  43. package/dist/core/peerHomebaseAwareness.d.ts +2 -0
  44. package/dist/core/peerHomebaseAwareness.d.ts.map +1 -1
  45. package/dist/core/peerHomebaseAwareness.js +12 -0
  46. package/dist/core/peerHomebaseAwareness.js.map +1 -1
  47. package/dist/data/standards-guard-index.json +1 -1
  48. package/dist/data/standards-guard-index.meta.json +2 -2
  49. package/dist/data/standards-registry.meta.json +1 -1
  50. package/dist/server/AgentServer.d.ts.map +1 -1
  51. package/dist/server/AgentServer.js +31 -0
  52. package/dist/server/AgentServer.js.map +1 -1
  53. package/dist/server/CapabilityIndex.js +1 -1
  54. package/dist/server/CapabilityIndex.js.map +1 -1
  55. package/dist/server/mobileConversationRoutes.d.ts +22 -9
  56. package/dist/server/mobileConversationRoutes.d.ts.map +1 -1
  57. package/dist/server/mobileConversationRoutes.js +99 -25
  58. package/dist/server/mobileConversationRoutes.js.map +1 -1
  59. package/dist/server/mobilePairingRoutes.d.ts +10 -1
  60. package/dist/server/mobilePairingRoutes.d.ts.map +1 -1
  61. package/dist/server/mobilePairingRoutes.js +105 -6
  62. package/dist/server/mobilePairingRoutes.js.map +1 -1
  63. package/dist/server/routes.d.ts.map +1 -1
  64. package/dist/server/routes.js +10 -2
  65. package/dist/server/routes.js.map +1 -1
  66. package/dist/threadline/PeerUserMeantJudge.d.ts +135 -0
  67. package/dist/threadline/PeerUserMeantJudge.d.ts.map +1 -0
  68. package/dist/threadline/PeerUserMeantJudge.js +394 -0
  69. package/dist/threadline/PeerUserMeantJudge.js.map +1 -0
  70. package/dist/threadline/ThreadlineEndpoints.d.ts +1 -0
  71. package/dist/threadline/ThreadlineEndpoints.d.ts.map +1 -1
  72. package/dist/threadline/ThreadlineEndpoints.js +2 -0
  73. package/dist/threadline/ThreadlineEndpoints.js.map +1 -1
  74. package/dist/threadline/ThreadlineRouter.d.ts +9 -1
  75. package/dist/threadline/ThreadlineRouter.d.ts.map +1 -1
  76. package/dist/threadline/ThreadlineRouter.js +17 -2
  77. package/dist/threadline/ThreadlineRouter.js.map +1 -1
  78. package/dist/threadline/TopicLinkageHandler.d.ts +21 -0
  79. package/dist/threadline/TopicLinkageHandler.d.ts.map +1 -1
  80. package/dist/threadline/TopicLinkageHandler.js +41 -15
  81. package/dist/threadline/TopicLinkageHandler.js.map +1 -1
  82. package/dist/threadline/peerHomebaseRelay.d.ts +32 -4
  83. package/dist/threadline/peerHomebaseRelay.d.ts.map +1 -1
  84. package/dist/threadline/peerHomebaseRelay.js +143 -35
  85. package/dist/threadline/peerHomebaseRelay.js.map +1 -1
  86. package/package.json +1 -1
  87. package/src/data/builtin-manifest.json +64 -64
  88. package/src/data/standards-guard-index.json +1 -1
  89. package/src/data/standards-guard-index.meta.json +2 -2
  90. package/src/data/standards-registry.meta.json +1 -1
  91. package/src/data/state-coherence-registry.json +17 -0
  92. package/upgrades/1.3.1583.md +42 -0
  93. package/upgrades/side-effects/mobile-native-conversations-inc2.md +142 -0
  94. package/upgrades/side-effects/peer-homebase-per-message.md +149 -0
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
@@ -4731,6 +4731,10 @@ export async function startServer(options) {
4731
4731
  // are on (no separate switch: for a native conversation the store is the only copy).
4732
4732
  const { APP_CONVERSATIONS_KIND_REGISTRATION } = await import('../core/AppConversationsReplicatedStore.js');
4733
4733
  replicatedKindRegistry.register(APP_CONVERSATIONS_KIND_REGISTRATION);
4734
+ // mobile-native-conversations §4.8 — the `mobileDeviceKeyLedger` kind (inert registration; on
4735
+ // whenever native app conversations are on, so a signed record can be checked on any machine).
4736
+ const { DEVICE_KEY_LEDGER_KIND_REGISTRATION } = await import('../core/MobileDeviceKeyLedgerReplicatedStore.js');
4737
+ replicatedKindRegistry.register(DEVICE_KEY_LEDGER_KIND_REGISTRATION);
4734
4738
  // WS2.3 (ws23-relationships-userregistry-security) — register the SECOND concrete
4735
4739
  // replicated kind, `relationship-record`, onto the registry: the FIRST PII kind.
4736
4740
  // Dual-registry's dynamic half (the static half is CoherenceJournal.JOURNAL_KINDS,
@@ -4916,6 +4920,15 @@ export async function startServer(options) {
4916
4920
  _appConversationService?.ingestReplicated(entries.map((e) => appEventFromReplicatedData(e.data)));
4917
4921
  }).catch(() => { });
4918
4922
  }
4923
+ // Device-key ledger rows: each lands only when it verifies under the pinned key of the machine it names.
4924
+ if (kind === 'mobile-device-key-ledger-row' && _appConversationService?.deps.keyLedger) {
4925
+ const ledger = _appConversationService.deps.keyLedger;
4926
+ for (const e of entries) {
4927
+ const r = ledger.ingestReplicated(e.data);
4928
+ if (r === 'bad-signature' || r === 'unverifiable')
4929
+ console.warn(` [device-key-ledger] a replicated row was not accepted (${r})`);
4930
+ }
4931
+ }
4919
4932
  });
4920
4933
  // Author-side HLC clock — persisted under the journal dir (atomic temp+rename)
4921
4934
  // so the merge total order survives restarts (§3.5). node = this machine's id.
@@ -5023,7 +5036,7 @@ export async function startServer(options) {
5023
5036
  const _stateSyncStoresResolved = (() => {
5024
5037
  let s = _stateSyncStoresBase ?? {};
5025
5038
  if (_appNativeOn)
5026
- s = { ...s, appConversations: { enabled: true } };
5039
+ s = { ...s, appConversations: { enabled: true }, mobileDeviceKeyLedger: { enabled: true } };
5027
5040
  if (_accountFollowMeEnabled)
5028
5041
  s = { ...s, subscriptionAccountMeta: { enabled: true } };
5029
5042
  if (_pinReplicateEnabled)
@@ -8588,7 +8601,24 @@ export async function startServer(options) {
8588
8601
  console.warn(` [app-conversations] reply relay: ${[...r.errors, ...r.degraded].join('; ')}`);
8589
8602
  }
8590
8603
  let appDispatch = null;
8604
+ // Signed records (§4.6): this machine's identity signer, the signing-key history lookup and
8605
+ // the device-key ledger. No identity ⇒ messages are still stored and delivered, read `unknown`.
8606
+ const [sigMod, ledgerMod, ledgerReplMod] = await Promise.all([
8607
+ import('../core/AppRecordSignatures.js'), import('../core/MobileDeviceKeyLedger.js'), import('../core/MobileDeviceKeyLedgerReplicatedStore.js'),
8608
+ ]);
8609
+ const appSigner = sigMod.loadMachineSigner(config.stateDir);
8610
+ const appKeyLookup = sigMod.identityKeyLookup(config.stateDir);
8611
+ if (!appSigner)
8612
+ console.warn(' [app-conversations] this machine has no identity key: app messages will read "couldn\'t check"');
8613
+ const appKeyLedger = appSigner ? new ledgerMod.MobileDeviceKeyLedger({
8614
+ stateDir: config.stateDir, signer: appSigner, keyLookup: appKeyLookup,
8615
+ reportDegradation: (what, detail) => degradationReporter.report({
8616
+ feature: what, primary: 'check signed app messages against the device-key ledger', fallback: 'messages read "couldn\'t check"',
8617
+ reason: detail, impact: 'Signed app messages cannot be checked on this machine.',
8618
+ }),
8619
+ }) : null;
8591
8620
  const appSvc = new AppConversationService({
8621
+ signer: appSigner, keyLookup: appKeyLookup, keyLedger: appKeyLedger,
8592
8622
  stateDir: config.stateDir,
8593
8623
  machineId: () => _meshSelfId ?? os.hostname(),
8594
8624
  registry: conversationRegistry,
@@ -8635,6 +8665,27 @@ export async function startServer(options) {
8635
8665
  const emitter = replicatedRecordEmitter;
8636
8666
  appSvc.store.setReplicationHook((ev) => emitter.emit(replMod.APP_CONVERSATIONS_STORE_KEY, replMod.deriveAppConversationRecordKey(ev.conversationKey, ev.eventId), replMod.buildAppConversationRecordData(ev)));
8637
8667
  }
8668
+ if (replicatedRecordEmitter && appKeyLedger) {
8669
+ const emitter = replicatedRecordEmitter;
8670
+ appKeyLedger.setReplicationHook((row) => emitter.emit(ledgerReplMod.DEVICE_KEY_LEDGER_STORE_KEY, ledgerReplMod.deriveDeviceKeyLedgerRecordKey(row), ledgerReplMod.buildDeviceKeyLedgerRecordData(row)));
8671
+ }
8672
+ if (replicatedPeerStreamReader && cjOwnMachineId && appKeyLedger) {
8673
+ try {
8674
+ const reader = replicatedPeerStreamReader;
8675
+ let landed = 0;
8676
+ for (const rk of reader.listRecordKeys(ledgerReplMod.DEVICE_KEY_LEDGER_STORE_KEY)) {
8677
+ for (const r of reader.loadOriginRecords(ledgerReplMod.DEVICE_KEY_LEDGER_STORE_KEY, rk)) {
8678
+ if (r.origin !== cjOwnMachineId && appKeyLedger.ingestReplicated(r.data) === 'accepted')
8679
+ landed++;
8680
+ }
8681
+ }
8682
+ if (landed > 0)
8683
+ console.log(pc.dim(` [device-key-ledger] landed ${landed} replicated row(s) at boot`));
8684
+ }
8685
+ catch (err) {
8686
+ console.warn(` [device-key-ledger] boot replication read failed: ${err instanceof Error ? err.message : String(err)}`);
8687
+ }
8688
+ }
8638
8689
  if (replicatedPeerStreamReader && cjOwnMachineId) {
8639
8690
  try {
8640
8691
  const reader = replicatedPeerStreamReader;
@@ -17301,10 +17352,41 @@ export async function startServer(options) {
17301
17352
  profileFor: (peer) => _peerProfileFor?.(peer) ?? null,
17302
17353
  conversationFor: (threadId) => conversationStore.get(threadId) ?? null,
17303
17354
  });
17355
+ // §2.7: "meant for the user?" is judged PER MESSAGE (Jev first, then the Codex fast tier,
17356
+ // then the shared provider — so a Claude-only agent still judges; no verdict posts). Read
17357
+ // live: `threadline.peerHomebase.perMessageClassification: false` restores first-only.
17358
+ let peerUserMeantJudge = null;
17359
+ try {
17360
+ const { buildPeerUserMeantJudge } = await import('../threadline/PeerUserMeantJudge.js');
17361
+ const { getFeatureMetricsRecorder } = await import('../core/CircuitBreakingIntelligenceProvider.js');
17362
+ const { SecretStore } = await import('../core/SecretStore.js');
17363
+ let userMeantCodex = null;
17364
+ try {
17365
+ const { LlmCircuitBreaker } = await import('../core/LlmCircuitBreaker.js');
17366
+ const { buildIntelligenceProvider: buildUserMeantReferee } = await import('../core/intelligenceProviderFactory.js');
17367
+ userMeantCodex = buildUserMeantReferee({ framework: 'codex-cli', breaker: new LlmCircuitBreaker() });
17368
+ }
17369
+ catch { /* @silent-fallback-ok — no Codex CLI; the shared provider is the next referee */ }
17370
+ peerUserMeantJudge = buildPeerUserMeantJudge({
17371
+ readPeerHomebaseBlock: () => liveConfig.get('threadline.peerHomebase', undefined),
17372
+ readSecret: (name) => new SecretStore({ stateDir: config.stateDir, forceFileKey: config.secrets?.forceFileKey }).get(name),
17373
+ referees: () => [
17374
+ ...(userMeantCodex ? [{ name: 'codex', provider: userMeantCodex }] : []),
17375
+ ...(sharedIntelligence ? [{ name: 'shared', provider: sharedIntelligence }] : []),
17376
+ ],
17377
+ stateDir: config.stateDir,
17378
+ metrics: { record: (r) => getFeatureMetricsRecorder()?.record(r) },
17379
+ });
17380
+ console.log(pc.dim(` Peer user-meant judge: constructed (referees: ${[userMeantCodex ? 'codex' : null, sharedIntelligence ? 'shared' : null].filter(Boolean).join(', ') || 'none'})`));
17381
+ }
17382
+ catch (err) {
17383
+ console.log(pc.yellow(` Peer user-meant judge: not constructed (${err?.message ?? 'unknown'}) — first-only rule applies`));
17384
+ }
17304
17385
  const peerHomebaseRelay = telegram
17305
17386
  ? new PeerHomebaseRelay({
17306
17387
  resolve: peerUserTopic,
17307
17388
  salienceGate: new (await import('../threadline/SalienceGate.js')).SalienceGate(),
17389
+ userMeantJudge: peerUserMeantJudge,
17308
17390
  sendToTopic: (topicId, text) => withThreadlineForwardedAuthor(telegramOriginBoot?.runtime.service, 'inbound', topicId, text, () => telegram.sendToTopic(topicId, text)),
17309
17391
  log: (m) => console.log(m),
17310
17392
  })
@@ -17924,7 +18006,11 @@ export async function startServer(options) {
17924
18006
  const a2aRelayForwardEnabled = () => a2aRelayForwardMod.resolveRelayForwardEnabled(liveConfig.get('threadline.relayForward.enabled', undefined), config);
17925
18007
  const salienceGate = new SalienceGate(); // fallback-only for v1; spec §5.4
17926
18008
  // §2.6: tell a spawned thread session which topic to use for the user.
17927
- threadlineRouter.setPeerUserTopicResolver((q) => peerUserTopic({ threadId: q.threadId, peers: q.peers }));
18009
+ threadlineRouter.setPeerUserTopicResolver((q) => peerUserTopic({ threadId: q.threadId, peers: q.peers }), {
18010
+ // §2.7: only where the relay has actually taken this thread under per-message judging.
18011
+ serverPostsUserMeant: (threadId) => peerHomebaseRelay?.postsUserMeantFor(threadId) === true
18012
+ && conversationStore.get(threadId)?.boundTopicId == null,
18013
+ });
17928
18014
  let topicLinkageHandler = null;
17929
18015
  if (commitmentTracker) {
17930
18016
  topicLinkageHandler = new TopicLinkageHandler({
@@ -17985,6 +18071,15 @@ export async function startServer(options) {
17985
18071
  const r = peerUserTopic({ threadId: q.threadId, peers: q.peers, existingTopicId: q.originTopicId });
17986
18072
  return r.topicId;
17987
18073
  },
18074
+ // §2.7: a reply redirected to the homebase is judged per message (null when switched off).
18075
+ judgeUserMeant: async (q) => {
18076
+ if (!peerUserMeantJudge || !peerUserMeantJudge.enabled())
18077
+ return null;
18078
+ const v = await peerUserMeantJudge.judge({ threadId: q.threadId, peer: q.peers[0] ?? '', senderName: q.senderName, text: q.text, deadlineMs: q.deadlineMs });
18079
+ if (v.firstOnly)
18080
+ return null; // the judge's budget is spent: the SalienceGate (first-only) decides
18081
+ return { verdict: v.userMeant ? 'user-visible' : 'agent-internal', reason: `per-message: ${v.decidedBy}/${v.reason}` };
18082
+ },
17988
18083
  onRemoteReplyEvent: (event) => {
17989
18084
  if (event === 'ask')
17990
18085
  a2aRelayForwardCtx.counters.replyAsks++;
@@ -18821,6 +18916,7 @@ export async function startServer(options) {
18821
18916
  peers: [senderFingerprint],
18822
18917
  senderName,
18823
18918
  text: textContent,
18919
+ messageType: typeof msgType === 'string' ? msgType : null,
18824
18920
  }).then((r) => { if (!r.posted)
18825
18921
  hubSurface(); }, () => hubSurface());
18826
18922
  }