@songsid/agend 2.2.0-beta.17 → 2.2.0-beta.19

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 (59) hide show
  1. package/README.md +18 -18
  2. package/README.zh-TW.md +18 -18
  3. package/dist/agent-whats-new.js +3 -1
  4. package/dist/agent-whats-new.js.map +1 -1
  5. package/dist/backend/kiro.d.ts +2 -0
  6. package/dist/backend/kiro.js +4 -0
  7. package/dist/backend/kiro.js.map +1 -1
  8. package/dist/backend/types.d.ts +5 -0
  9. package/dist/backend/types.js.map +1 -1
  10. package/dist/cli.js +1 -1
  11. package/dist/cli.js.map +1 -1
  12. package/dist/compact-watch.d.ts +9 -1
  13. package/dist/compact-watch.js +47 -6
  14. package/dist/compact-watch.js.map +1 -1
  15. package/dist/config-validator.js +26 -1
  16. package/dist/config-validator.js.map +1 -1
  17. package/dist/daemon.d.ts +8 -0
  18. package/dist/daemon.js +65 -5
  19. package/dist/daemon.js.map +1 -1
  20. package/dist/fleet-manager.js +49 -20
  21. package/dist/fleet-manager.js.map +1 -1
  22. package/dist/forged-envelope-scan.d.ts +4 -3
  23. package/dist/forged-envelope-scan.js +4 -3
  24. package/dist/forged-envelope-scan.js.map +1 -1
  25. package/dist/install-lock.d.ts +18 -1
  26. package/dist/install-lock.js +30 -3
  27. package/dist/install-lock.js.map +1 -1
  28. package/dist/kiro-compact-pane.d.ts +38 -0
  29. package/dist/kiro-compact-pane.js +57 -0
  30. package/dist/kiro-compact-pane.js.map +1 -0
  31. package/dist/locale.js +4 -4
  32. package/dist/locale.js.map +1 -1
  33. package/dist/outbound-schemas.js +1 -1
  34. package/dist/outbound-schemas.js.map +1 -1
  35. package/dist/public-web-gateway.d.ts +2 -0
  36. package/dist/public-web-gateway.js +6 -1
  37. package/dist/public-web-gateway.js.map +1 -1
  38. package/dist/types.d.ts +8 -0
  39. package/dist/ui/chat-render.js +31 -6
  40. package/dist/ui/chat-store.js +5 -5
  41. package/dist/ui/chat-strings.js +6 -2
  42. package/dist/ui/chat-thread.js +15 -2
  43. package/dist/ui/panel-chat.js +1 -1
  44. package/dist/ui/panel-settings.js +1 -1
  45. package/dist/ui/shared/app.css +7 -0
  46. package/dist/update-launch.d.ts +3 -1
  47. package/dist/update-launch.js +6 -1
  48. package/dist/update-launch.js.map +1 -1
  49. package/dist/web-api.d.ts +4 -0
  50. package/dist/web-api.js +135 -43
  51. package/dist/web-api.js.map +1 -1
  52. package/dist/web-chat-history.d.ts +14 -0
  53. package/dist/web-chat-history.js +18 -1
  54. package/dist/web-chat-history.js.map +1 -1
  55. package/dist/web-upload.d.ts +112 -1
  56. package/dist/web-upload.js +237 -36
  57. package/dist/web-upload.js.map +1 -1
  58. package/launcher/install-admission.cjs +61 -6
  59. package/package.json +3 -3
@@ -7,7 +7,7 @@ import { SettingsExecution, settingsRevision, noteSettingsWrite, settingsFileRes
7
7
  import { performance } from "node:perf_hooks";
8
8
  import { withSpawnHoldReply } from "./spawn-hold-reply.js";
9
9
  import { gatewayRequestContext } from "./web-request-context.js";
10
- import { createPublicWebGateway } from "./public-web-gateway.js";
10
+ import { createPublicWebGateway, PUBLIC_GATEWAY_RECEIVE_MS } from "./public-web-gateway.js";
11
11
  import { handleWebIconRequest } from "./web-icons.js";
12
12
  import { trackWebServer } from "./web-server-drain.js";
13
13
  import { renderPublicLinkProgress, ThrottledMessageEditor } from "./public-link-progress.js";
@@ -144,7 +144,7 @@ import { WEB_NAME_FALLBACK, safeWebName, validateWebName, webNameInUse, webPerso
144
144
  import { ReplyButtonStore, parseReplyButtons, replyButtonClickText, replyButtonsFallbackText, REPLY_BUTTON_PREFIX } from "./reply-buttons.js";
145
145
  import { ReplyButtonsController } from "./reply-buttons-controller.js";
146
146
  import { parsePoll } from "./reply-polls.js";
147
- import { publicAttachment, sweepOrphanedUploads, WebFileLedger } from "./web-upload.js";
147
+ import { isUploadRequest, limitRequestReceive, publicAttachment, sweepOrphanedUploads, UPLOAD_REQUEST_TIMEOUT_MS, uploadLimits, WebFileLedger } from "./web-upload.js";
148
148
  import { REPLY_FILES_MAX_BYTES, snapshotReplyFile, sweepReplySnapshots, webFilesDir } from "./web-reply-files.js";
149
149
  import { handleViewRequest, isViewPath, profileIdentities, resolveInstanceIdentity } from "./view-api.js";
150
150
  import { downloadStickerImage } from "./channel/sticker-download.js";
@@ -162,7 +162,7 @@ import { tightenInstanceDirs } from "./private-dir.js";
162
162
  import { decideSlash } from "./slash-authz.js";
163
163
  import { canonicalCommand, commandSpec, decideCommand, ruleFor } from "./command-table.js";
164
164
  import { runVisibilityCommand } from "./cross-instance-notice.js";
165
- import { installedChannel, isPrereleaseVersion, updateNoticeKey } from "./update-check.js";
165
+ import { installedChannel, updateNoticeKey } from "./update-check.js";
166
166
  import { resolveInstalledAgend } from "./update-dispatch.js";
167
167
  import { resolveUpdateLaunch, openUpdateLog, updateLogPath, watchUpdateLaunch } from "./update-launch.js";
168
168
  import { admitProfileLogin, LoginController, LOGIN_TOKEN_RESEND_PREFIX, POST_LOGIN_RECOVERY_DEADLINE_MS } from "./login-controller.js";
@@ -802,7 +802,8 @@ export class FleetManager {
802
802
  /** #1565: the web chat history's files (set at fleet start, once the home and the instances are known). */
803
803
  webChatStore = null;
804
804
  /** Uploaded files and the files the dashboard may fetch back (uploads, reply attachments). */
805
- webFiles = new WebFileLedger();
805
+ // #1937: the upload limits are the config's, read on each use (agend reload applies a change to the next upload).
806
+ webFiles = new WebFileLedger({ limits: () => uploadLimits(this.fleetConfig?.web) });
806
807
  /** #1770: per instance, its last web reply still copying its files — the next one is shown after it, in order. */
807
808
  webReplyOrder = new Map();
808
809
  /** #1770: an instance's web-files byte cap (a field, so a test can make it small). */
@@ -1319,8 +1320,10 @@ export class FleetManager {
1319
1320
  return false;
1320
1321
  reactions.consume();
1321
1322
  this.afterUserInboundDelivered(set.instance, inbound, deliveryEpoch);
1323
+ // The web chat shows the click as its button's label (a chip); `text` stays what the agent was given.
1322
1324
  this.emitSseEvent("message", {
1323
1325
  instance: set.instance, sender: by.username, role: "user", text: content, ts, ...(web ? { messageId } : {}),
1326
+ click: { label: button.label, of: set.id },
1324
1327
  });
1325
1328
  return true;
1326
1329
  }
@@ -2828,6 +2831,14 @@ export class FleetManager {
2828
2831
  return null;
2829
2832
  }
2830
2833
  },
2834
+ paneFollow: (name) => {
2835
+ try {
2836
+ return this.lifecycle.daemons.get(name)?.compactPaneUi() != null;
2837
+ }
2838
+ catch {
2839
+ return false;
2840
+ }
2841
+ },
2831
2842
  changed: () => this.emitSseEvent("status", this.getUiStatus()),
2832
2843
  });
2833
2844
  }
@@ -7280,7 +7291,12 @@ export class FleetManager {
7280
7291
  else if (msg.type === "raw_paste_written") {
7281
7292
  // Board 003648ac: a web /compact's paste reached the pane (or never will): its watch starts, or ends as lost.
7282
7293
  if (typeof msg.id === "string")
7283
- this.compactWatchService?.pasted(name, msg.id, msg.outcome === "written", { owner: msg.owner, checkpoint: msg.checkpoint });
7294
+ this.compactWatchService?.pasted(name, msg.id, msg.outcome === "written", { owner: msg.owner, checkpoint: msg.checkpoint, pane: msg.pane });
7295
+ }
7296
+ else if (msg.type === "compact_pane_result") {
7297
+ // Board 907c7899: kiro's pane said how that /compact ended.
7298
+ if (typeof msg.id === "string")
7299
+ this.compactWatchService?.paneResult(name, msg.id, msg.owner, msg.outcome);
7284
7300
  }
7285
7301
  else if (msg.type === "runtime_switch_result") {
7286
7302
  // #1690: what our own runtime /model came to (the daemon answered claude's "Switch model?" or saw the result).
@@ -14609,6 +14625,7 @@ export class FleetManager {
14609
14625
  messageId: typeof m.messageId === "string" ? m.messageId : undefined,
14610
14626
  role: typeof m.role === "string" ? m.role : undefined,
14611
14627
  buttons: m.buttons,
14628
+ click: m.click,
14612
14629
  });
14613
14630
  broadcastSseEvent(this.sseClients, event, recorded, onError, this.webChatHistory.cursorOf(recorded));
14614
14631
  return;
@@ -19371,37 +19388,41 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
19371
19388
  const currentVersion = JSON.parse(readFileSync(pkgPath, "utf-8")).version ?? "0.0.0";
19372
19389
  const latest = await npmVersion("@songsid/agend");
19373
19390
  let target = latest;
19391
+ let fromChannel = false;
19374
19392
  // The same channel rule as `agend update` (#1259): an alpha follows @alpha, any other prerelease @beta.
19375
19393
  const channel = installedChannel(currentVersion);
19376
19394
  if (channel !== "latest") {
19377
- // Prerelease users track their own channel (never fall back to @latest,
19378
- // which is older, and an alpha is never offered @beta), but should also
19379
- // hear when a newer STABLE ships — pick whichever is the newest.
19380
19395
  let pre = "";
19381
19396
  try {
19382
19397
  pre = await npmVersion(`@songsid/agend@${channel}`);
19383
19398
  }
19384
19399
  catch { /* no such tag */ }
19385
- target = pre || latest;
19386
- if (latest && this.semverGt(latest, target))
19400
+ // #1926: a beta install hears about @beta only: beta users stay on the beta line after a stable ships, so
19401
+ // @latest is not their target. Taking the higher of the two made a same-core stable (2.2.0 over beta.16) the
19402
+ // target, which the same-core guard then dropped, so the newer beta.17 was never announced.
19403
+ target = pre;
19404
+ fromChannel = true;
19405
+ // An alpha is still told about a stable that passed its own minor (with --stable, #1259): only a stable of
19406
+ // another core that is newer than its channel's release. A same-core @latest (2.2.0 for 2.2.0-alpha.3) is not
19407
+ // taken from @latest and no longer hides a newer alpha; once the release points @alpha at it, it comes from
19408
+ // @alpha like any channel release.
19409
+ const core = (v) => v.replace(/^v/, "").split("-")[0];
19410
+ if (channel === "alpha" && latest && core(latest) !== core(currentVersion) && (!pre || this.semverGt(latest, pre))) {
19387
19411
  target = latest;
19412
+ fromChannel = false;
19413
+ }
19388
19414
  }
19389
- // A beta already at/ahead of its matching stable must NOT be told to
19390
- // "update" to that stable — e.g. 2.0.11-beta.41 already contains everything
19391
- // in stable 2.0.11, so semverGt(2.0.11, 2.0.11-beta.41) being true (a stable
19392
- // outranks a prerelease of the same core) is a false positive here. Suppress
19393
- // only that same-core stable-vs-my-beta case; a higher stable core (2.0.12)
19394
- // or a newer beta (2.0.11-beta.50) still notifies via semverGt below.
19395
- const core = (v) => v.replace(/^v/, "").split("-")[0];
19396
- const betaSupersedesStable = isPrereleaseVersion(currentVersion) && !isPrereleaseVersion(target) && core(target) === core(currentVersion);
19397
19415
  // Only notify when target is genuinely newer (semver), so a beta user on
19398
19416
  // 2.0.8-beta.16 is never told that stable 2.0.7 is "available".
19399
- if (target && !betaSupersedesStable && this.semverGt(target, currentVersion)) {
19417
+ if (target && this.semverGt(target, currentVersion)) {
19400
19418
  const generalId = this.findGeneralInstance();
19401
19419
  if (generalId) {
19402
19420
  // No release URL — Discord's SuppressEmbeds proved unreliable and the
19403
19421
  // link preview looked bad. Version + /update instruction is enough.
19404
- this.notifyInstanceTopic(generalId, t(updateNoticeKey(currentVersion, target), `v${target}`, `v${currentVersion}`));
19422
+ // From the install's own channel tag, this is what /update installs — even a stable the release pointed the
19423
+ // tag at (2.2.0 on @alpha / @beta): the plain /update text, never "/update stays on … use --stable" (#1928).
19424
+ const key = fromChannel ? "update.available_current" : updateNoticeKey(currentVersion, target);
19425
+ this.notifyInstanceTopic(generalId, t(key, `v${target}`, `v${currentVersion}`));
19405
19426
  }
19406
19427
  }
19407
19428
  }
@@ -19468,6 +19489,10 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
19468
19489
  /** Shared handler dispatcher; gateway admission is enforced by its separate listener. */
19469
19490
  dispatchWebHttp(req, res, port, closing = false) {
19470
19491
  try {
19492
+ // #1937: the listeners allow an upload long enough for a slow link; every other request keeps its old bound on
19493
+ // arriving whole — 10 s through the public link's gateway, Node's 300 s here.
19494
+ if (!isUploadRequest(req))
19495
+ limitRequestReceive(req, gatewayRequestContext(req) ? PUBLIC_GATEWAY_RECEIVE_MS : 300_000);
19471
19496
  res.setHeader("Content-Type", "application/json");
19472
19497
  // No Referer to a tunnel host, an upstream proxy, or any page linked from
19473
19498
  // the panel — the dashboard URL is itself a credential-bearing address.
@@ -19809,6 +19834,10 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
19809
19834
  if (!this.webToken || !this.webSessions)
19810
19835
  this.initializeWebAuthTokens();
19811
19836
  const server = createServer((req, res) => this.dispatchWebHttp(req, res, port, drain.closing));
19837
+ // #1937: Node's default (300 s to receive a request) would cut a large upload on a slow link. The listener allows
19838
+ // more; the upload route keeps its own clocks (no byte for a minute, or 30 minutes in all), and every other request
19839
+ // keeps the old 300 s (limitRequestReceive in dispatchWebHttp).
19840
+ server.requestTimeout = UPLOAD_REQUEST_TIMEOUT_MS;
19812
19841
  const drain = trackWebServer(server);
19813
19842
  this.healthServer = server;
19814
19843
  this.healthServerDrain = drain;