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

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 (43) hide show
  1. package/README.md +1 -0
  2. package/README.zh-TW.md +1 -0
  3. package/dist/backend/antigravity.d.ts +25 -0
  4. package/dist/backend/antigravity.js +94 -9
  5. package/dist/backend/antigravity.js.map +1 -1
  6. package/dist/cli.js +19 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/compact-watch.d.ts +78 -0
  9. package/dist/compact-watch.js +247 -0
  10. package/dist/compact-watch.js.map +1 -0
  11. package/dist/daemon.js +46 -6
  12. package/dist/daemon.js.map +1 -1
  13. package/dist/fleet-manager.d.ts +3 -0
  14. package/dist/fleet-manager.js +76 -5
  15. package/dist/fleet-manager.js.map +1 -1
  16. package/dist/locale.js +4 -0
  17. package/dist/locale.js.map +1 -1
  18. package/dist/service-installer.d.ts +24 -0
  19. package/dist/service-installer.js +49 -1
  20. package/dist/service-installer.js.map +1 -1
  21. package/dist/topic-commands.d.ts +23 -2
  22. package/dist/topic-commands.js +48 -19
  23. package/dist/topic-commands.js.map +1 -1
  24. package/dist/transcript-jsonl.d.ts +7 -2
  25. package/dist/transcript-jsonl.js +13 -7
  26. package/dist/transcript-jsonl.js.map +1 -1
  27. package/dist/ui/chat-store.js +32 -2
  28. package/dist/ui/chat-strings.js +8 -2
  29. package/dist/ui/chat-thread.js +75 -12
  30. package/dist/ui/panel-chat.js +4 -1
  31. package/dist/ui/shared/app.css +1 -0
  32. package/dist/usage/providers.d.ts +28 -8
  33. package/dist/usage/providers.js +96 -41
  34. package/dist/usage/providers.js.map +1 -1
  35. package/dist/web-api.js +1 -1
  36. package/dist/web-api.js.map +1 -1
  37. package/dist/web-commands.d.ts +14 -1
  38. package/dist/web-commands.js +6 -3
  39. package/dist/web-commands.js.map +1 -1
  40. package/dist/web-upload.d.ts +7 -3
  41. package/dist/web-upload.js +20 -8
  42. package/dist/web-upload.js.map +1 -1
  43. package/package.json +1 -1
@@ -855,6 +855,9 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
855
855
  /** #1218: the Log tab's on-demand service — made on first use; with `web.log_tab` off it answers off and does nothing. */
856
856
  private logService;
857
857
  private get log();
858
+ /** Board 003648ac: a web /compact followed to its end, from the transcript the Log reads (none: nothing to follow). */
859
+ private compactWatchService;
860
+ private get compactWatch();
858
861
  /** #1218: GET /ui/steps — an instance's steps, read on demand (null: no such instance). */
859
862
  logSteps(name: string, q: LogQuery): Promise<LogAnswer | null>;
860
863
  /**
@@ -194,6 +194,8 @@ import { GENERAL_PAUSE_ERROR, isGeneralInstance } from "./general-instance.js";
194
194
  import { buildOrgChart } from "./web-org.js";
195
195
  import { CacheService, WINDOWS } from "./cache-service.js";
196
196
  import { runWebCommand } from "./web-commands.js";
197
+ import { CompactWatch, launchIdentity } from "./compact-watch.js";
198
+ import { agyLegacyAgentsFile, danglingSymlink, migrateAgyAgentsFile } from "./backend/antigravity.js";
197
199
  import { claudeConfigDirFor, claudeProjectKey } from "./backend/claude-code.js";
198
200
  import { sharedRolloutIndex } from "./rollout-index.js";
199
201
  import { mayUseTool, resolveToolSet, scheduleOpRefusal, toolForIpcType, toolRefusedMessage, } from "./tool-permissions.js";
@@ -2811,6 +2813,24 @@ export class FleetManager {
2811
2813
  emit: (event, data) => this.emitSseEvent(event, data),
2812
2814
  });
2813
2815
  }
2816
+ /** Board 003648ac: a web /compact followed to its end, from the transcript the Log reads (none: nothing to follow). */
2817
+ compactWatchService = null;
2818
+ get compactWatch() {
2819
+ return this.compactWatchService ??= new CompactWatch({
2820
+ source: (name) => { const src = this.logSource(name); return { path: src.path, format: src.format }; },
2821
+ // The launch that runs it now (the same value the daemon sends with raw_paste_written).
2822
+ owner: (name) => {
2823
+ const daemon = this.lifecycle.daemons.get(name);
2824
+ try {
2825
+ return daemon ? launchIdentity(daemon.getInteractionSnapshot().owner) : null;
2826
+ }
2827
+ catch {
2828
+ return null;
2829
+ }
2830
+ },
2831
+ changed: () => this.emitSseEvent("status", this.getUiStatus()),
2832
+ });
2833
+ }
2814
2834
  /** #1218: GET /ui/steps — an instance's steps, read on demand (null: no such instance). */
2815
2835
  logSteps(name, q) { return this.log.steps(name, q); }
2816
2836
  /**
@@ -7257,6 +7277,11 @@ export class FleetManager {
7257
7277
  });
7258
7278
  }
7259
7279
  }
7280
+ else if (msg.type === "raw_paste_written") {
7281
+ // Board 003648ac: a web /compact's paste reached the pane (or never will): its watch starts, or ends as lost.
7282
+ if (typeof msg.id === "string")
7283
+ this.compactWatchService?.pasted(name, msg.id, msg.outcome === "written", { owner: msg.owner, checkpoint: msg.checkpoint });
7284
+ }
7260
7285
  else if (msg.type === "runtime_switch_result") {
7261
7286
  // #1690: what our own runtime /model came to (the daemon answered claude's "Switch model?" or saw the result).
7262
7287
  const waiter = typeof msg.id === "string" ? this.runtimeSwitchWaiters.get(msg.id) : undefined;
@@ -10259,6 +10284,7 @@ export class FleetManager {
10259
10284
  // must not be shown the old one's conversation, and deleted names must not pile up.
10260
10285
  this.webChatHistory.forget(name);
10261
10286
  this.webFiles.forget(name);
10287
+ this.compactWatchService?.forget(name);
10262
10288
  // #1664: and it is nobody's favorite any more (every device sees that at its next status frame).
10263
10289
  try {
10264
10290
  this.webPrefs.forgetInstance(name);
@@ -12666,7 +12692,22 @@ export class FleetManager {
12666
12692
  return this.classicChannels?.getChannelIdByInstance(instance) !== undefined ? "classic" : null;
12667
12693
  },
12668
12694
  ctx: (instance) => this.topicCommands.getCtxText(instance),
12669
- compact: (instance, instructions) => this.topicCommands.sendCompact(instance, instructions),
12695
+ // Board 003648ac: a watch for the paste, armed when the daemon says it was written (raw_paste_written); one
12696
+ // for a command that was not handed over is dropped.
12697
+ compact: async (instance, instructions) => {
12698
+ let watch = null;
12699
+ try {
12700
+ const r = this.topicCommands.compactInstance(instance, instructions, () => (watch = this.compactWatch.begin(instance)));
12701
+ if (!r.sent && watch)
12702
+ this.compactWatch.cancel(instance, watch);
12703
+ return { ...r, watch: r.sent ? watch : null };
12704
+ }
12705
+ catch (err) {
12706
+ if (watch)
12707
+ this.compactWatch.cancel(instance, watch);
12708
+ throw err;
12709
+ }
12710
+ },
12670
12711
  applyModel: (instance, name) => this.applyModel(instance, name),
12671
12712
  modelChoices: choicesFromCache,
12672
12713
  applyEffort: (instance, level) => this.applyEffort(instance, level),
@@ -12740,7 +12781,8 @@ export class FleetManager {
12740
12781
  // sendClear sends its first IPC synchronously: no await separates this check from the effect.
12741
12782
  if (this.shuttingDown || !entry.fence())
12742
12783
  return { status: 409, text: t("menu.click_stale") };
12743
- return { status: 200, text: await this.topicCommands.sendClear(instance) };
12784
+ const r = this.topicCommands.clearInstance(instance);
12785
+ return r.sent ? { status: 200, text: r.text, sent: true } : { status: 409, text: r.text };
12744
12786
  },
12745
12787
  t: (key, ...args) => t(key, ...args),
12746
12788
  }, input, opts);
@@ -14676,12 +14718,12 @@ export class FleetManager {
14676
14718
  "codex": "AGENTS.md",
14677
14719
  "opencode": "AGENTS.md",
14678
14720
  "kiro-cli": ".kiro/steering/project.md",
14679
- // Grok reads AGENTS.md project docs; agy reads .agents/agents.md — the
14721
+ // Grok reads AGENTS.md project docs; agy reads .agents/AGENTS.md (board 0340ec89: not the lowercase name) — the
14680
14722
  // same files their writeConfig() appends fleet instructions to.
14681
14723
  "grok": "AGENTS.md",
14682
14724
  // muse init scaffolds AGENTS.md and the binary reads it as project rules.
14683
14725
  "muse": "AGENTS.md",
14684
- "antigravity": ".agents/agents.md",
14726
+ "antigravity": ".agents/AGENTS.md",
14685
14727
  "mock": "CLAUDE.md",
14686
14728
  };
14687
14729
  static GENERAL_INSTRUCTIONS = `# Fleet Coordinator
@@ -14721,10 +14763,17 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
14721
14763
  ensureGeneralInstructionsSync(workDir, backendName, instanceName) {
14722
14764
  const backend = backendName ?? "claude-code";
14723
14765
  workDir = this.resolveKnowledgeWorkDir(workDir, backend, instanceName);
14766
+ // Board 0340ec89: General's file from before, under the lowercase name agy never read, becomes the one it reads.
14767
+ if (backend === "antigravity")
14768
+ migrateAgyAgentsFile(workDir);
14724
14769
  const filename = FleetManager.INSTRUCTIONS_FILENAME[backend] ?? "CLAUDE.md";
14725
14770
  const filePath = join(workDir, filename);
14726
14771
  mkdirSync(dirname(filePath), { recursive: true });
14727
- if (!existsSync(filePath)) {
14772
+ if (backend === "antigravity" && danglingSymlink(filePath)) {
14773
+ // #1914 review: a person's dangling symlink there is kept and not followed (nothing created where it points).
14774
+ this.logger.warn({ filePath }, "General's agy instructions file is a dangling symlink; left as it is, not written");
14775
+ }
14776
+ else if (!existsSync(filePath)) {
14728
14777
  writeFileSync(filePath, FleetManager.GENERAL_INSTRUCTIONS, "utf-8");
14729
14778
  this.logger.info({ filePath }, "Created general instance instructions file");
14730
14779
  }
@@ -14821,6 +14870,8 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
14821
14870
  }
14822
14871
  const filename = FleetManager.INSTRUCTIONS_FILENAME[backend] ?? "CLAUDE.md";
14823
14872
  const instructionsPath = join(workDir, filename);
14873
+ if (backend === "antigravity" && danglingSymlink(instructionsPath))
14874
+ return; // kept, not followed (#1914 review; logged above)
14824
14875
  const body = files.map(f => readFileSync(join(srcSteering, f), "utf-8").trim()).join("\n\n");
14825
14876
  const block = `${FleetManager.STEERING_BLOCK_BEGIN}\n${body}\n${FleetManager.STEERING_BLOCK_END}`;
14826
14877
  let existing = "";
@@ -14841,6 +14892,22 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
14841
14892
  mkdirSync(dirname(instructionsPath), { recursive: true });
14842
14893
  writeFileSync(instructionsPath, next);
14843
14894
  }
14895
+ // Board 0340ec89: a legacy agy file that stays beside the new one (a person's own) loses only AgEnD's block.
14896
+ const legacy = backend === "antigravity" ? agyLegacyAgentsFile(workDir) : null;
14897
+ if (legacy) {
14898
+ try {
14899
+ const old = readFileSync(legacy, "utf-8");
14900
+ const b = old.indexOf(FleetManager.STEERING_BLOCK_BEGIN), e = old.indexOf(FleetManager.STEERING_BLOCK_END);
14901
+ if (b !== -1 && e > b) {
14902
+ const rest = (old.slice(0, b).trimEnd() + "\n" + old.slice(e + FleetManager.STEERING_BLOCK_END.length).trimStart()).trim();
14903
+ if (rest)
14904
+ writeFileSync(legacy, rest + "\n");
14905
+ else
14906
+ rmSync(legacy, { force: true });
14907
+ }
14908
+ }
14909
+ catch { /* best effort: the new file already has the block */ }
14910
+ }
14844
14911
  }
14845
14912
  static STEERING_BLOCK_BEGIN = "<!-- >>> agend:core-rules — managed by AgEnD; edits inside this block are overwritten -->";
14846
14913
  static STEERING_BLOCK_END = "<!-- <<< agend:core-rules -->";
@@ -17320,6 +17387,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
17320
17387
  this.eventLogPruneTimer = null;
17321
17388
  }
17322
17389
  this.logService?.unwatchAll(); // #1218: a Log's watch timers end with the fleet
17390
+ this.compactWatchService?.stopAll();
17323
17391
  if (this.outboxPruneTimer) {
17324
17392
  clearInterval(this.outboxPruneTimer);
17325
17393
  this.outboxPruneTimer = null;
@@ -19972,6 +20040,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
19972
20040
  const effort = (effortStrategy === "unsupported" || isAgy) ? null : effortResolved.effort;
19973
20041
  const effort_source = (effortStrategy === "unsupported" || isAgy) ? null : effortResolved.source;
19974
20042
  const identity = resolveInstanceIdentity({ cfg: this.fleetConfig?.instances[name], classic: classicRooms.get(name), profile: profiles.get(name) });
20043
+ const compact = this.compactWatchService?.view(name) ?? null;
19975
20044
  let image;
19976
20045
  try {
19977
20046
  image = this.webIdentityImage(name, identity.display_name, profiles.get(name)?.avatar_path, bots);
@@ -19999,6 +20068,8 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
19999
20068
  effort,
20000
20069
  effort_source,
20001
20070
  ...this.instancePresentation(name),
20071
+ // Board 003648ac: the last web /compact, while it runs and briefly after (the chat's card follows it).
20072
+ ...(compact ? { compact } : {}),
20002
20073
  };
20003
20074
  });
20004
20075
  return {