@alook/app 0.0.79 → 0.0.81

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 (152) hide show
  1. package/bundled/email-worker/index.js +33 -2
  2. package/bundled/web/.open-next/.build/durable-objects/queue.js +5 -5
  3. package/bundled/web/.open-next/assets/BUILD_ID +1 -1
  4. package/bundled/web/.open-next/assets/_next/static/chunks/{1816-z_y9qndk.js → 068.hh6m2g-gl.js} +1 -1
  5. package/bundled/web/.open-next/assets/_next/static/chunks/{0kcuw_83491vk.js → 0j2~fioc-ekva.js} +15 -15
  6. package/bundled/web/.open-next/assets/_next/static/chunks/{0ie.u1iu3pwrm.js → 0my5rezp-sbw3.js} +1 -1
  7. package/bundled/web/.open-next/cache/{3zHg2EwefkGKUXp1h2jId → grlt3qzClfRM3yvBh9bW4}/_global-error.cache +1 -1
  8. package/bundled/web/.open-next/cache/{3zHg2EwefkGKUXp1h2jId → grlt3qzClfRM3yvBh9bW4}/_not-found.cache +1 -1
  9. package/bundled/web/.open-next/cache/{3zHg2EwefkGKUXp1h2jId → grlt3qzClfRM3yvBh9bW4}/sitemap.xml.cache +1 -1
  10. package/bundled/web/.open-next/cloudflare/cache-assets-manifest.sql +1 -1
  11. package/bundled/web/.open-next/cloudflare/init.js +1 -1
  12. package/bundled/web/.open-next/dynamodb-provider/dynamodb-cache.json +1 -1
  13. package/bundled/web/.open-next/middleware/handler.mjs +7 -7
  14. package/bundled/web/.open-next/server-functions/default/src/web/.next/BUILD_ID +1 -1
  15. package/bundled/web/.open-next/server-functions/default/src/web/.next/app-path-routes-manifest.json +2 -0
  16. package/bundled/web/.open-next/server-functions/default/src/web/.next/build-manifest.json +3 -3
  17. package/bundled/web/.open-next/server-functions/default/src/web/.next/prerender-manifest.json +3 -3
  18. package/bundled/web/.open-next/server-functions/default/src/web/.next/routes-manifest.json +12 -0
  19. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/invite/[token]/page_client-reference-manifest.js +1 -1
  20. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/studio/new/page_client-reference-manifest.js +1 -1
  21. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/[id]/activity/page_client-reference-manifest.js +1 -1
  22. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/[id]/chat/[convId]/page_client-reference-manifest.js +1 -1
  23. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/[id]/chat/page_client-reference-manifest.js +1 -1
  24. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/[id]/email/page_client-reference-manifest.js +1 -1
  25. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/[id]/files/page_client-reference-manifest.js +1 -1
  26. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/[id]/meetings/page_client-reference-manifest.js +1 -1
  27. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/[id]/page_client-reference-manifest.js +1 -1
  28. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/new/page_client-reference-manifest.js +1 -1
  29. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/agents/page_client-reference-manifest.js +1 -1
  30. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/calendar/page_client-reference-manifest.js +1 -1
  31. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/flags/page_client-reference-manifest.js +1 -1
  32. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/help/email-setup/page_client-reference-manifest.js +1 -1
  33. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/home/page_client-reference-manifest.js +1 -1
  34. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/issues/page_client-reference-manifest.js +1 -1
  35. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/runtimes/page_client-reference-manifest.js +1 -1
  36. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/settings/page_client-reference-manifest.js +1 -1
  37. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/threads/[traceId]/page_client-reference-manifest.js +1 -1
  38. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/threads/page_client-reference-manifest.js +1 -1
  39. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/w/[slug]/unread/page_client-reference-manifest.js +1 -1
  40. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(app)/workspaces/page_client-reference-manifest.js +1 -1
  41. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/(auth)/sign-in/page_client-reference-manifest.js +1 -1
  42. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  43. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/agents/[id]/chat-init/route.js +4 -4
  44. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/agents/[id]/whitelist/route.js +1 -1
  45. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/agents/[id]/workspace/browse/route.js +4 -4
  46. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/agents/route.js +1 -1
  47. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/conversations/[id]/active-task/route.js +1 -1
  48. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/conversations/[id]/buffered-messages/route.js +2 -2
  49. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/conversations/[id]/init/route.js +4 -4
  50. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/conversations/[id]/messages/route.js +2 -2
  51. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/heartbeat/route.js +14 -0
  52. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/heartbeat/route_client-reference-manifest.js +3 -0
  53. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/sweep/route.js +15 -0
  54. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/sweep/route_client-reference-manifest.js +3 -0
  55. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/tasks/[taskId]/complete/route.js +1 -1
  56. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/tasks/[taskId]/fail/route.js +1 -1
  57. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/tasks/[taskId]/start/route.js +1 -1
  58. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/tasks/[taskId]/supersede/route.js +1 -1
  59. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/daemon/tasks/poll/route.js +6 -7
  60. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/email/notify/route.js +1 -1
  61. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/issues/[id]/comments/route.js +1 -1
  62. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/issues/[id]/route.js +1 -1
  63. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/issues/route.js +1 -1
  64. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/studios/route.js +1 -1
  65. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/api/tasks/[id]/retry/route.js +1 -1
  66. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/page_client-reference-manifest.js +1 -1
  67. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/templates/[id]/page_client-reference-manifest.js +1 -1
  68. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app/templates/page_client-reference-manifest.js +1 -1
  69. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/app-paths-manifest.json +2 -0
  70. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__04maj_8._.js +3 -0
  71. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__0_.051i._.js +1 -1
  72. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__0ko5xa_._.js +1 -1
  73. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__0kruv9u._.js +1 -1
  74. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__0l91rwj._.js +3 -0
  75. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__0m5a1_f._.js +1 -1
  76. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__0ntc1ld._.js +1 -1
  77. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__0om99x0._.js +1 -1
  78. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[turbopack]_runtime.js +25 -23
  79. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0-7a9se._.js +3 -0
  80. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_007pf4f._.js +3 -0
  81. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0266t8u._.js +6 -6
  82. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_037ndoa._.js +3 -0
  83. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_04gp_km._.js +3 -0
  84. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_05zp0zn._.js +3 -0
  85. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_05~18bk._.js +1 -1
  86. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_08z6pkf._.js +1 -1
  87. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_091mubc._.js +3 -0
  88. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_09j_99x._.js +6 -6
  89. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0aveiot._.js +5 -0
  90. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0cji8yc._.js +3 -0
  91. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0exm5_w._.js +6 -6
  92. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0f.tk9k._.js +3 -0
  93. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0g1_3hn._.js +3 -0
  94. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0h3guve._.js +1 -1
  95. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0h_dgb9._.js +3 -0
  96. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0hxvvdy._.js +3 -0
  97. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0ke5r~m._.js +3 -0
  98. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0l0biq~._.js +3 -0
  99. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0oyl16-._.js +3 -0
  100. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0pt9vvp._.js +3 -0
  101. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0s~hhx-._.js +1 -1
  102. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0w6dw.t._.js +6 -6
  103. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_12gxjds._.js +3 -0
  104. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_13hlohp._.js +3 -0
  105. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/src_0mko5ir._.js +3 -0
  106. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/src_web__next-internal_server_app_api_daemon_heartbeat_route_actions_059pb-t.js +3 -0
  107. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/src_web__next-internal_server_app_api_daemon_sweep_route_actions_00h1~h1.js +3 -0
  108. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/ssr/[turbopack]_runtime.js +25 -23
  109. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/ssr/_063q-hj._.js +2 -2
  110. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/ssr/_0ilz5db._.js +1 -1
  111. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/ssr/_0st8pau._.js +3 -0
  112. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/ssr/_12e18r-._.js +2 -2
  113. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/ssr/src_web_src_components_home_home-page_tsx_0to84tv._.js +1 -1
  114. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/middleware-build-manifest.js +3 -3
  115. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/middleware-manifest.json +5 -5
  116. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/server-reference-manifest.js +1 -1
  117. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/server-reference-manifest.json +1 -1
  118. package/bundled/web/.open-next/server-functions/default/src/web/handler.mjs +94 -58
  119. package/bundled/web/.open-next/server-functions/default/src/web/handler.mjs.meta.json +498 -327
  120. package/bundled/web/.open-next/server-functions/default/src/web/index.mjs +5 -5
  121. package/bundled/web/wrangler.toml +1 -1
  122. package/bundled/ws-do/index.js +72 -4
  123. package/dist/cli/index.js +380 -72
  124. package/dist/cli/session-runner.js +23 -1
  125. package/dist/index.js +31 -1
  126. package/package.json +1 -1
  127. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/[root-of-the-server]__0y4q15f._.js +0 -3
  128. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0--kvw_._.js +0 -3
  129. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0-r4t0g._.js +0 -3
  130. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_02t7kem._.js +0 -3
  131. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_05_d3df._.js +0 -3
  132. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_06.am62._.js +0 -3
  133. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_07-798_._.js +0 -3
  134. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_07zxhb2._.js +0 -3
  135. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_08mdiy6._.js +0 -3
  136. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0csh82a._.js +0 -3
  137. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0j.pt~7._.js +0 -3
  138. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0j1t6f2._.js +0 -3
  139. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0lmedw9._.js +0 -3
  140. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0ns9.qo._.js +0 -3
  141. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0piy2kq._.js +0 -3
  142. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0tarogd._.js +0 -3
  143. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0u3tu2x._.js +0 -5
  144. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_0x0qvxd._.js +0 -3
  145. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/_12wxdg1._.js +0 -3
  146. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/src_0q3gvkd._.js +0 -3
  147. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/ssr/_07q6-kr._.js +0 -3
  148. package/bundled/web/.open-next/server-functions/default/src/web/.next/server/chunks/ssr/src_web_src_0j12j81._.js +0 -3
  149. /package/bundled/web/.open-next/assets/_next/static/{3zHg2EwefkGKUXp1h2jId → grlt3qzClfRM3yvBh9bW4}/_buildManifest.js +0 -0
  150. /package/bundled/web/.open-next/assets/_next/static/{3zHg2EwefkGKUXp1h2jId → grlt3qzClfRM3yvBh9bW4}/_clientMiddlewareManifest.js +0 -0
  151. /package/bundled/web/.open-next/assets/_next/static/{3zHg2EwefkGKUXp1h2jId → grlt3qzClfRM3yvBh9bW4}/_ssgManifest.js +0 -0
  152. /package/bundled/web/.open-next/cache/{3zHg2EwefkGKUXp1h2jId → grlt3qzClfRM3yvBh9bW4}/robots.txt.cache +0 -0
package/dist/cli/index.js CHANGED
@@ -195,7 +195,7 @@ var TERMINAL_ISSUE_STATUSES = [
195
195
  IssueStatus.FAILED
196
196
  ];
197
197
  var POLL_INTERVAL_MS = Number(process.env.POLL_INTERVAL_MS) || 3000;
198
- var OFFLINE_THRESHOLD_MS = Number(process.env.OFFLINE_THRESHOLD_MS) || 9000;
198
+ var OFFLINE_THRESHOLD_MS = Number(process.env.OFFLINE_THRESHOLD_MS) || 30000;
199
199
  var EVENT_POLL_INTERVAL_MS = Number(process.env.EVENT_POLL_INTERVAL_MS) || 2000;
200
200
  var MeetingStatus = {
201
201
  PENDING: "pending",
@@ -14567,6 +14567,9 @@ var TaskApiSchema = TaskApiBaseSchema.extend({
14567
14567
  agent: TaskAgentDataApiSchema.nullable().optional(),
14568
14568
  sender: TaskSenderApiSchema.nullable().optional()
14569
14569
  });
14570
+ var HeartbeatRequestSchema = exports_external.object({
14571
+ daemon_id: exports_external.string().min(1)
14572
+ });
14570
14573
  var PollRequestSchema = exports_external.object({
14571
14574
  daemon_id: exports_external.string().min(1),
14572
14575
  max_tasks: exports_external.number().int().min(1).default(1),
@@ -14595,6 +14598,15 @@ var PollResponseSchema = exports_external.object({
14595
14598
  file_requests: exports_external.array(FileRequestItemSchema).optional(),
14596
14599
  meetings: exports_external.array(PollMeetingItemSchema).optional()
14597
14600
  });
14601
+ var DaemonPushMessageSchema = exports_external.discriminatedUnion("type", [
14602
+ exports_external.object({ type: exports_external.literal("daemon.tasks"), tasks: exports_external.array(TaskApiSchema) }),
14603
+ exports_external.object({ type: exports_external.literal("daemon.file_requests"), workspaceId: exports_external.string(), requests: exports_external.array(FileRequestItemSchema) }),
14604
+ exports_external.object({ type: exports_external.literal("daemon.meetings"), meetings: exports_external.array(PollMeetingItemSchema) }),
14605
+ exports_external.object({ type: exports_external.literal("daemon.evict"), workspaceId: exports_external.string() }),
14606
+ exports_external.object({ type: exports_external.literal("daemon.update"), version: exports_external.string() }),
14607
+ exports_external.object({ type: exports_external.literal("daemon.rescan") }),
14608
+ exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), taskId: exports_external.string(), targetTaskId: exports_external.string() })
14609
+ ]);
14598
14610
  var RegisterResponseSchema = exports_external.object({
14599
14611
  runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
14600
14612
  });
@@ -16979,6 +16991,9 @@ function loadDaemonConfig(profile) {
16979
16991
  codexModel: process.env.ALOOK_CODEX_MODEL || "",
16980
16992
  opencodeModel: process.env.ALOOK_OPENCODE_MODEL || "",
16981
16993
  pollInterval: parseDuration(process.env.ALOOK_DAEMON_POLL_INTERVAL || "3s"),
16994
+ wsPollInterval: parseDuration(process.env.ALOOK_DAEMON_WS_POLL_INTERVAL || "60s"),
16995
+ heartbeatInterval: parseDuration(process.env.ALOOK_DAEMON_HEARTBEAT_INTERVAL || "15s"),
16996
+ sweepInterval: parseDuration(process.env.ALOOK_DAEMON_SWEEP_INTERVAL || "60s"),
16982
16997
  agentTimeout: parseDuration(process.env.ALOOK_AGENT_TIMEOUT || "12h"),
16983
16998
  messageInactivityTimeout: parseDuration(process.env.ALOOK_MESSAGE_INACTIVITY_TIMEOUT || "20m"),
16984
16999
  maxConcurrentTasks: parseInt(process.env.ALOOK_DAEMON_MAX_CONCURRENT_TASKS || "20"),
@@ -17350,6 +17365,16 @@ class DaemonClient {
17350
17365
  const raw = await this.request("POST", "/api/daemon/register", token, body);
17351
17366
  return RegisterResponseSchema.parse(raw);
17352
17367
  }
17368
+ heartbeat(token, daemonId) {
17369
+ return this.request("POST", "/api/daemon/heartbeat", token, {
17370
+ daemon_id: daemonId
17371
+ });
17372
+ }
17373
+ sweep(token, daemonId) {
17374
+ return this.request("POST", "/api/daemon/sweep", token, {
17375
+ daemon_id: daemonId
17376
+ });
17377
+ }
17353
17378
  deregister(token, daemonId) {
17354
17379
  return this.request("POST", "/api/daemon/deregister", token, {
17355
17380
  daemon_id: daemonId
@@ -19615,6 +19640,155 @@ if (isDirectExecution) {
19615
19640
  main();
19616
19641
  }
19617
19642
 
19643
+ // daemon/ws-client.ts
19644
+ var log6 = createLogger2({ module: "ws-client" });
19645
+ var WS_RECONNECT_INIT = 1000;
19646
+ var WS_RECONNECT_MAX = 30000;
19647
+ var WS_PING_INTERVAL = 25000;
19648
+ var WS_LIVENESS_TIMEOUT = 50000;
19649
+ var WS_DO_DEV_PORT = 8789;
19650
+
19651
+ class DaemonWsClient {
19652
+ opts;
19653
+ ws = null;
19654
+ reconnectDelay = WS_RECONNECT_INIT;
19655
+ reconnectTimer = null;
19656
+ pingInterval = null;
19657
+ livenessInterval = null;
19658
+ lastMessageAt = 0;
19659
+ connected = false;
19660
+ closed = false;
19661
+ constructor(opts) {
19662
+ this.opts = opts;
19663
+ }
19664
+ getUrl() {
19665
+ const url2 = new URL(this.opts.serverURL);
19666
+ const isLocal = url2.hostname === "localhost" || url2.hostname === "127.0.0.1";
19667
+ if (isLocal) {
19668
+ return `ws://localhost:${WS_DO_DEV_PORT}/?daemonId=${this.opts.daemonId}`;
19669
+ }
19670
+ const protocol = url2.protocol === "https:" ? "wss:" : "ws:";
19671
+ return `${protocol}//${url2.host}/api/ws/daemon?daemonId=${this.opts.daemonId}`;
19672
+ }
19673
+ isConnected() {
19674
+ return this.connected;
19675
+ }
19676
+ connect() {
19677
+ if (this.closed)
19678
+ return;
19679
+ this.cleanup();
19680
+ const wsUrl = this.getUrl();
19681
+ log6.info("connecting", { url: wsUrl });
19682
+ try {
19683
+ this.ws = new WebSocket(wsUrl);
19684
+ } catch (err) {
19685
+ log6.warn("ws creation failed", { err: String(err) });
19686
+ this.scheduleReconnect();
19687
+ return;
19688
+ }
19689
+ this.ws.addEventListener("open", () => {
19690
+ this.reconnectDelay = WS_RECONNECT_INIT;
19691
+ this.ws.send(JSON.stringify({
19692
+ type: "auth",
19693
+ machineToken: this.opts.machineToken,
19694
+ daemonId: this.opts.daemonId
19695
+ }));
19696
+ this.lastMessageAt = Date.now();
19697
+ this.startHeartbeat();
19698
+ });
19699
+ this.ws.addEventListener("message", (event) => {
19700
+ this.lastMessageAt = Date.now();
19701
+ const str = typeof event.data === "string" ? event.data : "";
19702
+ if (str === "pong")
19703
+ return;
19704
+ try {
19705
+ const msg = JSON.parse(str);
19706
+ if (msg.type === "auth.ok") {
19707
+ log6.info("authenticated");
19708
+ this.connected = true;
19709
+ this.opts.onConnected();
19710
+ return;
19711
+ }
19712
+ const parsed = DaemonPushMessageSchema.safeParse(msg);
19713
+ if (!parsed.success) {
19714
+ log6.warn("invalid push message", { err: parsed.error.message });
19715
+ return;
19716
+ }
19717
+ this.opts.onMessage(parsed.data);
19718
+ } catch (err) {
19719
+ log6.debug("message parse error", { err: String(err) });
19720
+ }
19721
+ });
19722
+ this.ws.addEventListener("error", () => {
19723
+ log6.debug("ws error");
19724
+ });
19725
+ this.ws.addEventListener("close", () => {
19726
+ const wasConnected = this.connected;
19727
+ this.connected = false;
19728
+ this.stopHeartbeat();
19729
+ if (wasConnected) {
19730
+ this.opts.onDisconnected();
19731
+ }
19732
+ if (!this.closed) {
19733
+ this.scheduleReconnect();
19734
+ }
19735
+ });
19736
+ }
19737
+ close() {
19738
+ this.closed = true;
19739
+ this.cleanup();
19740
+ if (this.reconnectTimer) {
19741
+ clearTimeout(this.reconnectTimer);
19742
+ this.reconnectTimer = null;
19743
+ }
19744
+ }
19745
+ cleanup() {
19746
+ this.stopHeartbeat();
19747
+ if (this.ws) {
19748
+ try {
19749
+ this.ws.close();
19750
+ } catch {}
19751
+ this.ws = null;
19752
+ }
19753
+ this.connected = false;
19754
+ }
19755
+ scheduleReconnect() {
19756
+ if (this.closed)
19757
+ return;
19758
+ const delay = Math.min(this.reconnectDelay, WS_RECONNECT_MAX);
19759
+ this.reconnectDelay = Math.min(delay * 2, WS_RECONNECT_MAX);
19760
+ const jitter = Math.random() * 500;
19761
+ log6.debug("reconnecting", { delayMs: Math.round(delay + jitter) });
19762
+ this.reconnectTimer = setTimeout(() => {
19763
+ this.reconnectTimer = null;
19764
+ this.connect();
19765
+ }, delay + jitter);
19766
+ }
19767
+ startHeartbeat() {
19768
+ this.pingInterval = setInterval(() => {
19769
+ if (this.ws?.readyState === WebSocket.OPEN) {
19770
+ this.ws.send("ping");
19771
+ }
19772
+ }, WS_PING_INTERVAL);
19773
+ this.livenessInterval = setInterval(() => {
19774
+ if (Date.now() - this.lastMessageAt > WS_LIVENESS_TIMEOUT) {
19775
+ log6.warn("liveness timeout, closing");
19776
+ this.ws?.close();
19777
+ }
19778
+ }, 5000);
19779
+ }
19780
+ stopHeartbeat() {
19781
+ if (this.pingInterval) {
19782
+ clearInterval(this.pingInterval);
19783
+ this.pingInterval = null;
19784
+ }
19785
+ if (this.livenessInterval) {
19786
+ clearInterval(this.livenessInterval);
19787
+ this.livenessInterval = null;
19788
+ }
19789
+ }
19790
+ }
19791
+
19618
19792
  // daemon/update-handler.ts
19619
19793
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4 } from "fs";
19620
19794
 
@@ -19646,7 +19820,7 @@ function runNpmUpdate(targetVersion) {
19646
19820
  }
19647
19821
 
19648
19822
  // daemon/update-handler.ts
19649
- var log6 = createLogger2({ module: "updater" });
19823
+ var log7 = createLogger2({ module: "updater" });
19650
19824
  var updating = false;
19651
19825
  var retryCount = 0;
19652
19826
  var MAX_RETRIES = 3;
@@ -19676,29 +19850,29 @@ async function handleCliUpdate(version3, onSuccess, profile) {
19676
19850
  if (retryCount >= MAX_RETRIES)
19677
19851
  return;
19678
19852
  if (process.env.ALOOK_CMD_PREFIX) {
19679
- log6.info(`Skipping auto-update in app mode — user should run: npx @alook/app@latest update`);
19853
+ log7.info(`Skipping auto-update in app mode — user should run: npx @alook/app@latest update`);
19680
19854
  return;
19681
19855
  }
19682
19856
  const marker = readUpdateMarker(profile);
19683
19857
  if (marker === version3) {
19684
- log6.info(`Skipping update to v${version3} — already attempted (marker exists)`);
19858
+ log7.info(`Skipping update to v${version3} — already attempted (marker exists)`);
19685
19859
  return;
19686
19860
  }
19687
19861
  updating = true;
19688
19862
  try {
19689
- log6.info(`Updating CLI to v${version3}...`);
19863
+ log7.info(`Updating CLI to v${version3}...`);
19690
19864
  const result = await runNpmUpdate(version3);
19691
19865
  if (result.success) {
19692
19866
  writeUpdateMarker(version3, profile);
19693
- log6.info(`CLI updated to v${version3} — restarting`);
19867
+ log7.info(`CLI updated to v${version3} — restarting`);
19694
19868
  onSuccess();
19695
19869
  } else {
19696
19870
  retryCount++;
19697
- log6.error(`CLI update failed (attempt ${retryCount}/${MAX_RETRIES}): ${result.output}`);
19871
+ log7.error(`CLI update failed (attempt ${retryCount}/${MAX_RETRIES}): ${result.output}`);
19698
19872
  }
19699
19873
  } catch (e) {
19700
19874
  retryCount++;
19701
- log6.error(`CLI update error (attempt ${retryCount}/${MAX_RETRIES})`, e);
19875
+ log7.error(`CLI update error (attempt ${retryCount}/${MAX_RETRIES})`, e);
19702
19876
  } finally {
19703
19877
  updating = false;
19704
19878
  }
@@ -19841,7 +20015,7 @@ import { readdir as readdir2, readFile as readFile2, unlink, stat as fsStat } fr
19841
20015
  import { execSync as execSync4, spawn as spawn5 } from "child_process";
19842
20016
  import { fileURLToPath as fileURLToPath2 } from "url";
19843
20017
  import { dirname as dirname3, join as join10 } from "path";
19844
- var log7 = createLogger2({ module: "daemon" });
20018
+ var log8 = createLogger2({ module: "daemon" });
19845
20019
  var _dir = dirname3(fileURLToPath2(import.meta.url));
19846
20020
  var sessionRunnerPath = existsSync2(join10(_dir, "session-runner.js")) ? join10(_dir, "session-runner.js") : join10(_dir, "session-runner.ts");
19847
20021
  var meetingRunnerPath = existsSync2(join10(_dir, "meeting-runner.js")) ? join10(_dir, "meeting-runner.js") : join10(_dir, "meeting-runner.ts");
@@ -19948,14 +20122,14 @@ async function reconcilePendingCompletions(workspacesRoot) {
19948
20122
  try {
19949
20123
  parsed = JSON.parse(raw);
19950
20124
  } catch {
19951
- log7.warn(`reconcile: malformed marker ${name}, deleting`);
20125
+ log8.warn(`reconcile: malformed marker ${name}, deleting`);
19952
20126
  try {
19953
20127
  await unlink(filePath);
19954
20128
  } catch {}
19955
20129
  continue;
19956
20130
  }
19957
20131
  if (!isValidMarker(parsed)) {
19958
- log7.warn(`reconcile: invalid marker structure ${name}, deleting`);
20132
+ log8.warn(`reconcile: invalid marker structure ${name}, deleting`);
19959
20133
  try {
19960
20134
  await unlink(filePath);
19961
20135
  } catch {}
@@ -19964,7 +20138,7 @@ async function reconcilePendingCompletions(workspacesRoot) {
19964
20138
  const marker = parsed;
19965
20139
  const age = Date.now() - new Date(marker.createdAt).getTime();
19966
20140
  if (age > MARKER_STALE_MS) {
19967
- log7.warn(`reconcile: stale marker ${name} (${Math.round(age / 3600000)}h old), deleting`);
20141
+ log8.warn(`reconcile: stale marker ${name} (${Math.round(age / 3600000)}h old), deleting`);
19968
20142
  try {
19969
20143
  await unlink(filePath);
19970
20144
  } catch {}
@@ -19980,7 +20154,7 @@ async function reconcilePendingCompletions(workspacesRoot) {
19980
20154
  try {
19981
20155
  await unlink(filePath);
19982
20156
  } catch (delErr) {
19983
- log7.warn(`reconcile: delivered marker ${name} but failed to delete: ${delErr}`);
20157
+ log8.warn(`reconcile: delivered marker ${name} but failed to delete: ${delErr}`);
19984
20158
  }
19985
20159
  } catch (deliverErr) {
19986
20160
  if (isClientError2(deliverErr)) {
@@ -19988,11 +20162,11 @@ async function reconcilePendingCompletions(workspacesRoot) {
19988
20162
  await unlink(filePath);
19989
20163
  } catch {}
19990
20164
  } else {
19991
- log7.debug(`reconcile: delivery failed for ${name}, will retry next cycle`);
20165
+ log8.debug(`reconcile: delivery failed for ${name}, will retry next cycle`);
19992
20166
  }
19993
20167
  }
19994
20168
  } catch (e) {
19995
- log7.debug(`reconcile: error processing ${name}`, e);
20169
+ log8.debug(`reconcile: error processing ${name}`, e);
19996
20170
  }
19997
20171
  }
19998
20172
  }
@@ -20003,7 +20177,7 @@ async function startDaemon(profile, serverUrl) {
20003
20177
  }
20004
20178
  process.once("exit", () => releaseDaemonPid(profile));
20005
20179
  const bailOnUnexpected = (label, err) => {
20006
- log7.error(`${label} — shutting down`, err);
20180
+ log8.error(`${label} — shutting down`, err);
20007
20181
  releaseDaemonPid(profile);
20008
20182
  process.exit(1);
20009
20183
  };
@@ -20016,21 +20190,21 @@ async function startDaemon(profile, serverUrl) {
20016
20190
  if (marker) {
20017
20191
  clearUpdateMarker(profile);
20018
20192
  if (marker === config2.cliVersion) {
20019
- log7.info(`Cleared update marker — now running v${config2.cliVersion}`);
20193
+ log8.info(`Cleared update marker — now running v${config2.cliVersion}`);
20020
20194
  } else {
20021
- log7.info(`Cleared stale update marker (was v${marker}, running v${config2.cliVersion}) — update will be retried`);
20195
+ log8.info(`Cleared stale update marker (was v${marker}, running v${config2.cliVersion}) — update will be retried`);
20022
20196
  }
20023
20197
  }
20024
20198
  const cliConfig = loadCLIConfigForProfile(profile);
20025
20199
  const workspaces = cliConfig.watched_workspaces || [];
20026
20200
  if (workspaces.length === 0) {
20027
- log7.error("No watched workspaces configured.");
20201
+ log8.error("No watched workspaces configured.");
20028
20202
  process.exit(1);
20029
20203
  return;
20030
20204
  }
20031
20205
  const hasPerWorkspaceTokens = workspaces.every((ws) => !!ws.token);
20032
20206
  if (!hasPerWorkspaceTokens) {
20033
- log7.error(`Config uses old format. Run '${cmdPrefix()} register --token <token>' for each workspace to upgrade.`);
20207
+ log8.error(`Config uses old format. Run '${cmdPrefix()} register --token <token>' for each workspace to upgrade.`);
20034
20208
  process.exit(1);
20035
20209
  return;
20036
20210
  }
@@ -20050,11 +20224,11 @@ async function startDaemon(profile, serverUrl) {
20050
20224
  }
20051
20225
  }
20052
20226
  if (providers.length === 0) {
20053
- log7.error("No agent CLI tools found on PATH.");
20227
+ log8.error("No agent CLI tools found on PATH.");
20054
20228
  process.exit(1);
20055
20229
  return;
20056
20230
  }
20057
- log7.info(`Detected providers: ${providers.map((p) => `${p.type}@${p.version}`).join(", ")}`);
20231
+ log8.info(`Detected providers: ${providers.map((p) => `${p.type}@${p.version}`).join(", ")}`);
20058
20232
  const workspaceStates = [];
20059
20233
  const runtimeIndex = new Map;
20060
20234
  for (const ws of workspaces) {
@@ -20062,7 +20236,7 @@ async function startDaemon(profile, serverUrl) {
20062
20236
  type: p.type,
20063
20237
  version: p.version
20064
20238
  }));
20065
- log7.info(`Registering workspace ${ws.id} (${ws.name ?? "unnamed"}) with ${runtimes.length} runtime(s)...`);
20239
+ log8.info(`Registering workspace ${ws.id} (${ws.name ?? "unnamed"}) with ${runtimes.length} runtime(s)...`);
20066
20240
  let resp;
20067
20241
  try {
20068
20242
  resp = await client.register(ws.token, {
@@ -20075,13 +20249,13 @@ async function startDaemon(profile, serverUrl) {
20075
20249
  });
20076
20250
  } catch (e) {
20077
20251
  if (e instanceof Error && e.message.startsWith("HTTP 401")) {
20078
- log7.warn(`Workspace ${ws.id} token invalid — skipping (run '${cmdPrefix()} register --token <token>' to fix)`);
20252
+ log8.warn(`Workspace ${ws.id} token invalid — skipping (run '${cmdPrefix()} register --token <token>' to fix)`);
20079
20253
  } else {
20080
- log7.error(`Failed to register workspace ${ws.id}, skipping`, e);
20254
+ log8.error(`Failed to register workspace ${ws.id}, skipping`, e);
20081
20255
  }
20082
20256
  continue;
20083
20257
  }
20084
- log7.info(`Workspace ${ws.id} registered — ${resp.runtimes.length} runtime(s)`);
20258
+ log8.info(`Workspace ${ws.id} registered — ${resp.runtimes.length} runtime(s)`);
20085
20259
  const runtimeIds = resp.runtimes.map((r) => r.id);
20086
20260
  workspaceStates.push({ workspaceId: ws.id, token: ws.token, runtimeIds });
20087
20261
  for (let i = 0;i < runtimeIds.length; i++) {
@@ -20093,13 +20267,13 @@ async function startDaemon(profile, serverUrl) {
20093
20267
  }
20094
20268
  }
20095
20269
  if (workspaceStates.length === 0) {
20096
- log7.error("No workspaces registered successfully.");
20270
+ log8.error("No workspaces registered successfully.");
20097
20271
  process.exit(1);
20098
20272
  return;
20099
20273
  }
20100
20274
  const allRuntimeIds = workspaceStates.flatMap((ws) => ws.runtimeIds);
20101
20275
  health.setRuntimeCount(allRuntimeIds.length);
20102
- log7.info(`Daemon started — ${allRuntimeIds.length} runtime(s) across ${workspaceStates.length} workspace(s)`);
20276
+ log8.info(`Daemon started — ${allRuntimeIds.length} runtime(s) across ${workspaceStates.length} workspace(s)`);
20103
20277
  const activeTasks = new Set;
20104
20278
  const knownAgentIds = new Set(workspaces.flatMap((ws) => ws.agent_ids ?? []));
20105
20279
  function syncAgentId(agentId, workspaceId) {
@@ -20134,7 +20308,7 @@ async function startDaemon(profile, serverUrl) {
20134
20308
  cfg.watched_workspaces = (cfg.watched_workspaces || []).filter((w) => w.id !== workspaceId);
20135
20309
  saveCLIConfigForProfile(profile, cfg);
20136
20310
  } catch {}
20137
- log7.info(`Workspace ${workspaceId} deleted server-side — removed from config`);
20311
+ log8.info(`Workspace ${workspaceId} deleted server-side — removed from config`);
20138
20312
  }
20139
20313
  const pollCycle = async () => {
20140
20314
  let remaining = config2.maxConcurrentTasks - activeTasks.size;
@@ -20160,7 +20334,7 @@ async function startDaemon(profile, serverUrl) {
20160
20334
  handleCliUpdate(pending_update.version, () => requestRestart(), profile);
20161
20335
  }
20162
20336
  if (pending_rescan) {
20163
- log7.info("Rescan requested — restarting daemon to re-detect runtimes");
20337
+ log8.info("Rescan requested — restarting daemon to re-detect runtimes");
20164
20338
  for (const id of evictedIds) {
20165
20339
  evictWorkspace(id);
20166
20340
  }
@@ -20173,13 +20347,13 @@ async function startDaemon(profile, serverUrl) {
20173
20347
  activeTasks.add(task.id);
20174
20348
  remaining--;
20175
20349
  handleTask(client, config2, runtimeIndex, task, ws.token, activeTasks).catch((e) => {
20176
- log7.error("Task error", e);
20350
+ log8.error("Task error", e);
20177
20351
  activeTasks.delete(task.id);
20178
20352
  });
20179
20353
  }
20180
20354
  if (file_requests) {
20181
20355
  for (const req of file_requests) {
20182
- handleFileRequest(client, config2, ws.workspaceId, req, ws.token).catch((e) => log7.debug("File request error", e));
20356
+ handleFileRequest(client, config2, ws.workspaceId, req, ws.token).catch((e) => log8.debug("File request error", e));
20183
20357
  }
20184
20358
  }
20185
20359
  if (meetings) {
@@ -20202,26 +20376,157 @@ async function startDaemon(profile, serverUrl) {
20202
20376
  }
20203
20377
  } catch (e) {
20204
20378
  if (e instanceof Error && e.message.startsWith("HTTP 401")) {
20205
- log7.warn(`Workspace ${ws.workspaceId} poll returned 401 — will retry next cycle`);
20379
+ log8.warn(`Workspace ${ws.workspaceId} poll returned 401 — will retry next cycle`);
20206
20380
  } else {
20207
- log7.debug("Poll error", e);
20381
+ log8.debug("Poll error", e);
20208
20382
  }
20209
20383
  }
20210
20384
  }
20211
20385
  for (const id of evictedIds) {
20212
20386
  evictWorkspace(id);
20213
20387
  }
20388
+ if (workspaceStates.length === 0) {
20389
+ log8.info("All workspaces evicted — shutting down");
20390
+ shutdown();
20391
+ }
20392
+ };
20393
+ let pollTimer = setInterval(pollCycle, config2.pollInterval);
20394
+ const heartbeatPing = () => {
20395
+ for (const ws of workspaceStates) {
20396
+ client.heartbeat(ws.token, config2.daemonId).catch((e) => {
20397
+ log8.debug("heartbeat failed", { workspaceId: ws.workspaceId, err: String(e) });
20398
+ });
20399
+ }
20400
+ };
20401
+ const heartbeatTimer = setInterval(heartbeatPing, config2.heartbeatInterval);
20402
+ const firstToken = workspaceStates[0]?.token;
20403
+ function updatePollInterval(newInterval) {
20404
+ clearInterval(pollTimer);
20405
+ pollTimer = setInterval(pollCycle, newInterval);
20406
+ }
20407
+ function handleWsPush(msg) {
20408
+ switch (msg.type) {
20409
+ case "daemon.tasks":
20410
+ for (const apiTask of msg.tasks) {
20411
+ if (activeTasks.size >= config2.maxConcurrentTasks)
20412
+ break;
20413
+ const task = fromApiTask(apiTask);
20414
+ if (activeTasks.has(task.id))
20415
+ continue;
20416
+ const ws = workspaceStates.find((w) => w.workspaceId === task.workspaceId);
20417
+ if (!ws)
20418
+ continue;
20419
+ syncAgentId(task.agentId, ws.workspaceId);
20420
+ activeTasks.add(task.id);
20421
+ handleTask(client, config2, runtimeIndex, task, ws.token, activeTasks).catch((e) => {
20422
+ log8.error("WS task error", e);
20423
+ activeTasks.delete(task.id);
20424
+ });
20425
+ }
20426
+ break;
20427
+ case "daemon.file_requests": {
20428
+ const ws = workspaceStates.find((w) => w.workspaceId === msg.workspaceId);
20429
+ if (ws) {
20430
+ for (const req of msg.requests) {
20431
+ handleFileRequest(client, config2, ws.workspaceId, req, ws.token).catch((e) => log8.debug("WS file request error", e));
20432
+ }
20433
+ }
20434
+ break;
20435
+ }
20436
+ case "daemon.meetings":
20437
+ for (const m of msg.meetings) {
20438
+ const ws = workspaceStates.find((w) => w.workspaceId === m.workspace_id);
20439
+ if (!ws)
20440
+ continue;
20441
+ const agentBaseDir = join10(config2.workspacesRoot, m.workspace_id, m.agent_id, "workdir");
20442
+ const timelineDir = join10(agentBaseDir, ".context_timeline");
20443
+ spawnMeetingRunner({
20444
+ meetingId: m.id,
20445
+ meetingUrl: m.meeting_url,
20446
+ participants: m.participants,
20447
+ workspaceId: m.workspace_id,
20448
+ callbackUrl: config2.serverURL,
20449
+ authToken: ws.token,
20450
+ agentName: m.agent_name,
20451
+ agentId: m.agent_id,
20452
+ timelineDir,
20453
+ title: m.title
20454
+ });
20455
+ }
20456
+ break;
20457
+ case "daemon.evict":
20458
+ evictWorkspace(msg.workspaceId);
20459
+ break;
20460
+ case "daemon.update":
20461
+ if (!isUpdating() && msg.version !== config2.cliVersion) {
20462
+ handleCliUpdate(msg.version, () => requestRestart(), profile);
20463
+ }
20464
+ break;
20465
+ case "daemon.rescan":
20466
+ log8.info("WS rescan requested — restarting daemon");
20467
+ requestRestart();
20468
+ break;
20469
+ case "daemon.kill": {
20470
+ const ws = workspaceStates.find((w) => w.workspaceId === msg.workspaceId);
20471
+ if (ws) {
20472
+ const killTask = fromApiTask({
20473
+ id: msg.taskId,
20474
+ agent_id: "",
20475
+ runtime_id: "",
20476
+ conversation_id: "",
20477
+ workspace_id: ws.workspaceId,
20478
+ prompt: "",
20479
+ status: "queued",
20480
+ priority: 0,
20481
+ dispatched_at: null,
20482
+ started_at: null,
20483
+ completed_at: null,
20484
+ result: null,
20485
+ error: null,
20486
+ created_at: new Date().toISOString(),
20487
+ type: "kill_task",
20488
+ context: { target_task_id: msg.targetTaskId },
20489
+ agent: null,
20490
+ sender: null
20491
+ });
20492
+ activeTasks.add(killTask.id);
20493
+ handleTask(client, config2, runtimeIndex, killTask, ws.token, activeTasks).catch((e) => {
20494
+ log8.error("WS kill task error", e);
20495
+ activeTasks.delete(killTask.id);
20496
+ });
20497
+ }
20498
+ break;
20499
+ }
20500
+ }
20501
+ }
20502
+ const wsClient = firstToken ? new DaemonWsClient({
20503
+ serverURL: config2.serverURL,
20504
+ daemonId: config2.daemonId,
20505
+ machineToken: firstToken,
20506
+ onMessage: handleWsPush,
20507
+ onConnected: () => {
20508
+ log8.info("WS connected — switching to low-frequency poll");
20509
+ updatePollInterval(config2.wsPollInterval);
20510
+ },
20511
+ onDisconnected: () => {
20512
+ log8.info("WS disconnected — reverting to high-frequency poll");
20513
+ updatePollInterval(config2.pollInterval);
20514
+ }
20515
+ }) : null;
20516
+ wsClient?.connect();
20517
+ const sweepTick = async () => {
20518
+ for (const ws of workspaceStates) {
20519
+ client.sweep(ws.token, config2.daemonId).catch((e) => {
20520
+ log8.debug("sweep ping failed", { workspaceId: ws.workspaceId, err: String(e) });
20521
+ });
20522
+ }
20214
20523
  try {
20215
20524
  await reconcilePendingCompletions(config2.workspacesRoot);
20216
20525
  } catch (e) {
20217
- log7.debug("reconciliation error", e);
20218
- }
20219
- if (workspaceStates.length === 0) {
20220
- log7.info("All workspaces evicted — shutting down");
20221
- shutdown();
20526
+ log8.debug("reconciliation error", e);
20222
20527
  }
20223
20528
  };
20224
- const pollTimer = setInterval(pollCycle, config2.pollInterval);
20529
+ const sweepTimer = setInterval(sweepTick, config2.sweepInterval);
20225
20530
  let shuttingDown = false;
20226
20531
  let restartRequested = false;
20227
20532
  const requestRestart = () => {
@@ -20232,8 +20537,11 @@ async function startDaemon(profile, serverUrl) {
20232
20537
  if (shuttingDown)
20233
20538
  return;
20234
20539
  shuttingDown = true;
20235
- log7.info(restartRequested ? "Restarting..." : "Shutting down...");
20540
+ log8.info(restartRequested ? "Restarting..." : "Shutting down...");
20236
20541
  clearInterval(pollTimer);
20542
+ clearInterval(heartbeatTimer);
20543
+ clearInterval(sweepTimer);
20544
+ wsClient?.close();
20237
20545
  const shutdownMs = restartRequested ? 30000 : Number(process.env.ALOOK_SHUTDOWN_TIMEOUT_MS) || 5000;
20238
20546
  const timeout = setTimeout(() => process.exit(1), shutdownMs);
20239
20547
  try {
@@ -20256,7 +20564,7 @@ async function startDaemon(profile, serverUrl) {
20256
20564
  mkdirSync7(dirname3(logPath), { recursive: true, mode: 448 });
20257
20565
  logFd = openSync(logPath, "a", 384);
20258
20566
  } catch (e) {
20259
- log7.error(`Failed to open daemon log file ${logPath}`, e);
20567
+ log8.error(`Failed to open daemon log file ${logPath}`, e);
20260
20568
  }
20261
20569
  const child = spawn5(process.execPath, args, {
20262
20570
  detached: true,
@@ -20266,7 +20574,7 @@ async function startDaemon(profile, serverUrl) {
20266
20574
  child.unref();
20267
20575
  if (logFd != null)
20268
20576
  closeSync(logFd);
20269
- log7.info(`Spawned new daemon (pid=${child.pid}), logs: ${logPath}`);
20577
+ log8.info(`Spawned new daemon (pid=${child.pid}), logs: ${logPath}`);
20270
20578
  }
20271
20579
  clearTimeout(timeout);
20272
20580
  process.exit(0);
@@ -20277,7 +20585,7 @@ async function startDaemon(profile, serverUrl) {
20277
20585
  process.on("SIGHUP", async () => {
20278
20586
  if (shuttingDown)
20279
20587
  return;
20280
- log7.info("SIGHUP received — reloading config...");
20588
+ log8.info("SIGHUP received — reloading config...");
20281
20589
  try {
20282
20590
  const freshConfig = loadCLIConfigForProfile(profile);
20283
20591
  const freshWorkspaces = freshConfig.watched_workspaces || [];
@@ -20285,7 +20593,7 @@ async function startDaemon(profile, serverUrl) {
20285
20593
  const newWorkspaces = freshWorkspaces.filter((ws) => ws.token && !existingIds.has(ws.id));
20286
20594
  for (const ws of newWorkspaces) {
20287
20595
  const runtimes = providers.map((p) => ({ type: p.type, version: p.version }));
20288
- log7.info(`Registering new workspace ${ws.id} (${ws.name ?? "unnamed"})...`);
20596
+ log8.info(`Registering new workspace ${ws.id} (${ws.name ?? "unnamed"})...`);
20289
20597
  try {
20290
20598
  const resp = await client.register(ws.token, {
20291
20599
  workspace_id: ws.id,
@@ -20304,19 +20612,19 @@ async function startDaemon(profile, serverUrl) {
20304
20612
  provider: providers[i].type
20305
20613
  });
20306
20614
  }
20307
- log7.info(`Workspace ${ws.id} added — ${runtimeIds.length} runtime(s)`);
20615
+ log8.info(`Workspace ${ws.id} added — ${runtimeIds.length} runtime(s)`);
20308
20616
  } catch (e) {
20309
- log7.error(`Failed to register new workspace ${ws.id}`, e);
20617
+ log8.error(`Failed to register new workspace ${ws.id}`, e);
20310
20618
  }
20311
20619
  }
20312
20620
  if (newWorkspaces.length > 0) {
20313
20621
  health.setRuntimeCount(workspaceStates.reduce((sum, w) => sum + w.runtimeIds.length, 0));
20314
- log7.info(`Reload complete — now polling ${workspaceStates.length} workspace(s)`);
20622
+ log8.info(`Reload complete — now polling ${workspaceStates.length} workspace(s)`);
20315
20623
  } else {
20316
- log7.info("Reload complete — no new workspaces found");
20624
+ log8.info("Reload complete — no new workspaces found");
20317
20625
  }
20318
20626
  } catch (e) {
20319
- log7.error("Failed to reload config", e);
20627
+ log8.error("Failed to reload config", e);
20320
20628
  }
20321
20629
  });
20322
20630
  await pollCycle();
@@ -20331,7 +20639,7 @@ function spawnSessionRunner(input) {
20331
20639
  try {
20332
20640
  fd = openSync(logFilePath, "a");
20333
20641
  } catch (e) {
20334
- log7.error(`Failed to open log file ${logFilePath}`, e);
20642
+ log8.error(`Failed to open log file ${logFilePath}`, e);
20335
20643
  }
20336
20644
  const child = spawn5(process.execPath, [sessionRunnerPath, encoded], {
20337
20645
  detached: true,
@@ -20351,7 +20659,7 @@ function spawnMeetingRunner(input) {
20351
20659
  try {
20352
20660
  fd = openSync(logFilePath, "a");
20353
20661
  } catch (e) {
20354
- log7.error(`Failed to open meeting log file ${logFilePath}`, e);
20662
+ log8.error(`Failed to open meeting log file ${logFilePath}`, e);
20355
20663
  }
20356
20664
  const child = spawn5(process.execPath, [meetingRunnerPath, encoded], {
20357
20665
  detached: true,
@@ -20360,7 +20668,7 @@ function spawnMeetingRunner(input) {
20360
20668
  child.unref();
20361
20669
  if (fd != null)
20362
20670
  closeSync(fd);
20363
- log7.info(`Spawned meeting runner for ${input.meetingId} (pid=${child.pid})`);
20671
+ log8.info(`Spawned meeting runner for ${input.meetingId} (pid=${child.pid})`);
20364
20672
  return child;
20365
20673
  }
20366
20674
  async function handleFileRequest(client, config2, workspaceId, req, token) {
@@ -20387,7 +20695,7 @@ async function handleFileRequest(client, config2, workspaceId, req, token) {
20387
20695
  }
20388
20696
  }
20389
20697
  async function handleTask(client, config2, runtimeIndex, task, token, activeTasks) {
20390
- log7.info(`Task ${task.id} claimed agent=${task.agentId}`);
20698
+ log8.info(`Task ${task.id} claimed agent=${task.agentId}`);
20391
20699
  if (task.type === TASK_TYPES.KILL_TASK) {
20392
20700
  const targetTaskId = task.context?.target_task_id;
20393
20701
  if (!targetTaskId) {
@@ -20416,18 +20724,18 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
20416
20724
  try {
20417
20725
  process.kill(pid, "SIGTERM");
20418
20726
  await client.failTask(token, task.id, "killed");
20419
- log7.info(`Kill task ${task.id}: sent SIGTERM to pid=${pid} for target=${targetTaskId}`);
20727
+ log8.info(`Kill task ${task.id}: sent SIGTERM to pid=${pid} for target=${targetTaskId}`);
20420
20728
  } catch (e) {
20421
20729
  if (e?.code === "ESRCH") {
20422
20730
  await client.failTask(token, task.id, "target process already exited");
20423
- log7.info(`Kill task ${task.id}: target pid=${pid} already exited`);
20731
+ log8.info(`Kill task ${task.id}: target pid=${pid} already exited`);
20424
20732
  } else {
20425
20733
  await client.failTask(token, task.id, `kill failed: ${e}`);
20426
20734
  }
20427
20735
  }
20428
20736
  } else {
20429
20737
  await client.failTask(token, task.id, "target not found in timeline");
20430
- log7.info(`Kill task ${task.id}: target ${targetTaskId} not found in timeline`);
20738
+ log8.info(`Kill task ${task.id}: target ${targetTaskId} not found in timeline`);
20431
20739
  }
20432
20740
  activeTasks.delete(task.id);
20433
20741
  return;
@@ -20452,12 +20760,12 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
20452
20760
  const timelineDir = join10(agentBaseDir, ".context_timeline");
20453
20761
  const lockAcquired = acquireSteeringLock(agentBaseDir, task.contextKey);
20454
20762
  if (!lockAcquired) {
20455
- log7.warn(`Steering lock contention for context_key=${task.contextKey}, proceeding without steering`);
20763
+ log8.warn(`Steering lock contention for context_key=${task.contextKey}, proceeding without steering`);
20456
20764
  } else {
20457
20765
  try {
20458
20766
  const predecessor = findRunningEntryByContextKey(timelineDir, task.contextKey, provider);
20459
20767
  if (predecessor && predecessor.task_id !== task.id) {
20460
- log7.info(`Steering: task ${task.id} supersedes predecessor ${predecessor.task_id} (context_key=${task.contextKey})`);
20768
+ log8.info(`Steering: task ${task.id} supersedes predecessor ${predecessor.task_id} (context_key=${task.contextKey})`);
20461
20769
  if (predecessor.pid != null) {
20462
20770
  writeKillIntent(agentBaseDir, {
20463
20771
  reason: "superseded",
@@ -20467,12 +20775,12 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
20467
20775
  });
20468
20776
  try {
20469
20777
  process.kill(predecessor.pid, "SIGTERM");
20470
- log7.info(`Steering: sent SIGTERM to predecessor pid=${predecessor.pid}`);
20778
+ log8.info(`Steering: sent SIGTERM to predecessor pid=${predecessor.pid}`);
20471
20779
  } catch (e) {
20472
20780
  if (e?.code === "ESRCH") {
20473
- log7.info(`Steering: predecessor pid=${predecessor.pid} already exited`);
20781
+ log8.info(`Steering: predecessor pid=${predecessor.pid} already exited`);
20474
20782
  } else {
20475
- log7.warn(`Steering: kill failed for pid=${predecessor.pid}`, e);
20783
+ log8.warn(`Steering: kill failed for pid=${predecessor.pid}`, e);
20476
20784
  }
20477
20785
  }
20478
20786
  const waitStart = Date.now();
@@ -20485,14 +20793,14 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
20485
20793
  await new Promise((r) => setTimeout(r, POLL_MS));
20486
20794
  }
20487
20795
  if (findRunningPidByTaskId(timelineDir, predecessor.task_id) != null) {
20488
- log7.warn(`Steering: predecessor pid=${predecessor.pid} did not exit within ${MAX_WAIT_MS}ms, proceeding anyway`);
20796
+ log8.warn(`Steering: predecessor pid=${predecessor.pid} did not exit within ${MAX_WAIT_MS}ms, proceeding anyway`);
20489
20797
  }
20490
20798
  }
20491
20799
  try {
20492
20800
  await client.supersedeTask(token, predecessor.task_id);
20493
- log7.info(`Steering: predecessor ${predecessor.task_id} marked superseded`);
20801
+ log8.info(`Steering: predecessor ${predecessor.task_id} marked superseded`);
20494
20802
  } catch (e) {
20495
- log7.warn(`Steering: failed to mark predecessor superseded server-side`, e);
20803
+ log8.warn(`Steering: failed to mark predecessor superseded server-side`, e);
20496
20804
  }
20497
20805
  }
20498
20806
  } finally {
@@ -20520,7 +20828,7 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
20520
20828
  activeTasks.delete(task.id);
20521
20829
  if (code !== 0) {
20522
20830
  const msg = code === null ? `session-runner killed by signal (task ${task.id})` : `session-runner crashed (exit code ${code}, task ${task.id})`;
20523
- log7.warn(msg);
20831
+ log8.warn(msg);
20524
20832
  const timelineDir = join10(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir", ".context_timeline");
20525
20833
  updateEntry(timelineDir, task.id, (entry) => {
20526
20834
  entry.pid = null;
@@ -20531,10 +20839,10 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
20531
20839
  await client.failTask(token, task.id, msg);
20532
20840
  } catch (e) {
20533
20841
  if (isClientError2(e)) {
20534
- log7.info(`Backstop: task ${task.id} already in terminal state`);
20842
+ log8.info(`Backstop: task ${task.id} already in terminal state`);
20535
20843
  return;
20536
20844
  }
20537
- log7.error(`Backstop: failed to report crash for task ${task.id}`, e);
20845
+ log8.error(`Backstop: failed to report crash for task ${task.id}`, e);
20538
20846
  try {
20539
20847
  await writeMarkerFile(config2.workspacesRoot, {
20540
20848
  taskId: task.id,
@@ -20548,7 +20856,7 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
20548
20856
  }
20549
20857
  }
20550
20858
  });
20551
- log7.info(`Task ${task.id} dispatched to session-runner (pid=${child.pid})`);
20859
+ log8.info(`Task ${task.id} dispatched to session-runner (pid=${child.pid})`);
20552
20860
  }
20553
20861
 
20554
20862
  // commands/daemon.ts
@@ -20722,7 +21030,7 @@ function resolveAgentId(opts) {
20722
21030
  }
20723
21031
 
20724
21032
  // commands/email.ts
20725
- var log8 = createLogger2({ module: "email" });
21033
+ var log9 = createLogger2({ module: "email" });
20726
21034
  var VALID_STATUSES = ["unread", "read", "archived", "sent"];
20727
21035
  var VALID_FOLDERS = ["inbox", "sent", "untrust"];
20728
21036
  var EMAIL_BASE = tempDir("alook-emails");
@@ -20864,7 +21172,7 @@ function emailCommand() {
20864
21172
  } catch (err) {
20865
21173
  const msg = err instanceof Error ? err.message : String(err);
20866
21174
  if (msg.includes("404")) {
20867
- log8.warn(`email body not available for ${email3.id}, skipping`);
21175
+ log9.warn(`email body not available for ${email3.id}, skipping`);
20868
21176
  continue;
20869
21177
  }
20870
21178
  throw err;
@@ -20986,7 +21294,7 @@ function emailCommand() {
20986
21294
  references = [parentEmail.references, parentEmail.message_id].filter(Boolean).join(" ").trim() || undefined;
20987
21295
  }
20988
21296
  } catch {
20989
- log8.warn(`could not fetch parent email ${opts.inReplyTo}, sending without threading`);
21297
+ log9.warn(`could not fetch parent email ${opts.inReplyTo}, sending without threading`);
20990
21298
  }
20991
21299
  }
20992
21300
  const conversationId = process.env.ALOOK_CONVERSATION_ID;