instar 1.3.1654 → 1.3.1656

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 (83) hide show
  1. package/dashboard/console/allowed.js +1 -1
  2. package/dashboard/console/services-model.js +3 -2
  3. package/dashboard-pair/service-yes.js +22 -7
  4. package/dist/commands/server.d.ts.map +1 -1
  5. package/dist/commands/server.js +22 -1
  6. package/dist/commands/server.js.map +1 -1
  7. package/dist/core/GitLeaseStore.d.ts +13 -0
  8. package/dist/core/GitLeaseStore.d.ts.map +1 -1
  9. package/dist/core/GitLeaseStore.js +24 -0
  10. package/dist/core/GitLeaseStore.js.map +1 -1
  11. package/dist/core/LeaseCoordinator.d.ts +9 -0
  12. package/dist/core/LeaseCoordinator.d.ts.map +1 -1
  13. package/dist/core/LeaseCoordinator.js +41 -6
  14. package/dist/core/LeaseCoordinator.js.map +1 -1
  15. package/dist/core/PostUpdateMigrator.d.ts +6 -0
  16. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  17. package/dist/core/PostUpdateMigrator.js +25 -0
  18. package/dist/core/PostUpdateMigrator.js.map +1 -1
  19. package/dist/core/poolReadiness.d.ts.map +1 -1
  20. package/dist/core/poolReadiness.js +6 -2
  21. package/dist/core/poolReadiness.js.map +1 -1
  22. package/dist/core/poolTransferReason.d.ts +12 -0
  23. package/dist/core/poolTransferReason.d.ts.map +1 -0
  24. package/dist/core/poolTransferReason.js +16 -0
  25. package/dist/core/poolTransferReason.js.map +1 -0
  26. package/dist/core/rebalanceProof.d.ts +22 -0
  27. package/dist/core/rebalanceProof.d.ts.map +1 -1
  28. package/dist/core/rebalanceProof.js +44 -5
  29. package/dist/core/rebalanceProof.js.map +1 -1
  30. package/dist/data/standards-guard-index.json +50 -2
  31. package/dist/data/standards-guard-index.meta.json +3 -3
  32. package/dist/data/standards-registry.md +13 -4
  33. package/dist/data/standards-registry.meta.json +3 -3
  34. package/dist/messaging/telegram-origin/TelegramOriginRuntime.d.ts +43 -0
  35. package/dist/messaging/telegram-origin/TelegramOriginRuntime.d.ts.map +1 -1
  36. package/dist/messaging/telegram-origin/TelegramOriginRuntime.js +22 -3
  37. package/dist/messaging/telegram-origin/TelegramOriginRuntime.js.map +1 -1
  38. package/dist/monitoring/DashboardInsightEngine.d.ts +6 -2
  39. package/dist/monitoring/DashboardInsightEngine.d.ts.map +1 -1
  40. package/dist/monitoring/DashboardInsightEngine.js +7 -3
  41. package/dist/monitoring/DashboardInsightEngine.js.map +1 -1
  42. package/dist/monitoring/MaturationLadder.d.ts +2 -0
  43. package/dist/monitoring/MaturationLadder.d.ts.map +1 -1
  44. package/dist/monitoring/MaturationLadder.js +5 -0
  45. package/dist/monitoring/MaturationLadder.js.map +1 -1
  46. package/dist/native/InstarWedgeCanary.app/Contents/MacOS/InstarWedgeCanary +0 -0
  47. package/dist/native/instar-desktop-host +0 -0
  48. package/dist/recommended/confirmTokens.d.ts +5 -0
  49. package/dist/recommended/confirmTokens.d.ts.map +1 -1
  50. package/dist/recommended/confirmTokens.js +10 -0
  51. package/dist/recommended/confirmTokens.js.map +1 -1
  52. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  53. package/dist/server/CapabilityIndex.js +11 -1
  54. package/dist/server/CapabilityIndex.js.map +1 -1
  55. package/dist/server/mobilePairingRoutes.d.ts.map +1 -1
  56. package/dist/server/mobilePairingRoutes.js +16 -4
  57. package/dist/server/mobilePairingRoutes.js.map +1 -1
  58. package/dist/server/mobilePhonesMessagesRoutes.d.ts +142 -0
  59. package/dist/server/mobilePhonesMessagesRoutes.d.ts.map +1 -0
  60. package/dist/server/mobilePhonesMessagesRoutes.js +419 -0
  61. package/dist/server/mobilePhonesMessagesRoutes.js.map +1 -0
  62. package/dist/server/recommendedServiceRoutes.d.ts.map +1 -1
  63. package/dist/server/recommendedServiceRoutes.js +43 -3
  64. package/dist/server/recommendedServiceRoutes.js.map +1 -1
  65. package/dist/server/routes.d.ts.map +1 -1
  66. package/dist/server/routes.js +93 -4
  67. package/dist/server/routes.js.map +1 -1
  68. package/package.json +1 -1
  69. package/scripts/safe-merge.mjs +2 -0
  70. package/src/data/builtin-manifest.json +63 -63
  71. package/src/data/standards-guard-index.json +50 -2
  72. package/src/data/standards-guard-index.meta.json +3 -3
  73. package/src/data/standards-registry.md +13 -4
  74. package/src/data/standards-registry.meta.json +3 -3
  75. package/upgrades/1.3.1655.md +28 -0
  76. package/upgrades/1.3.1656.md +46 -0
  77. package/upgrades/side-effects/lease-takeover-no-shared-medium.md +51 -0
  78. package/upgrades/side-effects/maturation-path-amendment.eli16.md +7 -0
  79. package/upgrades/side-effects/maturation-path-amendment.md +68 -0
  80. package/upgrades/side-effects/mobile-native-phones-messages.md +102 -0
  81. package/upgrades/side-effects/pool-reading-honesty.md +49 -0
  82. package/upgrades/side-effects/rebalance-proof-via-holder.md +48 -0
  83. package/upgrades/side-effects/recommended-services-no.md +66 -0
@@ -183,7 +183,7 @@ function suggestionsBlock(sg, post, refresh) {
183
183
  // ── Outside services ──────────────────────────────────────────────────────
184
184
  function servicesBlock(services) {
185
185
  if (!services.ok) return el('section', { class: 'list-section', id: 'sec-services' }, el('div', { class: 'list-head' }, el('h2', { class: 'eyebrow', text: 'Outside services' })), cantTell(services.reason));
186
- const wrap = section({ id: 'services', title: 'Outside services', rows: services.rows, onRow: (r) => sheet({ title: r.main }, story(r.lines), r.yesHref ? el('a', { class: 'btn-primary btn-inline', href: r.yesHref, text: 'Set this up' }) : null) });
186
+ const wrap = section({ id: 'services', title: 'Outside services', rows: services.rows, onRow: (r) => sheet({ title: r.main }, story(r.lines), r.action ? el('a', { class: 'btn-primary btn-inline', href: r.action.href, text: r.action.label }) : null) });
187
187
  if (services.note) wrap.querySelector('.list-head').after(note(services.note));
188
188
  return wrap;
189
189
  }
@@ -30,8 +30,9 @@ export function buildServices(res) {
30
30
  main: s.name || s.service,
31
31
  sub: s.benefit || '',
32
32
  end: choiceWord(s),
33
- // Only something that can be set up gets a way to say yes, and a yes already given needs none.
34
- yesHref: s.available && s.choice !== 'yes' ? `/dashboard/service-yes?service=${encodeURIComponent(s.service)}` : null,
33
+ // The same page takes a yes or a no. A yes on another machine, or one a later no cancelled, leaves
34
+ // this machine still needing its own, so the choice alone doesn't decide this.
35
+ action: s.available ? { href: `/dashboard/service-yes?service=${encodeURIComponent(s.service)}`, label: s.grantedHere ? 'Change my answer' : 'Yes or no' } : null,
35
36
  lines: [
36
37
  { label: 'What you get', text: s.benefit },
37
38
  { label: 'What leaves this machine', text: s.dataLeaves },
@@ -14,15 +14,17 @@ const REFUSED = {
14
14
  'insecure-connection': 'This connection isn’t secure enough to take your PIN. Nothing changed. Open this page through your agent’s secure address.',
15
15
  'wrong-owner': 'The owner of this agent changed since this page opened. Nothing changed. Open it again.',
16
16
  'wrong-machine': 'This page belongs to a different machine. Nothing changed. Open it again.',
17
+ 'too-many-pages': 'This page has been opened too many times in a short while. Nothing changed. Wait a minute, then open it again.',
17
18
  'recommended-services-unavailable': 'I can’t save a choice on this machine right now. Nothing changed.',
18
19
  };
19
20
 
21
+ const REASONS = [['none', 'No particular reason'], ['cost', 'The cost'], ['privacy', 'Privacy'], ['not-needed', 'I don’t need it'], ['have-another', 'I use something else'], ['other', 'Something else']];
22
+
20
23
  function el(tag, attrs = {}, ...kids) {
21
24
  const n = document.createElement(tag);
22
25
  for (const [k, v] of Object.entries(attrs)) {
23
26
  if (k === 'text') n.textContent = v;
24
27
  else if (k === 'class') n.className = v;
25
- else if (k === 'onclick') n.addEventListener('click', v);
26
28
  else if (k === 'onsubmit') n.addEventListener('submit', v);
27
29
  else n.setAttribute(k, v);
28
30
  }
@@ -37,18 +39,25 @@ function line(label, text) { return text ? el('section', {}, el('h2', { text: la
37
39
  function page(d) {
38
40
  const error = el('p', { class: 'pair-error', role: 'alert' });
39
41
  const pin = el('input', { type: 'password', inputmode: 'numeric', autocomplete: 'off', 'aria-label': 'Your PIN', placeholder: 'Your PIN' });
40
- const button = el('button', { class: 'pair-button', type: 'submit', text: `Yes, set up ${d.name}` });
41
- const form = el('form', { class: 'pair-pin', onsubmit: async (e) => {
42
- e.preventDefault();
42
+ const button = el('button', { class: 'pair-button', type: 'button', text: `Yes, set up ${d.name}` });
43
+ const reason = el('select', { 'aria-label': 'Why not? This is optional.' },
44
+ ...REASONS.map(([value, text]) => el('option', { value, text })));
45
+ const noButton = el('button', { class: 'pair-link-button', type: 'button', text: `No, don’t set up ${d.name}` });
46
+ const send = async (answer) => {
43
47
  error.textContent = '';
44
48
  button.disabled = true;
49
+ noButton.disabled = true;
45
50
  try {
46
51
  const r = await fetch(`/recommended-services/${encodeURIComponent(service)}/confirm`, {
47
52
  method: 'POST', cache: 'no-store',
48
53
  headers: { 'Content-Type': 'application/json', 'X-Instar-Request': '1' },
49
- body: JSON.stringify({ token: d.token, pin: pin.value }),
54
+ body: JSON.stringify({ token: d.token, pin: pin.value, answer, reason: answer === 'no' ? reason.value : 'none' }),
50
55
  });
51
56
  const body = await r.json().catch(() => ({}));
57
+ if (r.ok && answer === 'no') {
58
+ message('Got it', `I won’t set up ${d.name} or bring it up again, unless a real problem comes up that it would help with, something changes that matters to you, or a long time has passed and there’s good reason to think you’d answer differently. If it’s already running, it stays on until you ask me to stop it.`);
59
+ return;
60
+ }
52
61
  if (r.ok) {
53
62
  message('Thank you', `I’ll set up ${d.name} from here and tell you when it’s working. I’ll only come back to you for a decision or a credential.`);
54
63
  return;
@@ -64,8 +73,13 @@ function page(d) {
64
73
  error.textContent = 'I couldn’t reach your agent. Nothing changed. Try again.';
65
74
  } finally {
66
75
  button.disabled = false;
76
+ noButton.disabled = false;
67
77
  }
68
- } }, pin, error, button);
78
+ };
79
+ noButton.addEventListener('click', () => send('no'));
80
+ button.addEventListener('click', () => send('yes'));
81
+ // Enter does nothing: someone typing their PIN to say no must never record a yes by pressing Go.
82
+ const form = el('form', { class: 'pair-pin', onsubmit: (e) => e.preventDefault() }, pin, error, button);
69
83
  show(
70
84
  el('div', { class: 'pair-head' }, el('h1', { text: `Set up ${d.name}?` })),
71
85
  line('What you get', d.benefit),
@@ -76,7 +90,8 @@ function page(d) {
76
90
  line('If you say no', d.backup || 'There’s no backup yet.'),
77
91
  el('section', {}, el('h2', { text: 'After you say yes' }), el('p', { text: 'Setup is my job. I’ll only ask you for a decision, such as a payment, or a credential.' })),
78
92
  form,
79
- el('p', { class: 'pair-hint', text: 'If you’d rather not, just close this page. Nothing changes.' }),
93
+ el('section', {}, el('h2', { text: 'Or say no' }), el('p', { class: 'pair-hint', text: 'Type your PIN above first. The reason is optional. Saying no stops me setting this up or offering it. If it’s already running, it stays on until you ask me to stop it.' }), reason, noButton),
94
+ el('p', { class: 'pair-hint', text: 'If you’re not sure yet, just close this page. Nothing changes.' }),
80
95
  );
81
96
  }
82
97
 
@@ -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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"}
@@ -5605,7 +5605,12 @@ export async function startServer(options) {
5605
5605
  const leaseGitRef = leaseMedium.medium === 'local' ? undefined : gitSyncRef;
5606
5606
  const leaseStoreName = leaseGitRef ? 'GitLeaseStore' : 'LocalLeaseStore';
5607
5607
  coordinator.attachLeaseMediumProvider(() => ({ ...leaseMedium, store: leaseStoreName }));
5608
- if (leaseGitRef && gitLeaseHasNoRemote(config.projectDir) === true) {
5608
+ // Read live: a peer can join and a preferred machine can be named without a restart.
5609
+ const survivorMayHold = () => !config.multiMachine?.leaseSelfHeal?.preferredAwakeMachineId
5610
+ && Object.entries(idMgr.loadRegistry().machines ?? {}).some(([id, entry]) => id !== selfMachineId && !entry.revokedAt);
5611
+ const takeoverFixOn = !!config.multiMachine?.leaseSelfHeal?.soloCaptainHold?.enabled
5612
+ && !config.multiMachine?.leaseSelfHeal?.preferredAwakeMachineId;
5613
+ if (leaseGitRef && !takeoverFixOn && gitLeaseHasNoRemote(config.projectDir) === true) {
5609
5614
  _takeoverReadiness = { state: 'failing', reason: 'this machine uses the git lease with no git remote, so it cannot take over' };
5610
5615
  }
5611
5616
  try {
@@ -5646,6 +5651,8 @@ export async function startServer(options) {
5646
5651
  registryAbsPath: idMgr.registryPath,
5647
5652
  pullRebase: () => gs.pullRebase(),
5648
5653
  commitAndPush: (msg, paths) => gs.commitAndPush(msg, paths),
5654
+ localCommitIsFinal: () => !!config.multiMachine?.leaseSelfHeal?.soloCaptainHold?.enabled
5655
+ && survivorMayHold() && gitLeaseHasNoRemote(config.projectDir) === true,
5649
5656
  logger: (m) => console.log(pc.dim(m)),
5650
5657
  });
5651
5658
  }
@@ -5822,6 +5829,7 @@ export async function startServer(options) {
5822
5829
  return !!pref && pref === selfMachineId;
5823
5830
  },
5824
5831
  allPeersPresumedGone: leaseLiveness.allPeersPresumedGone,
5832
+ survivorMayHold,
5825
5833
  onSelfSuspend: (reason) => console.log(pc.yellow(` [lease] self-suspend: ${reason}`)),
5826
5834
  onEscalate: (info) => console.log(pc.yellow(` [lease] split-brain escalation: ${info.reason} (holder ${info.holder})`)),
5827
5835
  logger: (m) => console.log(pc.dim(m)),
@@ -24787,6 +24795,19 @@ export async function startServer(options) {
24787
24795
  return false;
24788
24796
  },
24789
24797
  move: (t, to) => planner.proofMove(String(t), to),
24798
+ probeViaHolder: async (t, nonce) => {
24799
+ const holderUrl = _resolveRouterUrl?.() ?? null;
24800
+ if (!holderUrl)
24801
+ return null;
24802
+ const r = await fetch(`${holderUrl}/session-pool/rebalance-proof/probe`, {
24803
+ method: 'POST',
24804
+ headers: { Authorization: `Bearer ${config.authToken}`, 'Content-Type': 'application/json', 'X-Instar-AgentId': config.projectName },
24805
+ body: JSON.stringify({ topic: t, nonce }),
24806
+ signal: AbortSignal.timeout(30_000),
24807
+ });
24808
+ const j = await r.json().catch(() => ({})); // @silent-fallback-ok: a body we cannot read is reported as the status below
24809
+ return { action: j.action ?? j.refused ?? `holder-status-${r.status}`, holder: j.holder ?? null };
24810
+ },
24790
24811
  sendProbe: async (t, text, messageId) => {
24791
24812
  if (!_sessionRouter)
24792
24813
  return 'no-router';