@alook/daemon 0.1.11 → 0.1.13

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.
package/dist/index.js CHANGED
@@ -57,7 +57,7 @@ function cliCommandsSection() {
57
57
  "### Messaging",
58
58
  "",
59
59
  `1. \`${CLI} inbox pull\` — fetch unread messages (advances your read waterline by default, ` + `so they won't re-pull; \`--no-ack\` to peek without advancing).`,
60
- `2. \`${CLI} message send\` — send to a channel, DM, or thread. Attach with ` + `\`--attachment <id>\` (repeatable, order matters). Optionally add ` + `\`--remind-after <duration>\` to be reminded if the same channel, thread, or DM receives no ` + `newer message after your send. Use a whole number of minutes or hours from \`1m\` to \`24h\`, ` + `such as \`15m\` or \`2h\`.`,
60
+ `2. \`${CLI} message send\` — send to a channel, DM, or thread. ` + `For a short body, use explicit \`--stdin\` with a quoted heredoc; for a long or complicated body, ` + `use \`--file <path>\`. Attach with \`--attachment <id>\` (repeatable, order matters). Optionally add ` + `\`--remind-after <duration>\` to be reminded if the same channel, thread, or DM receives no ` + `newer message after your send. Use a whole number of minutes or hours from \`1m\` to \`24h\`, ` + `such as \`15m\` or \`2h\`.`,
61
61
  `3. \`${CLI} message attachment upload --target <ref> --file <path>\` — upload a file; ` + `returns an id stable across pending→persisted. Feed it into ` + `\`message send --attachment <id>\`.`,
62
62
  `4. \`${CLI} message attachment download --id <id> [--out <path>]\` — download any ` + `attachment you can see (or your own pending uploads).`,
63
63
  `5. \`${CLI} message emoji --target <ref> --emoji <e>\` — react with a single emoji. ` + `Works on channel messages (\`/<server>/<channel>#N\`), DM messages ` + `(\`/.dm/<peer>#N\`), and thread-reply messages (\`/<server>/<channel>/#N#M\`).`,
@@ -88,7 +88,7 @@ function cliCommandsSection() {
88
88
  "",
89
89
  "### Context Lifecycle",
90
90
  "",
91
- `1. \`${CLI} nap --handoff <file>\` (or \`--text <note>\`) — reset your current session and ` + `start fresh. The required handoff is injected into the new session so your future self can ` + `quickly pick up unfinished work. Never nap on your own; only do it when someone explicitly asks.`,
91
+ `1. \`${CLI} nap --handoff <file>\` — reset your current session and ` + `start fresh. The required handoff is injected into the new session so your future self can ` + `quickly pick up unfinished work. Never nap on your own; only do it when someone explicitly asks.`,
92
92
  "",
93
93
  "### Output format",
94
94
  "",
@@ -107,9 +107,11 @@ function messagingSection() {
107
107
  "You can initiate conversations — send to any channel or DM someone directly. You're not " + "limited to replying. Use the same `message send` command whether you're replying or " + "starting a conversation.",
108
108
  "",
109
109
  "- Reply where the message came from. Post results in the channel that owns the topic. " + "When uncertain, read history (below) or DM the relevant people.",
110
- `- Short reply: \`${CLI} message send --target <ref> --text "brief reply"\`.`,
111
- `- Long or complicated: write body to a tmp file, then \`${CLI} message send --target <ref> --file ./temp_msg.md\`.`,
112
- `- Cite a specific message: \`${CLI} message send --target <ref> --reply "#37" --text "on it"\` ` + "`--reply` takes the `#N` seq (within `--target`) of the message you're answering.",
110
+ "Free-form message bodies never go in command arguments.",
111
+ "",
112
+ `- Short reply: use \`${CLI} message send --target <ref> --stdin\` with the quoted-heredoc form shown ` + "under *Message formatting*. Choose a fresh quoted delimiter that does not occur as a standalone line in the body.",
113
+ `- Long or complicated: write the body to a temporary file with a filesystem tool, then ` + `\`${CLI} message send --target <ref> --file ./temp_msg.md\`.`,
114
+ `- Cite a specific message: add \`--reply "#37"\` to either form — \`--reply\` takes the \`#N\` seq ` + "(within `--target`) of the message you're answering.",
113
115
  "",
114
116
  "### Context refs",
115
117
  "",
@@ -149,8 +151,10 @@ function messagingSection() {
149
151
  "- **Mentions** — `@name#NNNN` calls that person's attention specifically. In a private " + "channel, first verify they " + `are a member with \`${CLI} channel member --channel <ref>\` before mentioning them.`,
150
152
  "",
151
153
  "```bash",
152
- "# Notify Alice and point her to a message for context.",
153
- `${CLI} message send --target "/demo#1234/general" --text "@alice#0001 Please review /demo#1234/general#42"`,
154
+ "# Choose a fresh quoted delimiter that does not occur as a standalone line in the body.",
155
+ `${CLI} message send --target "/demo#1234/general" --stdin <<'ALOOK_MESSAGE_7F3C'`,
156
+ "@alice#0001 Please review /demo#1234/general#42",
157
+ "ALOOK_MESSAGE_7F3C",
154
158
  "```",
155
159
  "",
156
160
  "### Pulled messages",
@@ -216,7 +220,7 @@ function utilsSection() {
216
220
  "",
217
221
  `Use \`message send --remind-after <duration>\` when you send something that may need a later ` + "follow-up — for example, a question, approval request, handoff, or blocker — and silence would " + "leave the work unfinished. If no newer message appears in that channel, thread, or DM during " + "the duration, you'll receive a reminder to return and decide what to do next. Don't add it to " + "ordinary messages that need no follow-up.",
218
222
  "",
219
- `Example: \`${CLI} message send --target /demo#1234/team --text "Please review the rollout plan" ` + `--remind-after 1m\`.`
223
+ `Example: ${CLI} message send --target "/demo#1234/team" --remind-after 1m --file ./message.md`
220
224
  ].join(`
221
225
  `);
222
226
  }
@@ -4815,624 +4819,248 @@ ${this.opts.wakePromptFooter}` : text;
4815
4819
  }
4816
4820
  }
4817
4821
  }
4818
- // src/manager/agentRouter.ts
4819
- var DEFINITIVE_EXECUTABLE_FAILURES = new Set(["ENOENT", "EACCES", "ENOEXEC", "EPERM"]);
4820
-
4821
- class UnknownBotError extends Error {
4822
- botId;
4823
- constructor(botId) {
4824
- super(`Bot not in this daemon's cache: ${botId}`);
4825
- this.botId = botId;
4826
- this.name = "UnknownBotError";
4827
- }
4828
- }
4829
-
4830
- class BotEnrollFailedError extends Error {
4831
- botId;
4832
- constructor(botId, cause) {
4833
- super(`Failed to enroll bot ${botId}: ${cause instanceof Error ? cause.message : String(cause)}`);
4834
- this.botId = botId;
4835
- this.name = "BotEnrollFailedError";
4836
- }
4837
- }
4838
- function classifyErrorCode(err) {
4839
- if (err instanceof UnknownBotError)
4840
- return "bot_unknown";
4841
- if (err instanceof BotEnrollFailedError)
4842
- return "bot_enroll_failed";
4843
- if (err instanceof UnknownRuntimeError)
4844
- return "bot_runtime_missing";
4845
- return "internal_error";
4846
- }
4847
-
4848
- class UnknownRuntimeError extends Error {
4849
- requested;
4850
- available;
4851
- constructor(requested, available) {
4852
- super(`Runtime not available on this host: ${requested ?? "<unspecified>"} — installed: ${available.join(", ") || "(none)"}`);
4853
- this.requested = requested;
4854
- this.available = available;
4855
- this.name = "UnknownRuntimeError";
4856
- }
4857
- }
4858
- function defaultFormatUnreadNoticeText(notice) {
4859
- return `You have unread messages in channel ${notice.channel}.`;
4860
- }
4861
- var REWAKE_PROMPT = "Your session was reset by your owner. Prior conversation context is gone. " + "Read @memory.md and your .context_timeline for durable context, then pull your inbox " + "before doing anything else. If it reports marked messages, run `$ALOOK_CLI message mark list` " + "and resume that outstanding work.";
4862
- var MODEL_SWITCH_REWAKE_PROMPT = "You were just switched to a different model. Continue any unfinished work.";
4863
- function buildNapRewakePrompt(handoff) {
4864
- return "You took a nap: you reset your own session, so prior conversation context " + `is gone. Before sleeping you left yourself this handoff —
4865
-
4866
- ` + handoff.trim() + `
4867
-
4868
- Then read @memory.md and your .context_timeline for durable context, and pull ` + "your inbox before doing anything else. If it reports marked messages, run `$ALOOK_CLI " + "message mark list` and resume that outstanding work.";
4869
- }
4870
-
4871
- class AgentRouter {
4872
- opts;
4873
- running = new Set;
4874
- runtimes = new Map;
4875
- pendingResend = false;
4876
- scheduleResend;
4877
- log;
4878
- constructor(opts) {
4879
- this.opts = opts;
4880
- this.log = opts.logger ?? createLogger({ header: "@alook/daemon:router" });
4881
- this.scheduleResend = opts.scheduleReadyResend ?? queueMicrotask.bind(globalThis);
4882
- for (const r of opts.runtimeReport) {
4883
- this.runtimes.set(r.id, {
4884
- id: r.id,
4885
- version: r.version,
4886
- status: r.status ?? "healthy",
4887
- lastError: r.lastError,
4888
- lastErrorAt: r.lastErrorAt
4889
- });
4890
- }
4891
- }
4892
- async start() {
4893
- this.opts.channel.onCommand((cmd) => this.onCommand(cmd));
4894
- this.opts.channel.onResync?.(() => ({
4895
- ready: this.buildReady(),
4896
- sessions: this.opts.manager.liveSessionReports(),
4897
- activities: this.opts.manager.liveAgentActivities()
4898
- }));
4899
- await this.opts.channel.reportReady(this.buildReady());
4900
- }
4901
- buildReady() {
4902
- return {
4903
- runtimeReport: [...this.runtimes.values()],
4904
- runningAgents: [...this.running],
4905
- hostname: this.opts.hostname,
4906
- platform: this.opts.platform,
4907
- arch: this.opts.arch,
4908
- osRelease: this.opts.osRelease,
4909
- daemonVersion: this.opts.daemonVersion
4910
- };
4911
- }
4912
- healthyRuntimeIds() {
4913
- const out = [];
4914
- for (const r of this.runtimes.values()) {
4915
- if (r.status === "healthy")
4916
- out.push(r.id);
4917
- }
4918
- return out;
4919
- }
4920
- isRuntimeHealthy(id) {
4921
- return this.runtimes.get(id)?.status === "healthy";
4922
- }
4923
- recordRuntimeSpawnFailure(id, reason) {
4924
- if (!DEFINITIVE_EXECUTABLE_FAILURES.has(reason))
4925
- return;
4926
- this.markRuntimeUnhealthy(id, reason);
4927
- }
4928
- markRuntimeUnhealthy(id, reason) {
4929
- const existing = this.runtimes.get(id);
4930
- if (!existing)
4931
- return;
4932
- const nowIso = new Date().toISOString();
4933
- if (existing.status === "unhealthy" && existing.lastError === reason)
4934
- return;
4935
- this.runtimes.set(id, {
4936
- ...existing,
4937
- status: "unhealthy",
4938
- lastError: reason,
4939
- lastErrorAt: nowIso
4940
- });
4941
- this.log.warn("runtime marked unhealthy", { runtimeId: id, reason });
4942
- this.scheduleReadyFrameResend();
4943
- }
4944
- markRuntimeHealthy(id) {
4945
- const existing = this.runtimes.get(id);
4946
- if (!existing)
4947
- return;
4948
- if (existing.status === "healthy" && !existing.lastError && !existing.lastErrorAt)
4949
- return;
4950
- this.runtimes.set(id, {
4951
- id: existing.id,
4952
- version: existing.version,
4953
- status: "healthy"
4954
- });
4955
- this.log.info("runtime marked healthy again", { runtimeId: id });
4956
- this.scheduleReadyFrameResend();
4957
- }
4958
- markLocallyStopped(agentId) {
4959
- if (!this.running.delete(agentId))
4960
- return;
4961
- this.log.info("agent removed from running set (local stop)", { agentId });
4962
- this.scheduleReadyFrameResend();
4963
- }
4964
- scheduleReadyFrameResend() {
4965
- if (this.pendingResend)
4966
- return;
4967
- this.pendingResend = true;
4968
- this.scheduleResend(() => {
4969
- this.pendingResend = false;
4970
- try {
4971
- this.opts.channel.sendReady?.(this.buildReady());
4972
- } catch {}
4973
- });
4974
- }
4975
- async runRestartCommand(agentId, launchId, opName, run) {
4976
- this.log.info(`${opName} received`, { agentId, launchId });
4977
- try {
4978
- await this.opts.onBeforeAgent?.(agentId);
4979
- await run();
4980
- this.running.add(agentId);
4981
- this.scheduleReadyFrameResend();
4982
- this.log.info(`${opName} ok`, { agentId });
4983
- } catch (err) {
4984
- if (err instanceof UnknownRuntimeError) {
4985
- const frame = {
4986
- type: "session.error",
4987
- code: "runtime_not_available",
4988
- agentId,
4989
- launchId,
4990
- payload: { requested: err.requested ?? null, available: err.available }
4991
- };
4992
- await this.opts.channel.reportSessionError?.(frame);
4993
- this.log.info(`${opName} error`, { agentId, "error.code": "runtime_not_available" });
4994
- return;
4995
- }
4996
- const code = classifyErrorCode(err);
4997
- await this.opts.channel.reportWakeAck?.({
4998
- agentId,
4999
- launchId,
5000
- status: "error",
5001
- error: { code, message: err instanceof Error ? err.message : String(err) }
5002
- });
5003
- this.log.warn(`${opName} failed`, {
5004
- agentId,
5005
- "error.code": code,
5006
- err: err instanceof Error ? err.message : String(err)
5007
- });
5008
- }
5009
- }
5010
- async onCommand(cmd) {
5011
- switch (cmd.type) {
5012
- case "agent:wake":
5013
- this.log.info("agent:wake received", {
5014
- agentId: cmd.agentId,
5015
- channel: cmd.unreadNotice.channel,
5016
- latestSeq: cmd.unreadNotice.latestSeq
5017
- });
5018
- try {
5019
- const beforeStatus = this.opts.manager.snapshot?.().agents?.[cmd.agentId]?.status ?? "unregistered";
5020
- const wasActive = this.opts.typingTracker?.hasAny(cmd.agentId) ?? false;
5021
- await this.opts.onBeforeAgent?.(cmd.agentId);
5022
- this.opts.manager.register(cmd.agentId, {
5023
- runtimeConfig: cmd.config,
5024
- sessionId: cmd.sessionId,
5025
- launchId: cmd.launchId
5026
- });
5027
- this.running.add(cmd.agentId);
5028
- const channelScope = cmd.unreadNotice.channelId;
5029
- if (channelScope)
5030
- this.opts.typingTracker?.add(cmd.agentId, channelScope);
5031
- const text = (this.opts.formatUnreadNoticeText ?? defaultFormatUnreadNoticeText)(cmd.unreadNotice);
5032
- const producedEffect = this.opts.manager.deliver(cmd.agentId, {
5033
- seq: cmd.unreadNotice.latestSeq,
5034
- text
5035
- });
5036
- if (producedEffect === false && beforeStatus === "running") {
5037
- this.log.info("agent:wake produced no effect (coalesced onto running agent)", {
5038
- agentId: cmd.agentId,
5039
- latestSeq: cmd.unreadNotice.latestSeq
5040
- });
5041
- }
5042
- if (channelScope && wasActive && beforeStatus === "running") {
5043
- this.opts.channel.reportAgentTyping?.({
5044
- agentId: cmd.agentId,
5045
- channelId: channelScope
5046
- });
5047
- }
5048
- await this.opts.channel.reportWakeAck?.({
5049
- agentId: cmd.agentId,
5050
- launchId: cmd.launchId,
5051
- status: "ok"
5052
- });
5053
- this.log.info("agent:wake ack", { agentId: cmd.agentId, status: "ok" });
5054
- } catch (err) {
5055
- if (err instanceof UnknownRuntimeError) {
5056
- const frame = {
5057
- type: "session.error",
5058
- code: "runtime_not_available",
5059
- agentId: cmd.agentId,
5060
- launchId: cmd.launchId,
5061
- payload: {
5062
- requested: err.requested ?? null,
5063
- available: err.available
5064
- }
5065
- };
5066
- await this.opts.channel.reportSessionError?.(frame);
5067
- await this.opts.channel.reportWakeAck?.({
5068
- agentId: cmd.agentId,
5069
- launchId: cmd.launchId,
5070
- status: "error",
5071
- error: {
5072
- code: "bot_runtime_missing",
5073
- message: err.message
5074
- }
5075
- });
5076
- this.log.info("agent:wake ack", {
5077
- agentId: cmd.agentId,
5078
- status: "error",
5079
- "error.code": "bot_runtime_missing"
5080
- });
5081
- return;
5082
- }
5083
- {
5084
- const code = classifyErrorCode(err);
5085
- await this.opts.channel.reportWakeAck?.({
5086
- agentId: cmd.agentId,
5087
- launchId: cmd.launchId,
5088
- status: "error",
5089
- error: {
5090
- code,
5091
- message: err instanceof Error ? err.message : String(err)
5092
- }
5093
- });
5094
- this.log.info("agent:wake ack", { agentId: cmd.agentId, status: "error", "error.code": code });
5095
- }
5096
- return;
5097
- }
5098
- break;
5099
- case "agent:reset":
5100
- await this.runRestartCommand(cmd.agentId, cmd.launchId, "agent:reset", () => this.opts.manager.resetSession(cmd.agentId, {
5101
- runtimeConfig: cmd.config,
5102
- launchId: cmd.launchId,
5103
- rewakePrompt: REWAKE_PROMPT
5104
- }));
5105
- break;
5106
- case "machine:reset_all":
5107
- for (const r of cmd.resets) {
5108
- await this.runRestartCommand(r.agentId, r.launchId, "agent:reset", () => this.opts.manager.resetSession(r.agentId, {
5109
- runtimeConfig: r.config,
5110
- launchId: r.launchId,
5111
- rewakePrompt: REWAKE_PROMPT
5112
- }));
5113
- }
5114
- break;
5115
- case "agent:nap":
5116
- await this.runRestartCommand(cmd.agentId, cmd.launchId, "agent:nap", () => this.opts.manager.resetSession(cmd.agentId, {
5117
- runtimeConfig: cmd.config,
5118
- launchId: cmd.launchId,
5119
- rewakePrompt: buildNapRewakePrompt(cmd.handoff),
5120
- barrierType: "nap"
5121
- }));
5122
- break;
5123
- case "agent:model_switch":
5124
- await this.runRestartCommand(cmd.agentId, cmd.launchId, "agent:model_switch", () => this.opts.manager.switchModel(cmd.agentId, {
5125
- runtimeConfig: cmd.config,
5126
- launchId: cmd.launchId,
5127
- rewakePrompt: MODEL_SWITCH_REWAKE_PROMPT
5128
- }));
5129
- break;
5130
- case "agent:stop":
5131
- this.log.info("agent:stop received", { agentId: cmd.agentId });
5132
- try {
5133
- this.running.delete(cmd.agentId);
5134
- this.opts.manager.stop(cmd.agentId);
5135
- await this.opts.channel.reportStoppedAck?.({
5136
- agentId: cmd.agentId,
5137
- status: "ok"
5138
- });
5139
- this.log.info("agent:stop ack", { agentId: cmd.agentId, status: "ok" });
5140
- } catch (err) {
5141
- const code = classifyErrorCode(err);
5142
- await this.opts.channel.reportStoppedAck?.({
5143
- agentId: cmd.agentId,
5144
- status: "error",
5145
- error: {
5146
- code,
5147
- message: err instanceof Error ? err.message : String(err)
5148
- }
5149
- });
5150
- this.log.info("agent:stop ack", { agentId: cmd.agentId, status: "error", "error.code": code });
5151
- }
5152
- break;
5153
- case "bot:added":
5154
- case "bot:updated":
5155
- case "bot:removed":
5156
- break;
5157
- }
5158
- }
5159
- }
5160
- // src/manager/typingScopeTracker.ts
5161
- function createTypingScopeTracker() {
5162
- const scopes = new Map;
5163
- return {
5164
- add(agentId, channelId) {
5165
- let set = scopes.get(agentId);
5166
- if (!set) {
5167
- set = new Set;
5168
- scopes.set(agentId, set);
5169
- }
5170
- set.add(channelId);
5171
- },
5172
- snapshot(agentId) {
5173
- const set = scopes.get(agentId);
5174
- return set ? [...set] : [];
5175
- },
5176
- hasAny(agentId) {
5177
- const set = scopes.get(agentId);
5178
- return !!set && set.size > 0;
5179
- },
5180
- clear(agentId) {
5181
- scopes.delete(agentId);
5182
- }
5183
- };
5184
- }
5185
- // src/credentials/credentialProxy.ts
5186
- import * as crypto2 from "crypto";
5187
- import * as fs5 from "fs";
5188
- import * as http from "http";
5189
- import * as https from "https";
5190
- import * as os2 from "os";
5191
- import * as path7 from "path";
5192
- import { URL as URL2 } from "url";
5193
-
5194
- // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
5195
- var exports_external = {};
5196
- __export(exports_external, {
5197
- xor: () => xor,
5198
- xid: () => xid2,
5199
- void: () => _void2,
5200
- uuidv7: () => uuidv7,
5201
- uuidv6: () => uuidv6,
5202
- uuidv4: () => uuidv4,
5203
- uuid: () => uuid2,
5204
- util: () => exports_util,
5205
- url: () => url,
5206
- uppercase: () => _uppercase,
5207
- unknown: () => unknown,
5208
- union: () => union,
5209
- undefined: () => _undefined3,
5210
- ulid: () => ulid2,
5211
- uint64: () => uint64,
5212
- uint32: () => uint32,
5213
- tuple: () => tuple,
5214
- trim: () => _trim,
5215
- treeifyError: () => treeifyError,
5216
- transform: () => transform,
5217
- toUpperCase: () => _toUpperCase,
5218
- toLowerCase: () => _toLowerCase,
5219
- toJSONSchema: () => toJSONSchema,
5220
- templateLiteral: () => templateLiteral,
5221
- symbol: () => symbol,
5222
- superRefine: () => superRefine,
5223
- success: () => success,
5224
- stringbool: () => stringbool,
5225
- stringFormat: () => stringFormat,
5226
- string: () => string2,
5227
- strictObject: () => strictObject,
5228
- startsWith: () => _startsWith,
5229
- slugify: () => _slugify,
5230
- size: () => _size,
5231
- setErrorMap: () => setErrorMap,
5232
- set: () => set,
5233
- safeParseAsync: () => safeParseAsync2,
5234
- safeParse: () => safeParse2,
5235
- safeEncodeAsync: () => safeEncodeAsync2,
5236
- safeEncode: () => safeEncode2,
5237
- safeDecodeAsync: () => safeDecodeAsync2,
5238
- safeDecode: () => safeDecode2,
5239
- registry: () => registry,
5240
- regexes: () => exports_regexes,
5241
- regex: () => _regex,
5242
- refine: () => refine,
5243
- record: () => record,
5244
- readonly: () => readonly,
5245
- property: () => _property,
5246
- promise: () => promise,
5247
- prettifyError: () => prettifyError,
5248
- preprocess: () => preprocess,
5249
- prefault: () => prefault,
5250
- positive: () => _positive,
5251
- pipe: () => pipe,
5252
- partialRecord: () => partialRecord,
5253
- parseAsync: () => parseAsync2,
5254
- parse: () => parse3,
5255
- overwrite: () => _overwrite,
5256
- optional: () => optional,
5257
- object: () => object,
5258
- number: () => number2,
5259
- nullish: () => nullish2,
5260
- nullable: () => nullable,
5261
- null: () => _null3,
5262
- normalize: () => _normalize,
5263
- nonpositive: () => _nonpositive,
5264
- nonoptional: () => nonoptional,
5265
- nonnegative: () => _nonnegative,
5266
- never: () => never,
5267
- negative: () => _negative,
5268
- nativeEnum: () => nativeEnum,
5269
- nanoid: () => nanoid2,
5270
- nan: () => nan,
5271
- multipleOf: () => _multipleOf,
5272
- minSize: () => _minSize,
5273
- minLength: () => _minLength,
5274
- mime: () => _mime,
5275
- meta: () => meta2,
5276
- maxSize: () => _maxSize,
5277
- maxLength: () => _maxLength,
5278
- map: () => map,
5279
- mac: () => mac2,
5280
- lte: () => _lte,
5281
- lt: () => _lt,
5282
- lowercase: () => _lowercase,
5283
- looseRecord: () => looseRecord,
5284
- looseObject: () => looseObject,
5285
- locales: () => exports_locales,
5286
- literal: () => literal,
5287
- length: () => _length,
5288
- lazy: () => lazy,
5289
- ksuid: () => ksuid2,
5290
- keyof: () => keyof,
5291
- jwt: () => jwt,
5292
- json: () => json,
5293
- iso: () => exports_iso,
5294
- ipv6: () => ipv62,
5295
- ipv4: () => ipv42,
5296
- invertCodec: () => invertCodec,
5297
- intersection: () => intersection,
5298
- int64: () => int64,
5299
- int32: () => int32,
5300
- int: () => int,
5301
- instanceof: () => _instanceof,
5302
- includes: () => _includes,
5303
- httpUrl: () => httpUrl,
5304
- hostname: () => hostname2,
5305
- hex: () => hex2,
5306
- hash: () => hash,
5307
- guid: () => guid2,
5308
- gte: () => _gte,
5309
- gt: () => _gt,
5310
- globalRegistry: () => globalRegistry,
5311
- getErrorMap: () => getErrorMap,
5312
- function: () => _function,
5313
- fromJSONSchema: () => fromJSONSchema,
5314
- formatError: () => formatError,
5315
- float64: () => float64,
5316
- float32: () => float32,
5317
- flattenError: () => flattenError,
5318
- file: () => file,
5319
- exactOptional: () => exactOptional,
5320
- enum: () => _enum2,
5321
- endsWith: () => _endsWith,
5322
- encodeAsync: () => encodeAsync2,
5323
- encode: () => encode2,
5324
- emoji: () => emoji2,
5325
- email: () => email2,
5326
- e164: () => e1642,
5327
- discriminatedUnion: () => discriminatedUnion,
5328
- describe: () => describe2,
5329
- decodeAsync: () => decodeAsync2,
5330
- decode: () => decode2,
5331
- date: () => date3,
5332
- custom: () => custom,
5333
- cuid2: () => cuid22,
5334
- cuid: () => cuid3,
5335
- core: () => exports_core2,
5336
- config: () => config,
5337
- coerce: () => exports_coerce,
5338
- codec: () => codec,
5339
- clone: () => clone2,
5340
- cidrv6: () => cidrv62,
5341
- cidrv4: () => cidrv42,
5342
- check: () => check,
5343
- catch: () => _catch2,
5344
- boolean: () => boolean2,
5345
- bigint: () => bigint2,
5346
- base64url: () => base64url2,
5347
- base64: () => base642,
5348
- array: () => array,
5349
- any: () => any,
5350
- _function: () => _function,
5351
- _default: () => _default2,
5352
- _ZodString: () => _ZodString,
5353
- ZodXor: () => ZodXor,
5354
- ZodXID: () => ZodXID,
5355
- ZodVoid: () => ZodVoid,
5356
- ZodUnknown: () => ZodUnknown,
5357
- ZodUnion: () => ZodUnion,
5358
- ZodUndefined: () => ZodUndefined,
5359
- ZodUUID: () => ZodUUID,
5360
- ZodURL: () => ZodURL,
5361
- ZodULID: () => ZodULID,
5362
- ZodType: () => ZodType,
5363
- ZodTuple: () => ZodTuple,
5364
- ZodTransform: () => ZodTransform,
5365
- ZodTemplateLiteral: () => ZodTemplateLiteral,
5366
- ZodSymbol: () => ZodSymbol,
5367
- ZodSuccess: () => ZodSuccess,
5368
- ZodStringFormat: () => ZodStringFormat,
5369
- ZodString: () => ZodString,
5370
- ZodSet: () => ZodSet,
5371
- ZodRecord: () => ZodRecord,
5372
- ZodRealError: () => ZodRealError,
5373
- ZodReadonly: () => ZodReadonly,
5374
- ZodPromise: () => ZodPromise,
5375
- ZodPreprocess: () => ZodPreprocess,
5376
- ZodPrefault: () => ZodPrefault,
5377
- ZodPipe: () => ZodPipe,
5378
- ZodOptional: () => ZodOptional,
5379
- ZodObject: () => ZodObject,
5380
- ZodNumberFormat: () => ZodNumberFormat,
5381
- ZodNumber: () => ZodNumber,
5382
- ZodNullable: () => ZodNullable,
5383
- ZodNull: () => ZodNull,
5384
- ZodNonOptional: () => ZodNonOptional,
5385
- ZodNever: () => ZodNever,
5386
- ZodNanoID: () => ZodNanoID,
5387
- ZodNaN: () => ZodNaN,
5388
- ZodMap: () => ZodMap,
5389
- ZodMAC: () => ZodMAC,
5390
- ZodLiteral: () => ZodLiteral,
5391
- ZodLazy: () => ZodLazy,
5392
- ZodKSUID: () => ZodKSUID,
5393
- ZodJWT: () => ZodJWT,
5394
- ZodIssueCode: () => ZodIssueCode,
5395
- ZodIntersection: () => ZodIntersection,
5396
- ZodISOTime: () => ZodISOTime,
5397
- ZodISODuration: () => ZodISODuration,
5398
- ZodISODateTime: () => ZodISODateTime,
5399
- ZodISODate: () => ZodISODate,
5400
- ZodIPv6: () => ZodIPv6,
5401
- ZodIPv4: () => ZodIPv4,
5402
- ZodGUID: () => ZodGUID,
5403
- ZodFunction: () => ZodFunction,
5404
- ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
5405
- ZodFile: () => ZodFile,
5406
- ZodExactOptional: () => ZodExactOptional,
5407
- ZodError: () => ZodError,
5408
- ZodEnum: () => ZodEnum,
5409
- ZodEmoji: () => ZodEmoji,
5410
- ZodEmail: () => ZodEmail,
5411
- ZodE164: () => ZodE164,
5412
- ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
5413
- ZodDefault: () => ZodDefault,
5414
- ZodDate: () => ZodDate,
5415
- ZodCustomStringFormat: () => ZodCustomStringFormat,
5416
- ZodCustom: () => ZodCustom,
5417
- ZodCodec: () => ZodCodec,
5418
- ZodCatch: () => ZodCatch,
5419
- ZodCUID2: () => ZodCUID2,
5420
- ZodCUID: () => ZodCUID,
5421
- ZodCIDRv6: () => ZodCIDRv6,
5422
- ZodCIDRv4: () => ZodCIDRv4,
5423
- ZodBoolean: () => ZodBoolean,
5424
- ZodBigIntFormat: () => ZodBigIntFormat,
5425
- ZodBigInt: () => ZodBigInt,
5426
- ZodBase64URL: () => ZodBase64URL,
5427
- ZodBase64: () => ZodBase64,
5428
- ZodArray: () => ZodArray,
5429
- ZodAny: () => ZodAny,
5430
- TimePrecision: () => TimePrecision,
5431
- NEVER: () => NEVER,
5432
- $output: () => $output,
5433
- $input: () => $input,
5434
- $brand: () => $brand
5435
- });
4822
+ // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
4823
+ var exports_external = {};
4824
+ __export(exports_external, {
4825
+ xor: () => xor,
4826
+ xid: () => xid2,
4827
+ void: () => _void2,
4828
+ uuidv7: () => uuidv7,
4829
+ uuidv6: () => uuidv6,
4830
+ uuidv4: () => uuidv4,
4831
+ uuid: () => uuid2,
4832
+ util: () => exports_util,
4833
+ url: () => url,
4834
+ uppercase: () => _uppercase,
4835
+ unknown: () => unknown,
4836
+ union: () => union,
4837
+ undefined: () => _undefined3,
4838
+ ulid: () => ulid2,
4839
+ uint64: () => uint64,
4840
+ uint32: () => uint32,
4841
+ tuple: () => tuple,
4842
+ trim: () => _trim,
4843
+ treeifyError: () => treeifyError,
4844
+ transform: () => transform,
4845
+ toUpperCase: () => _toUpperCase,
4846
+ toLowerCase: () => _toLowerCase,
4847
+ toJSONSchema: () => toJSONSchema,
4848
+ templateLiteral: () => templateLiteral,
4849
+ symbol: () => symbol,
4850
+ superRefine: () => superRefine,
4851
+ success: () => success,
4852
+ stringbool: () => stringbool,
4853
+ stringFormat: () => stringFormat,
4854
+ string: () => string2,
4855
+ strictObject: () => strictObject,
4856
+ startsWith: () => _startsWith,
4857
+ slugify: () => _slugify,
4858
+ size: () => _size,
4859
+ setErrorMap: () => setErrorMap,
4860
+ set: () => set,
4861
+ safeParseAsync: () => safeParseAsync2,
4862
+ safeParse: () => safeParse2,
4863
+ safeEncodeAsync: () => safeEncodeAsync2,
4864
+ safeEncode: () => safeEncode2,
4865
+ safeDecodeAsync: () => safeDecodeAsync2,
4866
+ safeDecode: () => safeDecode2,
4867
+ registry: () => registry,
4868
+ regexes: () => exports_regexes,
4869
+ regex: () => _regex,
4870
+ refine: () => refine,
4871
+ record: () => record,
4872
+ readonly: () => readonly,
4873
+ property: () => _property,
4874
+ promise: () => promise,
4875
+ prettifyError: () => prettifyError,
4876
+ preprocess: () => preprocess,
4877
+ prefault: () => prefault,
4878
+ positive: () => _positive,
4879
+ pipe: () => pipe,
4880
+ partialRecord: () => partialRecord,
4881
+ parseAsync: () => parseAsync2,
4882
+ parse: () => parse3,
4883
+ overwrite: () => _overwrite,
4884
+ optional: () => optional,
4885
+ object: () => object,
4886
+ number: () => number2,
4887
+ nullish: () => nullish2,
4888
+ nullable: () => nullable,
4889
+ null: () => _null3,
4890
+ normalize: () => _normalize,
4891
+ nonpositive: () => _nonpositive,
4892
+ nonoptional: () => nonoptional,
4893
+ nonnegative: () => _nonnegative,
4894
+ never: () => never,
4895
+ negative: () => _negative,
4896
+ nativeEnum: () => nativeEnum,
4897
+ nanoid: () => nanoid2,
4898
+ nan: () => nan,
4899
+ multipleOf: () => _multipleOf,
4900
+ minSize: () => _minSize,
4901
+ minLength: () => _minLength,
4902
+ mime: () => _mime,
4903
+ meta: () => meta2,
4904
+ maxSize: () => _maxSize,
4905
+ maxLength: () => _maxLength,
4906
+ map: () => map,
4907
+ mac: () => mac2,
4908
+ lte: () => _lte,
4909
+ lt: () => _lt,
4910
+ lowercase: () => _lowercase,
4911
+ looseRecord: () => looseRecord,
4912
+ looseObject: () => looseObject,
4913
+ locales: () => exports_locales,
4914
+ literal: () => literal,
4915
+ length: () => _length,
4916
+ lazy: () => lazy,
4917
+ ksuid: () => ksuid2,
4918
+ keyof: () => keyof,
4919
+ jwt: () => jwt,
4920
+ json: () => json,
4921
+ iso: () => exports_iso,
4922
+ ipv6: () => ipv62,
4923
+ ipv4: () => ipv42,
4924
+ invertCodec: () => invertCodec,
4925
+ intersection: () => intersection,
4926
+ int64: () => int64,
4927
+ int32: () => int32,
4928
+ int: () => int,
4929
+ instanceof: () => _instanceof,
4930
+ includes: () => _includes,
4931
+ httpUrl: () => httpUrl,
4932
+ hostname: () => hostname2,
4933
+ hex: () => hex2,
4934
+ hash: () => hash,
4935
+ guid: () => guid2,
4936
+ gte: () => _gte,
4937
+ gt: () => _gt,
4938
+ globalRegistry: () => globalRegistry,
4939
+ getErrorMap: () => getErrorMap,
4940
+ function: () => _function,
4941
+ fromJSONSchema: () => fromJSONSchema,
4942
+ formatError: () => formatError,
4943
+ float64: () => float64,
4944
+ float32: () => float32,
4945
+ flattenError: () => flattenError,
4946
+ file: () => file,
4947
+ exactOptional: () => exactOptional,
4948
+ enum: () => _enum2,
4949
+ endsWith: () => _endsWith,
4950
+ encodeAsync: () => encodeAsync2,
4951
+ encode: () => encode2,
4952
+ emoji: () => emoji2,
4953
+ email: () => email2,
4954
+ e164: () => e1642,
4955
+ discriminatedUnion: () => discriminatedUnion,
4956
+ describe: () => describe2,
4957
+ decodeAsync: () => decodeAsync2,
4958
+ decode: () => decode2,
4959
+ date: () => date3,
4960
+ custom: () => custom,
4961
+ cuid2: () => cuid22,
4962
+ cuid: () => cuid3,
4963
+ core: () => exports_core2,
4964
+ config: () => config,
4965
+ coerce: () => exports_coerce,
4966
+ codec: () => codec,
4967
+ clone: () => clone2,
4968
+ cidrv6: () => cidrv62,
4969
+ cidrv4: () => cidrv42,
4970
+ check: () => check,
4971
+ catch: () => _catch2,
4972
+ boolean: () => boolean2,
4973
+ bigint: () => bigint2,
4974
+ base64url: () => base64url2,
4975
+ base64: () => base642,
4976
+ array: () => array,
4977
+ any: () => any,
4978
+ _function: () => _function,
4979
+ _default: () => _default2,
4980
+ _ZodString: () => _ZodString,
4981
+ ZodXor: () => ZodXor,
4982
+ ZodXID: () => ZodXID,
4983
+ ZodVoid: () => ZodVoid,
4984
+ ZodUnknown: () => ZodUnknown,
4985
+ ZodUnion: () => ZodUnion,
4986
+ ZodUndefined: () => ZodUndefined,
4987
+ ZodUUID: () => ZodUUID,
4988
+ ZodURL: () => ZodURL,
4989
+ ZodULID: () => ZodULID,
4990
+ ZodType: () => ZodType,
4991
+ ZodTuple: () => ZodTuple,
4992
+ ZodTransform: () => ZodTransform,
4993
+ ZodTemplateLiteral: () => ZodTemplateLiteral,
4994
+ ZodSymbol: () => ZodSymbol,
4995
+ ZodSuccess: () => ZodSuccess,
4996
+ ZodStringFormat: () => ZodStringFormat,
4997
+ ZodString: () => ZodString,
4998
+ ZodSet: () => ZodSet,
4999
+ ZodRecord: () => ZodRecord,
5000
+ ZodRealError: () => ZodRealError,
5001
+ ZodReadonly: () => ZodReadonly,
5002
+ ZodPromise: () => ZodPromise,
5003
+ ZodPreprocess: () => ZodPreprocess,
5004
+ ZodPrefault: () => ZodPrefault,
5005
+ ZodPipe: () => ZodPipe,
5006
+ ZodOptional: () => ZodOptional,
5007
+ ZodObject: () => ZodObject,
5008
+ ZodNumberFormat: () => ZodNumberFormat,
5009
+ ZodNumber: () => ZodNumber,
5010
+ ZodNullable: () => ZodNullable,
5011
+ ZodNull: () => ZodNull,
5012
+ ZodNonOptional: () => ZodNonOptional,
5013
+ ZodNever: () => ZodNever,
5014
+ ZodNanoID: () => ZodNanoID,
5015
+ ZodNaN: () => ZodNaN,
5016
+ ZodMap: () => ZodMap,
5017
+ ZodMAC: () => ZodMAC,
5018
+ ZodLiteral: () => ZodLiteral,
5019
+ ZodLazy: () => ZodLazy,
5020
+ ZodKSUID: () => ZodKSUID,
5021
+ ZodJWT: () => ZodJWT,
5022
+ ZodIssueCode: () => ZodIssueCode,
5023
+ ZodIntersection: () => ZodIntersection,
5024
+ ZodISOTime: () => ZodISOTime,
5025
+ ZodISODuration: () => ZodISODuration,
5026
+ ZodISODateTime: () => ZodISODateTime,
5027
+ ZodISODate: () => ZodISODate,
5028
+ ZodIPv6: () => ZodIPv6,
5029
+ ZodIPv4: () => ZodIPv4,
5030
+ ZodGUID: () => ZodGUID,
5031
+ ZodFunction: () => ZodFunction,
5032
+ ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
5033
+ ZodFile: () => ZodFile,
5034
+ ZodExactOptional: () => ZodExactOptional,
5035
+ ZodError: () => ZodError,
5036
+ ZodEnum: () => ZodEnum,
5037
+ ZodEmoji: () => ZodEmoji,
5038
+ ZodEmail: () => ZodEmail,
5039
+ ZodE164: () => ZodE164,
5040
+ ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
5041
+ ZodDefault: () => ZodDefault,
5042
+ ZodDate: () => ZodDate,
5043
+ ZodCustomStringFormat: () => ZodCustomStringFormat,
5044
+ ZodCustom: () => ZodCustom,
5045
+ ZodCodec: () => ZodCodec,
5046
+ ZodCatch: () => ZodCatch,
5047
+ ZodCUID2: () => ZodCUID2,
5048
+ ZodCUID: () => ZodCUID,
5049
+ ZodCIDRv6: () => ZodCIDRv6,
5050
+ ZodCIDRv4: () => ZodCIDRv4,
5051
+ ZodBoolean: () => ZodBoolean,
5052
+ ZodBigIntFormat: () => ZodBigIntFormat,
5053
+ ZodBigInt: () => ZodBigInt,
5054
+ ZodBase64URL: () => ZodBase64URL,
5055
+ ZodBase64: () => ZodBase64,
5056
+ ZodArray: () => ZodArray,
5057
+ ZodAny: () => ZodAny,
5058
+ TimePrecision: () => TimePrecision,
5059
+ NEVER: () => NEVER,
5060
+ $output: () => $output,
5061
+ $input: () => $input,
5062
+ $brand: () => $brand
5063
+ });
5436
5064
 
5437
5065
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/index.js
5438
5066
  var exports_core2 = {};
@@ -21713,6 +21341,7 @@ var OwnerDiagnosticReportSchema = exports_external.discriminatedUnion("status",
21713
21341
 
21714
21342
  // ../shared/src/community-cli-contract.ts
21715
21343
  var DM_SERVER = ".dm";
21344
+ var CONTROL_HEARTBEAT_CAPABILITY = "control-heartbeat-v1";
21716
21345
  function parseRef(ref) {
21717
21346
  if (!ref.startsWith("/"))
21718
21347
  throw new Error(`ref must start with "/": ${ref}`);
@@ -21724,137 +21353,516 @@ function parseRef(ref) {
21724
21353
  if (server !== DM_SERVER && !parseNameAndTag(server)) {
21725
21354
  throw new Error(`server ref must use a name#discriminator handle: ${ref}`);
21726
21355
  }
21727
- let seq;
21728
- if (parts.length >= 3 && parts[parts.length - 1].startsWith("#")) {
21729
- const tail = parseThreadTail(parts[parts.length - 1]);
21730
- return { server, channel: parts[1], ...tail };
21356
+ let seq;
21357
+ if (parts.length >= 3 && parts[parts.length - 1].startsWith("#")) {
21358
+ const tail = parseThreadTail(parts[parts.length - 1]);
21359
+ return { server, channel: parts[1], ...tail };
21360
+ }
21361
+ if (parts.length >= 3 && server !== DM_SERVER) {
21362
+ throw new Error(`ref has too many segments: ${ref}`);
21363
+ }
21364
+ const chSeg = parts[1];
21365
+ if (server === DM_SERVER) {
21366
+ const lastHash = chSeg.lastIndexOf("#");
21367
+ if (lastHash < 0)
21368
+ return { server, channel: chSeg };
21369
+ const firstHash = chSeg.indexOf("#");
21370
+ const tail = chSeg.slice(lastHash + 1);
21371
+ const isBareHandle = firstHash === lastHash && /^\d{4,}$/.test(tail);
21372
+ if (isBareHandle)
21373
+ return { server, channel: chSeg };
21374
+ const tailNum = Number(tail.startsWith("#") ? tail.slice(1) : tail);
21375
+ if (!Number.isFinite(tailNum))
21376
+ return { server, channel: chSeg };
21377
+ seq = parseSeq(tail);
21378
+ return { server, channel: chSeg.slice(0, lastHash), seq };
21379
+ }
21380
+ const hashIdx = chSeg.indexOf("#");
21381
+ if (hashIdx >= 0) {
21382
+ seq = parseSeq(chSeg.slice(hashIdx));
21383
+ return { server, channel: chSeg.slice(0, hashIdx), seq };
21384
+ }
21385
+ return { server, channel: chSeg };
21386
+ }
21387
+ function parseThreadTail(segment) {
21388
+ const stripped = segment.startsWith("#") ? segment.slice(1) : segment;
21389
+ const tokens = stripped.split("#");
21390
+ if (tokens.length < 1 || tokens.length > 2) {
21391
+ throw new Error(`bad thread ref tail: #${stripped}`);
21392
+ }
21393
+ for (const t of tokens) {
21394
+ if (!t)
21395
+ throw new Error(`bad thread ref tail: #${stripped} (empty seq)`);
21396
+ }
21397
+ const threadRootSeq = parseSeq(tokens[0]);
21398
+ if (tokens.length === 1)
21399
+ return { threadRootSeq };
21400
+ return { threadRootSeq, seq: parseSeq(tokens[1]) };
21401
+ }
21402
+ function formatRef(p) {
21403
+ if (p.seq !== undefined && p.threadRootSeq === undefined) {
21404
+ throw new Error("formatRef: seq without threadRootSeq is not supported");
21405
+ }
21406
+ const base = `/${p.server}/${p.channel}`;
21407
+ if (p.threadRootSeq === undefined)
21408
+ return base;
21409
+ if (p.seq === undefined)
21410
+ return `${base}/#${p.threadRootSeq}`;
21411
+ return `${base}/#${p.threadRootSeq}#${p.seq}`;
21412
+ }
21413
+ function parseSeq(s) {
21414
+ const n = Number(s.startsWith("#") ? s.slice(1) : s);
21415
+ if (!Number.isFinite(n))
21416
+ throw new Error(`bad seq: ${s}`);
21417
+ return n;
21418
+ }
21419
+ var HostCommandSchema = exports_external.discriminatedUnion("type", [
21420
+ exports_external.strictObject({
21421
+ type: exports_external.literal("machine:heartbeat"),
21422
+ nonce: exports_external.string().min(1).max(128)
21423
+ }),
21424
+ exports_external.object({
21425
+ type: exports_external.literal("agent:wake"),
21426
+ agentId: exports_external.string().min(1),
21427
+ config: exports_external.unknown(),
21428
+ sessionId: exports_external.string().optional(),
21429
+ launchId: exports_external.string().min(1),
21430
+ unreadNotice: exports_external.unknown()
21431
+ }),
21432
+ exports_external.object({
21433
+ type: exports_external.literal("agent:stop"),
21434
+ agentId: exports_external.string().min(1)
21435
+ }),
21436
+ exports_external.object({
21437
+ type: exports_external.literal("agent:reset"),
21438
+ agentId: exports_external.string().min(1),
21439
+ config: exports_external.unknown(),
21440
+ launchId: exports_external.string().min(1)
21441
+ }),
21442
+ exports_external.object({
21443
+ type: exports_external.literal("agent:nap"),
21444
+ agentId: exports_external.string().min(1),
21445
+ config: exports_external.unknown(),
21446
+ launchId: exports_external.string().min(1),
21447
+ handoff: exports_external.string().min(1)
21448
+ }),
21449
+ exports_external.object({
21450
+ type: exports_external.literal("agent:model_switch"),
21451
+ agentId: exports_external.string().min(1),
21452
+ config: exports_external.unknown(),
21453
+ launchId: exports_external.string().min(1)
21454
+ }),
21455
+ exports_external.object({
21456
+ type: exports_external.literal("machine:reset_all"),
21457
+ resets: exports_external.array(exports_external.object({
21458
+ agentId: exports_external.string().min(1),
21459
+ config: exports_external.unknown(),
21460
+ launchId: exports_external.string().min(1)
21461
+ }))
21462
+ }),
21463
+ exports_external.strictObject({
21464
+ type: exports_external.literal("machine:update")
21465
+ }),
21466
+ exports_external.object({
21467
+ type: exports_external.literal("bot:added"),
21468
+ botId: exports_external.string().min(1),
21469
+ name: exports_external.string().optional(),
21470
+ discriminator: exports_external.string().optional(),
21471
+ description: exports_external.string().optional(),
21472
+ ownerName: exports_external.string().optional(),
21473
+ ownerDiscriminator: exports_external.string().optional()
21474
+ }),
21475
+ exports_external.object({
21476
+ type: exports_external.literal("bot:updated"),
21477
+ botId: exports_external.string().min(1),
21478
+ name: exports_external.string().optional(),
21479
+ discriminator: exports_external.string().optional(),
21480
+ description: exports_external.string().optional(),
21481
+ ownerName: exports_external.string().optional(),
21482
+ ownerDiscriminator: exports_external.string().optional()
21483
+ }),
21484
+ exports_external.object({
21485
+ type: exports_external.literal("bot:removed"),
21486
+ botId: exports_external.string().min(1)
21487
+ }),
21488
+ DiagnosticCollectCommandSchema
21489
+ ]);
21490
+ // src/manager/agentRouter.ts
21491
+ var DEFINITIVE_EXECUTABLE_FAILURES = new Set(["ENOENT", "EACCES", "ENOEXEC", "EPERM"]);
21492
+
21493
+ class UnknownBotError extends Error {
21494
+ botId;
21495
+ constructor(botId) {
21496
+ super(`Bot not in this daemon's cache: ${botId}`);
21497
+ this.botId = botId;
21498
+ this.name = "UnknownBotError";
21499
+ }
21500
+ }
21501
+
21502
+ class BotEnrollFailedError extends Error {
21503
+ botId;
21504
+ constructor(botId, cause) {
21505
+ super(`Failed to enroll bot ${botId}: ${cause instanceof Error ? cause.message : String(cause)}`);
21506
+ this.botId = botId;
21507
+ this.name = "BotEnrollFailedError";
21508
+ }
21509
+ }
21510
+ function classifyErrorCode(err) {
21511
+ if (err instanceof UnknownBotError)
21512
+ return "bot_unknown";
21513
+ if (err instanceof BotEnrollFailedError)
21514
+ return "bot_enroll_failed";
21515
+ if (err instanceof UnknownRuntimeError)
21516
+ return "bot_runtime_missing";
21517
+ return "internal_error";
21518
+ }
21519
+
21520
+ class UnknownRuntimeError extends Error {
21521
+ requested;
21522
+ available;
21523
+ constructor(requested, available) {
21524
+ super(`Runtime not available on this host: ${requested ?? "<unspecified>"} — installed: ${available.join(", ") || "(none)"}`);
21525
+ this.requested = requested;
21526
+ this.available = available;
21527
+ this.name = "UnknownRuntimeError";
21528
+ }
21529
+ }
21530
+ function defaultFormatUnreadNoticeText(notice) {
21531
+ return `You have unread messages in channel ${notice.channel}.`;
21532
+ }
21533
+ var REWAKE_PROMPT = "Your session was reset by your owner. Prior conversation context is gone. " + "Read @memory.md and your .context_timeline for durable context, then pull your inbox " + "before doing anything else. If it reports marked messages, run `$ALOOK_CLI message mark list` " + "and resume that outstanding work.";
21534
+ var MODEL_SWITCH_REWAKE_PROMPT = "You were just switched to a different model. Continue any unfinished work.";
21535
+ function buildNapRewakePrompt(handoff) {
21536
+ return "You took a nap: you reset your own session, so prior conversation context " + `is gone. Before sleeping you left yourself this handoff —
21537
+
21538
+ ` + handoff + `
21539
+
21540
+ Then read @memory.md and your .context_timeline for durable context, and pull ` + "your inbox before doing anything else. If it reports marked messages, run `$ALOOK_CLI " + "message mark list` and resume that outstanding work.";
21541
+ }
21542
+
21543
+ class AgentRouter {
21544
+ opts;
21545
+ running = new Set;
21546
+ runtimes = new Map;
21547
+ pendingResend = false;
21548
+ scheduleResend;
21549
+ log;
21550
+ constructor(opts) {
21551
+ this.opts = opts;
21552
+ this.log = opts.logger ?? createLogger({ header: "@alook/daemon:router" });
21553
+ this.scheduleResend = opts.scheduleReadyResend ?? queueMicrotask.bind(globalThis);
21554
+ for (const r of opts.runtimeReport) {
21555
+ this.runtimes.set(r.id, {
21556
+ id: r.id,
21557
+ version: r.version,
21558
+ status: r.status ?? "healthy",
21559
+ lastError: r.lastError,
21560
+ lastErrorAt: r.lastErrorAt
21561
+ });
21562
+ }
21563
+ }
21564
+ async start() {
21565
+ this.opts.channel.onCommand((cmd) => this.onCommand(cmd));
21566
+ this.opts.channel.onResync?.(() => ({
21567
+ ready: this.buildReady(),
21568
+ sessions: this.opts.manager.liveSessionReports(),
21569
+ activities: this.opts.manager.liveAgentActivities()
21570
+ }));
21571
+ await this.opts.channel.reportReady(this.buildReady());
21572
+ }
21573
+ buildReady() {
21574
+ return {
21575
+ runtimeReport: [...this.runtimes.values()],
21576
+ capabilities: [CONTROL_HEARTBEAT_CAPABILITY],
21577
+ runningAgents: [...this.running],
21578
+ hostname: this.opts.hostname,
21579
+ platform: this.opts.platform,
21580
+ arch: this.opts.arch,
21581
+ osRelease: this.opts.osRelease,
21582
+ daemonVersion: this.opts.daemonVersion
21583
+ };
21584
+ }
21585
+ healthyRuntimeIds() {
21586
+ const out = [];
21587
+ for (const r of this.runtimes.values()) {
21588
+ if (r.status === "healthy")
21589
+ out.push(r.id);
21590
+ }
21591
+ return out;
21592
+ }
21593
+ isRuntimeHealthy(id) {
21594
+ return this.runtimes.get(id)?.status === "healthy";
21731
21595
  }
21732
- if (parts.length >= 3 && server !== DM_SERVER) {
21733
- throw new Error(`ref has too many segments: ${ref}`);
21596
+ recordRuntimeSpawnFailure(id, reason) {
21597
+ if (!DEFINITIVE_EXECUTABLE_FAILURES.has(reason))
21598
+ return;
21599
+ this.markRuntimeUnhealthy(id, reason);
21734
21600
  }
21735
- const chSeg = parts[1];
21736
- if (server === DM_SERVER) {
21737
- const lastHash = chSeg.lastIndexOf("#");
21738
- if (lastHash < 0)
21739
- return { server, channel: chSeg };
21740
- const firstHash = chSeg.indexOf("#");
21741
- const tail = chSeg.slice(lastHash + 1);
21742
- const isBareHandle = firstHash === lastHash && /^\d{4,}$/.test(tail);
21743
- if (isBareHandle)
21744
- return { server, channel: chSeg };
21745
- const tailNum = Number(tail.startsWith("#") ? tail.slice(1) : tail);
21746
- if (!Number.isFinite(tailNum))
21747
- return { server, channel: chSeg };
21748
- seq = parseSeq(tail);
21749
- return { server, channel: chSeg.slice(0, lastHash), seq };
21601
+ markRuntimeUnhealthy(id, reason) {
21602
+ const existing = this.runtimes.get(id);
21603
+ if (!existing)
21604
+ return;
21605
+ const nowIso = new Date().toISOString();
21606
+ if (existing.status === "unhealthy" && existing.lastError === reason)
21607
+ return;
21608
+ this.runtimes.set(id, {
21609
+ ...existing,
21610
+ status: "unhealthy",
21611
+ lastError: reason,
21612
+ lastErrorAt: nowIso
21613
+ });
21614
+ this.log.warn("runtime marked unhealthy", { runtimeId: id, reason });
21615
+ this.scheduleReadyFrameResend();
21750
21616
  }
21751
- const hashIdx = chSeg.indexOf("#");
21752
- if (hashIdx >= 0) {
21753
- seq = parseSeq(chSeg.slice(hashIdx));
21754
- return { server, channel: chSeg.slice(0, hashIdx), seq };
21617
+ markRuntimeHealthy(id) {
21618
+ const existing = this.runtimes.get(id);
21619
+ if (!existing)
21620
+ return;
21621
+ if (existing.status === "healthy" && !existing.lastError && !existing.lastErrorAt)
21622
+ return;
21623
+ this.runtimes.set(id, {
21624
+ id: existing.id,
21625
+ version: existing.version,
21626
+ status: "healthy"
21627
+ });
21628
+ this.log.info("runtime marked healthy again", { runtimeId: id });
21629
+ this.scheduleReadyFrameResend();
21755
21630
  }
21756
- return { server, channel: chSeg };
21757
- }
21758
- function parseThreadTail(segment) {
21759
- const stripped = segment.startsWith("#") ? segment.slice(1) : segment;
21760
- const tokens = stripped.split("#");
21761
- if (tokens.length < 1 || tokens.length > 2) {
21762
- throw new Error(`bad thread ref tail: #${stripped}`);
21631
+ markLocallyStopped(agentId) {
21632
+ if (!this.running.delete(agentId))
21633
+ return;
21634
+ this.log.info("agent removed from running set (local stop)", { agentId });
21635
+ this.scheduleReadyFrameResend();
21763
21636
  }
21764
- for (const t of tokens) {
21765
- if (!t)
21766
- throw new Error(`bad thread ref tail: #${stripped} (empty seq)`);
21637
+ scheduleReadyFrameResend() {
21638
+ if (this.pendingResend)
21639
+ return;
21640
+ this.pendingResend = true;
21641
+ this.scheduleResend(() => {
21642
+ this.pendingResend = false;
21643
+ try {
21644
+ this.opts.channel.sendReady?.(this.buildReady());
21645
+ } catch {}
21646
+ });
21767
21647
  }
21768
- const threadRootSeq = parseSeq(tokens[0]);
21769
- if (tokens.length === 1)
21770
- return { threadRootSeq };
21771
- return { threadRootSeq, seq: parseSeq(tokens[1]) };
21772
- }
21773
- function formatRef(p) {
21774
- if (p.seq !== undefined && p.threadRootSeq === undefined) {
21775
- throw new Error("formatRef: seq without threadRootSeq is not supported");
21648
+ async runRestartCommand(agentId, launchId, opName, run) {
21649
+ this.log.info(`${opName} received`, { agentId, launchId });
21650
+ try {
21651
+ await this.opts.onBeforeAgent?.(agentId);
21652
+ await run();
21653
+ this.running.add(agentId);
21654
+ this.scheduleReadyFrameResend();
21655
+ this.log.info(`${opName} ok`, { agentId });
21656
+ } catch (err) {
21657
+ if (err instanceof UnknownRuntimeError) {
21658
+ const frame = {
21659
+ type: "session.error",
21660
+ code: "runtime_not_available",
21661
+ agentId,
21662
+ launchId,
21663
+ payload: { requested: err.requested ?? null, available: err.available }
21664
+ };
21665
+ await this.opts.channel.reportSessionError?.(frame);
21666
+ this.log.info(`${opName} error`, { agentId, "error.code": "runtime_not_available" });
21667
+ return;
21668
+ }
21669
+ const code = classifyErrorCode(err);
21670
+ await this.opts.channel.reportWakeAck?.({
21671
+ agentId,
21672
+ launchId,
21673
+ status: "error",
21674
+ error: { code, message: err instanceof Error ? err.message : String(err) }
21675
+ });
21676
+ this.log.warn(`${opName} failed`, {
21677
+ agentId,
21678
+ "error.code": code,
21679
+ err: err instanceof Error ? err.message : String(err)
21680
+ });
21681
+ }
21682
+ }
21683
+ async onCommand(cmd) {
21684
+ switch (cmd.type) {
21685
+ case "agent:wake":
21686
+ this.log.info("agent:wake received", {
21687
+ agentId: cmd.agentId,
21688
+ channel: cmd.unreadNotice.channel,
21689
+ latestSeq: cmd.unreadNotice.latestSeq
21690
+ });
21691
+ try {
21692
+ const beforeStatus = this.opts.manager.snapshot?.().agents?.[cmd.agentId]?.status ?? "unregistered";
21693
+ const wasActive = this.opts.typingTracker?.hasAny(cmd.agentId) ?? false;
21694
+ await this.opts.onBeforeAgent?.(cmd.agentId);
21695
+ this.opts.manager.register(cmd.agentId, {
21696
+ runtimeConfig: cmd.config,
21697
+ sessionId: cmd.sessionId,
21698
+ launchId: cmd.launchId
21699
+ });
21700
+ this.running.add(cmd.agentId);
21701
+ const channelScope = cmd.unreadNotice.channelId;
21702
+ if (channelScope)
21703
+ this.opts.typingTracker?.add(cmd.agentId, channelScope);
21704
+ const text2 = (this.opts.formatUnreadNoticeText ?? defaultFormatUnreadNoticeText)(cmd.unreadNotice);
21705
+ const producedEffect = this.opts.manager.deliver(cmd.agentId, {
21706
+ seq: cmd.unreadNotice.latestSeq,
21707
+ text: text2
21708
+ });
21709
+ if (producedEffect === false && beforeStatus === "running") {
21710
+ this.log.info("agent:wake produced no effect (coalesced onto running agent)", {
21711
+ agentId: cmd.agentId,
21712
+ latestSeq: cmd.unreadNotice.latestSeq
21713
+ });
21714
+ }
21715
+ if (channelScope && wasActive && beforeStatus === "running") {
21716
+ this.opts.channel.reportAgentTyping?.({
21717
+ agentId: cmd.agentId,
21718
+ channelId: channelScope
21719
+ });
21720
+ }
21721
+ await this.opts.channel.reportWakeAck?.({
21722
+ agentId: cmd.agentId,
21723
+ launchId: cmd.launchId,
21724
+ status: "ok"
21725
+ });
21726
+ this.log.info("agent:wake ack", { agentId: cmd.agentId, status: "ok" });
21727
+ } catch (err) {
21728
+ if (err instanceof UnknownRuntimeError) {
21729
+ const frame = {
21730
+ type: "session.error",
21731
+ code: "runtime_not_available",
21732
+ agentId: cmd.agentId,
21733
+ launchId: cmd.launchId,
21734
+ payload: {
21735
+ requested: err.requested ?? null,
21736
+ available: err.available
21737
+ }
21738
+ };
21739
+ await this.opts.channel.reportSessionError?.(frame);
21740
+ await this.opts.channel.reportWakeAck?.({
21741
+ agentId: cmd.agentId,
21742
+ launchId: cmd.launchId,
21743
+ status: "error",
21744
+ error: {
21745
+ code: "bot_runtime_missing",
21746
+ message: err.message
21747
+ }
21748
+ });
21749
+ this.log.info("agent:wake ack", {
21750
+ agentId: cmd.agentId,
21751
+ status: "error",
21752
+ "error.code": "bot_runtime_missing"
21753
+ });
21754
+ return;
21755
+ }
21756
+ {
21757
+ const code = classifyErrorCode(err);
21758
+ await this.opts.channel.reportWakeAck?.({
21759
+ agentId: cmd.agentId,
21760
+ launchId: cmd.launchId,
21761
+ status: "error",
21762
+ error: {
21763
+ code,
21764
+ message: err instanceof Error ? err.message : String(err)
21765
+ }
21766
+ });
21767
+ this.log.info("agent:wake ack", { agentId: cmd.agentId, status: "error", "error.code": code });
21768
+ }
21769
+ return;
21770
+ }
21771
+ break;
21772
+ case "agent:reset":
21773
+ await this.runRestartCommand(cmd.agentId, cmd.launchId, "agent:reset", () => this.opts.manager.resetSession(cmd.agentId, {
21774
+ runtimeConfig: cmd.config,
21775
+ launchId: cmd.launchId,
21776
+ rewakePrompt: REWAKE_PROMPT
21777
+ }));
21778
+ break;
21779
+ case "machine:reset_all":
21780
+ for (const r of cmd.resets) {
21781
+ await this.runRestartCommand(r.agentId, r.launchId, "agent:reset", () => this.opts.manager.resetSession(r.agentId, {
21782
+ runtimeConfig: r.config,
21783
+ launchId: r.launchId,
21784
+ rewakePrompt: REWAKE_PROMPT
21785
+ }));
21786
+ }
21787
+ break;
21788
+ case "agent:nap":
21789
+ await this.runRestartCommand(cmd.agentId, cmd.launchId, "agent:nap", () => this.opts.manager.resetSession(cmd.agentId, {
21790
+ runtimeConfig: cmd.config,
21791
+ launchId: cmd.launchId,
21792
+ rewakePrompt: buildNapRewakePrompt(cmd.handoff),
21793
+ barrierType: "nap"
21794
+ }));
21795
+ break;
21796
+ case "agent:model_switch":
21797
+ await this.runRestartCommand(cmd.agentId, cmd.launchId, "agent:model_switch", () => this.opts.manager.switchModel(cmd.agentId, {
21798
+ runtimeConfig: cmd.config,
21799
+ launchId: cmd.launchId,
21800
+ rewakePrompt: MODEL_SWITCH_REWAKE_PROMPT
21801
+ }));
21802
+ break;
21803
+ case "agent:stop":
21804
+ this.log.info("agent:stop received", { agentId: cmd.agentId });
21805
+ try {
21806
+ this.running.delete(cmd.agentId);
21807
+ this.opts.manager.stop(cmd.agentId);
21808
+ await this.opts.channel.reportStoppedAck?.({
21809
+ agentId: cmd.agentId,
21810
+ status: "ok"
21811
+ });
21812
+ this.log.info("agent:stop ack", { agentId: cmd.agentId, status: "ok" });
21813
+ } catch (err) {
21814
+ const code = classifyErrorCode(err);
21815
+ await this.opts.channel.reportStoppedAck?.({
21816
+ agentId: cmd.agentId,
21817
+ status: "error",
21818
+ error: {
21819
+ code,
21820
+ message: err instanceof Error ? err.message : String(err)
21821
+ }
21822
+ });
21823
+ this.log.info("agent:stop ack", { agentId: cmd.agentId, status: "error", "error.code": code });
21824
+ }
21825
+ break;
21826
+ case "bot:added":
21827
+ case "bot:updated":
21828
+ case "bot:removed":
21829
+ break;
21830
+ }
21776
21831
  }
21777
- const base = `/${p.server}/${p.channel}`;
21778
- if (p.threadRootSeq === undefined)
21779
- return base;
21780
- if (p.seq === undefined)
21781
- return `${base}/#${p.threadRootSeq}`;
21782
- return `${base}/#${p.threadRootSeq}#${p.seq}`;
21783
21832
  }
21784
- function parseSeq(s) {
21785
- const n = Number(s.startsWith("#") ? s.slice(1) : s);
21786
- if (!Number.isFinite(n))
21787
- throw new Error(`bad seq: ${s}`);
21788
- return n;
21833
+ // src/manager/typingScopeTracker.ts
21834
+ function createTypingScopeTracker() {
21835
+ const scopes = new Map;
21836
+ return {
21837
+ add(agentId, channelId) {
21838
+ let set2 = scopes.get(agentId);
21839
+ if (!set2) {
21840
+ set2 = new Set;
21841
+ scopes.set(agentId, set2);
21842
+ }
21843
+ set2.add(channelId);
21844
+ },
21845
+ snapshot(agentId) {
21846
+ const set2 = scopes.get(agentId);
21847
+ return set2 ? [...set2] : [];
21848
+ },
21849
+ hasAny(agentId) {
21850
+ const set2 = scopes.get(agentId);
21851
+ return !!set2 && set2.size > 0;
21852
+ },
21853
+ clear(agentId) {
21854
+ scopes.delete(agentId);
21855
+ }
21856
+ };
21789
21857
  }
21790
- var HostCommandSchema = exports_external.discriminatedUnion("type", [
21791
- exports_external.object({
21792
- type: exports_external.literal("agent:wake"),
21793
- agentId: exports_external.string().min(1),
21794
- config: exports_external.unknown(),
21795
- sessionId: exports_external.string().optional(),
21796
- launchId: exports_external.string().min(1),
21797
- unreadNotice: exports_external.unknown()
21798
- }),
21799
- exports_external.object({
21800
- type: exports_external.literal("agent:stop"),
21801
- agentId: exports_external.string().min(1)
21802
- }),
21803
- exports_external.object({
21804
- type: exports_external.literal("agent:reset"),
21805
- agentId: exports_external.string().min(1),
21806
- config: exports_external.unknown(),
21807
- launchId: exports_external.string().min(1)
21808
- }),
21809
- exports_external.object({
21810
- type: exports_external.literal("agent:nap"),
21811
- agentId: exports_external.string().min(1),
21812
- config: exports_external.unknown(),
21813
- launchId: exports_external.string().min(1),
21814
- handoff: exports_external.string().min(1)
21815
- }),
21816
- exports_external.object({
21817
- type: exports_external.literal("agent:model_switch"),
21818
- agentId: exports_external.string().min(1),
21819
- config: exports_external.unknown(),
21820
- launchId: exports_external.string().min(1)
21821
- }),
21822
- exports_external.object({
21823
- type: exports_external.literal("machine:reset_all"),
21824
- resets: exports_external.array(exports_external.object({
21825
- agentId: exports_external.string().min(1),
21826
- config: exports_external.unknown(),
21827
- launchId: exports_external.string().min(1)
21828
- }))
21829
- }),
21830
- exports_external.strictObject({
21831
- type: exports_external.literal("machine:update")
21832
- }),
21833
- exports_external.object({
21834
- type: exports_external.literal("bot:added"),
21835
- botId: exports_external.string().min(1),
21836
- name: exports_external.string().optional(),
21837
- discriminator: exports_external.string().optional(),
21838
- description: exports_external.string().optional(),
21839
- ownerName: exports_external.string().optional(),
21840
- ownerDiscriminator: exports_external.string().optional()
21841
- }),
21842
- exports_external.object({
21843
- type: exports_external.literal("bot:updated"),
21844
- botId: exports_external.string().min(1),
21845
- name: exports_external.string().optional(),
21846
- discriminator: exports_external.string().optional(),
21847
- description: exports_external.string().optional(),
21848
- ownerName: exports_external.string().optional(),
21849
- ownerDiscriminator: exports_external.string().optional()
21850
- }),
21851
- exports_external.object({
21852
- type: exports_external.literal("bot:removed"),
21853
- botId: exports_external.string().min(1)
21854
- }),
21855
- DiagnosticCollectCommandSchema
21856
- ]);
21857
21858
  // src/credentials/credentialProxy.ts
21859
+ import * as crypto2 from "crypto";
21860
+ import * as fs5 from "fs";
21861
+ import * as http from "http";
21862
+ import * as https from "https";
21863
+ import * as os2 from "os";
21864
+ import * as path7 from "path";
21865
+ import { URL as URL2 } from "url";
21858
21866
  var LOCAL_MESSAGE_REMINDER_PATH = "/__alook/local/message-reminder";
21859
21867
  var LOCAL_MESSAGE_REMINDER_BODY_MAX_BYTES = 4 * 1024;
21860
21868
  var LOCAL_MESSAGE_REMINDER_MIN_MS = 60000;
@@ -21980,6 +21988,14 @@ function writeJson(res, status, body) {
21980
21988
  res.writeHead(status, { "content-type": "application/json" });
21981
21989
  res.end(JSON.stringify(body));
21982
21990
  }
21991
+ function normalizeContentEncoding(value) {
21992
+ const normalized = (Array.isArray(value) ? value.join(",") : value ?? "").trim().toLowerCase();
21993
+ if (!normalized || normalized === "identity")
21994
+ return "identity";
21995
+ if (normalized === "gzip" || normalized === "deflate" || normalized === "br")
21996
+ return normalized;
21997
+ return "other";
21998
+ }
21983
21999
  function isCanonicalChannelScope(channel2) {
21984
22000
  try {
21985
22001
  const parsed = parseRef(channel2);
@@ -22076,6 +22092,11 @@ async function startCredentialProxy(broker, options = {}) {
22076
22092
  const upstreamClient = upstream.protocol === "https:" ? https : http;
22077
22093
  const onPull = options.onInboxPullResponse;
22078
22094
  const onProxyRequest = options.onProxyRequest;
22095
+ const reportInboxPullObservationError = (failure) => {
22096
+ try {
22097
+ options.onInboxPullObservationError?.(failure);
22098
+ } catch {}
22099
+ };
22079
22100
  const server = http.createServer((req, res) => {
22080
22101
  const pathname = new URL2(req.url ?? "/", "http://placeholder").pathname;
22081
22102
  if (pathname.startsWith("/__alook/local/")) {
@@ -22114,7 +22135,20 @@ async function startCredentialProxy(broker, options = {}) {
22114
22135
  return;
22115
22136
  }
22116
22137
  const reg = verdict.reg;
22117
- const isInboxPull = onPull && pathname === "/api/community/users/me/inbox/pull";
22138
+ const isInboxPull = req.method === "POST" && pathname === "/api/community/users/me/inbox/pull";
22139
+ const shouldObserveInboxPull = Boolean(isInboxPull && onPull);
22140
+ let inboxPullObservationToken;
22141
+ if (shouldObserveInboxPull) {
22142
+ try {
22143
+ inboxPullObservationToken = options.onInboxPullStart?.(reg.agentId);
22144
+ } catch {
22145
+ reportInboxPullObservationError({
22146
+ agentId: reg.agentId,
22147
+ reason: "observer_failed",
22148
+ contentEncoding: "identity"
22149
+ });
22150
+ }
22151
+ }
22118
22152
  if (onProxyRequest) {
22119
22153
  try {
22120
22154
  onProxyRequest(reg.agentId, req.method ?? "GET", pathname);
@@ -22127,6 +22161,8 @@ async function startCredentialProxy(broker, options = {}) {
22127
22161
  outHeaders[broker.headerNames.agentId.toLowerCase()] = reg.agentId;
22128
22162
  outHeaders[broker.headerNames.client.toLowerCase()] = broker.clientLabel;
22129
22163
  outHeaders[broker.headerNames.capabilities.toLowerCase()] = [...reg.capabilities].join(",");
22164
+ if (isInboxPull)
22165
+ outHeaders["accept-encoding"] = "identity";
22130
22166
  let responded = false;
22131
22167
  let upstreamRes;
22132
22168
  const upstreamReq = upstreamClient.request({
@@ -22145,18 +22181,54 @@ async function startCredentialProxy(broker, options = {}) {
22145
22181
  };
22146
22182
  res_.on("error", destroyResIfIncomplete);
22147
22183
  res_.on("close", destroyResIfIncomplete);
22148
- if (isInboxPull && res_.statusCode && res_.statusCode < 300) {
22184
+ if (shouldObserveInboxPull && res_.statusCode !== undefined && res_.statusCode >= 200 && res_.statusCode < 300) {
22149
22185
  const chunks = [];
22150
22186
  res_.on("data", (chunk) => chunks.push(chunk));
22151
22187
  res_.on("end", () => {
22152
22188
  const body = Buffer.concat(chunks);
22189
+ const contentEncoding = normalizeContentEncoding(res_.headers["content-encoding"]);
22190
+ if (contentEncoding !== "identity") {
22191
+ reportInboxPullObservationError({
22192
+ agentId: reg.agentId,
22193
+ reason: "unexpected_content_encoding",
22194
+ contentEncoding
22195
+ });
22196
+ } else {
22197
+ let parsed;
22198
+ try {
22199
+ parsed = JSON.parse(body.toString("utf8"));
22200
+ } catch {
22201
+ reportInboxPullObservationError({
22202
+ agentId: reg.agentId,
22203
+ reason: "invalid_json",
22204
+ contentEncoding
22205
+ });
22206
+ }
22207
+ if (parsed !== undefined) {
22208
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || !Array.isArray(parsed.messages)) {
22209
+ reportInboxPullObservationError({
22210
+ agentId: reg.agentId,
22211
+ reason: "invalid_inbox_shape",
22212
+ contentEncoding
22213
+ });
22214
+ } else {
22215
+ const messages = parsed.messages;
22216
+ if (messages.length > 0) {
22217
+ try {
22218
+ onPull(reg.agentId, messages, inboxPullObservationToken);
22219
+ } catch {
22220
+ reportInboxPullObservationError({
22221
+ agentId: reg.agentId,
22222
+ reason: "observer_failed",
22223
+ contentEncoding
22224
+ });
22225
+ }
22226
+ }
22227
+ }
22228
+ }
22229
+ }
22153
22230
  res.writeHead(res_.statusCode, res_.headers);
22154
22231
  res.end(body);
22155
- try {
22156
- const parsed = JSON.parse(body.toString());
22157
- if (parsed.messages)
22158
- onPull(reg.agentId, parsed.messages);
22159
- } catch {}
22160
22232
  });
22161
22233
  } else {
22162
22234
  res.writeHead(res_.statusCode ?? 502, res_.headers);
@@ -22481,6 +22553,236 @@ function writeStatusFile(path8, snapshot) {
22481
22553
  } catch {}
22482
22554
  }
22483
22555
 
22556
+ // src/manager/wakeCoordinator.ts
22557
+ class WakeCoordinator {
22558
+ agents = new Map;
22559
+ async run(command, dispatch, onDesiredAdvance) {
22560
+ const state = this.agent(command.agentId);
22561
+ const channel2 = command.unreadNotice.channel;
22562
+ const seq = command.unreadNotice.latestSeq;
22563
+ const scope = state.scopes.get(channel2);
22564
+ const coveredSeq = Math.max(scope?.modelSeenSeq ?? 0, scope?.admittedSeq ?? 0);
22565
+ if (seq <= coveredSeq) {
22566
+ this.rememberReplacement(state, command);
22567
+ return { state: "suppressed", coveredSeq };
22568
+ }
22569
+ const desiredAdvanced = !scope || seq > scope.desiredSeq;
22570
+ if (!scope || seq >= scope.desiredSeq) {
22571
+ state.scopes.set(channel2, {
22572
+ desiredSeq: Math.max(scope?.desiredSeq ?? 0, seq),
22573
+ admittedSeq: scope?.admittedSeq ?? 0,
22574
+ modelSeenSeq: scope?.modelSeenSeq ?? 0,
22575
+ command
22576
+ });
22577
+ }
22578
+ if (desiredAdvanced)
22579
+ onDesiredAdvance?.(command);
22580
+ state.dispatch = dispatch;
22581
+ if (state.active || state.admitting) {
22582
+ this.rememberReplacement(state, command);
22583
+ return { state: "suppressed", coveredSeq: seq };
22584
+ }
22585
+ await this.admit(command.agentId, command);
22586
+ return { state: "accepted" };
22587
+ }
22588
+ modelSeenGeneration(agentId) {
22589
+ return this.agent(agentId).observationGeneration;
22590
+ }
22591
+ recordModelSeen(agentId, messages, generation) {
22592
+ const state = this.agent(agentId);
22593
+ if (generation !== state.observationGeneration)
22594
+ return false;
22595
+ for (const message2 of messages) {
22596
+ const seq = Number(message2.seq.startsWith("#") ? message2.seq.slice(1) : message2.seq);
22597
+ if (!Number.isSafeInteger(seq) || seq <= 0)
22598
+ continue;
22599
+ const scope = state.scopes.get(message2.channel);
22600
+ if (scope) {
22601
+ scope.modelSeenSeq = Math.max(scope.modelSeenSeq, seq);
22602
+ } else {
22603
+ state.scopes.set(message2.channel, {
22604
+ desiredSeq: 0,
22605
+ admittedSeq: 0,
22606
+ modelSeenSeq: seq,
22607
+ command: null
22608
+ });
22609
+ }
22610
+ }
22611
+ return true;
22612
+ }
22613
+ recordAgentActivity(agentId, activity) {
22614
+ const state = this.agent(agentId);
22615
+ if (activity === "starting" || activity === "running") {
22616
+ state.active = true;
22617
+ return;
22618
+ }
22619
+ if (activity !== "idle")
22620
+ return;
22621
+ state.active = false;
22622
+ state.activeAdmission = null;
22623
+ const next = this.nextPending(state);
22624
+ if (next && state.dispatch)
22625
+ this.admit(agentId, next);
22626
+ }
22627
+ recordDeliveryAck(agentId, launchId, status) {
22628
+ const state = this.agents.get(agentId);
22629
+ const admission = state?.activeAdmission;
22630
+ if (!state || !admission || admission.launchId !== launchId)
22631
+ return;
22632
+ if (status === "ok")
22633
+ return;
22634
+ for (const [channel2, coverage] of admission.coverage) {
22635
+ const scope = state.scopes.get(channel2);
22636
+ if (scope?.admittedSeq === coverage.admittedSeq) {
22637
+ scope.admittedSeq = coverage.previousSeq;
22638
+ }
22639
+ }
22640
+ state.active = false;
22641
+ state.failedAdmissionLaunchId = launchId;
22642
+ state.retryAfterFailure = state.coalescedReplacement;
22643
+ state.coalescedReplacement = null;
22644
+ state.activeAdmission = null;
22645
+ if (!state.admitting && state.retryAfterFailure) {
22646
+ this.retryFailedAdmission(agentId, state).catch(() => {});
22647
+ }
22648
+ }
22649
+ invalidate(agentId, clearModelSeen) {
22650
+ const state = this.agent(agentId);
22651
+ state.observationGeneration += 1;
22652
+ state.active = false;
22653
+ state.admitting = false;
22654
+ state.activeAdmission = null;
22655
+ state.failedAdmissionLaunchId = null;
22656
+ state.coalescedReplacement = null;
22657
+ state.retryAfterFailure = null;
22658
+ if (clearModelSeen) {
22659
+ state.scopes.clear();
22660
+ return;
22661
+ }
22662
+ for (const scope of state.scopes.values()) {
22663
+ scope.desiredSeq = scope.modelSeenSeq;
22664
+ scope.admittedSeq = scope.modelSeenSeq;
22665
+ }
22666
+ }
22667
+ agent(agentId) {
22668
+ let state = this.agents.get(agentId);
22669
+ if (!state) {
22670
+ state = {
22671
+ active: false,
22672
+ admitting: false,
22673
+ activeAdmission: null,
22674
+ failedAdmissionLaunchId: null,
22675
+ coalescedReplacement: null,
22676
+ retryAfterFailure: null,
22677
+ observationGeneration: 0,
22678
+ scopes: new Map,
22679
+ dispatch: null
22680
+ };
22681
+ this.agents.set(agentId, state);
22682
+ }
22683
+ return state;
22684
+ }
22685
+ nextPending(state) {
22686
+ let next = null;
22687
+ for (const scope of state.scopes.values()) {
22688
+ if (scope.desiredSeq <= Math.max(scope.modelSeenSeq, scope.admittedSeq))
22689
+ continue;
22690
+ if (!scope.command)
22691
+ continue;
22692
+ if (scope.command.launchId === state.failedAdmissionLaunchId)
22693
+ continue;
22694
+ if (!next || scope.desiredSeq > next.desiredSeq)
22695
+ next = scope;
22696
+ }
22697
+ return next?.command ?? null;
22698
+ }
22699
+ async admit(agentId, preferred) {
22700
+ const state = this.agent(agentId);
22701
+ if (state.active || state.admitting || !state.dispatch)
22702
+ return;
22703
+ const admissionGeneration = state.observationGeneration;
22704
+ const command = preferred ?? this.nextPending(state);
22705
+ if (!command)
22706
+ return;
22707
+ const commandScope = state.scopes.get(command.unreadNotice.channel);
22708
+ if (commandScope && command.unreadNotice.latestSeq >= commandScope.desiredSeq) {
22709
+ commandScope.command = command;
22710
+ }
22711
+ const coverage = new Map;
22712
+ for (const [channel2, scope] of state.scopes) {
22713
+ if (scope.desiredSeq <= Math.max(scope.modelSeenSeq, scope.admittedSeq))
22714
+ continue;
22715
+ coverage.set(channel2, {
22716
+ previousSeq: scope.admittedSeq,
22717
+ admittedSeq: scope.desiredSeq
22718
+ });
22719
+ scope.admittedSeq = scope.desiredSeq;
22720
+ scope.command = command;
22721
+ }
22722
+ if (coverage.size === 0)
22723
+ return;
22724
+ state.admitting = true;
22725
+ state.active = true;
22726
+ state.failedAdmissionLaunchId = null;
22727
+ state.coalescedReplacement = null;
22728
+ state.activeAdmission = { launchId: command.launchId, coverage };
22729
+ let dispatchError;
22730
+ try {
22731
+ await state.dispatch(command);
22732
+ } catch (error51) {
22733
+ if (state.observationGeneration === admissionGeneration) {
22734
+ for (const [channel2, admitted] of coverage) {
22735
+ const scope = state.scopes.get(channel2);
22736
+ if (scope?.admittedSeq === admitted.admittedSeq) {
22737
+ scope.admittedSeq = admitted.previousSeq;
22738
+ }
22739
+ }
22740
+ state.active = false;
22741
+ state.failedAdmissionLaunchId = command.launchId;
22742
+ state.retryAfterFailure = state.coalescedReplacement;
22743
+ state.coalescedReplacement = null;
22744
+ state.activeAdmission = null;
22745
+ }
22746
+ dispatchError = error51;
22747
+ } finally {
22748
+ if (state.observationGeneration === admissionGeneration) {
22749
+ state.admitting = false;
22750
+ }
22751
+ }
22752
+ if (state.observationGeneration === admissionGeneration) {
22753
+ await this.retryFailedAdmission(agentId, state);
22754
+ if (!state.active && !state.admitting) {
22755
+ const next = this.nextPending(state);
22756
+ if (next)
22757
+ await this.admit(agentId, next);
22758
+ }
22759
+ }
22760
+ if (dispatchError)
22761
+ throw dispatchError;
22762
+ }
22763
+ rememberReplacement(state, command) {
22764
+ const failedOrActiveLaunchId = state.activeAdmission?.launchId ?? state.failedAdmissionLaunchId;
22765
+ if (!failedOrActiveLaunchId || command.launchId === failedOrActiveLaunchId)
22766
+ return;
22767
+ const scope = state.scopes.get(command.unreadNotice.channel);
22768
+ if (!scope || command.unreadNotice.latestSeq < scope.desiredSeq)
22769
+ return;
22770
+ state.coalescedReplacement = command;
22771
+ if (state.failedAdmissionLaunchId)
22772
+ state.retryAfterFailure = command;
22773
+ }
22774
+ async retryFailedAdmission(agentId, state) {
22775
+ if (state.active || state.admitting)
22776
+ return;
22777
+ const replacement = state.retryAfterFailure;
22778
+ state.retryAfterFailure = null;
22779
+ if (!replacement)
22780
+ return;
22781
+ state.failedAdmissionLaunchId = null;
22782
+ await this.admit(agentId, replacement);
22783
+ }
22784
+ }
22785
+
22484
22786
  // src/server/wsControlChannel.ts
22485
22787
  var WS_CONTROL_COMMAND_CONSUMED = Symbol("ws-control-command-consumed");
22486
22788
  var DEFAULT_PING_INTERVAL_MS = 15000;
@@ -22495,6 +22797,7 @@ class WsControlChannel {
22495
22797
  opts;
22496
22798
  statusValue = "idle";
22497
22799
  commandCbs = [];
22800
+ wakeDesiredCbs = [];
22498
22801
  resyncHooks = [];
22499
22802
  ws = null;
22500
22803
  attempt = 0;
@@ -22504,6 +22807,7 @@ class WsControlChannel {
22504
22807
  pongDeadline = 0;
22505
22808
  resyncProvider = null;
22506
22809
  log;
22810
+ wakeCoordinator = new WakeCoordinator;
22507
22811
  constructor(opts) {
22508
22812
  this.opts = opts;
22509
22813
  this.log = opts.logger ?? createLogger({ header: "@alook/daemon:ws" });
@@ -22526,6 +22830,30 @@ class WsControlChannel {
22526
22830
  onCommand(cb) {
22527
22831
  this.commandCbs.push(cb);
22528
22832
  }
22833
+ onWakeDesiredAdvance(cb) {
22834
+ this.wakeDesiredCbs.push(cb);
22835
+ }
22836
+ async ingestCommand(frame) {
22837
+ const parsed = HostCommandSchema.safeParse(frame);
22838
+ if (!parsed.success) {
22839
+ this.log.warn("dropped malformed HostCommand frame", {
22840
+ type: typeof frame === "object" && frame !== null && "type" in frame ? String(frame.type) : "unknown",
22841
+ issues: parsed.error.issues.map((issue3) => ({
22842
+ path: issue3.path.join("."),
22843
+ code: issue3.code
22844
+ }))
22845
+ });
22846
+ return false;
22847
+ }
22848
+ await this.dispatchIngressCommand(parsed.data);
22849
+ return true;
22850
+ }
22851
+ modelSeenGeneration(agentId) {
22852
+ return this.wakeCoordinator.modelSeenGeneration(agentId);
22853
+ }
22854
+ recordModelSeen(agentId, messages, generation = this.wakeCoordinator.modelSeenGeneration(agentId)) {
22855
+ return this.wakeCoordinator.recordModelSeen(agentId, messages, generation);
22856
+ }
22529
22857
  onResync(provider) {
22530
22858
  this.resyncProvider = provider;
22531
22859
  }
@@ -22542,6 +22870,7 @@ class WsControlChannel {
22542
22870
  this.sendFrame({ type: "agent_session", ...info });
22543
22871
  }
22544
22872
  async reportAgentActivity(info) {
22873
+ this.wakeCoordinator.recordAgentActivity(info.agentId, info.state);
22545
22874
  this.sendFrame({ type: "agent_activity", ...info });
22546
22875
  }
22547
22876
  reportAgentTyping(info) {
@@ -22554,6 +22883,7 @@ class WsControlChannel {
22554
22883
  this.sendFrame(frame);
22555
22884
  }
22556
22885
  async reportWakeAck(info) {
22886
+ this.wakeCoordinator.recordDeliveryAck(info.agentId, info.launchId, info.status);
22557
22887
  this.sendFrame({ type: "agent_wake_ack", ...info });
22558
22888
  }
22559
22889
  async reportStoppedAck(info) {
@@ -22625,27 +22955,75 @@ class WsControlChannel {
22625
22955
  return;
22626
22956
  }
22627
22957
  this.attempt = 0;
22628
- const parsed = HostCommandSchema.safeParse(frame);
22629
- if (!parsed.success) {
22630
- this.log.warn("dropped malformed HostCommand frame", {
22631
- type: frame.type,
22632
- issues: parsed.error.issues.map((i) => ({ path: i.path.join("."), code: i.code }))
22633
- });
22634
- return;
22635
- }
22636
- const cmd = parsed.data;
22958
+ this.ingestCommand(frame).catch((err) => {
22959
+ this.log.warn("command ingress failed", { type: frame.type, err: describeErr(err) });
22960
+ });
22961
+ }
22962
+ async dispatchListeners(cmd) {
22963
+ const pending = [];
22637
22964
  for (const cb of this.commandCbs) {
22638
22965
  try {
22639
22966
  const result = cb(cmd);
22640
22967
  if (result === WS_CONTROL_COMMAND_CONSUMED)
22641
22968
  break;
22642
- Promise.resolve(result).catch((err) => {
22969
+ pending.push(Promise.resolve(result).catch((err) => {
22643
22970
  this.log.warn("command listener threw", { type: cmd.type, err: describeErr(err) });
22644
- });
22971
+ }));
22645
22972
  } catch (err) {
22646
22973
  this.log.warn("command listener threw synchronously", { type: cmd.type, err: describeErr(err) });
22647
22974
  }
22648
22975
  }
22976
+ await Promise.all(pending);
22977
+ }
22978
+ async dispatchIngressCommand(cmd) {
22979
+ if (cmd.type === "machine:heartbeat") {
22980
+ this.sendFrame({ type: "machine_heartbeat_ack", nonce: cmd.nonce });
22981
+ return;
22982
+ }
22983
+ if (cmd.type === "diagnostics:collect") {
22984
+ this.sendFrame({ type: "diagnostics_ack", reportId: cmd.reportId });
22985
+ await this.dispatchListeners(cmd);
22986
+ return;
22987
+ }
22988
+ if (cmd.type === "agent:wake") {
22989
+ const result = await this.wakeCoordinator.run(cmd, (accepted) => this.dispatchListeners(accepted), (advanced) => {
22990
+ for (const cb of this.wakeDesiredCbs) {
22991
+ try {
22992
+ cb(advanced);
22993
+ } catch (err) {
22994
+ this.log.warn("wake desired observer threw", { err: describeErr(err) });
22995
+ }
22996
+ }
22997
+ });
22998
+ if (result.state === "suppressed") {
22999
+ await this.reportWakeAck({
23000
+ agentId: cmd.agentId,
23001
+ launchId: cmd.launchId,
23002
+ status: "ok"
23003
+ });
23004
+ this.log.debug("duplicate wake suppressed", {
23005
+ agentId: cmd.agentId,
23006
+ channel: cmd.unreadNotice.channel,
23007
+ latestSeq: cmd.unreadNotice.latestSeq,
23008
+ coveredSeq: result.coveredSeq
23009
+ });
23010
+ }
23011
+ return;
23012
+ }
23013
+ if (cmd.type === "machine:reset_all") {
23014
+ for (const reset of cmd.resets)
23015
+ this.wakeCoordinator.invalidate(reset.agentId, true);
23016
+ await this.dispatchListeners(cmd);
23017
+ return;
23018
+ }
23019
+ if (cmd.type === "agent:stop" || cmd.type === "agent:model_switch") {
23020
+ this.wakeCoordinator.invalidate(cmd.agentId, false);
23021
+ } else if (cmd.type === "agent:reset" || cmd.type === "agent:nap") {
23022
+ this.wakeCoordinator.invalidate(cmd.agentId, true);
23023
+ } else if (cmd.type === "bot:removed") {
23024
+ this.wakeCoordinator.invalidate(cmd.botId, true);
23025
+ }
23026
+ await this.dispatchListeners(cmd);
22649
23027
  }
22650
23028
  onSocketClosed(code, reason) {
22651
23029
  this.log.warn("control channel closed", { code, reason: reason ? String(reason) : "" });
@@ -22680,7 +23058,10 @@ class WsControlChannel {
22680
23058
  this.pingTimer = setInterval(() => {
22681
23059
  if (this.now() > this.pongDeadline) {
22682
23060
  this.log.warn("heartbeat pong timeout — forcing reconnect");
22683
- this.ws?.close();
23061
+ if (this.ws?.terminate)
23062
+ this.ws.terminate();
23063
+ else
23064
+ this.ws?.close();
22684
23065
  return;
22685
23066
  }
22686
23067
  this.log.debug("heartbeat ping");
@@ -23259,6 +23640,8 @@ function createTimelineRecorder(opts) {
23259
23640
  sessionByAgent.set(agentId, sessionId);
23260
23641
  },
23261
23642
  appendEntryForAgent(agentId, messages) {
23643
+ if (messages.length === 0)
23644
+ return;
23262
23645
  const dir = dirFor(agentId);
23263
23646
  if (!prepareTimelineDirectory(dir))
23264
23647
  return;
@@ -23747,7 +24130,16 @@ async function createDaemon(opts) {
23747
24130
  const typingTracker = createTypingScopeTracker();
23748
24131
  const broker = new CredentialBroker({ upstreamBaseUrl: opts.serverUrl });
23749
24132
  const proxy = await startCredentialProxy(broker, {
23750
- onInboxPullResponse: (agentId, messages) => timeline2.appendEntryForAgent(agentId, messages),
24133
+ onInboxPullStart: (agentId) => channelRef?.modelSeenGeneration(agentId),
24134
+ onInboxPullResponse: (agentId, messages, observationToken) => {
24135
+ timeline2.appendEntryForAgent(agentId, messages);
24136
+ if (typeof observationToken === "number") {
24137
+ channelRef?.recordModelSeen(agentId, messages, observationToken);
24138
+ }
24139
+ },
24140
+ onInboxPullObservationError: ({ agentId, reason, contentEncoding }) => {
24141
+ log.warn("inbox pull timeline observation failed", { agentId, reason, contentEncoding });
24142
+ },
23751
24143
  onMessageReminderArm: (input) => reminderSchedulerRef?.arm(input) ?? { armed: false, reason: "reminder_scheduler_unavailable" },
23752
24144
  onProxyRequest: (agentId, method, pathname) => {
23753
24145
  const subcommand = deriveAuditLogSubcommand(pathname, method);
@@ -23848,11 +24240,29 @@ async function createDaemon(opts) {
23848
24240
  if (!res.ok)
23849
24241
  throw new Error(`resync-wakes ${res.status}`);
23850
24242
  const json2 = await res.json();
23851
- log.info("wake resync completed", { woken: json2.woken ?? 0 });
24243
+ log.info("wake resync completed", { attempted: json2.attempted ?? 0 });
23852
24244
  } catch (err) {
23853
24245
  log.warn("wake resync failed", { err: err instanceof Error ? err.message : String(err) });
23854
24246
  }
23855
24247
  }
24248
+ async function resyncPendingDiagnostics() {
24249
+ try {
24250
+ const res = await fetch(`${opts.serverUrl}/api/community/daemon/resync-diagnostics`, {
24251
+ method: "POST",
24252
+ headers: { authorization: `Bearer ${opts.machineKey}` }
24253
+ });
24254
+ if (!res.ok)
24255
+ throw new Error(`resync-diagnostics ${res.status}`);
24256
+ const json2 = await res.json();
24257
+ log.info("diagnostics resync completed", {
24258
+ pending: json2.pending ?? 0,
24259
+ attempted: json2.attempted ?? 0,
24260
+ ambiguous: json2.ambiguous ?? 0
24261
+ });
24262
+ } catch (err) {
24263
+ log.warn("diagnostics resync failed", { err: err instanceof Error ? err.message : String(err) });
24264
+ }
24265
+ }
23856
24266
  const enrollAgent = async (agentId) => {
23857
24267
  const existing = enrolledKeys.get(agentId);
23858
24268
  if (existing)
@@ -24028,7 +24438,10 @@ async function createDaemon(opts) {
24028
24438
  let statusTimer = null;
24029
24439
  if (opts.statusFilePath) {
24030
24440
  const statusPath = opts.statusFilePath;
24031
- const writeStatus = () => writeStatusFile(statusPath, { writtenAt: Date.now(), agents: manager.statusProjection(Date.now()) });
24441
+ const writeStatus = () => {
24442
+ const nowMs = Date.now();
24443
+ writeStatusFile(statusPath, { writtenAt: nowMs, agents: manager.statusProjection(nowMs) });
24444
+ };
24032
24445
  writeStatus();
24033
24446
  statusTimer = setInterval(writeStatus, STATUS_WRITE_INTERVAL_MS);
24034
24447
  statusTimer.unref?.();
@@ -24075,11 +24488,11 @@ async function createDaemon(opts) {
24075
24488
  handleDiagnosticCommand: opts.handleDiagnosticCommand,
24076
24489
  reportDiagnosticFailure: opts.reportDiagnosticFailure
24077
24490
  }));
24491
+ channel2.onWakeDesiredAdvance((cmd) => {
24492
+ reminderSchedulerRef?.observe(cmd.agentId, cmd.unreadNotice.channel, cmd.unreadNotice.latestSeq);
24493
+ });
24078
24494
  channel2.onCommand((cmd) => {
24079
24495
  switch (cmd.type) {
24080
- case "agent:wake":
24081
- reminderSchedulerRef?.observe(cmd.agentId, cmd.unreadNotice.channel, cmd.unreadNotice.latestSeq);
24082
- break;
24083
24496
  case "agent:stop":
24084
24497
  reminderSchedulerRef?.clearAgent(cmd.agentId);
24085
24498
  break;
@@ -24096,6 +24509,7 @@ async function createDaemon(opts) {
24096
24509
  channel2.onOpen(() => {
24097
24510
  coldStartWarmup();
24098
24511
  resyncPendingWakes();
24512
+ resyncPendingDiagnostics();
24099
24513
  });
24100
24514
  channel2.connect();
24101
24515
  await router.start();