@songsid/agend 2.1.8 → 2.1.10-beta.1

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 (136) hide show
  1. package/README.md +8 -3
  2. package/README.zh-TW.md +11 -5
  3. package/dist/backend/claude-code.d.ts +35 -5
  4. package/dist/backend/claude-code.js +288 -17
  5. package/dist/backend/claude-code.js.map +1 -1
  6. package/dist/backend/codex.d.ts +13 -0
  7. package/dist/backend/codex.js +74 -2
  8. package/dist/backend/codex.js.map +1 -1
  9. package/dist/backend/credential-profile.d.ts +9 -0
  10. package/dist/backend/credential-profile.js +20 -0
  11. package/dist/backend/credential-profile.js.map +1 -1
  12. package/dist/backend/grok.js +19 -0
  13. package/dist/backend/grok.js.map +1 -1
  14. package/dist/backend/kiro.d.ts +0 -2
  15. package/dist/backend/kiro.js +8 -7
  16. package/dist/backend/kiro.js.map +1 -1
  17. package/dist/backend/muse.d.ts +6 -0
  18. package/dist/backend/muse.js +7 -1
  19. package/dist/backend/muse.js.map +1 -1
  20. package/dist/backend/types.d.ts +1 -1
  21. package/dist/channel/adapters/telegram.js +4 -3
  22. package/dist/channel/adapters/telegram.js.map +1 -1
  23. package/dist/channel/mcp-operation-response.js +5 -4
  24. package/dist/channel/mcp-operation-response.js.map +1 -1
  25. package/dist/channel/mcp-server.js +9 -0
  26. package/dist/channel/mcp-server.js.map +1 -1
  27. package/dist/channel/mcp-tools.js +1 -1
  28. package/dist/channel/mcp-tools.js.map +1 -1
  29. package/dist/channel/types.d.ts +7 -0
  30. package/dist/cli.js +150 -52
  31. package/dist/cli.js.map +1 -1
  32. package/dist/config-validator.js +24 -0
  33. package/dist/config-validator.js.map +1 -1
  34. package/dist/config.d.ts +1 -0
  35. package/dist/config.js +27 -1
  36. package/dist/config.js.map +1 -1
  37. package/dist/cross-instance-envelope.d.ts +7 -0
  38. package/dist/cross-instance-envelope.js +19 -1
  39. package/dist/cross-instance-envelope.js.map +1 -1
  40. package/dist/daemon.d.ts +112 -3
  41. package/dist/daemon.js +360 -41
  42. package/dist/daemon.js.map +1 -1
  43. package/dist/delivery-outbox.d.ts +9 -1
  44. package/dist/delivery-outbox.js +43 -27
  45. package/dist/delivery-outbox.js.map +1 -1
  46. package/dist/fleet-manager.d.ts +133 -5
  47. package/dist/fleet-manager.js +525 -86
  48. package/dist/fleet-manager.js.map +1 -1
  49. package/dist/forged-envelope-scan.d.ts +160 -0
  50. package/dist/forged-envelope-scan.js +309 -0
  51. package/dist/forged-envelope-scan.js.map +1 -0
  52. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +5 -2
  53. package/dist/instance-lifecycle.d.ts +90 -6
  54. package/dist/instance-lifecycle.js +238 -30
  55. package/dist/instance-lifecycle.js.map +1 -1
  56. package/dist/locale.js +180 -8
  57. package/dist/locale.js.map +1 -1
  58. package/dist/login-controller.d.ts +38 -0
  59. package/dist/login-controller.js +236 -8
  60. package/dist/login-controller.js.map +1 -1
  61. package/dist/login-flows.d.ts +25 -1
  62. package/dist/login-flows.js +10 -2
  63. package/dist/login-flows.js.map +1 -1
  64. package/dist/ls-rows.d.ts +17 -0
  65. package/dist/ls-rows.js +73 -0
  66. package/dist/ls-rows.js.map +1 -1
  67. package/dist/outbound-handlers.d.ts +3 -0
  68. package/dist/outbound-handlers.js +17 -19
  69. package/dist/outbound-handlers.js.map +1 -1
  70. package/dist/outbound-schemas.d.ts +2 -0
  71. package/dist/outbound-schemas.js +1 -1
  72. package/dist/outbound-schemas.js.map +1 -1
  73. package/dist/paths.js +4 -2
  74. package/dist/paths.js.map +1 -1
  75. package/dist/pause-marker.d.ts +10 -1
  76. package/dist/pause-marker.js +26 -2
  77. package/dist/pause-marker.js.map +1 -1
  78. package/dist/routing-engine.d.ts +17 -2
  79. package/dist/routing-engine.js +48 -5
  80. package/dist/routing-engine.js.map +1 -1
  81. package/dist/service-installer.d.ts +8 -0
  82. package/dist/service-installer.js +36 -1
  83. package/dist/service-installer.js.map +1 -1
  84. package/dist/settings-api.d.ts +2 -0
  85. package/dist/settings-api.js +3 -1
  86. package/dist/settings-api.js.map +1 -1
  87. package/dist/setup-wizard.js +3 -1
  88. package/dist/setup-wizard.js.map +1 -1
  89. package/dist/status-emojis.d.ts +9 -1
  90. package/dist/status-emojis.js +11 -1
  91. package/dist/status-emojis.js.map +1 -1
  92. package/dist/target-queue-worker.d.ts +67 -0
  93. package/dist/target-queue-worker.js +76 -0
  94. package/dist/target-queue-worker.js.map +1 -0
  95. package/dist/tool-permissions.js +7 -2
  96. package/dist/tool-permissions.js.map +1 -1
  97. package/dist/topic-commands.js +23 -19
  98. package/dist/topic-commands.js.map +1 -1
  99. package/dist/transcript-sources.d.ts +30 -0
  100. package/dist/transcript-sources.js +78 -0
  101. package/dist/transcript-sources.js.map +1 -1
  102. package/dist/tunnel/cloudflared.d.ts +23 -5
  103. package/dist/tunnel/cloudflared.js +32 -13
  104. package/dist/tunnel/cloudflared.js.map +1 -1
  105. package/dist/tunnel/manager.d.ts +6 -0
  106. package/dist/tunnel/manager.js +60 -22
  107. package/dist/tunnel/manager.js.map +1 -1
  108. package/dist/tunnel/public-fetch.d.ts +30 -0
  109. package/dist/tunnel/public-fetch.js +156 -0
  110. package/dist/tunnel/public-fetch.js.map +1 -0
  111. package/dist/tunnel/types.d.ts +8 -2
  112. package/dist/tunnel/types.js +8 -2
  113. package/dist/tunnel/types.js.map +1 -1
  114. package/dist/types.d.ts +50 -1
  115. package/dist/types.js +9 -1
  116. package/dist/types.js.map +1 -1
  117. package/dist/ui/settings.html +2 -2
  118. package/dist/ui/view.html +14 -0
  119. package/dist/ui/web-terminal/terminal.html +1 -0
  120. package/dist/wake-coordinator.d.ts +128 -0
  121. package/dist/wake-coordinator.js +337 -0
  122. package/dist/wake-coordinator.js.map +1 -0
  123. package/dist/web-api.d.ts +3 -1
  124. package/dist/web-api.js +1 -1
  125. package/dist/web-api.js.map +1 -1
  126. package/dist/web-host-guard.d.ts +47 -0
  127. package/dist/web-host-guard.js +75 -0
  128. package/dist/web-host-guard.js.map +1 -0
  129. package/dist/web-terminal-http.d.ts +34 -0
  130. package/dist/web-terminal-http.js +63 -0
  131. package/dist/web-terminal-http.js.map +1 -1
  132. package/dist/web-terminal.d.ts +2 -0
  133. package/dist/web-terminal.js +3 -6
  134. package/dist/web-terminal.js.map +1 -1
  135. package/package.json +1 -1
  136. package/templates/systemd.service.ejs +7 -0
@@ -0,0 +1,156 @@
1
+ /**
2
+ * The readiness probe's way of seeing a tunnel the way a phone does.
3
+ *
4
+ * A brand-new `*.trycloudflare.com` name resolves instantly through a public
5
+ * resolver and, on a corporate or WSL network, can take a minute or more
6
+ * through the machine's own forwarder (measured: 1.1.1.1 in ~13 ms, the system
7
+ * resolver 60 s+). Probing through the system resolver therefore reports a
8
+ * tunnel that works as "never became reachable". So the probe asks a public
9
+ * resolver, then connects to the address it was given with the real hostname
10
+ * as SNI and `Host` — certificate verification still runs against the
11
+ * hostname, so a wrong or forged answer yields a TLS failure, never a page.
12
+ *
13
+ * Only the probe does this. The browser that actually uses the tunnel resolves
14
+ * however it resolves. The one thing that leaves the machine is a DNS query
15
+ * for the tunnel's random hostname, sent to Cloudflare, who is already
16
+ * carrying the tunnel.
17
+ *
18
+ * The system resolver stays as the fallback: a network that blocks outbound
19
+ * DNS to 1.1.1.1 must not make a working tunnel look dead.
20
+ */
21
+ import { isIPv4 } from "node:net";
22
+ import { request as httpsRequest } from "node:https";
23
+ import { Resolver } from "node:dns/promises";
24
+ export const PUBLIC_RESOLVERS = ["1.1.1.1", "1.0.0.1"];
25
+ /** One DNS attempt is short: the caller retries, and the whole probe has a deadline. */
26
+ const RESOLVE_TIMEOUT_MS = 3_000;
27
+ /** The probe wants a page, not a download. Exactly this many bytes are accepted; one more is refused. */
28
+ export const MAX_PROBE_BODY = 256 * 1024;
29
+ /** Addresses a public resolver has no business returning for a Cloudflare edge name. */
30
+ export function isPublicIPv4(ip) {
31
+ if (!isIPv4(ip))
32
+ return false;
33
+ const [a, b] = ip.split(".").map(Number);
34
+ if (a === 0 || a === 10 || a === 127)
35
+ return false;
36
+ if (a === 100 && b >= 64 && b <= 127)
37
+ return false; // CGNAT
38
+ if (a === 169 && b === 254)
39
+ return false; // link-local
40
+ if (a === 172 && b >= 16 && b <= 31)
41
+ return false;
42
+ if (a === 192 && b === 168)
43
+ return false;
44
+ if (a >= 224)
45
+ return false; // multicast / reserved
46
+ return true;
47
+ }
48
+ function defaultResolve4(resolvers) {
49
+ const resolver = new Resolver({ timeout: RESOLVE_TIMEOUT_MS, tries: 1 });
50
+ resolver.setServers([...resolvers]);
51
+ return host => resolver.resolve4(host);
52
+ }
53
+ export async function fetchViaPublicResolver(url, signal, deps) {
54
+ const resolvers = deps.resolvers ?? PUBLIC_RESOLVERS;
55
+ let target;
56
+ try {
57
+ target = new URL(url);
58
+ }
59
+ catch {
60
+ return deps.fallback(url, signal);
61
+ }
62
+ if (resolvers.length === 0 || target.protocol !== "https:")
63
+ return deps.fallback(url, signal);
64
+ let address;
65
+ try {
66
+ const resolve4 = deps.resolve4 ?? defaultResolve4(resolvers);
67
+ address = (await resolve4(target.hostname)).find(isPublicIPv4);
68
+ }
69
+ catch {
70
+ address = undefined;
71
+ }
72
+ // No usable answer from the public resolver: say nothing about the tunnel,
73
+ // just let the ordinary path try.
74
+ if (!address)
75
+ return deps.fallback(url, signal);
76
+ return requestAt(address, target, signal, deps.request ?? httpsRequest);
77
+ }
78
+ function requestAt(address, target, signal, request) {
79
+ return new Promise((resolve, reject) => {
80
+ const options = {
81
+ host: address,
82
+ port: 443,
83
+ method: "GET",
84
+ path: `${target.pathname}${target.search}`,
85
+ // Both, and the hostname for both purposes: SNI picks the certificate,
86
+ // `servername` is also what the certificate is verified against, and
87
+ // `Host` is what the edge routes on. Never `rejectUnauthorized: false`.
88
+ servername: target.hostname,
89
+ headers: { host: target.hostname, "cache-control": "no-store" },
90
+ signal,
91
+ };
92
+ const req = request(options, res => {
93
+ const status = res.statusCode ?? 0;
94
+ const contentType = String(res.headers["content-type"] ?? "");
95
+ res.on("error", reject);
96
+ // Only a 200 can be the page. Any other body is discarded as it arrives, not collected.
97
+ if (status !== 200) {
98
+ res.on("end", () => resolve({ status, contentType, body: "" }));
99
+ res.resume();
100
+ return;
101
+ }
102
+ const chunks = [];
103
+ let size = 0;
104
+ res.on("data", (chunk) => {
105
+ size += chunk.length;
106
+ if (size > MAX_PROBE_BODY) {
107
+ req.destroy(new Error("probe response too large"));
108
+ return;
109
+ }
110
+ chunks.push(chunk);
111
+ });
112
+ res.on("end", () => resolve({ status, contentType, body: Buffer.concat(chunks).toString("utf8") }));
113
+ });
114
+ req.on("error", reject);
115
+ req.end();
116
+ });
117
+ }
118
+ /**
119
+ * The ordinary path — the system resolver, a plain `fetch` — with the same limits as the public one.
120
+ *
121
+ * `res.text()` has no ceiling, and this is the path taken exactly when public DNS is unavailable, so the
122
+ * cap has to live here too. A redirect is not this page, and following one would let the edge decide
123
+ * what we call ready.
124
+ */
125
+ export async function fetchViaSystemResolver(url, signal, fetchImpl = fetch) {
126
+ const res = await fetchImpl(url, { signal, redirect: "manual", cache: "no-store" });
127
+ const contentType = res.headers.get("content-type") ?? "";
128
+ if (res.status !== 200) {
129
+ await res.body?.cancel().catch(() => { });
130
+ return { status: res.status, contentType, body: "" };
131
+ }
132
+ return { status: 200, contentType, body: await readBoundedText(res.body) };
133
+ }
134
+ async function readBoundedText(body) {
135
+ if (!body)
136
+ return "";
137
+ const reader = body.getReader();
138
+ const chunks = [];
139
+ let size = 0;
140
+ try {
141
+ for (;;) {
142
+ const { done, value } = await reader.read();
143
+ if (done)
144
+ break;
145
+ size += value.byteLength;
146
+ if (size > MAX_PROBE_BODY)
147
+ throw new Error("probe response too large");
148
+ chunks.push(value);
149
+ }
150
+ }
151
+ finally {
152
+ await reader.cancel().catch(() => { });
153
+ }
154
+ return Buffer.concat(chunks).toString("utf8");
155
+ }
156
+ //# sourceMappingURL=public-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-fetch.js","sourceRoot":"","sources":["../../src/tunnel/public-fetch.ts"],"names":[],"mappings":"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"}
@@ -111,8 +111,14 @@ export declare class TunnelStartError extends Error {
111
111
  readonly identity: string | null;
112
112
  } | undefined);
113
113
  }
114
- /** One wall-clock budget for spawn, URL and readiness together — not per step. */
115
- export declare const TUNNEL_STARTUP_DEADLINE_MS = 30000;
114
+ /**
115
+ * One wall-clock budget for spawn, URL and readiness together — not per step.
116
+ *
117
+ * A minute: with QUIC blocked and a slow resolver in between, a tunnel that does
118
+ * work can take well over thirty seconds to become reachable, and giving up on it
119
+ * is a worse outcome than waiting for it.
120
+ */
121
+ export declare const TUNNEL_STARTUP_DEADLINE_MS = 60000;
116
122
  /** Preflight is a stat, not a network call. */
117
123
  export declare const TUNNEL_PREFLIGHT_TIMEOUT_MS = 5000;
118
124
  /** How long a polite stop is given before escalating, and again before giving up. */
@@ -24,8 +24,14 @@ export class TunnelStartError extends Error {
24
24
  this.name = "TunnelStartError";
25
25
  }
26
26
  }
27
- /** One wall-clock budget for spawn, URL and readiness together — not per step. */
28
- export const TUNNEL_STARTUP_DEADLINE_MS = 30_000;
27
+ /**
28
+ * One wall-clock budget for spawn, URL and readiness together — not per step.
29
+ *
30
+ * A minute: with QUIC blocked and a slow resolver in between, a tunnel that does
31
+ * work can take well over thirty seconds to become reachable, and giving up on it
32
+ * is a worse outcome than waiting for it.
33
+ */
34
+ export const TUNNEL_STARTUP_DEADLINE_MS = 60_000;
29
35
  /** Preflight is a stat, not a network call. */
30
36
  export const TUNNEL_PREFLIGHT_TIMEOUT_MS = 5_000;
31
37
  /** How long a polite stop is given before escalating, and again before giving up. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tunnel/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoGH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAE9B;IAGA;IAJX,YACW,SAA0B,EACnC,OAAe;IACf,6EAA6E;IACpE,WAA+E;QAExF,KAAK,CAAC,OAAO,CAAC,CAAC;QALN,cAAS,GAAT,SAAS,CAAiB;QAG1B,gBAAW,GAAX,WAAW,CAAoE;QAGxF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC;AACjD,+CAA+C;AAC/C,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,qFAAqF;AACrF,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tunnel/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoGH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAE9B;IAGA;IAJX,YACW,SAA0B,EACnC,OAAe;IACf,6EAA6E;IACpE,WAA+E;QAExF,KAAK,CAAC,OAAO,CAAC,CAAC;QALN,cAAS,GAAT,SAAS,CAAiB;QAG1B,gBAAW,GAAX,WAAW,CAAoE;QAGxF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC;AACjD,+CAA+C;AAC/C,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,qFAAqF;AACrF,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC"}
package/dist/types.d.ts CHANGED
@@ -76,6 +76,17 @@ export interface ChannelConfig {
76
76
  /** Topic ID for mirroring all cross-instance messages (read-only observation). */
77
77
  mirror_topic_id?: number | string;
78
78
  }
79
+ export type DeliveryWorkerMode = "off" | "wake_only" | "on";
80
+ export declare const DELIVERY_WORKER_MODES: readonly DeliveryWorkerMode[];
81
+ /** The delivery_worker mode for `name`: instance override, then fleet default, then "off". */
82
+ export declare function resolveDeliveryWorkerMode(config: {
83
+ defaults?: {
84
+ delivery_worker?: unknown;
85
+ };
86
+ instances?: Record<string, {
87
+ delivery_worker?: unknown;
88
+ }>;
89
+ } | null | undefined, name: string): DeliveryWorkerMode;
79
90
  export interface InstanceConfig {
80
91
  working_directory: string;
81
92
  /** Minutes an idle CLI may remain resident before auto-pause. 0 disables it. */
@@ -87,12 +98,26 @@ export interface InstanceConfig {
87
98
  * 0 = unlimited (default). Complementary to auto_pause_after (time-based).
88
99
  */
89
100
  warm_cap?: number;
101
+ /**
102
+ * Phase 2b: how far `defaults.warm_cap` may be exceeded to wake targets for
103
+ * queued durable work (delivery_worker ≠ off). Default 2. The hard cap is
104
+ * warm_cap + warm_overflow; with warm_cap 0 there is no cap at all.
105
+ */
106
+ warm_overflow?: number;
107
+ /**
108
+ * Phase 2 delivery owner for this target (docs/design/phase2-submit-contract.md):
109
+ * `off` (default) keeps the current outbox pump, `wake_only` adds the wake
110
+ * coordinator (2b), `on` hands claim/submit to the per-target worker (2c).
111
+ * Read from `defaults.delivery_worker` with a per-instance override. In 2a
112
+ * only the setting exists — every mode behaves as `off`.
113
+ */
114
+ delivery_worker?: DeliveryWorkerMode;
90
115
  /** Agent display name (e.g. "Kuro", "Luna") — chosen by the agent itself */
91
116
  display_name?: string;
92
117
  /**
93
118
  * #1005: this instance's own delivery-status emojis, overriding its
94
119
  * channel's `options.status_emojis` per key. Keys: received, queued,
95
- * processing, delivered, failed, progress_prefix.
120
+ * processing, delivered, failed, progress_prefix, photo, attachment.
96
121
  */
97
122
  status_emojis?: import("./status-emojis.js").StatusEmojiConfig;
98
123
  /** Human-readable description of what this instance does */
@@ -293,12 +318,34 @@ export interface WebConfig {
293
318
  usage_panel?: boolean;
294
319
  /** Expose provider API-key verify/apply endpoints (default false until dark-shipped). */
295
320
  provider_secrets?: boolean;
321
+ /**
322
+ * Extra `Host` names the dashboard answers to, for a reverse proxy or port
323
+ * forward that presents a name other than localhost / 127.0.0.1 / [::1] / `hostname`.
324
+ * Names only (a port is ignored); anything not listed is refused with 403.
325
+ */
326
+ allowed_hosts?: string[];
296
327
  }
297
328
  /** `login:` section — remote /login behaviour. */
298
329
  export interface LoginConfig {
299
330
  /** web (default): token-gated browser terminal. relay: the pre-2.1.5 chat relay, kept for rollback in 2.1.5 only. */
300
331
  mode?: "web" | "relay";
301
332
  }
333
+ /**
334
+ * `web_terminal.tunnel:` — the escape hatch for finishing a /login from somewhere that cannot
335
+ * reach this machine (a phone away from the desk). Off unless `allow_public` is true, and even
336
+ * then each login asks first and only flows marked `tunnelOk` may use it.
337
+ */
338
+ export interface WebTerminalTunnelConfig {
339
+ /** The only provider today: a Cloudflare Quick Tunnel. */
340
+ provider?: "cloudflared";
341
+ /**
342
+ * Whether /login may offer to open a temporary public HTTPS link to its terminal.
343
+ * Default false — nothing is ever exposed beyond this machine unless you set it.
344
+ */
345
+ allow_public?: boolean;
346
+ /** How cloudflared reaches Cloudflare. Default http2 (QUIC is blocked on many networks); auto lets cloudflared choose. */
347
+ protocol?: "http2" | "quic" | "auto";
348
+ }
302
349
  /** `web_terminal:` section — the browser terminal behind /login and /install-cli. */
303
350
  export interface WebTerminalConfig {
304
351
  /** Default true. false makes /login refuse in web mode. */
@@ -307,6 +354,8 @@ export interface WebTerminalConfig {
307
354
  bind?: string;
308
355
  /** Session time limit. 1..20 (engine hard cap 20), default 10. */
309
356
  ttl_minutes?: number;
357
+ /** Optional public link for /login (see WebTerminalTunnelConfig). Absent = never. */
358
+ tunnel?: WebTerminalTunnelConfig;
310
359
  }
311
360
  export interface FleetConfig {
312
361
  channel?: ChannelConfig;
package/dist/types.js CHANGED
@@ -1,2 +1,10 @@
1
- export {};
1
+ export const DELIVERY_WORKER_MODES = ["off", "wake_only", "on"];
2
+ /** The delivery_worker mode for `name`: instance override, then fleet default, then "off". */
3
+ export function resolveDeliveryWorkerMode(config, name) {
4
+ for (const value of [config?.instances?.[name]?.delivery_worker, config?.defaults?.delivery_worker]) {
5
+ if (typeof value === "string" && DELIVERY_WORKER_MODES.includes(value))
6
+ return value;
7
+ }
8
+ return "off";
9
+ }
2
10
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAgFA,MAAM,CAAC,MAAM,qBAAqB,GAAkC,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;AAE/F,8FAA8F;AAC9F,MAAM,UAAU,yBAAyB,CACvC,MAAkI,EAClI,IAAY;IAEZ,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,CAAC,EAAE,CAAC;QACpG,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAK,qBAA2C,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,KAA2B,CAAC;IACpI,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -263,7 +263,7 @@
263
263
  description: "Description", systemPrompt: "System prompt", tags: "Tags", generalTopicField: "General topic (fleet dispatcher)",
264
264
  runtimeResources: "Runtime & Resources", autoPause: "Auto-pause after", minutes: "minutes", inheritDefault: "Inherit from defaults",
265
265
  statusEmojis: "Status emojis", statusEmojisInstance: "This agent's status emojis (override the connection's)", statusEmojisChannel: "Status emojis for this connection's bots",
266
- se_received: "Received", se_queued: "Queued", se_processing: "Processing", se_delivered: "Delivered", se_failed: "Failed", se_progress_prefix: "Progress prefix",
266
+ se_received: "Received", se_queued: "Queued", se_processing: "Processing", se_delivered: "Delivered", se_failed: "Failed", se_progress_prefix: "Progress prefix", se_photo: "Photo saved", se_attachment: "File saved",
267
267
  se_src_instance: "agent", se_src_platform: "connection", se_src_builtin: "default", se_pick: "Pick…", se_useDefault: "Use the default", se_fallback: "the default is used instead",
268
268
  se_unicode: "Emoji", se_telegramOnly: "Telegram allows only these reactions", se_serverEmojis: "Server emojis", se_noServerEmojis: "No custom emojis on this server.", se_primaryServer: "primary", se_externalEmojiHint: "Another server's emoji needs the bot's Use External Emojis permission in the channel it reacts in.", se_unavailable: "unavailable",
269
269
  se_hint: "Empty keeps the default. The preview is resolved by AgEnD exactly as the bot will react.",
@@ -315,7 +315,7 @@
315
315
  },
316
316
  "zh-TW": {
317
317
  statusEmojis: "狀態 emoji", statusEmojisInstance: "此 Agent 的狀態 emoji(覆蓋連線設定)", statusEmojisChannel: "此連線 bot 的狀態 emoji",
318
- se_received: "已收到", se_queued: "排隊中", se_processing: "處理中", se_delivered: "已送達", se_failed: "失敗", se_progress_prefix: "進度前綴",
318
+ se_received: "已收到", se_queued: "排隊中", se_processing: "處理中", se_delivered: "已送達", se_failed: "失敗", se_progress_prefix: "進度前綴", se_photo: "已存圖片", se_attachment: "已存檔案",
319
319
  se_src_instance: "Agent", se_src_platform: "連線", se_src_builtin: "預設", se_pick: "選擇…", se_useDefault: "使用預設", se_fallback: "改用預設值",
320
320
  se_unicode: "Emoji", se_telegramOnly: "Telegram 只允許這些反應", se_serverEmojis: "伺服器 emoji", se_noServerEmojis: "此伺服器沒有自訂 emoji。", se_primaryServer: "主伺服器", se_externalEmojiHint: "使用其他伺服器的 emoji,bot 在要加反應的頻道需有「使用外部表情符號」權限。", se_unavailable: "無法使用",
321
321
  se_hint: "留空即使用預設值。預覽由 AgEnD 依 bot 實際 react 的方式解析。",
package/dist/ui/view.html CHANGED
@@ -18,6 +18,20 @@
18
18
  #sidebar { width: 240px; flex: 0 0 240px; background: var(--panel); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow: hidden; }
19
19
  /* The list scrolls; the filter stays pinned to the bottom of the sidebar. */
20
20
  #list { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
21
+ /* The UA's default scrollbar is a light grey bar with arrows on this dark panel.
22
+ Tinted from the sidebar's own --dim; scoped to #list so the terminal's own
23
+ scrollbar (#term) is left alone. WebKit/Blink get the pseudo-elements (thin,
24
+ rounded, no arrows, a hover state); Chromium 121+ would ignore them if
25
+ scrollbar-color were also set, so the standard properties are Firefox-only. */
26
+ #list::-webkit-scrollbar { width: 10px; }
27
+ #list::-webkit-scrollbar-track { background: transparent; }
28
+ #list::-webkit-scrollbar-button { display: none; }
29
+ #list::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--dim) 35%, transparent); background-clip: padding-box; border: 2px solid transparent; border-radius: 5px; }
30
+ #list::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--dim) 70%, transparent); background-clip: padding-box; }
31
+ @supports (-moz-appearance: none) {
32
+ #list { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--dim) 35%, transparent) transparent; }
33
+ #list:hover { scrollbar-color: color-mix(in srgb, var(--dim) 70%, transparent) transparent; }
34
+ }
21
35
  #filterBar { flex: 0 0 auto; border-top: 1px solid var(--border); padding: 8px 10px 9px; background: var(--panel); }
22
36
  .filter-wrap { position: relative; display: flex; align-items: center; }
23
37
  .filter-wrap .filter-icon { position: absolute; left: 8px; width: 13px; height: 13px; color: var(--dim); pointer-events: none; }
@@ -4,6 +4,7 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
6
6
  <meta name="referrer" content="no-referrer">
7
+ <meta name="agend-terminal" content="__AGEND_TERMINAL_MARKER__">
7
8
  <title>AgEnD terminal</title>
8
9
  <link rel="stylesheet" href="assets/xterm.css">
9
10
  <link rel="stylesheet" href="assets/terminal.css">
@@ -0,0 +1,128 @@
1
+ import type { DeliveryWorkerMode } from "./types.js";
2
+ import type { OutboxDelivery } from "./delivery-outbox.js";
3
+ import type { PauseReason } from "./pause-marker.js";
4
+ /** Row kinds that never warrant waking a target. Empty: every current kind asks for action (§1.2). */
5
+ export declare const PASSIVE_KINDS: ReadonlySet<string>;
6
+ export declare const isWakeEligibleKind: (kind: string) => boolean;
7
+ export declare const WAKE_BACKOFF_BASE_MS = 1000;
8
+ export declare const WAKE_BACKOFF_MAX_MS = 60000;
9
+ /** Consecutive wake failures before both topics are told, once per failure episode. */
10
+ export declare const WAKE_FAILURE_NOTICE_AFTER = 3;
11
+ /** Pre-claim lease length (mirrors instance-lifecycle PRECLAIM_LEASE_MS). */
12
+ export declare const PRECLAIM_WINDOW_MS = 120000;
13
+ export declare const PARK_BASE_MS = 60000;
14
+ export declare const PARK_MAX_MS: number;
15
+ /** Queued work on an awake target that has not been claimed for this long is reported once (§1.4). */
16
+ export declare const CLAIM_BLOCK_NOTICE_MS: number;
17
+ /** How far `warm_cap` may be exceeded for leased work (§1.6). */
18
+ export declare const DEFAULT_WARM_OVERFLOW = 2;
19
+ export declare const WATCHDOG_INTERVAL_MS = 30000;
20
+ export interface WakeCoordinatorDeps {
21
+ mode(target: string): DeliveryWorkerMode;
22
+ /**
23
+ * Whether the outbox can be read now. False once its database is closed
24
+ * (fleet shutdown, a test tearing down): the coordinator then does nothing,
25
+ * and stops itself, instead of querying a closed connection.
26
+ */
27
+ available(): boolean;
28
+ /** Rows still to deliver: queued, retry_wait, delivering, submission_started (created_seq order). */
29
+ listPending(): OutboxDelivery[];
30
+ isPaused(target: string): boolean;
31
+ pauseReason(target: string): PauseReason | null;
32
+ isRestarting(target: string): boolean;
33
+ /** The single-flight lifecycle wake, marked as the coordinator's own. */
34
+ wake(target: string): Promise<void>;
35
+ /** Instances counted against warm_cap right now (running, not paused, not general). */
36
+ residentNames(): string[];
37
+ /** warm_cap; 0 or less means unlimited. */
38
+ warmCap(): number;
39
+ warmOverflow(): number;
40
+ /** When the target was last woken or started for work (the pre-claim clock). */
41
+ wokeAt(target: string): number | undefined;
42
+ /** Why an awake target is not accepting input (dialog, auth hold…), or null. */
43
+ notAcceptingReason(target: string): string | null;
44
+ notifyTarget(target: string, text: string): void;
45
+ /**
46
+ * Run the outbox pump. A wake changes no outbox row, so nothing else would
47
+ * tell the pump that the target it skipped while paused can be claimed now.
48
+ */
49
+ kickPump(): void;
50
+ notifySender(source: string, text: string): void;
51
+ now(): number;
52
+ logger: {
53
+ info(obj: object, msg: string): void;
54
+ warn(obj: object, msg: string): void;
55
+ debug?(obj: object, msg: string): void;
56
+ };
57
+ }
58
+ export declare class WakeCoordinator {
59
+ private readonly deps;
60
+ private readonly states;
61
+ /** Targets whose wake is in flight (a reservation each, counted against the hard cap). */
62
+ private readonly reserved;
63
+ private scanTimer;
64
+ private watchdog;
65
+ private stopped;
66
+ constructor(deps: WakeCoordinatorDeps);
67
+ start(): void;
68
+ stop(): void;
69
+ /** Schedule a scan soon (coalesced). Safe to call from any event. */
70
+ kick(delayMs?: number): void;
71
+ private state;
72
+ private active;
73
+ /**
74
+ * The pump must not claim this target (§3.1, §1.4): it is paused, being
75
+ * woken, or awake but not accepting input by the daemon's own account
76
+ * (a dialog, a login failure, stuck). Called from the pump's claim
77
+ * callback, in the same synchronous step that reads the daemon's bootId, so
78
+ * the verdict and the claim are about the same daemon generation. A busy
79
+ * (working) target is accepting: the manager's idle gate still waits it out.
80
+ */
81
+ blocksClaim(target: string): boolean;
82
+ isWaking(target: string): boolean;
83
+ /** The last wake error, while the target is failing to wake (for the expiry reason). */
84
+ wakeFailure(target: string): string | null;
85
+ isParked(target: string): boolean;
86
+ /**
87
+ * A wake that did not come from here (an operator's /wake or wake_instance,
88
+ * a user's channel message) *succeeded*. Park and backoff block only
89
+ * automatic wakes, so such a wake always gets its one attempt (§1.6); only
90
+ * its success lifts them. A failed attempt, or a refusal for capacity,
91
+ * leaves the park and backoff exactly as they were. If the hold persists
92
+ * after a successful wake, the park comes back on its next window with the
93
+ * level kept (it is reset only by a delivery).
94
+ */
95
+ noteExternalWake(target: string): void;
96
+ /** A row for `target` was delivered: the hold is over, and so are the backoffs. */
97
+ noteDelivered(target: string): void;
98
+ /** Free warm slots under the hard cap, counting every target exactly once. */
99
+ private freeSlots;
100
+ /** Synchronously take a slot for `target` (before any await). */
101
+ tryReserve(target: string): symbol | null;
102
+ /** Return exactly the slot `token` holds (idempotent). */
103
+ release(target: string, token: symbol): void;
104
+ get reservedCount(): number;
105
+ /**
106
+ * One pass over the pending queue: wake what needs waking, park what will
107
+ * not take work. Runs from timers, so it must never throw: an exception
108
+ * there is uncaught and takes the whole fleet process down (e.g. a scan
109
+ * landing after the outbox's database was closed). The next kick or the
110
+ * watchdog retries.
111
+ */
112
+ scan(): void;
113
+ private scanOnce;
114
+ /**
115
+ * An awake target with queued work: past its pre-claim window without the
116
+ * work moving, it is parked (no automatic re-wake until the hold clears or
117
+ * the park expires), and a long pre-claim block is reported once.
118
+ */
119
+ private checkAwakeTarget;
120
+ /**
121
+ * Whether a wake completion must do nothing but return its slot: the
122
+ * coordinator was stopped, or the outbox went away while the wake was in
123
+ * flight (the coordinator stops itself then). Checked before any side
124
+ * effect, so a late completion can never kick a pump at a closed database.
125
+ */
126
+ private fencedCompletion;
127
+ private startWake;
128
+ }