@songsid/agend 2.2.0-beta.16 → 2.2.0-beta.18
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/README.md +19 -18
- package/README.zh-TW.md +19 -18
- package/dist/agent-whats-new.js +3 -1
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/backend/antigravity.d.ts +25 -0
- package/dist/backend/antigravity.js +94 -9
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/kiro.d.ts +2 -0
- package/dist/backend/kiro.js +4 -0
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +5 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/compact-watch.d.ts +9 -1
- package/dist/compact-watch.js +47 -6
- package/dist/compact-watch.js.map +1 -1
- package/dist/config-validator.js +26 -1
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +8 -0
- package/dist/daemon.js +66 -6
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.js +78 -23
- package/dist/fleet-manager.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +4 -3
- package/dist/forged-envelope-scan.js +4 -3
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/kiro-compact-pane.d.ts +38 -0
- package/dist/kiro-compact-pane.js +57 -0
- package/dist/kiro-compact-pane.js.map +1 -0
- package/dist/locale.js +8 -4
- package/dist/locale.js.map +1 -1
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.d.ts +2 -0
- package/dist/public-web-gateway.js +6 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/topic-commands.d.ts +9 -2
- package/dist/topic-commands.js +18 -6
- package/dist/topic-commands.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/ui/chat-render.js +31 -6
- package/dist/ui/chat-store.js +5 -5
- package/dist/ui/chat-strings.js +6 -2
- package/dist/ui/chat-thread.js +90 -14
- package/dist/ui/panel-chat.js +1 -1
- package/dist/ui/panel-settings.js +1 -1
- package/dist/ui/shared/app.css +7 -0
- package/dist/web-api.d.ts +4 -0
- package/dist/web-api.js +135 -43
- package/dist/web-api.js.map +1 -1
- package/dist/web-chat-history.d.ts +14 -0
- package/dist/web-chat-history.js +18 -1
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-upload.d.ts +112 -1
- package/dist/web-upload.js +237 -36
- package/dist/web-upload.js.map +1 -1
- package/package.json +3 -3
package/dist/fleet-manager.js
CHANGED
|
@@ -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,
|
|
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";
|
|
@@ -195,6 +195,7 @@ import { buildOrgChart } from "./web-org.js";
|
|
|
195
195
|
import { CacheService, WINDOWS } from "./cache-service.js";
|
|
196
196
|
import { runWebCommand } from "./web-commands.js";
|
|
197
197
|
import { CompactWatch, launchIdentity } from "./compact-watch.js";
|
|
198
|
+
import { agyLegacyAgentsFile, danglingSymlink, migrateAgyAgentsFile } from "./backend/antigravity.js";
|
|
198
199
|
import { claudeConfigDirFor, claudeProjectKey } from "./backend/claude-code.js";
|
|
199
200
|
import { sharedRolloutIndex } from "./rollout-index.js";
|
|
200
201
|
import { mayUseTool, resolveToolSet, scheduleOpRefusal, toolForIpcType, toolRefusedMessage, } from "./tool-permissions.js";
|
|
@@ -801,7 +802,8 @@ export class FleetManager {
|
|
|
801
802
|
/** #1565: the web chat history's files (set at fleet start, once the home and the instances are known). */
|
|
802
803
|
webChatStore = null;
|
|
803
804
|
/** Uploaded files and the files the dashboard may fetch back (uploads, reply attachments). */
|
|
804
|
-
|
|
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) });
|
|
805
807
|
/** #1770: per instance, its last web reply still copying its files — the next one is shown after it, in order. */
|
|
806
808
|
webReplyOrder = new Map();
|
|
807
809
|
/** #1770: an instance's web-files byte cap (a field, so a test can make it small). */
|
|
@@ -1318,8 +1320,10 @@ export class FleetManager {
|
|
|
1318
1320
|
return false;
|
|
1319
1321
|
reactions.consume();
|
|
1320
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.
|
|
1321
1324
|
this.emitSseEvent("message", {
|
|
1322
1325
|
instance: set.instance, sender: by.username, role: "user", text: content, ts, ...(web ? { messageId } : {}),
|
|
1326
|
+
click: { label: button.label, of: set.id },
|
|
1323
1327
|
});
|
|
1324
1328
|
return true;
|
|
1325
1329
|
}
|
|
@@ -2827,6 +2831,14 @@ export class FleetManager {
|
|
|
2827
2831
|
return null;
|
|
2828
2832
|
}
|
|
2829
2833
|
},
|
|
2834
|
+
paneFollow: (name) => {
|
|
2835
|
+
try {
|
|
2836
|
+
return this.lifecycle.daemons.get(name)?.compactPaneUi() != null;
|
|
2837
|
+
}
|
|
2838
|
+
catch {
|
|
2839
|
+
return false;
|
|
2840
|
+
}
|
|
2841
|
+
},
|
|
2830
2842
|
changed: () => this.emitSseEvent("status", this.getUiStatus()),
|
|
2831
2843
|
});
|
|
2832
2844
|
}
|
|
@@ -7279,7 +7291,12 @@ export class FleetManager {
|
|
|
7279
7291
|
else if (msg.type === "raw_paste_written") {
|
|
7280
7292
|
// Board 003648ac: a web /compact's paste reached the pane (or never will): its watch starts, or ends as lost.
|
|
7281
7293
|
if (typeof msg.id === "string")
|
|
7282
|
-
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);
|
|
7283
7300
|
}
|
|
7284
7301
|
else if (msg.type === "runtime_switch_result") {
|
|
7285
7302
|
// #1690: what our own runtime /model came to (the daemon answered claude's "Switch model?" or saw the result).
|
|
@@ -14608,6 +14625,7 @@ export class FleetManager {
|
|
|
14608
14625
|
messageId: typeof m.messageId === "string" ? m.messageId : undefined,
|
|
14609
14626
|
role: typeof m.role === "string" ? m.role : undefined,
|
|
14610
14627
|
buttons: m.buttons,
|
|
14628
|
+
click: m.click,
|
|
14611
14629
|
});
|
|
14612
14630
|
broadcastSseEvent(this.sseClients, event, recorded, onError, this.webChatHistory.cursorOf(recorded));
|
|
14613
14631
|
return;
|
|
@@ -14717,12 +14735,12 @@ export class FleetManager {
|
|
|
14717
14735
|
"codex": "AGENTS.md",
|
|
14718
14736
|
"opencode": "AGENTS.md",
|
|
14719
14737
|
"kiro-cli": ".kiro/steering/project.md",
|
|
14720
|
-
// Grok reads AGENTS.md project docs; agy reads .agents/
|
|
14738
|
+
// Grok reads AGENTS.md project docs; agy reads .agents/AGENTS.md (board 0340ec89: not the lowercase name) — the
|
|
14721
14739
|
// same files their writeConfig() appends fleet instructions to.
|
|
14722
14740
|
"grok": "AGENTS.md",
|
|
14723
14741
|
// muse init scaffolds AGENTS.md and the binary reads it as project rules.
|
|
14724
14742
|
"muse": "AGENTS.md",
|
|
14725
|
-
"antigravity": ".agents/
|
|
14743
|
+
"antigravity": ".agents/AGENTS.md",
|
|
14726
14744
|
"mock": "CLAUDE.md",
|
|
14727
14745
|
};
|
|
14728
14746
|
static GENERAL_INSTRUCTIONS = `# Fleet Coordinator
|
|
@@ -14762,10 +14780,17 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
14762
14780
|
ensureGeneralInstructionsSync(workDir, backendName, instanceName) {
|
|
14763
14781
|
const backend = backendName ?? "claude-code";
|
|
14764
14782
|
workDir = this.resolveKnowledgeWorkDir(workDir, backend, instanceName);
|
|
14783
|
+
// Board 0340ec89: General's file from before, under the lowercase name agy never read, becomes the one it reads.
|
|
14784
|
+
if (backend === "antigravity")
|
|
14785
|
+
migrateAgyAgentsFile(workDir);
|
|
14765
14786
|
const filename = FleetManager.INSTRUCTIONS_FILENAME[backend] ?? "CLAUDE.md";
|
|
14766
14787
|
const filePath = join(workDir, filename);
|
|
14767
14788
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
14768
|
-
if (
|
|
14789
|
+
if (backend === "antigravity" && danglingSymlink(filePath)) {
|
|
14790
|
+
// #1914 review: a person's dangling symlink there is kept and not followed (nothing created where it points).
|
|
14791
|
+
this.logger.warn({ filePath }, "General's agy instructions file is a dangling symlink; left as it is, not written");
|
|
14792
|
+
}
|
|
14793
|
+
else if (!existsSync(filePath)) {
|
|
14769
14794
|
writeFileSync(filePath, FleetManager.GENERAL_INSTRUCTIONS, "utf-8");
|
|
14770
14795
|
this.logger.info({ filePath }, "Created general instance instructions file");
|
|
14771
14796
|
}
|
|
@@ -14862,6 +14887,8 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
14862
14887
|
}
|
|
14863
14888
|
const filename = FleetManager.INSTRUCTIONS_FILENAME[backend] ?? "CLAUDE.md";
|
|
14864
14889
|
const instructionsPath = join(workDir, filename);
|
|
14890
|
+
if (backend === "antigravity" && danglingSymlink(instructionsPath))
|
|
14891
|
+
return; // kept, not followed (#1914 review; logged above)
|
|
14865
14892
|
const body = files.map(f => readFileSync(join(srcSteering, f), "utf-8").trim()).join("\n\n");
|
|
14866
14893
|
const block = `${FleetManager.STEERING_BLOCK_BEGIN}\n${body}\n${FleetManager.STEERING_BLOCK_END}`;
|
|
14867
14894
|
let existing = "";
|
|
@@ -14882,6 +14909,22 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
14882
14909
|
mkdirSync(dirname(instructionsPath), { recursive: true });
|
|
14883
14910
|
writeFileSync(instructionsPath, next);
|
|
14884
14911
|
}
|
|
14912
|
+
// Board 0340ec89: a legacy agy file that stays beside the new one (a person's own) loses only AgEnD's block.
|
|
14913
|
+
const legacy = backend === "antigravity" ? agyLegacyAgentsFile(workDir) : null;
|
|
14914
|
+
if (legacy) {
|
|
14915
|
+
try {
|
|
14916
|
+
const old = readFileSync(legacy, "utf-8");
|
|
14917
|
+
const b = old.indexOf(FleetManager.STEERING_BLOCK_BEGIN), e = old.indexOf(FleetManager.STEERING_BLOCK_END);
|
|
14918
|
+
if (b !== -1 && e > b) {
|
|
14919
|
+
const rest = (old.slice(0, b).trimEnd() + "\n" + old.slice(e + FleetManager.STEERING_BLOCK_END.length).trimStart()).trim();
|
|
14920
|
+
if (rest)
|
|
14921
|
+
writeFileSync(legacy, rest + "\n");
|
|
14922
|
+
else
|
|
14923
|
+
rmSync(legacy, { force: true });
|
|
14924
|
+
}
|
|
14925
|
+
}
|
|
14926
|
+
catch { /* best effort: the new file already has the block */ }
|
|
14927
|
+
}
|
|
14885
14928
|
}
|
|
14886
14929
|
static STEERING_BLOCK_BEGIN = "<!-- >>> agend:core-rules — managed by AgEnD; edits inside this block are overwritten -->";
|
|
14887
14930
|
static STEERING_BLOCK_END = "<!-- <<< agend:core-rules -->";
|
|
@@ -19345,37 +19388,41 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
19345
19388
|
const currentVersion = JSON.parse(readFileSync(pkgPath, "utf-8")).version ?? "0.0.0";
|
|
19346
19389
|
const latest = await npmVersion("@songsid/agend");
|
|
19347
19390
|
let target = latest;
|
|
19391
|
+
let fromChannel = false;
|
|
19348
19392
|
// The same channel rule as `agend update` (#1259): an alpha follows @alpha, any other prerelease @beta.
|
|
19349
19393
|
const channel = installedChannel(currentVersion);
|
|
19350
19394
|
if (channel !== "latest") {
|
|
19351
|
-
// Prerelease users track their own channel (never fall back to @latest,
|
|
19352
|
-
// which is older, and an alpha is never offered @beta), but should also
|
|
19353
|
-
// hear when a newer STABLE ships — pick whichever is the newest.
|
|
19354
19395
|
let pre = "";
|
|
19355
19396
|
try {
|
|
19356
19397
|
pre = await npmVersion(`@songsid/agend@${channel}`);
|
|
19357
19398
|
}
|
|
19358
19399
|
catch { /* no such tag */ }
|
|
19359
|
-
|
|
19360
|
-
|
|
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))) {
|
|
19361
19411
|
target = latest;
|
|
19412
|
+
fromChannel = false;
|
|
19413
|
+
}
|
|
19362
19414
|
}
|
|
19363
|
-
// A beta already at/ahead of its matching stable must NOT be told to
|
|
19364
|
-
// "update" to that stable — e.g. 2.0.11-beta.41 already contains everything
|
|
19365
|
-
// in stable 2.0.11, so semverGt(2.0.11, 2.0.11-beta.41) being true (a stable
|
|
19366
|
-
// outranks a prerelease of the same core) is a false positive here. Suppress
|
|
19367
|
-
// only that same-core stable-vs-my-beta case; a higher stable core (2.0.12)
|
|
19368
|
-
// or a newer beta (2.0.11-beta.50) still notifies via semverGt below.
|
|
19369
|
-
const core = (v) => v.replace(/^v/, "").split("-")[0];
|
|
19370
|
-
const betaSupersedesStable = isPrereleaseVersion(currentVersion) && !isPrereleaseVersion(target) && core(target) === core(currentVersion);
|
|
19371
19415
|
// Only notify when target is genuinely newer (semver), so a beta user on
|
|
19372
19416
|
// 2.0.8-beta.16 is never told that stable 2.0.7 is "available".
|
|
19373
|
-
if (target &&
|
|
19417
|
+
if (target && this.semverGt(target, currentVersion)) {
|
|
19374
19418
|
const generalId = this.findGeneralInstance();
|
|
19375
19419
|
if (generalId) {
|
|
19376
19420
|
// No release URL — Discord's SuppressEmbeds proved unreliable and the
|
|
19377
19421
|
// link preview looked bad. Version + /update instruction is enough.
|
|
19378
|
-
|
|
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}`));
|
|
19379
19426
|
}
|
|
19380
19427
|
}
|
|
19381
19428
|
}
|
|
@@ -19442,6 +19489,10 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
19442
19489
|
/** Shared handler dispatcher; gateway admission is enforced by its separate listener. */
|
|
19443
19490
|
dispatchWebHttp(req, res, port, closing = false) {
|
|
19444
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);
|
|
19445
19496
|
res.setHeader("Content-Type", "application/json");
|
|
19446
19497
|
// No Referer to a tunnel host, an upstream proxy, or any page linked from
|
|
19447
19498
|
// the panel — the dashboard URL is itself a credential-bearing address.
|
|
@@ -19783,6 +19834,10 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
19783
19834
|
if (!this.webToken || !this.webSessions)
|
|
19784
19835
|
this.initializeWebAuthTokens();
|
|
19785
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;
|
|
19786
19841
|
const drain = trackWebServer(server);
|
|
19787
19842
|
this.healthServer = server;
|
|
19788
19843
|
this.healthServerDrain = drain;
|