@songsid/agend 2.2.0-beta.6 → 2.2.0-beta.8

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 (134) hide show
  1. package/dist/agent-cli-instructions.md +6 -2
  2. package/dist/agent-cli.js +9 -5
  3. package/dist/agent-cli.js.map +1 -1
  4. package/dist/agent-whats-new.js +9 -3
  5. package/dist/agent-whats-new.js.map +1 -1
  6. package/dist/backend/kiro.d.ts +9 -0
  7. package/dist/backend/kiro.js +23 -0
  8. package/dist/backend/kiro.js.map +1 -1
  9. package/dist/backend/muse.d.ts +26 -0
  10. package/dist/backend/muse.js +131 -0
  11. package/dist/backend/muse.js.map +1 -1
  12. package/dist/channel/adapters/discord.js +12 -1
  13. package/dist/channel/adapters/discord.js.map +1 -1
  14. package/dist/channel/adapters/telegram.js +6 -0
  15. package/dist/channel/adapters/telegram.js.map +1 -1
  16. package/dist/channel/mcp-tools.js +3 -3
  17. package/dist/channel/mcp-tools.js.map +1 -1
  18. package/dist/channel/types.d.ts +3 -0
  19. package/dist/config-validator.js +3 -0
  20. package/dist/config-validator.js.map +1 -1
  21. package/dist/cross-instance-envelope.js +2 -0
  22. package/dist/cross-instance-envelope.js.map +1 -1
  23. package/dist/daemon.d.ts +8 -0
  24. package/dist/daemon.js +126 -54
  25. package/dist/daemon.js.map +1 -1
  26. package/dist/delivery-outbox.d.ts +27 -0
  27. package/dist/delivery-outbox.js +175 -6
  28. package/dist/delivery-outbox.js.map +1 -1
  29. package/dist/fleet-manager.d.ts +40 -1
  30. package/dist/fleet-manager.js +248 -40
  31. package/dist/fleet-manager.js.map +1 -1
  32. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +16 -1
  33. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -0
  34. package/dist/human-delivery-receipts.d.ts +83 -0
  35. package/dist/human-delivery-receipts.js +194 -0
  36. package/dist/human-delivery-receipts.js.map +1 -0
  37. package/dist/instance-config-impact.js +1 -0
  38. package/dist/instance-config-impact.js.map +1 -1
  39. package/dist/instructions.d.ts +1 -1
  40. package/dist/instructions.js +1 -1
  41. package/dist/instructions.js.map +1 -1
  42. package/dist/locale.js +8 -0
  43. package/dist/locale.js.map +1 -1
  44. package/dist/log-service.d.ts +99 -0
  45. package/dist/log-service.js +235 -0
  46. package/dist/log-service.js.map +1 -0
  47. package/dist/log-steps.d.ts +143 -0
  48. package/dist/log-steps.js +646 -0
  49. package/dist/log-steps.js.map +1 -0
  50. package/dist/outbound-handlers.d.ts +3 -0
  51. package/dist/outbound-handlers.js +20 -4
  52. package/dist/outbound-handlers.js.map +1 -1
  53. package/dist/outbound-schemas.d.ts +15 -13
  54. package/dist/outbound-schemas.js +5 -0
  55. package/dist/outbound-schemas.js.map +1 -1
  56. package/dist/public-link-progress.d.ts +1 -1
  57. package/dist/public-link-progress.js +8 -2
  58. package/dist/public-link-progress.js.map +1 -1
  59. package/dist/public-preview.d.ts +2 -1
  60. package/dist/public-preview.js +8 -5
  61. package/dist/public-preview.js.map +1 -1
  62. package/dist/public-web-gateway.js +1 -1
  63. package/dist/public-web-gateway.js.map +1 -1
  64. package/dist/public-web-link.d.ts +4 -1
  65. package/dist/public-web-link.js +42 -13
  66. package/dist/public-web-link.js.map +1 -1
  67. package/dist/reply-buttons-controller.d.ts +3 -0
  68. package/dist/reply-buttons-controller.js +3 -2
  69. package/dist/reply-buttons-controller.js.map +1 -1
  70. package/dist/settings-api.js +4 -0
  71. package/dist/settings-api.js.map +1 -1
  72. package/dist/settings-change.js +2 -0
  73. package/dist/settings-change.js.map +1 -1
  74. package/dist/settings-effect.js +2 -0
  75. package/dist/settings-effect.js.map +1 -1
  76. package/dist/tool-progress.js +3 -2
  77. package/dist/tool-progress.js.map +1 -1
  78. package/dist/topic-commands.d.ts +3 -2
  79. package/dist/topic-commands.js +14 -4
  80. package/dist/topic-commands.js.map +1 -1
  81. package/dist/transcript-jsonl.d.ts +80 -3
  82. package/dist/transcript-jsonl.js +147 -10
  83. package/dist/transcript-jsonl.js.map +1 -1
  84. package/dist/transcript-monitor.d.ts +11 -0
  85. package/dist/transcript-monitor.js +61 -13
  86. package/dist/transcript-monitor.js.map +1 -1
  87. package/dist/transcript-sources.d.ts +24 -0
  88. package/dist/transcript-sources.js +205 -55
  89. package/dist/transcript-sources.js.map +1 -1
  90. package/dist/tunnel/cloudflared.d.ts +7 -15
  91. package/dist/tunnel/cloudflared.js +36 -1
  92. package/dist/tunnel/cloudflared.js.map +1 -1
  93. package/dist/tunnel/types.d.ts +5 -1
  94. package/dist/tunnel/types.js.map +1 -1
  95. package/dist/types.d.ts +6 -0
  96. package/dist/ui/chat-strings.js +8 -2
  97. package/dist/ui/chat-thread.js +24 -4
  98. package/dist/ui/chat-time.js +80 -0
  99. package/dist/ui/panel-log.js +388 -0
  100. package/dist/ui/panel-settings.js +6 -1
  101. package/dist/ui/preview-panel.js +7 -1
  102. package/dist/ui/preview.js +5 -1
  103. package/dist/ui/settings-strings.js +2 -0
  104. package/dist/ui/shared/app-i18n.js +2 -2
  105. package/dist/ui/shared/app-route.js +6 -1
  106. package/dist/ui/shared/app-shell.js +3 -3
  107. package/dist/ui/shared/app-stream.js +2 -1
  108. package/dist/ui/shared/app.css +44 -6
  109. package/dist/ui/shared/app.js +5 -0
  110. package/dist/ui/shared/fonts/README.md +1 -1
  111. package/dist/ui/shared/instance-nav.js +9 -6
  112. package/dist/ui/shared/instance-switch.js +4 -4
  113. package/dist/ui/shared/tokens.css +1 -1
  114. package/dist/web-api.d.ts +5 -0
  115. package/dist/web-api.js +49 -1
  116. package/dist/web-api.js.map +1 -1
  117. package/dist/web-auth.js +2 -1
  118. package/dist/web-auth.js.map +1 -1
  119. package/dist/web-commands.d.ts +2 -2
  120. package/dist/web-commands.js +3 -2
  121. package/dist/web-commands.js.map +1 -1
  122. package/dist/web-reply-files.d.ts +45 -0
  123. package/dist/web-reply-files.js +223 -0
  124. package/dist/web-reply-files.js.map +1 -0
  125. package/dist/web-settings-patch.d.ts +3 -1
  126. package/dist/web-settings-patch.js +4 -1
  127. package/dist/web-settings-patch.js.map +1 -1
  128. package/dist/web-shell-routes.d.ts +4 -0
  129. package/dist/web-shell-routes.js +9 -0
  130. package/dist/web-shell-routes.js.map +1 -1
  131. package/dist/web-upload.d.ts +17 -4
  132. package/dist/web-upload.js +64 -13
  133. package/dist/web-upload.js.map +1 -1
  134. package/package.json +1 -1
@@ -0,0 +1,223 @@
1
+ /**
2
+ * #1770: the files an agent attaches to a reply, as the web chat shows them. When the reply is routed, each file is
3
+ * copied (a snapshot) into the instance's own `workspaces/<instance>/web-files/`, named by the id it is served under,
4
+ * and that copy is what the chat serves and what its history keeps. The agent's file may then be rewritten, moved or
5
+ * cleaned out of /tmp, and the fleet may restart: the message still shows what was sent. Telegram and Discord send
6
+ * the agent's own path, as before; nothing here touches that.
7
+ *
8
+ * Bounded: a snapshot leaves with its message (WebFileLedger.dropServed unlinks it), an instance's directory is held
9
+ * to REPLY_FILES_MAX_BYTES (the oldest go first), and a fleet start removes what no restored message holds.
10
+ */
11
+ import { constants as fsConstants, closeSync, existsSync, fstatSync, lstatSync, openSync, readdirSync, realpathSync, unlinkSync } from "node:fs";
12
+ import { lstat, mkdir, open, realpath, rename, stat, unlink } from "node:fs/promises";
13
+ import { basename, dirname, extname, join, resolve, sep } from "node:path";
14
+ import { isSafeInstanceDirName } from "./web-chat-store.js";
15
+ import { MAX_SERVED_BYTES } from "./web-upload.js";
16
+ export const WEB_FILES_DIR = "web-files";
17
+ /** Per instance. One reply can carry 20 files of 50 MB: the newest reply always fits whole. */
18
+ export const REPLY_FILES_MAX_BYTES = 1024 ** 3;
19
+ /** A snapshot's name: its served id and the source's plain extension. Nothing else in the directory is ours. */
20
+ const SNAPSHOT_NAME = /^([0-9a-f]{32})(\.[a-z0-9]{1,10})?$/;
21
+ const TMP_NAME = /^\.tmp-[0-9a-f]{32}$/;
22
+ const CHUNK = 1 << 20;
23
+ const PROC_FD = process.platform === "linux" ? "/proc/self/fd" : null;
24
+ /** An instance's snapshot directory; null for a name that is not a safe directory name. */
25
+ export function webFilesDir(home, instance) {
26
+ return isSafeInstanceDirName(instance) ? join(home, "workspaces", instance, WEB_FILES_DIR) : null;
27
+ }
28
+ /** The extension a snapshot keeps (lower-cased, so the served type is the one the source's name gives); "" when not plain. */
29
+ export function snapshotExt(path) {
30
+ const ext = extname(path).toLowerCase();
31
+ return /^\.[a-z0-9]{1,10}$/.test(ext) ? ext : "";
32
+ }
33
+ /** The reply tool's own rule (tool-router assertSendable), applied again when copying: nothing under the state dir but inbox/. */
34
+ function isStateFile(real, stateDir) {
35
+ const roots = new Set([resolve(stateDir)]);
36
+ try {
37
+ roots.add(realpathSync(stateDir));
38
+ }
39
+ catch { /* not there: the resolved path is the rule */ }
40
+ return [...roots].some((root) => real.startsWith(root + sep) && !real.includes(sep + "inbox" + sep));
41
+ }
42
+ /**
43
+ * Copy the agent's file `src` into `dir` as `<id><ext>`. The path itself must not be a symlink; it is opened without
44
+ * following one, and what is copied is that open file — a regular file within maxFileBytes, the same file (device +
45
+ * inode) its real path names, and not a state file. Written to a temp name and renamed, so a snapshot is never partial.
46
+ */
47
+ export async function snapshotReplyFile(src, dir, id, opts) {
48
+ const maxFile = opts.maxFileBytes ?? MAX_SERVED_BYTES;
49
+ const refused = (reason) => ({ ok: false, refused: true, reason });
50
+ let fh = null;
51
+ let tmp = null;
52
+ let evicted = [];
53
+ try {
54
+ let size, mtimeMs;
55
+ try {
56
+ const own = await lstat(src);
57
+ if (own.isSymbolicLink())
58
+ return refused("a symlink");
59
+ if (!own.isFile())
60
+ return refused("not a regular file");
61
+ fh = await open(src, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
62
+ const st = await fh.stat();
63
+ if (!st.isFile())
64
+ return refused("not a regular file");
65
+ if (st.size > maxFile)
66
+ return refused("over the size limit");
67
+ const real = await realpath(src);
68
+ const named = await stat(real);
69
+ if (named.dev !== st.dev || named.ino !== st.ino)
70
+ return refused("replaced while it was opened");
71
+ if (isStateFile(real, opts.stateDir))
72
+ return refused("inside the AgEnD state directory");
73
+ ({ size, mtimeMs } = st);
74
+ }
75
+ catch {
76
+ return refused("cannot be read");
77
+ }
78
+ await mkdir(dir, { recursive: true, mode: 0o700 });
79
+ if (!inTree(dir))
80
+ return { ok: false, refused: false, reason: "the snapshot directory is not the instance's own", evicted };
81
+ evicted = makeRoom(dir, size, opts.maxDirBytes ?? REPLY_FILES_MAX_BYTES);
82
+ tmp = join(dir, `.tmp-${id}`);
83
+ const out = await open(tmp, "wx", 0o600);
84
+ let off = 0;
85
+ try {
86
+ const buf = Buffer.alloc(Math.min(CHUNK, Math.max(1, size)));
87
+ while (off < size) {
88
+ const { bytesRead } = await fh.read(buf, 0, Math.min(buf.length, size - off), off);
89
+ if (bytesRead <= 0)
90
+ break;
91
+ await out.write(buf, 0, bytesRead);
92
+ off += bytesRead;
93
+ }
94
+ }
95
+ finally {
96
+ await out.close();
97
+ }
98
+ const after = await fh.stat();
99
+ // Rewritten while it was copied: the copy may be torn — not kept.
100
+ if (off !== size || after.size !== size || after.mtimeMs !== mtimeMs)
101
+ return { ok: false, refused: false, reason: "changed while it was copied", evicted };
102
+ const path = join(dir, id + snapshotExt(src));
103
+ await rename(tmp, path);
104
+ tmp = null;
105
+ return { ok: true, path, size, evicted };
106
+ }
107
+ catch (err) {
108
+ return { ok: false, refused: false, reason: err.code ?? "copy failed", evicted };
109
+ }
110
+ finally {
111
+ if (fh)
112
+ await fh.close().catch(() => { });
113
+ if (tmp)
114
+ await unlink(tmp).catch(() => { });
115
+ }
116
+ }
117
+ /** `dir` is a real directory at its own path (not a link elsewhere, nor under one). */
118
+ function inTree(dir) {
119
+ try {
120
+ return lstatSync(dir).isDirectory() && realpathSync(dir) === join(realpathSync(dirname(dir)), basename(dir));
121
+ }
122
+ catch {
123
+ return false;
124
+ }
125
+ }
126
+ /**
127
+ * Run `fn` on `dir` opened as a directory and checked to be the in-tree one: removals go through the open directory
128
+ * where the platform allows it, so a parent swapped for a link afterwards cannot redirect them (as sweepOrphanedUploads).
129
+ */
130
+ function withPinnedDir(dir, fallback, fn) {
131
+ let fd = null;
132
+ try {
133
+ if (!inTree(dir))
134
+ return fallback;
135
+ fd = openSync(dir, fsConstants.O_RDONLY | fsConstants.O_DIRECTORY | fsConstants.O_NOFOLLOW);
136
+ const pinned = fstatSync(fd);
137
+ const here = lstatSync(dir);
138
+ if (pinned.dev !== here.dev || pinned.ino !== here.ino)
139
+ return fallback;
140
+ const anchor = PROC_FD && existsSync(`${PROC_FD}/${fd}`) ? `${PROC_FD}/${fd}` : dir;
141
+ const stillPinned = () => {
142
+ if (anchor !== dir)
143
+ return true;
144
+ const now = lstatSync(dir);
145
+ return now.isDirectory() && now.dev === pinned.dev && now.ino === pinned.ino && inTree(dir);
146
+ };
147
+ return fn(anchor, stillPinned);
148
+ }
149
+ catch {
150
+ return fallback;
151
+ }
152
+ finally {
153
+ if (fd !== null)
154
+ try {
155
+ closeSync(fd);
156
+ }
157
+ catch { /* closed */ }
158
+ }
159
+ }
160
+ /** The snapshots in `dir` (regular files named as ours), oldest first. */
161
+ function snapshotsIn(anchor) {
162
+ const out = [];
163
+ for (const name of readdirSync(anchor)) {
164
+ const m = SNAPSHOT_NAME.exec(name);
165
+ if (!m)
166
+ continue;
167
+ try {
168
+ const st = lstatSync(join(anchor, name));
169
+ if (st.isFile())
170
+ out.push({ id: m[1], name, size: st.size, mtimeMs: st.mtimeMs });
171
+ }
172
+ catch { /* vanished */ }
173
+ }
174
+ return out.sort((a, b) => a.mtimeMs - b.mtimeMs || (a.name < b.name ? -1 : 1));
175
+ }
176
+ /** Remove the oldest snapshots until `incoming` more bytes fit under `maxBytes`; the ids removed. */
177
+ export function makeRoom(dir, incoming, maxBytes) {
178
+ return withPinnedDir(dir, [], (anchor, stillPinned) => {
179
+ const all = snapshotsIn(anchor);
180
+ let total = all.reduce((n, s) => n + s.size, 0);
181
+ const evicted = [];
182
+ for (const s of all) {
183
+ if (total + incoming <= maxBytes)
184
+ break;
185
+ if (!stillPinned())
186
+ break;
187
+ try {
188
+ unlinkSync(join(anchor, s.name));
189
+ }
190
+ catch {
191
+ continue;
192
+ }
193
+ total -= s.size;
194
+ evicted.push(s.id);
195
+ }
196
+ return evicted;
197
+ });
198
+ }
199
+ /**
200
+ * At fleet start, after the web chat history is restored: remove every snapshot of `dir` whose id no restored message
201
+ * holds (`held`), and every temp file a stopped copy left. Nothing but our own names is touched. How many were removed.
202
+ */
203
+ export function sweepReplySnapshots(dir, held) {
204
+ return withPinnedDir(dir, 0, (anchor, stillPinned) => {
205
+ let removed = 0;
206
+ for (const name of readdirSync(anchor)) {
207
+ const m = SNAPSHOT_NAME.exec(name);
208
+ if (!(m && !held.has(m[1])) && !TMP_NAME.test(name))
209
+ continue;
210
+ try {
211
+ if (!lstatSync(join(anchor, name)).isFile())
212
+ continue; // a link or a directory is not one of ours
213
+ if (!stillPinned())
214
+ break;
215
+ unlinkSync(join(anchor, name));
216
+ removed++;
217
+ }
218
+ catch { /* vanished */ }
219
+ }
220
+ return removed;
221
+ });
222
+ }
223
+ //# sourceMappingURL=web-reply-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-reply-files.js","sourceRoot":"","sources":["../src/web-reply-files.ts"],"names":[],"mappings":"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"}
@@ -4,9 +4,11 @@ export declare const WEB_TERMINAL_TTL_MINUTES: {
4
4
  readonly max: 20;
5
5
  };
6
6
  /** What Settings → Web may write; everything else under web: / web_terminal: is fleet.yaml's. */
7
- export declare const WEB_SETTINGS_BODY_KEYS: readonly ["echo_to_channel", "public_link", "session", "web_terminal"];
7
+ export declare const WEB_SETTINGS_BODY_KEYS: readonly ["echo_to_channel", "log_tab", "public_link", "session", "web_terminal"];
8
8
  export type WebSettingsBody = {
9
9
  echo?: boolean;
10
+ /** #1218: the Log tab on (true) or off (false). */
11
+ logTab?: boolean;
10
12
  publicLink?: NonNullable<WebConfig["public_link"]>;
11
13
  session: Record<string, number | null> | null;
12
14
  terminal: Record<string, boolean | number | null> | null;
@@ -9,7 +9,7 @@
9
9
  import { validPublicLinkPatch } from "./public-web-link.js";
10
10
  export const WEB_TERMINAL_TTL_MINUTES = { min: 1, max: 20 };
11
11
  /** What Settings → Web may write; everything else under web: / web_terminal: is fleet.yaml's. */
12
- export const WEB_SETTINGS_BODY_KEYS = ["echo_to_channel", "public_link", "session", "web_terminal"];
12
+ export const WEB_SETTINGS_BODY_KEYS = ["echo_to_channel", "log_tab", "public_link", "session", "web_terminal"];
13
13
  /** The whole body, or what is wrong with it (the handler's 400 message). */
14
14
  export function parseWebSettingsBody(body) {
15
15
  if (!body || typeof body !== "object" || Array.isArray(body)
@@ -18,6 +18,8 @@ export function parseWebSettingsBody(body) {
18
18
  const patch = body;
19
19
  if (patch.echo_to_channel !== undefined && typeof patch.echo_to_channel !== "boolean")
20
20
  return { error: "echo_to_channel must be a boolean" };
21
+ if (patch.log_tab !== undefined && typeof patch.log_tab !== "boolean")
22
+ return { error: "log_tab must be a boolean" };
21
23
  if (patch.public_link !== undefined && !validPublicLinkPatch(patch.public_link))
22
24
  return { error: "invalid public_link settings" };
23
25
  const session = webSessionPatch(patch.session);
@@ -28,6 +30,7 @@ export function parseWebSettingsBody(body) {
28
30
  return { error: "web_terminal must be { enabled?: boolean|null, ttl_minutes?: 1..20|null }" };
29
31
  return {
30
32
  ...(patch.echo_to_channel !== undefined ? { echo: patch.echo_to_channel } : {}),
33
+ ...(patch.log_tab !== undefined ? { logTab: patch.log_tab } : {}),
31
34
  ...(patch.public_link !== undefined ? { publicLink: patch.public_link } : {}),
32
35
  session, terminal,
33
36
  };
@@ -1 +1 @@
1
- {"version":3,"file":"web-settings-patch.js","sourceRoot":"","sources":["../src/web-settings-patch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"web-settings-patch.js","sourceRoot":"","sources":["../src/web-settings-patch.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,9 @@ export type ShellRoute = {
31
31
  } | {
32
32
  panel: "details";
33
33
  instance: string;
34
+ } | {
35
+ panel: "log";
36
+ instance: string;
34
37
  };
35
38
  /** What a path is: the app shell (with its route), a malformed shell path (400), or not the shell at all (null). */
36
39
  export type ShellMatch = {
@@ -60,4 +63,5 @@ export declare function isWebPageNavigation(path: string): boolean;
60
63
  /** The app's client-side paths, for links the server builds (#1386 links, the tour). */
61
64
  export declare function chatPath(instance: string): string;
62
65
  export declare function viewPath(instance: string): string;
66
+ export declare function logPath(instance: string): string;
63
67
  export declare function detailsPath(instance: string): string;
@@ -54,6 +54,12 @@ export function shellRoute(method, path) {
54
54
  const fleet = /^\/ui\/fleet\/([^/]+)$/.exec(path);
55
55
  if (fleet)
56
56
  return FLEET_TABS.includes(fleet[1]) ? { kind: "shell", route: { panel: "fleet", tab: fleet[1] } } : null;
57
+ // #1218: one instance's Log.
58
+ const log = /^\/ui\/log\/([^/]+)$/.exec(path);
59
+ if (log) {
60
+ const name = decodeSegment(log[1]);
61
+ return name !== null && isSafeInstanceName(name) ? { kind: "shell", route: { panel: "log", instance: name } } : { kind: "malformed" };
62
+ }
57
63
  const chat = /^\/ui\/chat\/([^/]+)$/.exec(path);
58
64
  if (chat) {
59
65
  const name = decodeSegment(chat[1]);
@@ -100,6 +106,9 @@ export function chatPath(instance) {
100
106
  export function viewPath(instance) {
101
107
  return `/view/${encodeURIComponent(instance)}`;
102
108
  }
109
+ export function logPath(instance) {
110
+ return `/ui/log/${encodeURIComponent(instance)}`;
111
+ }
103
112
  export function detailsPath(instance) {
104
113
  return `/ui/fleet/agent/${encodeURIComponent(instance)}`;
105
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"web-shell-routes.js","sourceRoot":"","sources":["../src/web-shell-routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"web-shell-routes.js","sourceRoot":"","sources":["../src/web-shell-routes.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,11 @@ export interface ServedFile {
57
57
  /** The instance whose chat shows it. */
58
58
  instance: string;
59
59
  kind: UploadKind;
60
+ /**
61
+ * #1770: a reply file's snapshot (workspaces/<instance>/web-files): the fleet's own copy, kept as long as its message
62
+ * — outside the served-id cap, and removed from disk when the message leaves the history (dropServed).
63
+ */
64
+ snapshot?: true;
60
65
  }
61
66
  export declare const isFileId: (id: string) => boolean;
62
67
  /** Images the browser may show inline; everything else is served as a download. */
@@ -128,6 +133,7 @@ export declare class WebFileLedger {
128
133
  mime?: string;
129
134
  instance: string;
130
135
  kind?: UploadKind;
136
+ snapshot?: boolean;
131
137
  }): ServedFile | null;
132
138
  /**
133
139
  * The bytes of a served file, re-checked at read time: opened without following a symlink, still a
@@ -155,14 +161,21 @@ export declare class WebFileLedger {
155
161
  * nothing is served under the id, or it is an upload no message has taken (web-pending-…): that is never kept.
156
162
  */
157
163
  storablePath(id: string): string | null;
158
- /** #1565: these ids are no longer shown (their message left the history's window): nothing is served under them. */
164
+ /**
165
+ * #1565: these ids are no longer shown (their message left the history's window): nothing is served under them.
166
+ * #1770: a snapshot goes from disk with them — the file registered, still that file (device + inode), never a link.
167
+ */
159
168
  dropServed(ids: Iterable<string>): void;
169
+ /** #1770: the ids of an instance's snapshots served now — what a fleet start's sweep keeps. */
170
+ heldSnapshots(instance: string): Set<string>;
160
171
  /**
161
172
  * #1565: serve again, under the id it had, a file a message showed before the fleet restarted — only if it still
162
173
  * passes every check: an id of ours in shape; never an upload no message took (web-pending-…); the path itself not a
163
174
  * symlink, and a regular file within MAX_SERVED_BYTES; its real path inside this instance's inbox
164
- * (`workspaces/<instance>/inbox`, itself not a link elsewhere). Null when any check fails (the message then shows the
165
- * name as unavailable). Fetched by id only, as ever: the stored path is the fleet's own file, never a request's.
175
+ * (`workspaces/<instance>/inbox`, itself not a link elsewhere) — or (#1770) inside its reply snapshots
176
+ * (`webFilesDir`, `workspaces/<instance>/web-files`, the same checks), then served as a snapshot again. Null when any
177
+ * check fails (the message then shows the name as unavailable). Fetched by id only, as ever: the stored path is the
178
+ * fleet's own file, never a request's.
166
179
  */
167
180
  restoreServed(entry: {
168
181
  id: unknown;
@@ -170,7 +183,7 @@ export declare class WebFileLedger {
170
183
  name?: unknown;
171
184
  mime?: unknown;
172
185
  kind?: unknown;
173
- }, instance: string, inboxDir: string): ServedFile | null;
186
+ }, instance: string, inboxDir: string, webFilesDir?: string): ServedFile | null;
174
187
  /** Forget an instance's files (it was deleted). The files themselves are its workspace's to remove. */
175
188
  forget(instance: string): void;
176
189
  /**
@@ -284,10 +284,24 @@ export class WebFileLedger {
284
284
  realPath, size, dev, ino, mime, instance: input.instance,
285
285
  name: displayName(input.name ?? basename(realPath), "file"),
286
286
  kind: input.kind ?? (INLINE_MIME.has(mime) ? "photo" : "document"),
287
+ ...(input.snapshot ? { snapshot: true } : {}),
287
288
  };
288
289
  this.served.set(file.id, file);
289
- while (this.served.size > this.maxServed)
290
- this.served.delete(this.served.keys().next().value);
290
+ // The cap bounds what this process registered by path. A snapshot is bounded by the history it belongs to (#1770):
291
+ // it leaves with its message, never here — dropping it here would lose a file its message still shows.
292
+ let over = 0;
293
+ for (const f of this.served.values())
294
+ if (!f.snapshot)
295
+ over++;
296
+ over -= this.maxServed;
297
+ for (const [id, f] of this.served) {
298
+ if (over <= 0)
299
+ break;
300
+ if (!f.snapshot) {
301
+ this.served.delete(id);
302
+ over--;
303
+ }
304
+ }
291
305
  return file;
292
306
  }
293
307
  /**
@@ -350,39 +364,76 @@ export class WebFileLedger {
350
364
  const file = isFileId(id) ? this.served.get(id) : undefined;
351
365
  return file && !basename(file.realPath).startsWith(PENDING_PREFIX) ? file.realPath : null;
352
366
  }
353
- /** #1565: these ids are no longer shown (their message left the history's window): nothing is served under them. */
354
- dropServed(ids) { for (const id of ids)
355
- this.served.delete(id); }
367
+ /**
368
+ * #1565: these ids are no longer shown (their message left the history's window): nothing is served under them.
369
+ * #1770: a snapshot goes from disk with them — the file registered, still that file (device + inode), never a link.
370
+ */
371
+ dropServed(ids) {
372
+ for (const id of ids) {
373
+ const f = this.served.get(id);
374
+ this.served.delete(id);
375
+ if (!f?.snapshot)
376
+ continue;
377
+ try {
378
+ const st = lstatSync(f.realPath);
379
+ if (st.isFile() && st.dev === f.dev && st.ino === f.ino)
380
+ unlinkSync(f.realPath);
381
+ }
382
+ catch { /* already gone */ }
383
+ }
384
+ }
385
+ /** #1770: the ids of an instance's snapshots served now — what a fleet start's sweep keeps. */
386
+ heldSnapshots(instance) {
387
+ const out = new Set();
388
+ for (const f of this.served.values())
389
+ if (f.snapshot && f.instance === instance)
390
+ out.add(f.id);
391
+ return out;
392
+ }
356
393
  /**
357
394
  * #1565: serve again, under the id it had, a file a message showed before the fleet restarted — only if it still
358
395
  * passes every check: an id of ours in shape; never an upload no message took (web-pending-…); the path itself not a
359
396
  * symlink, and a regular file within MAX_SERVED_BYTES; its real path inside this instance's inbox
360
- * (`workspaces/<instance>/inbox`, itself not a link elsewhere). Null when any check fails (the message then shows the
361
- * name as unavailable). Fetched by id only, as ever: the stored path is the fleet's own file, never a request's.
397
+ * (`workspaces/<instance>/inbox`, itself not a link elsewhere) — or (#1770) inside its reply snapshots
398
+ * (`webFilesDir`, `workspaces/<instance>/web-files`, the same checks), then served as a snapshot again. Null when any
399
+ * check fails (the message then shows the name as unavailable). Fetched by id only, as ever: the stored path is the
400
+ * fleet's own file, never a request's.
362
401
  */
363
- restoreServed(entry, instance, inboxDir) {
402
+ restoreServed(entry, instance, inboxDir, webFilesDir) {
364
403
  if (typeof entry.id !== "string" || !isFileId(entry.id) || typeof entry.path !== "string" || !entry.path)
365
404
  return null;
366
405
  if (basename(entry.path).startsWith(PENDING_PREFIX))
367
406
  return null;
368
407
  let real;
369
408
  let own;
409
+ let snapshot = false;
370
410
  try {
371
411
  own = lstatSync(entry.path);
372
412
  if (own.isSymbolicLink() || !own.isFile() || own.size > MAX_SERVED_BYTES)
373
413
  return null;
374
- const inbox = realpathSync(inboxDir);
375
- if (inbox !== join(realpathSync(dirname(inboxDir)), basename(inboxDir)))
376
- return null;
377
414
  real = realpathSync(entry.path);
378
- if (!real.startsWith(inbox + sep))
415
+ // The directory, resolved, must be itself (not a link elsewhere); the file's real path directly under one of them.
416
+ const within = (dir) => {
417
+ if (!dir)
418
+ return false;
419
+ try {
420
+ const d = realpathSync(dir);
421
+ return d === join(realpathSync(dirname(dir)), basename(dir)) && real.startsWith(d + sep);
422
+ }
423
+ catch {
424
+ return false;
425
+ }
426
+ };
427
+ if (within(webFilesDir))
428
+ snapshot = true;
429
+ else if (!within(inboxDir))
379
430
  return null;
380
431
  }
381
432
  catch {
382
433
  return null;
383
434
  }
384
435
  const kind = entry.kind === "photo" || entry.kind === "document" ? entry.kind : undefined;
385
- const file = this.registerServed({ id: entry.id, path: real, instance,
436
+ const file = this.registerServed({ id: entry.id, path: real, instance, ...(snapshot ? { snapshot } : {}),
386
437
  ...(typeof entry.name === "string" ? { name: entry.name } : {}), ...(typeof entry.mime === "string" ? { mime: entry.mime } : {}), ...(kind ? { kind } : {}) });
387
438
  // The file checked is the file registered (nothing swapped in between).
388
439
  if (file && (file.realPath !== real || file.dev !== own.dev || file.ino !== own.ino)) {