instar 1.3.1608 → 1.3.1610

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 (61) hide show
  1. package/dashboard/console/console.css +17 -9
  2. package/dashboard/console/console.js +10 -1
  3. package/dashboard/console/home-model.js +18 -1
  4. package/dashboard/console/nav-model.js +12 -14
  5. package/dist/commands/server.d.ts.map +1 -1
  6. package/dist/commands/server.js +7 -2
  7. package/dist/commands/server.js.map +1 -1
  8. package/dist/core/AppConversationService.d.ts +22 -0
  9. package/dist/core/AppConversationService.d.ts.map +1 -1
  10. package/dist/core/AppConversationService.js +49 -4
  11. package/dist/core/AppConversationService.js.map +1 -1
  12. package/dist/core/PostUpdateMigrator.d.ts +3 -0
  13. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  14. package/dist/core/PostUpdateMigrator.js +13 -1
  15. package/dist/core/PostUpdateMigrator.js.map +1 -1
  16. package/dist/core/VoiceTranscription.d.ts +40 -0
  17. package/dist/core/VoiceTranscription.d.ts.map +1 -0
  18. package/dist/core/VoiceTranscription.js +86 -0
  19. package/dist/core/VoiceTranscription.js.map +1 -0
  20. package/dist/data/standards-guard-index.json +1 -1
  21. package/dist/data/standards-guard-index.meta.json +2 -2
  22. package/dist/data/standards-registry.meta.json +1 -1
  23. package/dist/messaging/TelegramAdapter.d.ts +9 -7
  24. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  25. package/dist/messaging/TelegramAdapter.js +32 -71
  26. package/dist/messaging/TelegramAdapter.js.map +1 -1
  27. package/dist/messaging/shared/channelIdentity.d.ts +11 -0
  28. package/dist/messaging/shared/channelIdentity.d.ts.map +1 -1
  29. package/dist/messaging/shared/channelIdentity.js +42 -0
  30. package/dist/messaging/shared/channelIdentity.js.map +1 -1
  31. package/dist/messaging/telegram-origin/OriginLegacy.d.ts +2 -0
  32. package/dist/messaging/telegram-origin/OriginLegacy.d.ts.map +1 -1
  33. package/dist/messaging/telegram-origin/OriginLegacy.js +20 -2
  34. package/dist/messaging/telegram-origin/OriginLegacy.js.map +1 -1
  35. package/dist/native/instar-desktop-host +0 -0
  36. package/dist/server/AgentServer.d.ts.map +1 -1
  37. package/dist/server/AgentServer.js +4 -1
  38. package/dist/server/AgentServer.js.map +1 -1
  39. package/dist/server/poolPeerFetch.d.ts +30 -7
  40. package/dist/server/poolPeerFetch.d.ts.map +1 -1
  41. package/dist/server/poolPeerFetch.js +74 -16
  42. package/dist/server/poolPeerFetch.js.map +1 -1
  43. package/package.json +1 -1
  44. package/scripts/lint-no-direct-llm-http.js +1 -1
  45. package/src/data/builtin-manifest.json +20 -20
  46. package/src/data/standards-guard-index.json +1 -1
  47. package/src/data/standards-guard-index.meta.json +2 -2
  48. package/src/data/standards-registry.meta.json +1 -1
  49. package/upgrades/1.3.1609.md +20 -0
  50. package/upgrades/1.3.1610.md +62 -0
  51. package/upgrades/side-effects/attention-names-not-ids.eli16.md +7 -0
  52. package/upgrades/side-effects/attention-names-not-ids.md +36 -0
  53. package/upgrades/side-effects/console-mobile-composer-work-tiles.eli16.md +7 -0
  54. package/upgrades/side-effects/console-mobile-composer-work-tiles.md +32 -0
  55. package/upgrades/side-effects/console-needs-you-links.eli16.md +7 -0
  56. package/upgrades/side-effects/console-needs-you-links.md +33 -0
  57. package/upgrades/side-effects/mobile-native-chat-media-voice.md +92 -0
  58. package/upgrades/side-effects/origin-legacy-boot-beacon-replay.eli16.md +7 -0
  59. package/upgrades/side-effects/origin-legacy-boot-beacon-replay.md +36 -0
  60. package/upgrades/side-effects/pool-read-cloudflare-edge-fallthrough.eli16.md +7 -0
  61. package/upgrades/side-effects/pool-read-cloudflare-edge-fallthrough.md +39 -0
@@ -57,6 +57,10 @@
57
57
  --ease: cubic-bezier(.4, 0, .2, 1);
58
58
  --dur: .18s;
59
59
  --nav-h: 64px;
60
+ /* The phone's bottom inset. Inside the Instar app this is the app's own tab
61
+ bar (about 68px), so everything that sits above the bottom nav adds it. */
62
+ --safe-b: env(safe-area-inset-bottom, 0px);
63
+ --nav-total: calc(var(--nav-h) + var(--safe-b));
60
64
  color-scheme: dark;
61
65
  }
62
66
 
@@ -131,14 +135,14 @@ button { font: inherit; color: inherit; }
131
135
  .chip:hover { border-color: var(--hairline-strong); color: var(--text); }
132
136
  .icon-btn { width: 34px; padding: 0; justify-content: center; }
133
137
 
134
- .main { padding: var(--s-5) var(--s-4) calc(var(--nav-h) + 32px); max-width: 1120px; width: 100%; margin: 0 auto; }
138
+ .main { padding: var(--s-5) var(--s-4) calc(var(--nav-total) + 32px); max-width: 1120px; width: 100%; margin: 0 auto; }
135
139
 
136
140
  /* Bottom nav (phone) → sidebar (desktop) */
137
141
  .nav {
138
142
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
139
143
  display: grid; grid-template-columns: repeat(5, 1fr);
140
- height: calc(var(--nav-h) + env(safe-area-inset-bottom));
141
- padding-bottom: env(safe-area-inset-bottom);
144
+ height: var(--nav-total);
145
+ padding-bottom: var(--safe-b);
142
146
  background: color-mix(in srgb, var(--surface-solid) 86%, transparent);
143
147
  backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
144
148
  border-top: 1px solid var(--hairline);
@@ -490,7 +494,7 @@ button.list-head { cursor: pointer; }
490
494
  .sheet { position: absolute; left: 0; right: 0; bottom: 0; height: 88dvh; max-height: 88vh; display: flex; flex-direction: column;
491
495
  background: var(--surface-solid); border: 1px solid var(--hairline-strong); border-bottom: 0; border-radius: var(--r-xl) var(--r-xl) 0 0;
492
496
  box-shadow: 0 -20px 60px rgb(0 0 0 / .45); transform: translateY(24px); opacity: 0;
493
- transition: transform .22s var(--ease), opacity .22s var(--ease); padding-bottom: env(safe-area-inset-bottom); }
497
+ transition: transform .22s var(--ease), opacity .22s var(--ease); padding-bottom: var(--safe-b); }
494
498
  .sheet-root.open .scrim { opacity: 1; }
495
499
  .sheet-root.open .sheet { transform: none; opacity: 1; }
496
500
  .sheet-grab { width: 40px; height: 4px; border-radius: 4px; background: var(--hairline-strong); margin: 10px auto 2px; }
@@ -633,7 +637,7 @@ select.reason-input { height: 44px; }
633
637
  .switch.on::after { transform: translateX(18px); background: var(--accent); }
634
638
  .switch:disabled { opacity: .5; cursor: progress; }
635
639
  .switch:focus-visible, .seg-step:focus-visible, .feat-main:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
636
- .toast { position: fixed; left: 50%; bottom: calc(var(--nav-h) + 16px); transform: translateX(-50%); z-index: 60; display: flex; align-items: center; gap: 16px; max-width: calc(100vw - 32px);
640
+ .toast { position: fixed; left: 50%; bottom: calc(var(--nav-total) + 16px); transform: translateX(-50%); z-index: 60; display: flex; align-items: center; gap: 16px; max-width: calc(100vw - 32px);
637
641
  padding: 12px 18px; border-radius: var(--r-md); border: 1px solid var(--hairline-strong); background: var(--surface-solid); color: var(--text-strong); font-size: 14px; box-shadow: 0 8px 30px rgb(0 0 0 / .35); }
638
642
  /* ── Plans (work.md: Projects + Initiatives + Blockers) ───────────────── */
639
643
  .fact.unknown { border-style: dashed; border-color: var(--unknown-line); } .fact.unknown .fact-n { color: var(--unknown); }
@@ -822,7 +826,7 @@ button.run-mark:focus-visible { outline: 2px solid var(--accent); outline-offset
822
826
  .agents-page .waiting-card { margin-bottom: var(--s-3); }
823
827
  .waiting-card.bad { border-color: var(--bad-line); background: var(--bad-wash); }
824
828
  .need-card.muted-card { border-color: var(--unknown-line); border-style: dashed; background: var(--surface); margin-top: var(--s-4); }
825
- .page-primary { position: sticky; bottom: calc(var(--nav-h) + 12px); display: flex; justify-content: center; margin-top: var(--s-4); z-index: 5; }
829
+ .page-primary { position: sticky; bottom: calc(var(--nav-total) + 12px); display: flex; justify-content: center; margin-top: var(--s-4); z-index: 5; }
826
830
  @media (min-width: 900px) { .page-primary { position: static; justify-content: flex-start; } }
827
831
  .connect-btn { box-shadow: var(--accent-glow); }
828
832
  .select-input { height: 44px; appearance: auto; }
@@ -943,7 +947,7 @@ button.run-mark:focus-visible { outline: 2px solid var(--accent); outline-offset
943
947
  .hljs-comment, .hljs-quote { color: var(--text-faint); font-style: italic; }
944
948
  .hljs-number, .hljs-literal { color: #b48ead; }
945
949
  .hljs-title, .hljs-section, .hljs-name { color: var(--text-strong); font-weight: 600; }
946
- .files-edit-fab { position: fixed; right: var(--s-4); bottom: calc(var(--nav-h) + 16px + env(safe-area-inset-bottom)); z-index: 6; min-height: 48px; padding: 0 var(--s-6); border-radius: 999px; box-shadow: 0 8px 24px rgb(0 0 0 / .35); width: auto; }
950
+ .files-edit-fab { position: fixed; right: var(--s-4); bottom: calc(var(--nav-total) + 16px); z-index: 6; min-height: 48px; padding: 0 var(--s-6); border-radius: 999px; box-shadow: 0 8px 24px rgb(0 0 0 / .35); width: auto; }
947
951
  .files-editor { width: 100%; min-height: calc(100vh - 220px); padding: var(--s-3); font-family: var(--font-mono); font-size: 16px; line-height: 1.5; color: var(--text-strong); background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); white-space: pre-wrap; overflow-wrap: anywhere; resize: vertical; }
948
952
  .files-kept { margin-bottom: var(--s-2); }
949
953
  .files-menu, .files-conflict { display: flex; flex-direction: column; align-items: stretch; gap: var(--s-2); }
@@ -1010,7 +1014,9 @@ button.run-mark:focus-visible { outline: 2px solid var(--accent); outline-offset
1010
1014
  .sess-h-tool { margin: 0; font-family: var(--font-mono); font-size: 12px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
1011
1015
  .sess-pre { margin: 0; font-family: var(--font-mono); font-size: 12px; line-height: 1.4; color: #d6dbe4; white-space: pre-wrap; overflow-wrap: anywhere; }
1012
1016
  .sess-resume { position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); z-index: 2; }
1013
- .sess-composer { display: flex; gap: var(--s-2); align-items: flex-end; position: sticky; bottom: 0; padding: var(--s-2) 0; background: var(--bg); }
1017
+ /* Phone: the bottom nav is fixed over the last --nav-h of the screen, so the
1018
+ composer sticks just above it, never underneath. Desktop resets it to static. */
1019
+ .sess-composer { display: flex; gap: var(--s-2); align-items: flex-end; position: sticky; bottom: var(--nav-total); z-index: 5; padding: var(--s-2) 0; background: var(--bg); }
1014
1020
  .sess-input { flex: 1; min-height: 44px; font-size: 16px; resize: none; }
1015
1021
  .sess-input::placeholder { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1016
1022
  .sess-composer-btns { display: flex; gap: var(--s-2); }
@@ -1045,7 +1051,9 @@ button.run-mark:focus-visible { outline: 2px solid var(--accent); outline-offset
1045
1051
  .sess-default-box { border: 1px solid var(--hairline); border-radius: var(--r-md); padding: var(--s-3); margin: var(--s-2) 0; }
1046
1052
  .skeleton-row { pointer-events: none; }
1047
1053
  .sess-view, .sess-open, .sess-term-wrap, .sess-composer { min-width: 0; max-width: 100%; }
1048
- .sess-view { overflow-x: hidden; }
1054
+ /* clip, not hidden: hidden makes .sess-view a scroll container, which pins the
1055
+ sticky composer to a box that never scrolls (it then sat under the bottom nav). */
1056
+ .sess-view { overflow-x: clip; }
1049
1057
  .sess-input { min-width: 0; }
1050
1058
  .sess-term .xterm { max-width: 100%; }
1051
1059
 
@@ -47,6 +47,8 @@ const expanded = new Set(); // cards the operator expanded; kept acr
47
47
  const openRows = new Set(); // rows the operator opened; kept across refreshes
48
48
  let rowSeq = 0;
49
49
  let homeChatId = null;
50
+ // The agent-wide Attention topic, where every attention item is posted.
51
+ let attentionTopicId = null;
50
52
  let homeRun = null;
51
53
 
52
54
  // ── tiny DOM helpers ──────────────────────────────────────────────────────
@@ -257,6 +259,8 @@ function actionHref(action) {
257
259
  return `#/work/promises?${params}`;
258
260
  }
259
261
  if (action.kind === 'topic') return topicLink(homeChatId, action.topicId);
262
+ if (action.kind === 'href') return action.href || null;
263
+ if (action.kind === 'attention') return attentionTopicId ? topicLink(homeChatId, attentionTopicId) : null;
260
264
  return null;
261
265
  }
262
266
 
@@ -449,7 +453,7 @@ function loadHome() {
449
453
  ? { ...model, retrying: true, line: [model.line, 'Checking again automatically.'].filter(Boolean).join(' ') } : model;
450
454
  const refresh = (key) => {
451
455
  const names = results.pool ? machineNames(results.pool) : {};
452
- if ((key === 'attention' || key === 'pool') && results.attention) set('needs', annotate(buildNeeds(results.attention, Date.now(), names), results.attention));
456
+ if ((key === 'attention' || key === 'pool' || key === 'systemTopics') && results.attention) set('needs', annotate(buildNeeds(results.attention, Date.now(), names), results.attention));
453
457
  if ((key === 'sessions' || key === 'pool' || key === 'topics') && results.sessions) set('doing', annotate(buildDoing(results.sessions, Date.now(), names, topicNameMap(results.topics)), results.sessions));
454
458
  if (key === 'pool' && results.pool) set('running', annotate(buildRunning(results.pool), results.pool));
455
459
  if ((key === 'commitments' || key === 'pool') && results.commitments) {
@@ -472,6 +476,10 @@ function loadHome() {
472
476
  || key === 'topics' && ((result.data?.pool?.failed?.length || 0) > 0 || (result.data?.pool?.unresolved?.length || 0) > 0));
473
477
  const retryable = partial || (!result.ok && !['not signed in', 'switched off on this agent'].includes(result.reason));
474
478
  results[key] = partial ? { ...result, retrying: true } : result;
479
+ if (key === 'systemTopics' && result.ok) {
480
+ const t = Number(result.data?.topics?.attention);
481
+ attentionTopicId = Number.isFinite(t) && t > 1 ? t : null;
482
+ }
475
483
  if (key === 'pool' && result.ok) {
476
484
  const self = (result.data?.machines || []).find((m) => m.isSelf || m.machineId === result.data.selfMachineId);
477
485
  homeChatId = self?.servesChannels?.telegram?.chatIds?.[0] || null;
@@ -489,6 +497,7 @@ function loadHome() {
489
497
  // A slow source cannot block another card or start overlapping refreshes.
490
498
  read('pool', '/pool', POOL_TIMEOUT_MS);
491
499
  read('attention', '/attention?scope=pool', POOL_TIMEOUT_MS);
500
+ read('systemTopics', '/telegram/system-topics');
492
501
  read('sessions', '/sessions?scope=pool', POOL_TIMEOUT_MS);
493
502
  read('topics', '/telegram/topics?scope=pool', POOL_TIMEOUT_MS);
494
503
  read('commitments', '/commitments?scope=mesh', COMMITMENTS_TIMEOUT_MS);
@@ -115,6 +115,23 @@ export function needsOperator(item) {
115
115
  return /decision|approval|review|sign-?in|relogin/i.test(cat);
116
116
  }
117
117
 
118
+ /**
119
+ * Where the operator acts on an item. Every row in "needs you" gets one: its
120
+ * own conversation; the Desktop page for that machine; the Accounts page for a
121
+ * sign-in; otherwise the Attention topic, where the item was posted and can be
122
+ * answered.
123
+ */
124
+ export function needAction(item) {
125
+ if (item && item.topicId != null && Number.isFinite(Number(item.topicId))) return { kind: 'topic', topicId: Number(item.topicId) };
126
+ const cat = String((item && item.category) || '');
127
+ if (/^desktop$/i.test(cat)) {
128
+ const q = item.machineId ? `?machineId=${encodeURIComponent(item.machineId)}` : '';
129
+ return { kind: 'href', href: `/dashboard/desktop.html${q}` };
130
+ }
131
+ if (/sign-?in|relogin/i.test(cat)) return { kind: 'href', href: '#/capacity/accounts' };
132
+ return { kind: 'attention' };
133
+ }
134
+
118
135
  export const STALE_DAYS = 14;
119
136
  const DAY = 86400000;
120
137
 
@@ -154,7 +171,7 @@ export function buildNeeds(res, now = Date.now(), names = {}) {
154
171
  tone: stale ? 'unknown' : (top.priority === 'URGENT' ? 'bad' : 'attn'),
155
172
  detail: top.description || top.body || '',
156
173
  expandable: true,
157
- action: Number.isFinite(Number(top.topicId)) ? { kind: 'topic', topicId: Number(top.topicId) } : null,
174
+ action: needAction(top),
158
175
  };
159
176
  }).sort((a, b) => Number(a.stale) - Number(b.stale) || a.prio - b.prio || b.last - a.last);
160
177
  const current = all.filter((g) => !g.stale);
@@ -44,9 +44,6 @@ export const GROUPS = [
44
44
  { id: 'jobs', label: 'Schedule', icon: 'clock', desc: 'What runs on its own, and whether it actually ran: late, failing and running-long jobs first.' },
45
45
  { id: 'commitments', label: 'Promises', icon: 'check', desc: 'Every promise I made, what it is waiting on, and whether it was kept.' },
46
46
  { id: 'projects', label: 'Plans', icon: 'layers', desc: 'Longer work and multi-round plans: how each is going, what needs your OK, and what is in the way.' },
47
- { id: 'initiatives', label: 'Initiatives', icon: 'flag', desc: 'Now inside Plans: longer efforts in flight, and the items that need you.' },
48
- { id: 'blockers', label: 'Blockers', icon: 'alert', desc: 'Now inside Plans, under “In the way”: what looked stuck, and how sure that still is.' },
49
- { id: 'dropzone', label: 'Send content', icon: 'send', desc: 'Paste text or files straight into a session.' },
50
47
  ],
51
48
  },
52
49
  {
@@ -58,7 +55,6 @@ export const GROUPS = [
58
55
  { id: 'subscriptions', label: 'Accounts', icon: 'key', desc: 'Subscriptions and sign-ins: usage left before reset, and fixing a sign-in from your phone.' },
59
56
  { id: 'resources', label: 'Resource usage', icon: 'cpu', desc: 'CPU and memory for the server and every session.' },
60
57
  { id: 'spend', label: 'What thinking costs', icon: 'wallet', desc: 'Recorded AI cost on this machine: pay-per-use by model, background checks, what your conversations process, and the spending limits.' },
61
- { id: 'tokens', label: 'Tokens', icon: 'coins', desc: 'Now inside What thinking costs: how much text your conversations process, Claude and Codex kept apart.' },
62
58
  ],
63
59
  },
64
60
  {
@@ -66,7 +62,7 @@ export const GROUPS = [
66
62
  question: 'Who is talking to whom, and with what permission?',
67
63
  tabs: [
68
64
  { id: 'threadline', label: 'Other agents', icon: 'chat', desc: 'Who is waiting on you, whether each agent I work with is actually there, pairing new ones, and our conversations.' },
69
- { id: 'origins', label: 'Messages to you', icon: 'chat', desc: 'Replies held back from you, and the who-wrote-this footer.' },
65
+ { id: 'origins', label: 'Messages to you', icon: 'chat', desc: 'Who wrote each message I sent you, and the who-wrote-this footer.' },
70
66
  { id: 'mandates', label: 'Permission slips', icon: 'stamp', desc: 'What you have let agents do together, who is waiting for your approval, and every allow and refusal.' },
71
67
  { id: 'phones', label: 'Phones', icon: 'phone', desc: 'Pair the Instar iPhone app (the code shows here after a PIN unlock when there is no Telegram), and the phones already paired.' },
72
68
  { id: 'secrets', label: 'Passwords & keys', icon: 'lock', desc: 'Whether the agent is waiting on a password, a safe one-time link to hand one over, and the keys it already holds.' },
@@ -82,21 +78,21 @@ export const GROUPS = [
82
78
  ],
83
79
  hood: [
84
80
  { id: 'insights', label: 'Insights', desc: 'A plain-language read of what stands out.' },
85
- { id: 'pr-pipeline', label: 'PR pipeline', desc: 'Now inside Development (Shipping): pull requests on their way to merge, and which need you.' },
86
- { id: 'evidence', label: 'Evidence', desc: 'Now inside How I’m doing inside: look up why I believe something, and what else relies on the same source.' },
87
- { id: 'routing-map', label: 'Routing map', desc: 'Now inside What thinking costs: which model handles each kind of background work, and the backups.' },
88
- { id: 'llm-activity', label: 'LLM activity', desc: 'Now inside What thinking costs: what the automatic AI checks did, failed or skipped.' },
89
- { id: 'throughput', label: 'Throughput', desc: 'Now inside Development (Pace): how much merged, how fast, and how much was redone.' },
90
81
  { id: 'integrated-being', label: 'How I’m doing inside', desc: 'Conversation history, recurring problems, checks on my claims.' },
91
- { id: 'process-health', label: 'Process health', desc: 'Now inside How I’m doing inside: problems I caught in my own work, and what keeps coming back.' },
92
- { id: 'feedback-drain', label: 'Feedback drain', desc: 'Now inside Development (Feedback): the approvals it needs from you, and feedback being turned into fixes.' },
93
82
  ],
94
83
  },
95
84
  ];
96
85
 
97
- /** Every legacy tab id the console links to. */
86
+ /**
87
+ * Legacy tabs whose content now lives inside another console page. They get
88
+ * no tile of their own (a tile that only says "now inside X" is clutter);
89
+ * their old links still land on the page that holds them (NATIVE_PAGES).
90
+ */
91
+ export const MERGED = ['dropzone', 'initiatives', 'blockers', 'tokens', 'pr-pipeline', 'evidence', 'routing-map', 'llm-activity', 'throughput', 'process-health', 'feedback-drain'];
92
+
93
+ /** Every legacy tab id the console links to, including the merged ones. */
98
94
  export function allTabIds() {
99
- return GROUPS.flatMap((g) => [...g.tabs, ...(g.hood || [])].map((t) => t.id));
95
+ return [...GROUPS.flatMap((g) => [...g.tabs, ...(g.hood || [])].map((t) => t.id)), ...MERGED];
100
96
  }
101
97
 
102
98
  /** Features rebuilt natively in the console, by legacy tab id. */
@@ -104,6 +100,8 @@ export const NATIVE_PAGES = {
104
100
  commitments: '#/work/promises',
105
101
  jobs: '#/work/schedule',
106
102
  sessions: '#/work/sessions',
103
+ // Sending text or files now happens in each session's own message box.
104
+ dropzone: '#/work/sessions',
107
105
  projects: '#/work/plans',
108
106
  initiatives: '#/work/plans',
109
107
  blockers: '#/work/plans?s=in-the-way',
@@ -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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"}
@@ -7833,6 +7833,7 @@ export async function startServer(options) {
7833
7833
  // Send-only still owns the attention store: run its hourly lifecycle
7834
7834
  // pass here too (start() is never called on this path). Idempotent.
7835
7835
  telegram.startSendOnlyMaintenance();
7836
+ telegram.setMachineNameResolver((machineId) => _listPoolMachines?.().find((m) => m.machineId === machineId)?.nickname ?? null);
7836
7837
  // Resolve any topic names still using the fallback "topic-NNNN" pattern
7837
7838
  telegram.resolveUnknownTopicNames().catch(err => {
7838
7839
  console.warn(`[telegram] Topic name resolution failed: ${err}`);
@@ -7943,6 +7944,7 @@ export async function startServer(options) {
7943
7944
  // When the user renames a Telegram forum topic, update the bound
7944
7945
  // session's display `name` ONLY (never tmuxSession or id). Fire-and-
7945
7946
  // forget; rename-display is non-critical.
7947
+ telegram.setMachineNameResolver((machineId) => _listPoolMachines?.().find((m) => m.machineId === machineId)?.nickname ?? null);
7946
7948
  telegram.setTopicRenamedHandler((topicId, newName) => {
7947
7949
  const tmuxSession = telegram?.getSessionForTopic(topicId);
7948
7950
  if (!tmuxSession)
@@ -8615,7 +8617,7 @@ export async function startServer(options) {
8615
8617
  // its request store with the Telegram chat route).
8616
8618
  if (_appNativeOn) {
8617
8619
  try {
8618
- const [{ AppConversationService }, { createAppInboundDispatch }, { MobileChatRequestStore }, { appOwnerName }, relayMod, replMod] = await Promise.all([
8620
+ const [{ AppConversationService, VOICE_TRANSCRIBE_DEADLINE_MS }, { createAppInboundDispatch }, { MobileChatRequestStore }, { appOwnerName }, relayMod, replMod] = await Promise.all([
8619
8621
  import('../core/AppConversationService.js'), import('../core/appInboundDispatch.js'), import('../core/MobileChatRequests.js'),
8620
8622
  import('../server/mobileConversationRoutes.js'), import('../core/AppReplyRelayInstaller.js'), import('../core/AppConversationsReplicatedStore.js'),
8621
8623
  ]);
@@ -8629,8 +8631,9 @@ export async function startServer(options) {
8629
8631
  let appDispatch = null;
8630
8632
  // Signed records (§4.6): this machine's identity signer, the signing-key history lookup and
8631
8633
  // the device-key ledger. No identity ⇒ messages are still stored and delivered, read `unknown`.
8632
- const [sigMod, ledgerMod, ledgerReplMod] = await Promise.all([
8634
+ const [sigMod, ledgerMod, ledgerReplMod, voiceMod] = await Promise.all([
8633
8635
  import('../core/AppRecordSignatures.js'), import('../core/MobileDeviceKeyLedger.js'), import('../core/MobileDeviceKeyLedgerReplicatedStore.js'),
8636
+ import('../core/VoiceTranscription.js'),
8634
8637
  ]);
8635
8638
  const appSigner = sigMod.loadMachineSigner(config.stateDir);
8636
8639
  const appKeyLookup = sigMod.identityKeyLookup(config.stateDir);
@@ -8673,6 +8676,8 @@ export async function startServer(options) {
8673
8676
  catch { /* @silent-fallback-ok — the stop record is observability */ }
8674
8677
  },
8675
8678
  mediaMaxStoreBytes: () => config.mobilePairing?.native?.media?.maxStoreBytes,
8679
+ // §4.5.1: voice notes the phone did not transcribe use Telegram's transcriber (same provider).
8680
+ transcribeVoice: (filePath, mediaType) => voiceMod.transcribeAudioFile(filePath, { explicit: voiceMod.configuredVoiceProvider(config), mediaType, timeoutMs: VOICE_TRANSCRIBE_DEADLINE_MS }),
8676
8681
  raiseAttention: (item) => { void telegram?.createAttentionItem({ id: item.conditionKey, conditionKey: item.conditionKey, title: item.title, summary: item.body, category: 'general', priority: 'NORMAL', sourceContext: 'app-media' }); },
8677
8682
  clearAttention: (conditionKey) => { telegram?.clearAttentionCondition({ conditionKey }, 'cleared: the app media store has room again'); },
8678
8683
  reportDegradation: (what, detail) => degradationReporter.report({