@ouro.bot/cli 0.1.0-alpha.33 → 0.1.0-alpha.331

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 (315) hide show
  1. package/README.md +188 -187
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
  4. package/changelog.json +1982 -0
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +140 -0
  7. package/dist/arc/episodes.js +117 -0
  8. package/dist/arc/intentions.js +133 -0
  9. package/dist/arc/json-store.js +117 -0
  10. package/dist/arc/obligations.js +237 -0
  11. package/dist/arc/packets.js +193 -0
  12. package/dist/arc/presence.js +185 -0
  13. package/dist/arc/task-lifecycle.js +65 -0
  14. package/dist/heart/active-work.js +832 -0
  15. package/dist/heart/agent-entry.js +37 -2
  16. package/dist/heart/attachments/image-normalize.js +194 -0
  17. package/dist/heart/attachments/materialize.js +97 -0
  18. package/dist/heart/attachments/originals.js +88 -0
  19. package/dist/heart/attachments/render.js +29 -0
  20. package/dist/heart/attachments/sources/adapter.js +2 -0
  21. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  22. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  23. package/dist/heart/attachments/sources/index.js +16 -0
  24. package/dist/heart/attachments/store.js +103 -0
  25. package/dist/heart/attachments/types.js +93 -0
  26. package/dist/heart/auth/auth-flow.js +463 -0
  27. package/dist/heart/bridges/manager.js +358 -0
  28. package/dist/heart/bridges/state-machine.js +135 -0
  29. package/dist/heart/bridges/store.js +123 -0
  30. package/dist/heart/bundle-state.js +168 -0
  31. package/dist/heart/commitments.js +111 -0
  32. package/dist/heart/config-registry.js +304 -0
  33. package/dist/heart/config.js +53 -21
  34. package/dist/heart/core.js +695 -195
  35. package/dist/heart/cross-chat-delivery.js +131 -0
  36. package/dist/heart/daemon/agent-config-check.js +149 -0
  37. package/dist/heart/daemon/agent-discovery.js +79 -3
  38. package/dist/heart/daemon/agent-service.js +360 -0
  39. package/dist/heart/daemon/agentic-repair.js +170 -0
  40. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  41. package/dist/heart/daemon/cadence.js +70 -0
  42. package/dist/heart/daemon/cli-defaults.js +591 -0
  43. package/dist/heart/daemon/cli-exec.js +2277 -0
  44. package/dist/heart/daemon/cli-help.js +306 -0
  45. package/dist/heart/daemon/cli-parse.js +824 -0
  46. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  47. package/dist/heart/daemon/cli-render.js +512 -0
  48. package/dist/heart/daemon/cli-types.js +8 -0
  49. package/dist/heart/daemon/daemon-cli.js +29 -1171
  50. package/dist/heart/daemon/daemon-entry.js +358 -3
  51. package/dist/heart/daemon/daemon-health.js +141 -0
  52. package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
  53. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  54. package/dist/heart/daemon/daemon.js +751 -58
  55. package/dist/heart/daemon/doctor-types.js +8 -0
  56. package/dist/heart/daemon/doctor.js +401 -0
  57. package/dist/heart/daemon/health-monitor.js +79 -1
  58. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  59. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  60. package/dist/heart/daemon/http-health-probe.js +80 -0
  61. package/dist/heart/daemon/inner-status.js +89 -0
  62. package/dist/heart/daemon/interactive-repair.js +69 -0
  63. package/dist/heart/daemon/launchd.js +46 -9
  64. package/dist/heart/daemon/log-tailer.js +82 -12
  65. package/dist/heart/daemon/logs-prune.js +105 -0
  66. package/dist/heart/daemon/message-router.js +17 -8
  67. package/dist/heart/daemon/os-cron-deps.js +134 -0
  68. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  69. package/dist/heart/daemon/process-manager.js +201 -0
  70. package/dist/heart/daemon/provider-discovery.js +105 -0
  71. package/dist/heart/daemon/pulse.js +463 -0
  72. package/dist/heart/daemon/run-hooks.js +2 -0
  73. package/dist/heart/daemon/runtime-logging.js +67 -16
  74. package/dist/heart/daemon/runtime-metadata.js +101 -0
  75. package/dist/heart/daemon/runtime-mode.js +67 -0
  76. package/dist/heart/daemon/safe-mode.js +161 -0
  77. package/dist/heart/daemon/sense-manager.js +72 -3
  78. package/dist/heart/daemon/session-id-resolver.js +131 -0
  79. package/dist/heart/daemon/skill-management-installer.js +94 -0
  80. package/dist/heart/daemon/socket-client.js +307 -0
  81. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  82. package/dist/heart/daemon/startup-tui.js +237 -0
  83. package/dist/heart/daemon/task-scheduler.js +3 -25
  84. package/dist/heart/daemon/thoughts.js +510 -0
  85. package/dist/heart/daemon/up-progress.js +135 -0
  86. package/dist/heart/delegation.js +62 -0
  87. package/dist/heart/habits/habit-migration.js +181 -0
  88. package/dist/heart/habits/habit-parser.js +140 -0
  89. package/dist/heart/habits/habit-scheduler.js +371 -0
  90. package/dist/heart/{daemon → hatch}/hatch-flow.js +32 -120
  91. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  92. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  93. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  94. package/dist/heart/identity.js +154 -59
  95. package/dist/heart/kicks.js +2 -20
  96. package/dist/heart/mcp/mcp-server.js +653 -0
  97. package/dist/heart/migrate-config.js +127 -0
  98. package/dist/heart/model-capabilities.js +59 -0
  99. package/dist/heart/outlook/outlook-http-hooks.js +64 -0
  100. package/dist/heart/outlook/outlook-http-response.js +7 -0
  101. package/dist/heart/outlook/outlook-http-routes.js +232 -0
  102. package/dist/heart/outlook/outlook-http-static.js +99 -0
  103. package/dist/heart/outlook/outlook-http-transport.js +116 -0
  104. package/dist/heart/outlook/outlook-http.js +99 -0
  105. package/dist/heart/outlook/outlook-read.js +28 -0
  106. package/dist/heart/outlook/outlook-render.js +1032 -0
  107. package/dist/heart/outlook/outlook-types.js +27 -0
  108. package/dist/heart/outlook/outlook-view.js +194 -0
  109. package/dist/heart/outlook/readers/agent-machine.js +355 -0
  110. package/dist/heart/outlook/readers/continuity-readers.js +332 -0
  111. package/dist/heart/outlook/readers/runtime-readers.js +660 -0
  112. package/dist/heart/outlook/readers/sessions.js +231 -0
  113. package/dist/heart/outlook/readers/shared.js +111 -0
  114. package/dist/heart/progress-story.js +42 -0
  115. package/dist/heart/provider-failover.js +135 -0
  116. package/dist/heart/provider-models.js +81 -0
  117. package/dist/heart/provider-ping.js +162 -0
  118. package/dist/heart/providers/anthropic-token.js +163 -0
  119. package/dist/heart/providers/anthropic.js +169 -46
  120. package/dist/heart/providers/azure.js +98 -11
  121. package/dist/heart/providers/error-classification.js +63 -0
  122. package/dist/heart/providers/github-copilot.js +136 -0
  123. package/dist/heart/providers/minimax-vlm.js +189 -0
  124. package/dist/heart/providers/minimax.js +23 -5
  125. package/dist/heart/providers/openai-codex.js +33 -22
  126. package/dist/heart/session-activity.js +190 -0
  127. package/dist/heart/session-events.js +726 -0
  128. package/dist/heart/session-recall.js +162 -0
  129. package/dist/heart/start-of-turn-packet.js +341 -0
  130. package/dist/heart/streaming.js +36 -27
  131. package/dist/heart/sync.js +332 -0
  132. package/dist/heart/target-resolution.js +127 -0
  133. package/dist/heart/tempo.js +93 -0
  134. package/dist/heart/temporal-view.js +41 -0
  135. package/dist/heart/tool-activity-callbacks.js +36 -0
  136. package/dist/heart/tool-description.js +135 -0
  137. package/dist/heart/tool-friction.js +55 -0
  138. package/dist/heart/tool-loop.js +200 -0
  139. package/dist/heart/turn-context.js +358 -0
  140. package/dist/heart/turn-coordinator.js +28 -0
  141. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  142. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  143. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  144. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  145. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  146. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  147. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  148. package/dist/mind/associative-recall.js +137 -66
  149. package/dist/mind/bundle-manifest.js +7 -1
  150. package/dist/mind/context.js +89 -93
  151. package/dist/mind/diary-integrity.js +60 -0
  152. package/dist/mind/{memory.js → diary.js} +84 -96
  153. package/dist/mind/embedding-provider.js +60 -0
  154. package/dist/mind/file-state.js +179 -0
  155. package/dist/mind/first-impressions.js +14 -1
  156. package/dist/mind/friends/channel.js +56 -0
  157. package/dist/mind/friends/group-context.js +144 -0
  158. package/dist/mind/friends/resolver.js +37 -0
  159. package/dist/mind/friends/store-file.js +58 -3
  160. package/dist/mind/friends/trust-explanation.js +74 -0
  161. package/dist/mind/friends/types.js +8 -0
  162. package/dist/mind/journal-index.js +161 -0
  163. package/dist/mind/obligation-steering.js +221 -0
  164. package/dist/mind/pending.js +74 -7
  165. package/dist/mind/prompt.js +949 -111
  166. package/dist/mind/provenance-trust.js +26 -0
  167. package/dist/mind/scrutiny.js +173 -0
  168. package/dist/mind/token-estimate.js +8 -12
  169. package/dist/nerves/cli-logging.js +7 -1
  170. package/dist/nerves/coverage/audit.js +1 -1
  171. package/dist/nerves/coverage/file-completeness.js +83 -5
  172. package/dist/nerves/coverage/run-artifacts.js +1 -1
  173. package/dist/nerves/event-buffer.js +111 -0
  174. package/dist/nerves/index.js +224 -4
  175. package/dist/nerves/observation.js +20 -0
  176. package/dist/nerves/redact.js +79 -0
  177. package/dist/nerves/runtime.js +5 -1
  178. package/dist/outlook-ui/assets/index-IuR4F6y6.js +61 -0
  179. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  180. package/dist/outlook-ui/index.html +15 -0
  181. package/dist/repertoire/ado-client.js +15 -56
  182. package/dist/repertoire/ado-semantic.js +11 -10
  183. package/dist/repertoire/api-client.js +97 -0
  184. package/dist/repertoire/bitwarden-store.js +319 -0
  185. package/dist/repertoire/bundle-templates.js +72 -0
  186. package/dist/repertoire/bw-installer.js +79 -0
  187. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  188. package/dist/repertoire/coding/context-pack.js +330 -0
  189. package/dist/repertoire/coding/feedback.js +197 -30
  190. package/dist/repertoire/coding/manager.js +158 -9
  191. package/dist/repertoire/coding/spawner.js +55 -9
  192. package/dist/repertoire/coding/tools.js +170 -7
  193. package/dist/repertoire/commerce-errors.js +109 -0
  194. package/dist/repertoire/commerce-self-test.js +156 -0
  195. package/dist/repertoire/credential-access.js +527 -0
  196. package/dist/repertoire/duffel-client.js +185 -0
  197. package/dist/repertoire/github-client.js +14 -55
  198. package/dist/repertoire/graph-client.js +11 -52
  199. package/dist/repertoire/guardrails.js +375 -0
  200. package/dist/repertoire/mcp-client.js +255 -0
  201. package/dist/repertoire/mcp-manager.js +305 -0
  202. package/dist/repertoire/mcp-tools.js +63 -0
  203. package/dist/repertoire/shell-sessions.js +133 -0
  204. package/dist/repertoire/skills.js +14 -23
  205. package/dist/repertoire/stripe-client.js +131 -0
  206. package/dist/repertoire/tasks/board.js +43 -5
  207. package/dist/repertoire/tasks/fix.js +182 -0
  208. package/dist/repertoire/tasks/index.js +28 -10
  209. package/dist/repertoire/tasks/lifecycle.js +2 -2
  210. package/dist/repertoire/tasks/parser.js +3 -2
  211. package/dist/repertoire/tasks/scanner.js +194 -37
  212. package/dist/repertoire/tasks/transitions.js +16 -79
  213. package/dist/repertoire/tool-results.js +29 -0
  214. package/dist/repertoire/tools-attachments.js +316 -0
  215. package/dist/repertoire/tools-base.js +45 -771
  216. package/dist/repertoire/tools-bluebubbles.js +1 -0
  217. package/dist/repertoire/tools-bridge.js +141 -0
  218. package/dist/repertoire/tools-bundle.js +984 -0
  219. package/dist/repertoire/tools-config.js +185 -0
  220. package/dist/repertoire/tools-continuity.js +248 -0
  221. package/dist/repertoire/tools-credential.js +182 -0
  222. package/dist/repertoire/tools-files.js +342 -0
  223. package/dist/repertoire/tools-flight.js +224 -0
  224. package/dist/repertoire/tools-flow.js +105 -0
  225. package/dist/repertoire/tools-github.js +1 -7
  226. package/dist/repertoire/tools-memory.js +376 -0
  227. package/dist/repertoire/tools-session.js +739 -0
  228. package/dist/repertoire/tools-shell.js +120 -0
  229. package/dist/repertoire/tools-stripe.js +180 -0
  230. package/dist/repertoire/tools-surface.js +243 -0
  231. package/dist/repertoire/tools-teams.js +12 -62
  232. package/dist/repertoire/tools-travel.js +125 -0
  233. package/dist/repertoire/tools-user-profile.js +144 -0
  234. package/dist/repertoire/tools-vault.js +110 -0
  235. package/dist/repertoire/tools.js +144 -138
  236. package/dist/repertoire/travel-api-client.js +360 -0
  237. package/dist/repertoire/user-profile.js +118 -0
  238. package/dist/repertoire/vault-setup.js +241 -0
  239. package/dist/scripts/claude-code-hook.js +41 -0
  240. package/dist/scripts/claude-code-stop-hook.js +47 -0
  241. package/dist/senses/attention-queue.js +116 -0
  242. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  243. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  244. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +225 -9
  245. package/dist/senses/bluebubbles/entry.js +13 -0
  246. package/dist/senses/bluebubbles/inbound-log.js +113 -0
  247. package/dist/senses/bluebubbles/index.js +1590 -0
  248. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  249. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +43 -12
  250. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  251. package/dist/senses/bluebubbles/replay.js +129 -0
  252. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  253. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  254. package/dist/senses/cli/bracketed-paste.js +82 -0
  255. package/dist/senses/cli/image-paste.js +287 -0
  256. package/dist/senses/cli/image-ref-navigation.js +75 -0
  257. package/dist/senses/cli/ink-app.js +156 -0
  258. package/dist/senses/cli/inline-diff.js +64 -0
  259. package/dist/senses/cli/input-keys.js +174 -0
  260. package/dist/senses/cli/kill-ring.js +86 -0
  261. package/dist/senses/cli/message-list.js +51 -0
  262. package/dist/senses/cli/ouro-tui.js +605 -0
  263. package/dist/senses/cli/spinner-imperative.js +135 -0
  264. package/dist/senses/cli/spinner.js +101 -0
  265. package/dist/senses/cli/status-line.js +60 -0
  266. package/dist/senses/cli/streaming-markdown.js +526 -0
  267. package/dist/senses/cli/tool-display.js +83 -0
  268. package/dist/senses/cli/tool-render.js +85 -0
  269. package/dist/senses/cli/tui-store.js +240 -0
  270. package/dist/senses/cli/virtual-list.js +35 -0
  271. package/dist/senses/cli-entry.js +1 -1
  272. package/dist/senses/cli-layout.js +187 -0
  273. package/dist/senses/cli.js +595 -246
  274. package/dist/senses/commands.js +65 -1
  275. package/dist/senses/continuity.js +94 -0
  276. package/dist/senses/habit-turn-message.js +108 -0
  277. package/dist/senses/inner-dialog-worker.js +112 -19
  278. package/dist/senses/inner-dialog.js +633 -86
  279. package/dist/senses/pipeline.js +567 -0
  280. package/dist/senses/shared-turn.js +199 -0
  281. package/dist/senses/surface-tool.js +68 -0
  282. package/dist/senses/teams.js +665 -160
  283. package/dist/senses/trust-gate.js +112 -2
  284. package/package.json +28 -7
  285. package/skills/agent-commerce.md +106 -0
  286. package/skills/browser-navigation.md +110 -0
  287. package/skills/commerce-setup-guide.md +116 -0
  288. package/skills/commerce-setup.md +84 -0
  289. package/skills/configure-dev-tools.md +81 -0
  290. package/skills/travel-planning.md +138 -0
  291. package/dist/heart/daemon/subagent-installer.js +0 -134
  292. package/dist/senses/bluebubbles-entry.js +0 -11
  293. package/dist/senses/bluebubbles.js +0 -544
  294. package/dist/senses/debug-activity.js +0 -108
  295. package/subagents/README.md +0 -73
  296. package/subagents/work-doer.md +0 -235
  297. package/subagents/work-merger.md +0 -618
  298. package/subagents/work-planner.md +0 -382
  299. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  300. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  301. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  302. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  303. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  304. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  305. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  306. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  307. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  308. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  309. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  310. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  311. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  312. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  313. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  314. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  315. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,1032 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderOutlookApp = renderOutlookApp;
4
+ const runtime_1 = require("../../nerves/runtime");
5
+ const outlook_types_1 = require("./outlook-types");
6
+ function escapeHtml(value) {
7
+ return value
8
+ .replaceAll("&", "&")
9
+ .replaceAll("<", "&lt;")
10
+ .replaceAll(">", "&gt;")
11
+ .replaceAll("\"", "&quot;");
12
+ }
13
+ function escapeJsonForHtml(value) {
14
+ return JSON.stringify(value)
15
+ .replaceAll("&", "\\u0026")
16
+ .replaceAll("<", "\\u003c")
17
+ .replaceAll(">", "\\u003e");
18
+ }
19
+ function firstAgentName(machineView) {
20
+ return machineView?.agents[0]?.agentName ?? "";
21
+ }
22
+ function renderAgentButtons(machineView) {
23
+ const agents = machineView?.agents ?? [];
24
+ if (agents.length === 0) {
25
+ return [
26
+ "<div class=\"outlook-empty-agent-list\">",
27
+ " <p>No agents are visible yet.</p>",
28
+ " <p>When the daemon sees enabled bundles on this machine, they will gather here.</p>",
29
+ "</div>",
30
+ ].join("\n");
31
+ }
32
+ return agents.map((agent, index) => [
33
+ `<button class="outlook-agent-chip${index === 0 ? " is-selected" : ""}" type="button" data-agent-name="${escapeHtml(agent.agentName)}">`,
34
+ ` <span class="outlook-agent-chip__name">${escapeHtml(agent.agentName)}</span>`,
35
+ ` <span class="outlook-agent-chip__attention attention-${escapeHtml(agent.attention.level)}">${escapeHtml(agent.attention.label)}</span>`,
36
+ ` <span class="outlook-agent-chip__meta">${agent.tasks.liveCount} live tasks · ${agent.coding.activeCount} coding · ${agent.obligations.openCount} obligations</span>`,
37
+ "</button>",
38
+ ].join("\n")).join("\n");
39
+ }
40
+ function renderOverviewCards(machineView, machine) {
41
+ const agents = Array.isArray(machine.agents) ? machine.agents : [];
42
+ const totals = machineView?.overview.totals ?? {
43
+ agents: machine.agentCount,
44
+ enabledAgents: agents.filter((agent) => agent.enabled).length,
45
+ degradedAgents: agents.filter((agent) => agent.degraded?.status === "degraded").length,
46
+ staleAgents: agents.filter((agent) => agent.freshness?.status === "stale").length,
47
+ liveTasks: agents.reduce((sum, agent) => sum + (agent.tasks?.liveCount ?? 0), 0),
48
+ blockedTasks: agents.reduce((sum, agent) => sum + (agent.tasks?.blockedCount ?? 0), 0),
49
+ openObligations: agents.reduce((sum, agent) => sum + (agent.obligations?.openCount ?? 0), 0),
50
+ activeCodingAgents: agents.reduce((sum, agent) => sum + (agent.coding?.activeCount ?? 0), 0),
51
+ blockedCodingAgents: agents.reduce((sum, agent) => sum + (agent.coding?.blockedCount ?? 0), 0),
52
+ };
53
+ const cards = [
54
+ { label: "Visible agents", value: totals.agents.toString() },
55
+ { label: "Live tasks", value: totals.liveTasks.toString() },
56
+ { label: "Open obligations", value: totals.openObligations.toString() },
57
+ { label: "Active coding", value: totals.activeCodingAgents.toString() },
58
+ { label: "Blocked tasks", value: totals.blockedTasks.toString() },
59
+ { label: "Stale agents", value: totals.staleAgents.toString() },
60
+ ];
61
+ return cards.map((card) => [
62
+ "<article class=\"outlook-stat-card\">",
63
+ ` <span class="outlook-kicker">${escapeHtml(card.label)}</span>`,
64
+ ` <strong class="outlook-stat-card__value">${escapeHtml(card.value)}</strong>`,
65
+ "</article>",
66
+ ].join("\n")).join("\n");
67
+ }
68
+ function renderEntrypoints(machineView, origin) {
69
+ const entrypoints = machineView?.overview.entrypoints ?? [
70
+ { kind: "web", label: "Open Outlook", target: `${origin}/outlook` },
71
+ { kind: "cli", label: "CLI JSON", target: "ouro outlook --json" },
72
+ ];
73
+ return entrypoints.map((entrypoint) => [
74
+ "<div class=\"outlook-entrypoint\">",
75
+ ` <span class="outlook-kicker">${escapeHtml(entrypoint.label)}</span>`,
76
+ ` <code>${escapeHtml(entrypoint.target)}</code>`,
77
+ "</div>",
78
+ ].join("\n")).join("\n");
79
+ }
80
+ function renderRuntimeFacts(machineView, machine) {
81
+ const runtime = machineView?.overview.runtime ?? machine.runtime;
82
+ const daemon = machineView?.overview.daemon;
83
+ const freshness = machineView?.overview.freshness ?? machine.freshness;
84
+ const degraded = machineView?.overview.degraded ?? machine.degraded;
85
+ const latestActivity = freshness?.latestActivityAt ?? "unknown";
86
+ return [
87
+ `<div class="outlook-fact"><span class="outlook-kicker">Runtime</span><strong>${escapeHtml(runtime?.version ?? "unknown")}</strong></div>`,
88
+ `<div class="outlook-fact"><span class="outlook-kicker">Mode</span><strong>${escapeHtml(daemon?.mode ?? "production")}</strong></div>`,
89
+ `<div class="outlook-fact"><span class="outlook-kicker">Freshness</span><strong>${escapeHtml(freshness?.status ?? "unknown")}</strong></div>`,
90
+ `<div class="outlook-fact"><span class="outlook-kicker">Latest activity</span><strong>${escapeHtml(latestActivity)}</strong></div>`,
91
+ `<div class="outlook-fact"><span class="outlook-kicker">Degraded</span><strong>${escapeHtml(degraded?.status ?? "ok")}</strong></div>`,
92
+ ].join("\n");
93
+ }
94
+ const APP_CSS = `
95
+ @import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400;1,600&family=Outfit:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');
96
+
97
+ :root {
98
+ --outlook-void: #07110d;
99
+ --outlook-deep: #102116;
100
+ --outlook-moss: #183325;
101
+ --outlook-scale: #2f8f4e;
102
+ --outlook-glow: #74e08f;
103
+ --outlook-bone: #eef2ea;
104
+ --outlook-mist: #a5b8a8;
105
+ --outlook-shadow: #708373;
106
+ --outlook-fang: #d35f47;
107
+ --outlook-line: rgba(110, 160, 117, 0.22);
108
+ --outlook-panel: rgba(10, 22, 16, 0.74);
109
+ --outlook-panel-strong: rgba(13, 29, 21, 0.9);
110
+ --outlook-glass: rgba(21, 44, 32, 0.4);
111
+ --outlook-ring: rgba(116, 224, 143, 0.14);
112
+ --outlook-gold: #d6b56f;
113
+ --outlook-font-display: "Cormorant Garamond", "Iowan Old Style", "Palatino Linotype", serif;
114
+ --outlook-font-body: "Outfit", "Avenir Next", "Segoe UI Variable Text", sans-serif;
115
+ --outlook-font-mono: "JetBrains Mono", "SFMono-Regular", monospace;
116
+ --outlook-radius-lg: 28px;
117
+ --outlook-radius-md: 18px;
118
+ --outlook-radius-sm: 12px;
119
+ --outlook-shadow-lg: 0 30px 80px rgba(0, 0, 0, 0.38);
120
+ }
121
+ * { box-sizing: border-box; margin: 0; }
122
+ html { color-scheme: dark; }
123
+ body {
124
+ min-height: 100vh;
125
+ font-family: var(--outlook-font-body);
126
+ color: var(--outlook-bone);
127
+ background:
128
+ radial-gradient(circle at top left, rgba(116, 224, 143, 0.12), transparent 28%),
129
+ radial-gradient(circle at 85% 15%, rgba(214, 181, 111, 0.08), transparent 24%),
130
+ linear-gradient(180deg, #08110d 0%, #0c1a12 42%, #07110d 100%);
131
+ }
132
+ body::before {
133
+ content: "";
134
+ position: fixed;
135
+ inset: 0;
136
+ pointer-events: none;
137
+ background-image:
138
+ linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px),
139
+ linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px);
140
+ background-size: 34px 34px;
141
+ mask-image: radial-gradient(circle at center, black 30%, transparent 82%);
142
+ opacity: 0.35;
143
+ }
144
+ body::after {
145
+ content: "";
146
+ position: fixed;
147
+ inset: 0;
148
+ pointer-events: none;
149
+ background:
150
+ radial-gradient(circle at 20% 20%, rgba(116, 224, 143, 0.09), transparent 22%),
151
+ radial-gradient(circle at 70% 78%, rgba(47, 143, 78, 0.08), transparent 25%);
152
+ filter: blur(40px);
153
+ opacity: 0.75;
154
+ }
155
+ .outlook-shell {
156
+ position: relative;
157
+ z-index: 1;
158
+ max-width: 1520px;
159
+ margin: 0 auto;
160
+ padding: 28px 22px 48px;
161
+ }
162
+ .outlook-sr-only {
163
+ position: absolute;
164
+ width: 1px; height: 1px; padding: 0; margin: -1px;
165
+ overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
166
+ }
167
+
168
+ /* ─── NAV ─── */
169
+ .outlook-nav {
170
+ display: flex; align-items: center; justify-content: space-between;
171
+ gap: 16px; margin-bottom: 20px; padding: 14px 18px;
172
+ border: 1px solid var(--outlook-line); border-radius: 999px;
173
+ background: rgba(8, 18, 13, 0.58); backdrop-filter: blur(20px);
174
+ box-shadow: 0 14px 40px rgba(0,0,0,0.22);
175
+ }
176
+ .outlook-wordmark { display: flex; align-items: center; gap: 12px; }
177
+ .outlook-orb {
178
+ width: 14px; height: 14px; border-radius: 999px;
179
+ background: radial-gradient(circle at 30% 30%, #c9ffd7 0%, var(--outlook-glow) 30%, var(--outlook-scale) 70%, #173725 100%);
180
+ box-shadow: 0 0 24px rgba(116, 224, 143, 0.45);
181
+ animation: orb-breathe 4s ease-in-out infinite;
182
+ }
183
+ @keyframes orb-breathe {
184
+ 0%, 100% { box-shadow: 0 0 24px rgba(116, 224, 143, 0.45); }
185
+ 50% { box-shadow: 0 0 32px rgba(116, 224, 143, 0.65); }
186
+ }
187
+ .outlook-product {
188
+ margin: 0; font-family: var(--outlook-font-display);
189
+ font-size: clamp(1.5rem, 2vw, 2rem); font-style: italic; font-weight: 600;
190
+ }
191
+ .outlook-subtitle { margin: 2px 0 0; color: var(--outlook-mist); font-size: 0.85rem; }
192
+ .outlook-nav-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
193
+ .outlook-badge {
194
+ display: inline-flex; align-items: center; gap: 8px;
195
+ padding: 6px 11px; border-radius: 999px;
196
+ border: 1px solid var(--outlook-line); background: rgba(12, 29, 20, 0.7);
197
+ color: var(--outlook-bone); font-family: var(--outlook-font-mono);
198
+ font-size: 0.7rem; letter-spacing: 0.05em; text-transform: uppercase;
199
+ }
200
+ .outlook-badge::before {
201
+ content: ""; width: 7px; height: 7px; border-radius: 999px;
202
+ background: var(--outlook-glow); box-shadow: 0 0 18px rgba(116, 224, 143, 0.45);
203
+ }
204
+
205
+ /* ─── HERO ─── */
206
+ .outlook-hero {
207
+ position: relative; overflow: hidden;
208
+ padding: clamp(24px, 3vw, 42px); border-radius: calc(var(--outlook-radius-lg) + 8px);
209
+ border: 1px solid rgba(116, 224, 143, 0.14);
210
+ background: linear-gradient(135deg, rgba(9,18,14,0.97) 0%, rgba(13,30,21,0.94) 52%, rgba(11,21,16,0.95) 100%);
211
+ box-shadow: var(--outlook-shadow-lg);
212
+ }
213
+ .outlook-hero::before {
214
+ content: ""; position: absolute;
215
+ inset: auto -8% -42% 36%; height: 420px;
216
+ background: radial-gradient(circle, rgba(116,224,143,0.18) 0%, rgba(116,224,143,0.04) 35%, transparent 70%);
217
+ filter: blur(10px);
218
+ }
219
+ .outlook-hero__grid {
220
+ position: relative; display: grid; gap: 22px;
221
+ grid-template-columns: minmax(0, 1.4fr) minmax(280px, 0.9fr);
222
+ }
223
+ .outlook-kicker {
224
+ display: inline-block; font-family: var(--outlook-font-mono);
225
+ font-size: 0.68rem; letter-spacing: 0.22em; text-transform: uppercase;
226
+ color: var(--outlook-glow);
227
+ }
228
+ .outlook-hero h1,
229
+ .outlook-section h2, .outlook-panel h2,
230
+ .outlook-agent-panel__empty strong,
231
+ .outlook-agent-card h3 {
232
+ font-family: var(--outlook-font-display); font-style: italic;
233
+ font-weight: 600; letter-spacing: 0.01em;
234
+ }
235
+ .outlook-hero h1 { margin-top: 14px; font-size: clamp(2.6rem, 5vw, 4.8rem); line-height: 0.94; max-width: 8.3ch; }
236
+ .outlook-hero p { max-width: 60ch; margin: 14px 0 0; color: var(--outlook-mist); font-size: 0.98rem; line-height: 1.8; }
237
+
238
+ /* ─── STATS ─── */
239
+ .outlook-stat-grid { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
240
+ .outlook-stat-card,
241
+ .outlook-facts, .outlook-entrypoints-panel, .outlook-agent-panel, .outlook-agents-rail {
242
+ border: 1px solid var(--outlook-line); border-radius: var(--outlook-radius-lg);
243
+ background: var(--outlook-panel); backdrop-filter: blur(18px);
244
+ }
245
+ .outlook-stat-card {
246
+ min-height: 110px; padding: 16px; display: flex;
247
+ flex-direction: column; justify-content: space-between;
248
+ box-shadow: inset 0 1px 0 rgba(255,255,255,0.02);
249
+ }
250
+ .outlook-stat-card__value { font-size: clamp(1.8rem, 3.5vw, 2.6rem); letter-spacing: -0.05em; }
251
+
252
+ /* ─── LAYOUT ─── */
253
+ .outlook-layout {
254
+ display: grid; gap: 20px; margin-top: 20px;
255
+ grid-template-columns: minmax(260px, 0.85fr) minmax(0, 1.75fr);
256
+ }
257
+ .outlook-agents-rail, .outlook-agent-panel, .outlook-facts, .outlook-entrypoints-panel { padding: 18px; }
258
+ .outlook-section, .outlook-panel { display: grid; gap: 14px; }
259
+ .outlook-section h2, .outlook-panel h2 { font-size: clamp(1.6rem, 2.2vw, 2.2rem); }
260
+
261
+ /* ─── AGENT CHIPS ─── */
262
+ .outlook-agent-list { display: grid; gap: 10px; }
263
+ .outlook-agent-chip {
264
+ width: 100%; display: grid; gap: 4px; padding: 14px;
265
+ border-radius: var(--outlook-radius-md);
266
+ border: 1px solid rgba(116, 224, 143, 0.08);
267
+ background: rgba(14, 31, 22, 0.72); color: inherit;
268
+ text-align: left; cursor: pointer;
269
+ transition: transform 180ms ease, border-color 180ms ease, background 180ms ease;
270
+ }
271
+ .outlook-agent-chip:hover, .outlook-agent-chip.is-selected {
272
+ transform: translateY(-2px);
273
+ border-color: rgba(116, 224, 143, 0.28); background: rgba(17, 39, 28, 0.88);
274
+ }
275
+ .outlook-agent-chip__name { font-family: var(--outlook-font-display); font-size: 1.35rem; font-style: italic; }
276
+ .outlook-agent-chip__attention, .outlook-agent-chip__meta { color: var(--outlook-mist); font-size: 0.84rem; }
277
+ .outlook-agent-chip__attention.attention-degraded,
278
+ .outlook-agent-chip__attention.attention-blocked { color: #ff8d79; }
279
+ .outlook-agent-chip__attention.attention-stale { color: var(--outlook-gold); }
280
+ .outlook-agent-chip__attention.attention-active { color: var(--outlook-glow); }
281
+ .outlook-empty-agent-list, .outlook-agent-panel__empty {
282
+ padding: 20px; border-radius: var(--outlook-radius-md);
283
+ border: 1px dashed rgba(116, 224, 143, 0.2);
284
+ background: rgba(10, 22, 16, 0.6); color: var(--outlook-mist);
285
+ }
286
+ .outlook-empty-agent-list p, .outlook-agent-panel__empty p { margin: 0; line-height: 1.7; }
287
+ .outlook-empty-agent-list p + p, .outlook-agent-panel__empty p + p { margin-top: 8px; }
288
+
289
+ /* ─── TABS ─── */
290
+ .outlook-tabs {
291
+ display: flex; gap: 2px; overflow-x: auto; padding-bottom: 2px;
292
+ border-bottom: 1px solid var(--outlook-line);
293
+ -webkit-overflow-scrolling: touch; scrollbar-width: none;
294
+ }
295
+ .outlook-tabs::-webkit-scrollbar { display: none; }
296
+ .outlook-tab {
297
+ padding: 10px 16px; border: none; border-radius: 12px 12px 0 0;
298
+ background: transparent; color: var(--outlook-mist); cursor: pointer;
299
+ font-family: var(--outlook-font-mono); font-size: 0.72rem;
300
+ letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap;
301
+ transition: color 140ms ease, background 140ms ease;
302
+ }
303
+ .outlook-tab:hover { color: var(--outlook-bone); background: rgba(116, 224, 143, 0.06); }
304
+ .outlook-tab.is-active {
305
+ color: var(--outlook-glow); background: rgba(116, 224, 143, 0.1);
306
+ border-bottom: 2px solid var(--outlook-glow);
307
+ }
308
+ .outlook-tab-content { display: none; padding-top: 16px; }
309
+ .outlook-tab-content.is-visible { display: block; animation: tab-fade 200ms ease; }
310
+ @keyframes tab-fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
311
+
312
+ /* ─── MACHINE FACTS ─── */
313
+ .outlook-facts { display: grid; gap: 14px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
314
+ .outlook-fact {
315
+ min-height: 82px; padding: 14px; border-radius: var(--outlook-radius-md);
316
+ background: rgba(12, 26, 18, 0.74); border: 1px solid rgba(116, 224, 143, 0.08);
317
+ }
318
+ .outlook-fact strong { display: block; margin-top: 8px; font-size: 0.95rem; line-height: 1.5; }
319
+ .outlook-entrypoints-panel { display: grid; gap: 12px; }
320
+ .outlook-entrypoint {
321
+ padding: 12px 14px; border-radius: var(--outlook-radius-md);
322
+ background: rgba(12, 27, 19, 0.7); border: 1px solid rgba(116, 224, 143, 0.08);
323
+ }
324
+ .outlook-entrypoint code, .outlook-agent-panel code {
325
+ display: block; margin-top: 6px; font-family: var(--outlook-font-mono);
326
+ font-size: 0.8rem; color: var(--outlook-bone); white-space: pre-wrap; word-break: break-word;
327
+ }
328
+
329
+ /* ─── AGENT CARD / METERS ─── */
330
+ .outlook-agent-card { display: grid; gap: 16px; }
331
+ .outlook-agent-card h3 { font-size: clamp(1.8rem, 2.5vw, 2.6rem); }
332
+ .outlook-agent-card__lede { margin: 0; color: var(--outlook-mist); line-height: 1.8; font-size: 0.95rem; }
333
+ .outlook-agent-meta, .outlook-agent-senses, .outlook-agent-recent { display: grid; gap: 8px; }
334
+ .outlook-agent-meta { grid-template-columns: repeat(2, minmax(0, 1fr)); }
335
+ .outlook-agent-meter {
336
+ padding: 12px; border-radius: var(--outlook-radius-md);
337
+ background: rgba(11, 23, 17, 0.75); border: 1px solid rgba(116, 224, 143, 0.08);
338
+ }
339
+ .outlook-agent-meter strong { display: block; margin-top: 6px; font-size: 1.4rem; }
340
+ .outlook-agent-meter span { color: var(--outlook-mist); font-size: 0.84rem; }
341
+
342
+ /* ─── PILLS ─── */
343
+ .outlook-pills { display: flex; flex-wrap: wrap; gap: 6px; }
344
+ .outlook-pill {
345
+ display: inline-flex; align-items: center; padding: 5px 10px;
346
+ border-radius: 999px; background: rgba(15, 39, 27, 0.7);
347
+ border: 1px solid rgba(116, 224, 143, 0.1);
348
+ color: var(--outlook-bone); font-size: 0.8rem;
349
+ }
350
+
351
+ /* ─── RECENT ACTIVITY ─── */
352
+ .outlook-recent-list { display: grid; gap: 8px; }
353
+ .outlook-recent-item {
354
+ display: grid; gap: 3px; padding: 12px; border-radius: var(--outlook-radius-md);
355
+ background: rgba(11, 23, 17, 0.72); border: 1px solid rgba(116, 224, 143, 0.08);
356
+ }
357
+ .outlook-recent-item small { color: var(--outlook-shadow); font-family: var(--outlook-font-mono); font-size: 0.7rem; }
358
+ .outlook-recent-item strong { font-size: 0.9rem; }
359
+ .outlook-recent-item span { color: var(--outlook-mist); font-size: 0.84rem; }
360
+
361
+ /* ─── SESSION LIST ─── */
362
+ .outlook-session-list { display: grid; gap: 8px; }
363
+ .outlook-session-row {
364
+ display: grid; gap: 4px; padding: 14px; border-radius: var(--outlook-radius-md);
365
+ background: rgba(11, 23, 17, 0.72); border: 1px solid rgba(116, 224, 143, 0.08);
366
+ cursor: pointer; transition: border-color 140ms ease, background 140ms ease;
367
+ }
368
+ .outlook-session-row:hover, .outlook-session-row.is-expanded {
369
+ border-color: rgba(116, 224, 143, 0.22); background: rgba(14, 30, 22, 0.85);
370
+ }
371
+ .outlook-session-row__header { display: flex; justify-content: space-between; align-items: center; }
372
+ .outlook-session-row__name { font-family: var(--outlook-font-display); font-style: italic; font-size: 1.15rem; }
373
+ .outlook-session-row__meta { color: var(--outlook-mist); font-size: 0.8rem; }
374
+ .outlook-session-row__excerpt { color: var(--outlook-shadow); font-size: 0.82rem; line-height: 1.5; margin-top: 2px; }
375
+
376
+ /* ─── TRANSCRIPT VIEWER ─── */
377
+ .outlook-transcript { display: none; margin-top: 10px; padding: 14px; border-radius: var(--outlook-radius-sm); background: rgba(7, 17, 13, 0.85); border: 1px solid var(--outlook-line); max-height: 500px; overflow-y: auto; }
378
+ .outlook-transcript.is-visible { display: block; animation: tab-fade 200ms ease; }
379
+ .outlook-msg { padding: 10px 0; border-bottom: 1px solid rgba(110, 160, 117, 0.1); }
380
+ .outlook-msg:last-child { border-bottom: none; }
381
+ .outlook-msg__role {
382
+ display: inline-block; padding: 2px 8px; border-radius: 6px; margin-bottom: 4px;
383
+ font-family: var(--outlook-font-mono); font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase;
384
+ }
385
+ .outlook-msg__role--system { background: rgba(116, 224, 143, 0.12); color: var(--outlook-glow); }
386
+ .outlook-msg__role--user { background: rgba(214, 181, 111, 0.15); color: var(--outlook-gold); }
387
+ .outlook-msg__role--assistant { background: rgba(116, 224, 143, 0.08); color: var(--outlook-bone); }
388
+ .outlook-msg__role--tool { background: rgba(165, 184, 168, 0.1); color: var(--outlook-mist); }
389
+ .outlook-msg__content { color: var(--outlook-bone); font-size: 0.85rem; line-height: 1.7; white-space: pre-wrap; word-break: break-word; }
390
+ .outlook-msg__tool-calls { margin-top: 6px; }
391
+ .outlook-tool-call {
392
+ padding: 8px 10px; margin-top: 4px; border-radius: 8px;
393
+ background: rgba(15, 35, 25, 0.6); border: 1px solid rgba(116, 224, 143, 0.08);
394
+ font-family: var(--outlook-font-mono); font-size: 0.76rem; color: var(--outlook-glow);
395
+ }
396
+ .outlook-tool-call__args { color: var(--outlook-mist); font-size: 0.72rem; margin-top: 4px; white-space: pre-wrap; word-break: break-word; max-height: 120px; overflow-y: auto; }
397
+
398
+ /* ─── OBLIGATION / CODING DEEP ─── */
399
+ .outlook-obligation-row, .outlook-coding-row, .outlook-bridge-row, .outlook-habit-row {
400
+ padding: 12px; border-radius: var(--outlook-radius-md);
401
+ background: rgba(11, 23, 17, 0.72); border: 1px solid rgba(116, 224, 143, 0.08);
402
+ margin-bottom: 8px;
403
+ }
404
+ .outlook-obligation-row__status, .outlook-coding-row__status {
405
+ display: inline-block; padding: 2px 8px; border-radius: 6px;
406
+ font-family: var(--outlook-font-mono); font-size: 0.68rem; text-transform: uppercase;
407
+ background: rgba(116, 224, 143, 0.08); color: var(--outlook-glow);
408
+ }
409
+ .outlook-coding-row__status--failed { background: rgba(211, 95, 71, 0.15); color: var(--outlook-fang); }
410
+ .outlook-coding-row__stdout, .outlook-coding-row__stderr {
411
+ margin-top: 6px; padding: 8px; border-radius: 6px;
412
+ background: rgba(7, 17, 13, 0.7); font-family: var(--outlook-font-mono);
413
+ font-size: 0.72rem; color: var(--outlook-mist); white-space: pre-wrap; max-height: 100px; overflow-y: auto;
414
+ }
415
+
416
+ /* ─── DIARY / MEMORY ─── */
417
+ .outlook-diary-entry {
418
+ padding: 10px 12px; border-radius: var(--outlook-radius-sm);
419
+ background: rgba(11, 23, 17, 0.6); border: 1px solid rgba(116, 224, 143, 0.06);
420
+ margin-bottom: 6px; font-size: 0.85rem; line-height: 1.6;
421
+ }
422
+ .outlook-diary-entry__source { color: var(--outlook-shadow); font-family: var(--outlook-font-mono); font-size: 0.68rem; }
423
+
424
+ /* ─── CENTER OF GRAVITY ─── */
425
+ .outlook-cog {
426
+ padding: 18px; border-radius: var(--outlook-radius-lg);
427
+ background: linear-gradient(135deg, rgba(13, 30, 22, 0.92) 0%, rgba(10, 22, 16, 0.88) 100%);
428
+ border: 1px solid rgba(116, 224, 143, 0.18);
429
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
430
+ }
431
+ .outlook-cog__heading { font-family: var(--outlook-font-display); font-style: italic; font-size: 1.5rem; margin-bottom: 10px; }
432
+ .outlook-cog__pressures { display: grid; gap: 6px; margin-top: 10px; }
433
+ .outlook-pressure {
434
+ display: flex; gap: 8px; align-items: center; padding: 8px 12px;
435
+ border-radius: 10px; background: rgba(10, 22, 16, 0.65);
436
+ border: 1px solid rgba(116, 224, 143, 0.06); font-size: 0.85rem;
437
+ }
438
+ .outlook-pressure__dot {
439
+ width: 8px; height: 8px; border-radius: 999px; flex-shrink: 0;
440
+ }
441
+ .outlook-pressure__dot--high { background: var(--outlook-fang); box-shadow: 0 0 10px rgba(211, 95, 71, 0.4); }
442
+ .outlook-pressure__dot--medium { background: var(--outlook-gold); box-shadow: 0 0 10px rgba(214, 181, 111, 0.3); }
443
+ .outlook-pressure__dot--low { background: var(--outlook-glow); box-shadow: 0 0 10px rgba(116, 224, 143, 0.3); }
444
+
445
+ /* ─── FOOTER ─── */
446
+ .outlook-footer-note {
447
+ margin-top: 12px; color: var(--outlook-shadow);
448
+ font-family: var(--outlook-font-mono); font-size: 0.72rem; letter-spacing: 0.04em;
449
+ }
450
+
451
+ /* ─── LOADING ─── */
452
+ .outlook-loading {
453
+ padding: 20px; text-align: center; color: var(--outlook-mist);
454
+ font-family: var(--outlook-font-mono); font-size: 0.8rem;
455
+ }
456
+
457
+ @media (max-width: 1080px) {
458
+ .outlook-hero__grid, .outlook-layout { grid-template-columns: 1fr; }
459
+ }
460
+ @media (max-width: 760px) {
461
+ .outlook-shell { padding-inline: 14px; }
462
+ .outlook-nav { border-radius: 28px; align-items: flex-start; flex-direction: column; }
463
+ .outlook-nav-meta, .outlook-facts, .outlook-stat-grid, .outlook-agent-meta { grid-template-columns: 1fr; }
464
+ }
465
+ `;
466
+ const APP_SCRIPT = `
467
+ (function () {
468
+ var root = document.querySelector('[data-outlook-app]');
469
+ if (!root) return;
470
+
471
+ var machineScript = document.getElementById('outlook-machine-view');
472
+ var panel = document.querySelector('[data-outlook-agent-panel]');
473
+ var list = document.querySelector('[data-outlook-agent-list]');
474
+ var title = document.querySelector('[data-outlook-agent-title]');
475
+ var subtitle = document.querySelector('[data-outlook-agent-subtitle]');
476
+ var machineEndpoint = root.getAttribute('data-machine-endpoint');
477
+ var agentEndpointBase = root.getAttribute('data-agent-endpoint-base');
478
+ var selectedAgent = root.getAttribute('data-initial-agent') || '';
479
+ var machineView = machineScript ? JSON.parse(machineScript.textContent || '{}') : null;
480
+ var activeTab = 'overview';
481
+ var cachedSurfaces = {};
482
+ var lastAgentView = null;
483
+
484
+ function setSelected(name) {
485
+ selectedAgent = name || '';
486
+ root.setAttribute('data-selected-agent', selectedAgent);
487
+ cachedSurfaces = {};
488
+ activeTab = 'overview';
489
+ lastAgentView = null;
490
+ if (!list) return;
491
+ list.querySelectorAll('[data-agent-name]').forEach(function (button) {
492
+ button.classList.toggle('is-selected', button.getAttribute('data-agent-name') === selectedAgent);
493
+ });
494
+ }
495
+
496
+ function escapeHtml(value) {
497
+ return String(value).replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('"','&quot;');
498
+ }
499
+
500
+ function relTime(iso) {
501
+ if (!iso) return 'unknown';
502
+ var ms = Date.now() - new Date(iso).getTime();
503
+ if (ms < 60000) return 'just now';
504
+ if (ms < 3600000) return Math.floor(ms/60000) + 'm ago';
505
+ if (ms < 86400000) return Math.floor(ms/3600000) + 'h ago';
506
+ return Math.floor(ms/86400000) + 'd ago';
507
+ }
508
+
509
+ function truncate(str, max) {
510
+ if (!str) return '';
511
+ return str.length > max ? str.slice(0, max) + '...' : str;
512
+ }
513
+
514
+ var TABS = [
515
+ { id: 'overview', label: 'Overview' },
516
+ { id: 'sessions', label: 'Sessions' },
517
+ { id: 'work', label: 'Work' },
518
+ { id: 'connections', label: 'Connections' },
519
+ { id: 'inner', label: 'Inner' },
520
+ { id: 'memory', label: 'Memory' },
521
+ { id: 'runtime', label: 'Runtime' },
522
+ ];
523
+
524
+ function renderTabs() {
525
+ return '<div class="outlook-tabs">' +
526
+ TABS.map(function(t) {
527
+ return '<button class="outlook-tab' + (t.id === activeTab ? ' is-active' : '') + '" data-tab="' + t.id + '">' + t.label + '</button>';
528
+ }).join('') + '</div>';
529
+ }
530
+
531
+ function renderOverviewTab(view) {
532
+ var senses = view.agent.senses.length
533
+ ? view.agent.senses.map(function(s) { return '<span class="outlook-pill">' + escapeHtml(s) + '</span>'; }).join('')
534
+ : '<span class="outlook-pill">No active senses</span>';
535
+ var bridges = view.work.bridges.length
536
+ ? view.work.bridges.map(function(b) { return '<span class="outlook-pill">' + escapeHtml(b) + '</span>'; }).join('')
537
+ : '<span class="outlook-pill">No active bridges</span>';
538
+ var recent = view.activity.recent.length
539
+ ? '<div class="outlook-recent-list">' + view.activity.recent.map(function(item) {
540
+ return '<article class="outlook-recent-item"><small>' + escapeHtml(item.kind) + ' · ' + relTime(item.at) + '</small><strong>' + escapeHtml(truncate(item.label, 100)) + '</strong><span>' + escapeHtml(truncate(item.detail, 80)) + '</span></article>';
541
+ }).join('') + '</div>'
542
+ : '<p class="outlook-agent-card__lede">No recent activity yet.</p>';
543
+ var innerSummary = view.inner.summary || view.inner.status;
544
+
545
+ return '<article class="outlook-agent-card">' +
546
+ '<div class="outlook-cog">' +
547
+ ' <div class="outlook-kicker">Center of gravity</div>' +
548
+ ' <div class="outlook-cog__heading">' + escapeHtml(view.agent.attention.label) + '</div>' +
549
+ ' <p class="outlook-agent-card__lede">' + escapeHtml(view.agent.agentName) +
550
+ ' has ' + view.work.tasks.liveCount + ' live tasks, ' +
551
+ view.work.obligations.openCount + ' open obligations, ' +
552
+ view.work.coding.activeCount + ' coding lanes, ' +
553
+ 'and is ' + escapeHtml(view.activity.freshness.status) + '.</p>' +
554
+ (view.agent.degraded.status === 'degraded' && view.agent.degraded.issues.length
555
+ ? '<div class="outlook-cog__pressures">' + view.agent.degraded.issues.map(function(issue) {
556
+ return '<div class="outlook-pressure"><span class="outlook-pressure__dot outlook-pressure__dot--high"></span><span><strong>' + escapeHtml(issue.code) + '</strong> — ' + escapeHtml(issue.detail) + '</span></div>';
557
+ }).join('') + '</div>'
558
+ : '') +
559
+ '</div>' +
560
+ '<div class="outlook-agent-meta">' +
561
+ ' <div class="outlook-agent-meter"><span class="outlook-kicker">Tasks</span><strong>' + view.work.tasks.liveCount + '</strong><span>' + view.work.tasks.blockedCount + ' blocked</span></div>' +
562
+ ' <div class="outlook-agent-meter"><span class="outlook-kicker">Obligations</span><strong>' + view.work.obligations.openCount + '</strong><span>' + view.work.sessions.liveCount + ' live sessions</span></div>' +
563
+ ' <div class="outlook-agent-meter"><span class="outlook-kicker">Coding</span><strong>' + view.work.coding.activeCount + '</strong><span>' + view.work.coding.blockedCount + ' blocked</span></div>' +
564
+ ' <div class="outlook-agent-meter"><span class="outlook-kicker">Inner</span><strong>' + escapeHtml(view.inner.status) + '</strong><span>' + escapeHtml(truncate(innerSummary, 60) || 'No summary') + '</span></div>' +
565
+ '</div>' +
566
+ '<section class="outlook-agent-senses"><span class="outlook-kicker">Senses</span><div class="outlook-pills">' + senses + '</div></section>' +
567
+ '<section class="outlook-agent-senses"><span class="outlook-kicker">Bridges</span><div class="outlook-pills">' + bridges + '</div></section>' +
568
+ '<section class="outlook-agent-recent"><span class="outlook-kicker">Recent activity</span>' + recent + '</section>' +
569
+ '</article>';
570
+ }
571
+
572
+ function renderSessionsTab(data) {
573
+ if (!data || !data.items || data.items.length === 0) {
574
+ return '<p class="outlook-agent-card__lede">No sessions found.</p>';
575
+ }
576
+ return '<div class="outlook-kicker" style="margin-bottom:8px">' + data.totalCount + ' sessions (' + data.activeCount + ' active, ' + data.staleCount + ' stale)</div>' +
577
+ '<div class="outlook-session-list" data-session-list>' +
578
+ data.items.map(function(s) {
579
+ return '<div class="outlook-session-row" data-session-key="' + escapeHtml(s.friendId + '/' + s.channel + '/' + s.key) + '">' +
580
+ '<div class="outlook-session-row__header">' +
581
+ ' <span class="outlook-session-row__name">' + escapeHtml(s.friendName) + ' <small style="color:var(--outlook-mist);font-style:normal;font-size:0.75rem">via ' + escapeHtml(s.channel) + '</small></span>' +
582
+ ' <span class="outlook-session-row__meta">' + s.messageCount + ' msgs · ' + relTime(s.lastActivityAt) + (s.lastUsage ? ' · ' + s.lastUsage.total_tokens + ' tok' : '') + '</span>' +
583
+ '</div>' +
584
+ (s.latestUserExcerpt ? '<div class="outlook-session-row__excerpt">' + escapeHtml(truncate(s.latestUserExcerpt, 120)) + '</div>' : '') +
585
+ '<div class="outlook-transcript" data-transcript-for="' + escapeHtml(s.friendId + '/' + s.channel + '/' + s.key) + '"></div>' +
586
+ '</div>';
587
+ }).join('') + '</div>';
588
+ }
589
+
590
+ function renderTranscript(messages) {
591
+ return messages.map(function(m) {
592
+ var roleClass = 'outlook-msg__role--' + m.role;
593
+ var html = '<div class="outlook-msg">';
594
+ html += '<span class="outlook-msg__role ' + roleClass + '">' + escapeHtml(m.role) + (m.name ? ' (' + escapeHtml(m.name) + ')' : '') + '</span>';
595
+ if (m.content) html += '<div class="outlook-msg__content">' + escapeHtml(m.content) + '</div>';
596
+ if (m.tool_calls && m.tool_calls.length) {
597
+ html += '<div class="outlook-msg__tool-calls">';
598
+ m.tool_calls.forEach(function(tc) {
599
+ html += '<div class="outlook-tool-call">' + escapeHtml(tc.function.name) +
600
+ '<div class="outlook-tool-call__args">' + escapeHtml(truncate(tc.function.arguments, 300)) + '</div></div>';
601
+ });
602
+ html += '</div>';
603
+ }
604
+ if (m.tool_call_id) html += '<div style="color:var(--outlook-shadow);font-size:0.7rem;margin-top:2px">tool_call_id: ' + escapeHtml(m.tool_call_id) + '</div>';
605
+ html += '</div>';
606
+ return html;
607
+ }).join('');
608
+ }
609
+
610
+ function renderWorkTab(view, coding) {
611
+ var html = '';
612
+ // Obligations
613
+ html += '<div class="outlook-kicker" style="margin-bottom:6px">Obligations (' + view.work.obligations.openCount + ' open)</div>';
614
+ if (view.work.obligations.items && view.work.obligations.items.length) {
615
+ view.work.obligations.items.forEach(function(o) {
616
+ html += '<div class="outlook-obligation-row">' +
617
+ '<span class="outlook-obligation-row__status">' + escapeHtml(o.status) + '</span> ' +
618
+ '<strong style="font-size:0.9rem">' + escapeHtml(truncate(o.content, 100)) + '</strong>' +
619
+ (o.nextAction ? '<div style="color:var(--outlook-mist);font-size:0.82rem;margin-top:4px">Next: ' + escapeHtml(o.nextAction) + '</div>' : '') +
620
+ '<div style="color:var(--outlook-shadow);font-size:0.72rem;margin-top:2px">' + relTime(o.updatedAt) + '</div></div>';
621
+ });
622
+ } else { html += '<p class="outlook-agent-card__lede">No open obligations.</p>'; }
623
+
624
+ // Coding
625
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Coding lanes (' + (coding ? coding.totalCount : 0) + ')</div>';
626
+ if (coding && coding.items && coding.items.length) {
627
+ coding.items.forEach(function(c) {
628
+ var statusClass = c.status === 'failed' ? ' outlook-coding-row__status--failed' : '';
629
+ html += '<div class="outlook-coding-row">' +
630
+ '<span class="outlook-coding-row__status' + statusClass + '">' + escapeHtml(c.status) + '</span> ' +
631
+ '<strong style="font-size:0.88rem">' + escapeHtml(c.runner) + ' — ' + escapeHtml(c.workdir) + '</strong>' +
632
+ (c.checkpoint ? '<div style="color:var(--outlook-mist);font-size:0.82rem;margin-top:4px">' + escapeHtml(truncate(c.checkpoint, 100)) + '</div>' : '') +
633
+ '<div style="color:var(--outlook-shadow);font-size:0.72rem;margin-top:2px">pid ' + (c.pid||'-') + ' · restarts ' + c.restartCount + ' · ' + relTime(c.lastActivityAt) + '</div>' +
634
+ (c.stdoutTail ? '<div class="outlook-coding-row__stdout">' + escapeHtml(truncate(c.stdoutTail, 300)) + '</div>' : '') +
635
+ (c.failure ? '<div class="outlook-coding-row__stderr">FAILURE: ' + escapeHtml(c.failure.command) + ' exited ' + (c.failure.code||c.failure.signal) + '</div>' : '') +
636
+ '</div>';
637
+ });
638
+ } else { html += '<p class="outlook-agent-card__lede">No coding sessions.</p>'; }
639
+
640
+ // Tasks
641
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Tasks (' + view.work.tasks.liveCount + ' live)</div>';
642
+ if (view.work.tasks.liveTaskNames && view.work.tasks.liveTaskNames.length) {
643
+ html += '<div class="outlook-pills">' + view.work.tasks.liveTaskNames.map(function(t) { return '<span class="outlook-pill">' + escapeHtml(t) + '</span>'; }).join('') + '</div>';
644
+ } else { html += '<p class="outlook-agent-card__lede">No live tasks.</p>'; }
645
+
646
+ return html;
647
+ }
648
+
649
+ function renderConnectionsTab(attention, bridges, friends) {
650
+ var html = '';
651
+ // Attention queue
652
+ html += '<div class="outlook-kicker" style="margin-bottom:6px">Attention queue (' + (attention ? attention.queueLength : 0) + ')</div>';
653
+ if (attention && attention.queueItems && attention.queueItems.length) {
654
+ attention.queueItems.forEach(function(item) {
655
+ html += '<div class="outlook-obligation-row">' +
656
+ '<strong style="font-size:0.88rem">' + escapeHtml(item.friendName) + '</strong> via ' + escapeHtml(item.channel) +
657
+ '<div style="color:var(--outlook-mist);font-size:0.82rem;margin-top:3px">' + escapeHtml(truncate(item.delegatedContent, 120)) + '</div>' +
658
+ (item.bridgeId ? '<div style="color:var(--outlook-shadow);font-size:0.72rem">bridge: ' + escapeHtml(item.bridgeId) + '</div>' : '') +
659
+ '</div>';
660
+ });
661
+ } else { html += '<p class="outlook-agent-card__lede">Nothing waiting.</p>'; }
662
+
663
+ // Bridges
664
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Bridges (' + (bridges ? bridges.totalCount : 0) + ')</div>';
665
+ if (bridges && bridges.items && bridges.items.length) {
666
+ bridges.items.forEach(function(b) {
667
+ html += '<div class="outlook-bridge-row">' +
668
+ '<span class="outlook-obligation-row__status">' + escapeHtml(b.lifecycle) + '</span> ' +
669
+ '<strong style="font-size:0.88rem">' + escapeHtml(b.objective) + '</strong>' +
670
+ '<div style="color:var(--outlook-mist);font-size:0.82rem;margin-top:3px">' + escapeHtml(truncate(b.summary, 120)) + '</div>' +
671
+ '<div style="color:var(--outlook-shadow);font-size:0.72rem;margin-top:2px">' + b.attachedSessions.length + ' sessions · ' + relTime(b.updatedAt) + '</div>' +
672
+ '</div>';
673
+ });
674
+ } else { html += '<p class="outlook-agent-card__lede">No bridges.</p>'; }
675
+
676
+ // Friends
677
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Friends (' + (friends ? friends.totalFriends : 0) + ')</div>';
678
+ if (friends && friends.friends && friends.friends.length) {
679
+ friends.friends.forEach(function(f) {
680
+ html += '<div class="outlook-obligation-row">' +
681
+ '<strong style="font-size:0.88rem">' + escapeHtml(f.friendName) + '</strong>' +
682
+ '<div style="color:var(--outlook-mist);font-size:0.82rem">' + f.totalTokens.toLocaleString() + ' tokens · ' + f.sessionCount + ' sessions · ' + f.channels.join(', ') + '</div>' +
683
+ '</div>';
684
+ });
685
+ }
686
+
687
+ return html;
688
+ }
689
+
690
+ function renderInnerTab(view, habits) {
691
+ var html = '';
692
+ html += '<div class="outlook-cog" style="margin-bottom:12px">' +
693
+ '<div class="outlook-kicker">Inner work</div>' +
694
+ '<div class="outlook-cog__heading">' + escapeHtml(view.inner.status) + '</div>' +
695
+ (view.inner.summary ? '<p class="outlook-agent-card__lede">' + escapeHtml(view.inner.summary) + '</p>' : '') +
696
+ '<p class="outlook-agent-card__lede" style="margin-top:6px">' + (view.inner.hasPending ? 'Pending inner work queued.' : 'No pending inner work.') + '</p>' +
697
+ '</div>';
698
+
699
+ if (view.inner.mode === 'deep' && view.inner.origin) {
700
+ html += '<div class="outlook-kicker" style="margin:10px 0 4px">Origin</div>' +
701
+ '<code style="font-size:0.8rem;display:block">' + escapeHtml(JSON.stringify(view.inner.origin)) + '</code>';
702
+ }
703
+
704
+ // Habits
705
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Habits (' + (habits ? habits.totalCount : 0) + ')</div>';
706
+ if (habits && habits.items && habits.items.length) {
707
+ habits.items.forEach(function(h) {
708
+ var overdueTag = h.isOverdue ? ' <span style="color:var(--outlook-fang)">OVERDUE</span>' : '';
709
+ html += '<div class="outlook-habit-row">' +
710
+ '<strong style="font-size:0.88rem">' + escapeHtml(h.title) + overdueTag + '</strong>' +
711
+ '<div style="color:var(--outlook-mist);font-size:0.82rem">' + escapeHtml(h.status) + (h.cadence ? ' · every ' + escapeHtml(h.cadence) : '') + (h.lastRun ? ' · last ' + relTime(h.lastRun) : ' · never run') + '</div>' +
712
+ (h.bodyExcerpt ? '<div style="color:var(--outlook-shadow);font-size:0.8rem;margin-top:3px">' + escapeHtml(h.bodyExcerpt) + '</div>' : '') +
713
+ '</div>';
714
+ });
715
+ } else { html += '<p class="outlook-agent-card__lede">No habits configured.</p>'; }
716
+
717
+ return html;
718
+ }
719
+
720
+ function renderMemoryTab(memory) {
721
+ var html = '';
722
+ html += '<div class="outlook-kicker" style="margin-bottom:6px">Diary (' + (memory ? memory.diaryEntryCount : 0) + ' entries)</div>';
723
+ if (memory && memory.recentDiaryEntries && memory.recentDiaryEntries.length) {
724
+ memory.recentDiaryEntries.forEach(function(e) {
725
+ html += '<div class="outlook-diary-entry">' +
726
+ '<div class="outlook-diary-entry__source">' + escapeHtml(e.source) + ' · ' + relTime(e.createdAt) + '</div>' +
727
+ escapeHtml(e.text) + '</div>';
728
+ });
729
+ } else { html += '<p class="outlook-agent-card__lede">No diary entries.</p>'; }
730
+
731
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Journal (' + (memory ? memory.journalEntryCount : 0) + ' entries)</div>';
732
+ if (memory && memory.recentJournalEntries && memory.recentJournalEntries.length) {
733
+ memory.recentJournalEntries.forEach(function(e) {
734
+ html += '<div class="outlook-diary-entry">' +
735
+ '<strong style="font-size:0.85rem">' + escapeHtml(e.filename) + '</strong>' +
736
+ '<div style="color:var(--outlook-mist);font-size:0.82rem">' + escapeHtml(e.preview) + '</div></div>';
737
+ });
738
+ } else { html += '<p class="outlook-agent-card__lede">No journal entries.</p>'; }
739
+
740
+ return html;
741
+ }
742
+
743
+ function renderRuntimeTab(health, logs, view) {
744
+ var html = '';
745
+
746
+ // Agent-level issues first — this is what "degraded" actually means
747
+ if (view && view.agent.degraded.status === 'degraded' && view.agent.degraded.issues.length) {
748
+ html += '<div class="outlook-kicker" style="margin-bottom:6px">Agent issues (' + view.agent.degraded.issues.length + ')</div>';
749
+ view.agent.degraded.issues.forEach(function(issue) {
750
+ html += '<div class="outlook-obligation-row" style="border-color:rgba(211,95,71,0.2)">' +
751
+ '<strong style="color:var(--outlook-fang)">' + escapeHtml(issue.code) + '</strong>' +
752
+ '<div style="color:var(--outlook-mist);font-size:0.82rem">' + escapeHtml(issue.detail) + '</div></div>';
753
+ });
754
+ }
755
+
756
+ // Agent identity / config
757
+ if (view) {
758
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Agent config</div>';
759
+ html += '<div class="outlook-facts">' +
760
+ '<div class="outlook-fact"><span class="outlook-kicker">Provider</span><strong>' + escapeHtml(view.agent.provider || 'none') + '</strong></div>' +
761
+ '<div class="outlook-fact"><span class="outlook-kicker">Enabled</span><strong>' + (view.agent.enabled ? 'yes' : 'no') + '</strong></div>' +
762
+ '<div class="outlook-fact"><span class="outlook-kicker">Freshness</span><strong>' + escapeHtml(view.agent.freshness.status) + (view.agent.freshness.ageMs ? ' (' + Math.floor(view.agent.freshness.ageMs / 60000) + 'm)' : '') + '</strong></div>' +
763
+ '</div>';
764
+ }
765
+
766
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Daemon health</div>';
767
+ if (health && health.status !== 'unavailable') {
768
+ html += '<div class="outlook-facts" style="margin-bottom:12px">' +
769
+ '<div class="outlook-fact"><span class="outlook-kicker">Status</span><strong>' + escapeHtml(health.status) + '</strong></div>' +
770
+ '<div class="outlook-fact"><span class="outlook-kicker">Mode</span><strong>' + escapeHtml(health.mode) + '</strong></div>' +
771
+ '<div class="outlook-fact"><span class="outlook-kicker">Uptime</span><strong>' + Math.floor(health.uptimeSeconds / 60) + 'm</strong></div>' +
772
+ '</div>';
773
+ if (health.degradedComponents && health.degradedComponents.length) {
774
+ html += '<div class="outlook-kicker" style="margin-bottom:4px">Degraded</div>';
775
+ health.degradedComponents.forEach(function(d) {
776
+ html += '<div class="outlook-obligation-row" style="border-color:rgba(211,95,71,0.2)">' +
777
+ '<strong style="color:var(--outlook-fang)">' + escapeHtml(d.component) + '</strong>' +
778
+ '<div style="color:var(--outlook-mist);font-size:0.82rem">' + escapeHtml(d.reason) + '</div></div>';
779
+ });
780
+ }
781
+ } else { html += '<p class="outlook-agent-card__lede">Health data unavailable.</p>'; }
782
+
783
+ html += '<div class="outlook-kicker" style="margin:14px 0 6px">Recent logs (' + (logs ? logs.totalLines : 0) + ' total)</div>';
784
+ if (logs && logs.entries && logs.entries.length) {
785
+ html += '<div style="max-height:400px;overflow-y:auto">';
786
+ logs.entries.slice(-30).reverse().forEach(function(e) {
787
+ var levelColor = e.level === 'error' ? 'var(--outlook-fang)' : e.level === 'warn' ? 'var(--outlook-gold)' : 'var(--outlook-mist)';
788
+ html += '<div style="padding:6px 0;border-bottom:1px solid rgba(110,160,117,0.08);font-size:0.78rem">' +
789
+ '<span style="color:' + levelColor + ';font-family:var(--outlook-font-mono);font-size:0.68rem">[' + escapeHtml(e.level) + ']</span> ' +
790
+ '<span style="color:var(--outlook-shadow);font-size:0.68rem">' + relTime(e.ts) + '</span> ' +
791
+ '<span style="color:var(--outlook-glow)">' + escapeHtml(e.event) + '</span> ' +
792
+ '<span style="color:var(--outlook-bone)">' + escapeHtml(truncate(e.message, 80)) + '</span></div>';
793
+ });
794
+ html += '</div>';
795
+ } else { html += '<p class="outlook-agent-card__lede">No log entries.</p>'; }
796
+
797
+ return html;
798
+ }
799
+
800
+ function fetchSurface(name) {
801
+ if (cachedSurfaces[name]) return Promise.resolve(cachedSurfaces[name]);
802
+ var url = agentEndpointBase + encodeURIComponent(selectedAgent) + '/' + name;
803
+ return fetch(url, { headers: { accept: 'application/json' } })
804
+ .then(function(r) { return r.ok ? r.json() : null; })
805
+ .then(function(data) { cachedSurfaces[name] = data; return data; })
806
+ .catch(function() { return null; });
807
+ }
808
+
809
+ function renderTabContent(tabId, view) {
810
+ if (!panel) return;
811
+ var contentEl = panel.querySelector('[data-tab-content]');
812
+ if (!contentEl) return;
813
+
814
+ if (tabId === 'overview') {
815
+ contentEl.innerHTML = renderOverviewTab(view);
816
+ return;
817
+ }
818
+
819
+ contentEl.innerHTML = '<div class="outlook-loading">Loading...</div>';
820
+
821
+ if (tabId === 'sessions') {
822
+ fetchSurface('sessions').then(function(data) { contentEl.innerHTML = renderSessionsTab(data); attachTranscriptListeners(contentEl); });
823
+ } else if (tabId === 'work') {
824
+ fetchSurface('coding').then(function(coding) { contentEl.innerHTML = renderWorkTab(view, coding); });
825
+ } else if (tabId === 'connections') {
826
+ Promise.all([fetchSurface('attention'), fetchSurface('bridges'), fetchSurface('friends')])
827
+ .then(function(results) { contentEl.innerHTML = renderConnectionsTab(results[0], results[1], results[2]); });
828
+ } else if (tabId === 'inner') {
829
+ fetchSurface('habits').then(function(habits) { contentEl.innerHTML = renderInnerTab(view, habits); });
830
+ } else if (tabId === 'memory') {
831
+ fetchSurface('memory').then(function(memory) { contentEl.innerHTML = renderMemoryTab(memory); });
832
+ } else if (tabId === 'runtime') {
833
+ Promise.all([
834
+ fetch(machineEndpoint.replace('/api/machine', '/api/machine/health'), { headers: { accept: 'application/json' } }).then(function(r) { return r.ok ? r.json() : null; }).catch(function() { return null; }),
835
+ fetch(machineEndpoint.replace('/api/machine', '/api/machine/logs'), { headers: { accept: 'application/json' } }).then(function(r) { return r.ok ? r.json() : null; }).catch(function() { return null; }),
836
+ ]).then(function(results) { contentEl.innerHTML = renderRuntimeTab(results[0], results[1], view); });
837
+ }
838
+ }
839
+
840
+ function attachTranscriptListeners(container) {
841
+ container.querySelectorAll('.outlook-session-row').forEach(function(row) {
842
+ row.addEventListener('click', function(e) {
843
+ var transcriptEl = row.querySelector('.outlook-transcript');
844
+ if (!transcriptEl) return;
845
+ if (transcriptEl.classList.contains('is-visible')) {
846
+ transcriptEl.classList.remove('is-visible');
847
+ row.classList.remove('is-expanded');
848
+ return;
849
+ }
850
+ var key = row.getAttribute('data-session-key');
851
+ if (!key) return;
852
+ row.classList.add('is-expanded');
853
+ transcriptEl.innerHTML = '<div class="outlook-loading">Loading transcript...</div>';
854
+ transcriptEl.classList.add('is-visible');
855
+ var parts = key.split('/');
856
+ var url = agentEndpointBase + encodeURIComponent(selectedAgent) + '/sessions/' +
857
+ encodeURIComponent(parts[0]) + '/' + encodeURIComponent(parts[1]) + '/' + encodeURIComponent(parts[2]);
858
+ fetch(url, { headers: { accept: 'application/json' } })
859
+ .then(function(r) { return r.ok ? r.json() : null; })
860
+ .then(function(data) {
861
+ if (data && data.messages) {
862
+ transcriptEl.innerHTML = renderTranscript(data.messages);
863
+ } else {
864
+ transcriptEl.innerHTML = '<p class="outlook-agent-card__lede">Could not load transcript.</p>';
865
+ }
866
+ })
867
+ .catch(function() { transcriptEl.innerHTML = '<p class="outlook-agent-card__lede">Error loading transcript.</p>'; });
868
+ });
869
+ });
870
+ }
871
+
872
+ function renderAgentPanel(view, isRefresh) {
873
+ if (!panel || !title || !subtitle) return;
874
+ if (!view) {
875
+ lastAgentView = null;
876
+ title.textContent = 'Choose an agent';
877
+ subtitle.textContent = 'Per-agent detail appears here as soon as you focus on one thread of the organism.';
878
+ panel.innerHTML = '<div class="outlook-agent-panel__empty"><strong>Choose an agent</strong><p>Select a visible agent from the left rail to inspect current work, obligations, senses, bridges, and inward pressure.</p></div>';
879
+ return;
880
+ }
881
+
882
+ lastAgentView = view;
883
+ title.textContent = view.agent.agentName;
884
+ subtitle.textContent = view.agent.attention.label + ' · ' + view.activity.freshness.status + ' freshness';
885
+
886
+ if (isRefresh && panel.querySelector('.outlook-tabs')) {
887
+ // Preserve tab state — just refresh the current tab content
888
+ cachedSurfaces = {};
889
+ renderTabContent(activeTab, view);
890
+ return;
891
+ }
892
+
893
+ panel.innerHTML = renderTabs() + '<div data-tab-content></div>';
894
+
895
+ panel.querySelector('.outlook-tabs').addEventListener('click', function(e) {
896
+ var btn = e.target.closest('.outlook-tab');
897
+ if (!btn) return;
898
+ activeTab = btn.getAttribute('data-tab');
899
+ panel.querySelectorAll('.outlook-tab').forEach(function(t) { t.classList.toggle('is-active', t.getAttribute('data-tab') === activeTab); });
900
+ cachedSurfaces = {};
901
+ renderTabContent(activeTab, view);
902
+ });
903
+
904
+ renderTabContent(activeTab, view);
905
+ }
906
+
907
+ function refreshMachine() {
908
+ if (!machineEndpoint) return Promise.resolve();
909
+ return fetch(machineEndpoint, { headers: { accept: 'application/json' } })
910
+ .then(function(r) { return r.ok ? r.json() : null; })
911
+ .then(function(next) {
912
+ if (!next) return;
913
+ machineView = next;
914
+ if (!selectedAgent && next.agents && next.agents[0]) setSelected(next.agents[0].agentName);
915
+ })
916
+ .catch(function() {});
917
+ }
918
+
919
+ function refreshAgent() {
920
+ if (!selectedAgent || !agentEndpointBase) { renderAgentPanel(null, false); return Promise.resolve(); }
921
+ return fetch(agentEndpointBase + encodeURIComponent(selectedAgent), { headers: { accept: 'application/json' } })
922
+ .then(function(r) { return r.ok ? r.json() : null; })
923
+ .then(function(view) { renderAgentPanel(view, !!lastAgentView); })
924
+ .catch(function() { renderAgentPanel(null, false); });
925
+ }
926
+
927
+ if (list) {
928
+ list.addEventListener('click', function(e) {
929
+ var target = e.target;
930
+ if (!(target instanceof Element)) return;
931
+ var button = target.closest('[data-agent-name]');
932
+ if (!(button instanceof HTMLElement)) return;
933
+ setSelected(button.getAttribute('data-agent-name') || '');
934
+ refreshAgent();
935
+ });
936
+ }
937
+
938
+ if (!selectedAgent && machineView && machineView.agents && machineView.agents[0]) {
939
+ setSelected(machineView.agents[0].agentName);
940
+ }
941
+
942
+ refreshAgent();
943
+ window.setInterval(refreshMachine, 20000);
944
+ window.setInterval(refreshAgent, 15000);
945
+ })();
946
+ `;
947
+ function renderOutlookApp(input) {
948
+ /* v8 ignore next */
949
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.outlook_render", message: "rendering outlook app", meta: {} });
950
+ const machineView = input.machineView;
951
+ const initialAgent = firstAgentName(machineView);
952
+ const productName = machineView?.overview.productName ?? input.machine.productName ?? outlook_types_1.OUTLOOK_PRODUCT_NAME;
953
+ const daemonMode = machineView?.overview.daemon?.mode ?? "production";
954
+ const freshnessStatus = machineView?.overview.freshness?.status ?? input.machine.freshness?.status ?? "unknown";
955
+ return [
956
+ "<!doctype html>",
957
+ "<html lang=\"en\">",
958
+ "<head>",
959
+ " <meta charset=\"utf-8\" />",
960
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />",
961
+ ` <title>${escapeHtml(productName)}</title>`,
962
+ " <meta name=\"color-scheme\" content=\"dark\" />",
963
+ " <meta name=\"description\" content=\"Ouro Outlook is the daemon-hosted shared orientation surface for the agents alive on this machine.\" />",
964
+ ` <style>${APP_CSS}</style>`,
965
+ "</head>",
966
+ `<body data-outlook-app="${escapeHtml(productName)}" data-machine-endpoint="${escapeHtml(`${input.origin}/outlook/api/machine`)}" data-agent-endpoint-base="${escapeHtml(`${input.origin}/outlook/api/agents/`)}" data-initial-agent="${escapeHtml(initialAgent)}">`,
967
+ " <div class=\"outlook-shell\">",
968
+ ` <h1 class="outlook-sr-only">${escapeHtml(productName)}</h1>`,
969
+ " <header class=\"outlook-nav\">",
970
+ " <div class=\"outlook-wordmark\">",
971
+ " <span class=\"outlook-orb\" aria-hidden=\"true\"></span>",
972
+ " <div>",
973
+ ` <p class="outlook-product">${escapeHtml(productName)}</p>`,
974
+ " <p class=\"outlook-subtitle\">Regain the plot together.</p>",
975
+ " </div>",
976
+ " </div>",
977
+ " <div class=\"outlook-nav-meta\">",
978
+ ` <span class="outlook-badge">${escapeHtml(daemonMode)}</span>`,
979
+ ` <span class="outlook-badge">${escapeHtml(freshnessStatus)}</span>`,
980
+ " </div>",
981
+ " </header>",
982
+ " <section class=\"outlook-hero\">",
983
+ " <div class=\"outlook-hero__grid\">",
984
+ " <div>",
985
+ " <span class=\"outlook-kicker\">Machine Overview</span>",
986
+ ` <h1>${escapeHtml(productName)}</h1>`,
987
+ " <p>Where agents regain the plot together. The daemon keeps watch, and Outlook makes the body legible: runtime truth, active obligations, coding lanes, senses, bridges, habits, and inward pressure, all on the same living field.</p>",
988
+ " <div class=\"outlook-footer-note\">Daemon-hosted, loopback-only, direct-read, and read-only by design.</div>",
989
+ " </div>",
990
+ ` <div class="outlook-stat-grid">${renderOverviewCards(machineView, input.machine)}</div>`,
991
+ " </div>",
992
+ " </section>",
993
+ " <section class=\"outlook-layout\">",
994
+ " <aside class=\"outlook-agents-rail\">",
995
+ " <div class=\"outlook-section\">",
996
+ " <span class=\"outlook-kicker\">Visible agents</span>",
997
+ " <h2>Choose a thread</h2>",
998
+ " <div class=\"outlook-agent-list\" data-outlook-agent-list>",
999
+ renderAgentButtons(machineView),
1000
+ " </div>",
1001
+ " </div>",
1002
+ " </aside>",
1003
+ " <main class=\"outlook-panel\">",
1004
+ " <div class=\"outlook-section\">",
1005
+ " <span class=\"outlook-kicker\">Machine facts</span>",
1006
+ " <h2>Current posture</h2>",
1007
+ ` <div class="outlook-facts">${renderRuntimeFacts(machineView, input.machine)}</div>`,
1008
+ " </div>",
1009
+ " <div class=\"outlook-entrypoints-panel\">",
1010
+ " <span class=\"outlook-kicker\">Entrypoints</span>",
1011
+ renderEntrypoints(machineView, input.origin),
1012
+ " </div>",
1013
+ " <section class=\"outlook-agent-panel\">",
1014
+ " <span class=\"outlook-kicker\">Agent detail</span>",
1015
+ " <h2 data-outlook-agent-title>Choose an agent</h2>",
1016
+ " <p class=\"outlook-agent-card__lede\" data-outlook-agent-subtitle>Per-agent detail appears here as soon as you focus on one thread of the organism.</p>",
1017
+ " <div data-outlook-agent-panel>",
1018
+ " <div class=\"outlook-agent-panel__empty\">",
1019
+ " <strong>Choose an agent</strong>",
1020
+ " <p>Select a visible agent from the left rail to inspect current work, obligations, senses, bridges, and inward pressure.</p>",
1021
+ " </div>",
1022
+ " </div>",
1023
+ " </section>",
1024
+ " </main>",
1025
+ " </section>",
1026
+ " </div>",
1027
+ ` <script id="outlook-machine-view" type="application/json">${escapeJsonForHtml(machineView ?? null)}</script>`,
1028
+ ` <script>${APP_SCRIPT}</script>`,
1029
+ "</body>",
1030
+ "</html>",
1031
+ ].join("\n");
1032
+ }