@songsid/agend 2.1.11-beta.6 → 2.1.12-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 (46) hide show
  1. package/README.md +23 -15
  2. package/README.zh-TW.md +50 -11
  3. package/dist/backend/claude-code.d.ts +2 -0
  4. package/dist/backend/claude-code.js +63 -0
  5. package/dist/backend/claude-code.js.map +1 -1
  6. package/dist/backend/types.d.ts +25 -0
  7. package/dist/backend/types.js.map +1 -1
  8. package/dist/classic-bindings.d.ts +48 -0
  9. package/dist/classic-bindings.js +128 -0
  10. package/dist/classic-bindings.js.map +1 -0
  11. package/dist/classic-channel-manager.d.ts +21 -0
  12. package/dist/classic-channel-manager.js +31 -0
  13. package/dist/classic-channel-manager.js.map +1 -1
  14. package/dist/daemon.d.ts +41 -0
  15. package/dist/daemon.js +299 -16
  16. package/dist/daemon.js.map +1 -1
  17. package/dist/delivery-reconciliation.d.ts +13 -0
  18. package/dist/delivery-reconciliation.js +71 -7
  19. package/dist/delivery-reconciliation.js.map +1 -1
  20. package/dist/delivery-reconciliation.test.js +115 -2
  21. package/dist/delivery-reconciliation.test.js.map +1 -1
  22. package/dist/fleet-manager.d.ts +14 -1
  23. package/dist/fleet-manager.js +75 -8
  24. package/dist/fleet-manager.js.map +1 -1
  25. package/dist/instance-lifecycle.js +4 -2
  26. package/dist/instance-lifecycle.js.map +1 -1
  27. package/dist/interaction-observation.d.ts +32 -0
  28. package/dist/interaction-observation.js +118 -0
  29. package/dist/interaction-observation.js.map +1 -0
  30. package/dist/kiro-engine-status.d.ts +2 -0
  31. package/dist/kiro-engine-status.js +3 -1
  32. package/dist/kiro-engine-status.js.map +1 -1
  33. package/dist/locale.js +22 -0
  34. package/dist/locale.js.map +1 -1
  35. package/dist/outbound-handlers.d.ts +2 -0
  36. package/dist/outbound-handlers.js +28 -11
  37. package/dist/outbound-handlers.js.map +1 -1
  38. package/dist/outbound-schemas.js +1 -1
  39. package/dist/outbound-schemas.js.map +1 -1
  40. package/dist/output-log-tail.d.ts +21 -0
  41. package/dist/output-log-tail.js +89 -0
  42. package/dist/output-log-tail.js.map +1 -0
  43. package/dist/usage/providers.d.ts +11 -0
  44. package/dist/usage/providers.js +34 -7
  45. package/dist/usage/providers.js.map +1 -1
  46. package/package.json +1 -1
@@ -75,6 +75,7 @@ import { handleAgentRequest, ToolNotPermittedError } from "./agent-endpoint.js";
75
75
  import { ClassicChannelManager, getClassicBackendChoices, isSelectableClassicBackend, readClassicLastActivityAt } from "./classic-channel-manager.js";
76
76
  import { assertExplicitInstanceRemoval } from "./instance-removal.js";
77
77
  import { validateFleetConfig } from "./config-validator.js";
78
+ import { presentationState, interactionSummary } from "./interaction-observation.js";
78
79
  import { readLastInboundAt } from "./daemon.js";
79
80
  import { clearPausedMarker, readPausedAt, readPauseReason, writePausedMarker } from "./pause-marker.js";
80
81
  import { DEFAULT_WARM_OVERFLOW, WakeCoordinator } from "./wake-coordinator.js";
@@ -97,7 +98,8 @@ import { PROVIDER_SECRET_SPECS, providerSecretSpec, providerRegistryEnvKeys, isR
97
98
  /** A self-restart is a whole service restart; 120s is the apply budget, not this. */
98
99
  const SELF_RESTART_DEADLINE_MS = 300_000;
99
100
  import { APPLY_FLEET_TARGET, ApplyJobStore, viewOf, } from "./apply-job.js";
100
- import { instanceCredentialProfile } from "./backend/credential-profile.js";
101
+ import { credentialSwitchStartsFresh, instanceCredentialProfile } from "./backend/credential-profile.js";
102
+ import { mergeBackendOptions, profileKey, profileName, profileOf } from "./classic-bindings.js";
101
103
  import { classifyInstanceChange, CLASSIC_HOT_CONFIG_KEYS, HOT_INSTANCE_CONFIG_KEYS, hotConfigUpdate, } from "./instance-config-impact.js";
102
104
  import { formatRestartProgressCompletion, RESTART_PROGRESS_TERMINAL_TIMEOUT_MS, RestartProgress, } from "./restart-progress.js";
103
105
  import { launchFullRestartHelper } from "./full-restart.js";
@@ -1774,6 +1776,21 @@ export class FleetManager {
1774
1776
  return null;
1775
1777
  return this.instanceStateCache.get(name)?.state ?? null;
1776
1778
  }
1779
+ /** Pure read of the single daemon-owned observation; no probe/capture or second tracker. */
1780
+ getInstanceInteraction(name) {
1781
+ if (this.lifecycle.isPaused(name) || this.instanceProcessStatus.has(name))
1782
+ return null;
1783
+ const daemon = this.lifecycle.daemons.get(name);
1784
+ if (daemon?.getProcessStatus?.() !== "running")
1785
+ return null;
1786
+ return daemon.getInteractionSnapshot?.() ?? null;
1787
+ }
1788
+ instancePresentation(name) {
1789
+ const execution_state = this.getInstanceExecutionState(name);
1790
+ const interaction = this.getInstanceInteraction(name);
1791
+ return { state: presentationState(execution_state, interaction), execution_state,
1792
+ interaction, interaction_summary: interactionSummary(interaction) };
1793
+ }
1777
1794
  /**
1778
1795
  * Subscription providers currently relevant to this fleet. Stopped/crashed
1779
1796
  * rows do not keep a usage card alive, while persisted paused instances do.
@@ -1805,6 +1822,34 @@ export class FleetManager {
1805
1822
  }
1806
1823
  return providers;
1807
1824
  }
1825
+ /** #1220: the login a running classic instance was launched on, from its own config; null when not running. */
1826
+ classicLaunchedProfile(name) {
1827
+ const launched = this.daemons.get(name)?.getConfigSnapshot?.();
1828
+ return launched ? profileOf(launched.backend_options, launched.backend ?? "claude-code") : null;
1829
+ }
1830
+ /**
1831
+ * #1220: whether a running classic instance's login differs from what its
1832
+ * configuration now says — compared with what it was launched on, not with an
1833
+ * earlier reading of the config (a reload consumes that), and with an invalid
1834
+ * setting as a value of its own rather than the shared login it would fall to.
1835
+ * A switch on the same backend that loses the conversation (kiro) starts fresh.
1836
+ */
1837
+ classicProfileChange(name, backendChanged) {
1838
+ const launched = this.classicLaunchedProfile(name);
1839
+ if (!launched || !this.classicChannels)
1840
+ return { changed: false, startsFresh: false };
1841
+ const next = this.classicChannels.getCredentialProfileByInstance(name, this.fleetConfig?.defaults);
1842
+ if (profileKey(launched) === profileKey(next))
1843
+ return { changed: false, startsFresh: false };
1844
+ const backend = this.classicChannels.getBackendByInstance(name, this.fleetConfig?.defaults?.backend);
1845
+ return {
1846
+ changed: true,
1847
+ // A backend change keeps its own (existing) restart semantics.
1848
+ startsFresh: !backendChanged && credentialSwitchStartsFresh(backend),
1849
+ from: profileKey(launched),
1850
+ to: profileKey(next),
1851
+ };
1852
+ }
1808
1853
  /** `[instance, effective backend, credential profile]` for everything that is
1809
1854
  * running or paused — the one place both usage views agree on who is live. */
1810
1855
  activeBackendBindings() {
@@ -1823,8 +1868,9 @@ export class FleetManager {
1823
1868
  }
1824
1869
  for (const channel of this.classicChannels?.getAll() ?? []) {
1825
1870
  const backend = this.classicChannels?.getBackendByInstance(channel.instanceName, this.fleetConfig?.defaults?.backend);
1826
- // Classic channels carry no backend_options, so they run the shared login.
1827
- add(channel.instanceName, backend, null);
1871
+ // The login it was launched on; before a launch, the one it would get (#1220).
1872
+ add(channel.instanceName, backend, profileName(this.classicLaunchedProfile(channel.instanceName)
1873
+ ?? this.classicChannels.getCredentialProfileByInstance(channel.instanceName, this.fleetConfig?.defaults)));
1828
1874
  }
1829
1875
  return bindings;
1830
1876
  }
@@ -2693,9 +2739,15 @@ export class FleetManager {
2693
2739
  const backendChanged = oldBackends.get(ch.instanceName) !== newBackend;
2694
2740
  const modelChanged = oldModels.get(ch.instanceName) !== newModel;
2695
2741
  const autoPauseChanged = oldAutoPause.get(ch.instanceName) !== newAutoPause;
2696
- if (this.daemons.has(ch.instanceName) && (backendChanged || modelChanged || autoPauseChanged)) {
2697
- this.logger.info({ instanceName: ch.instanceName, backendFrom: oldBackends.get(ch.instanceName), backendTo: newBackend, modelFrom: oldModels.get(ch.instanceName), modelTo: newModel }, "Backend/model changed — restarting");
2742
+ // #1220: a running CLI keeps the login it was launched with, so a new
2743
+ // credential_profile only takes effect through a restart.
2744
+ const { changed: profileChanged, startsFresh: profileSwitchStartsFresh, from: profileFrom, to: profileTo } = this.classicProfileChange(ch.instanceName, backendChanged);
2745
+ if (this.daemons.has(ch.instanceName) && (backendChanged || modelChanged || autoPauseChanged || profileChanged)) {
2746
+ this.logger.info({ instanceName: ch.instanceName, backendFrom: oldBackends.get(ch.instanceName), backendTo: newBackend, modelFrom: oldModels.get(ch.instanceName), modelTo: newModel,
2747
+ ...(profileChanged ? { profileFrom, profileTo } : {}) }, "Backend/model/profile changed — restarting");
2698
2748
  await this.stopInstance(ch.instanceName).catch(() => { });
2749
+ if (profileSwitchStartsFresh)
2750
+ this.writeFreshStartMarker(ch.instanceName);
2699
2751
  // Small delay to let tmux window clean up
2700
2752
  await new Promise(r => setTimeout(r, 2000));
2701
2753
  // The manager already holds the new backend/model/auto-pause; the
@@ -12123,6 +12175,17 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
12123
12175
  ...(autoPauseAfter !== undefined ? { auto_pause_after: autoPauseAfter } : {}),
12124
12176
  ...(preTaskCommand ? { pre_task_command: preTaskCommand } : {}),
12125
12177
  };
12178
+ // #1220: a channel's own backend_options (credential_profile) override the
12179
+ // fleet defaults' per backend, as a fleet.yaml instance's do.
12180
+ config.backend_options = mergeBackendOptions(config.backend_options, classicIdentity?.backendOptions);
12181
+ // ClassicBot has no validator in front of its launch, and a backend that
12182
+ // cannot read a profile name falls back to the shared login. Refuse here,
12183
+ // so a bad name never runs the agent on the wrong subscription.
12184
+ const profile = profileOf(config.backend_options, config.backend ?? "claude-code");
12185
+ if (profile.state === "invalid") {
12186
+ throw new Error(`Classic instance '${instanceName}' has an invalid credential_profile (${profile.reason}) — `
12187
+ + "fix it in classicBot.yaml; it will not start on another login");
12188
+ }
12126
12189
  const topicMode = this.fleetConfig?.channel?.mode === "topic";
12127
12190
  await this.startInstance(instanceName, config, topicMode, "classic", false, transition);
12128
12191
  }
@@ -12631,10 +12694,13 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
12631
12694
  const backend = this.classicChannels.getBackend(ch.channelId, ch.adapterId, this.fleetConfig?.defaults?.backend);
12632
12695
  const model = this.classicChannels.getModel(ch.channelId, ch.adapterId, this.fleetConfig?.defaults?.model);
12633
12696
  const autoPauseAfter = this.classicChannels.getAutoPauseAfter(ch.channelId, ch.adapterId, this.fleetConfig?.defaults?.auto_pause_after);
12634
- if (old.backend !== backend || old.model !== model || old.autoPauseAfter !== autoPauseAfter) {
12635
- this.logger.info({ instanceName: ch.instanceName }, "Classic cold config changed — restarting");
12697
+ const profile = this.classicProfileChange(ch.instanceName, old.backend !== backend);
12698
+ if (old.backend !== backend || old.model !== model || old.autoPauseAfter !== autoPauseAfter || profile.changed) {
12699
+ this.logger.info({ instanceName: ch.instanceName, ...(profile.changed ? { profileFrom: profile.from, profileTo: profile.to } : {}) }, "Classic cold config changed — restarting");
12636
12700
  observe?.(ch.instanceName, "restart", "running");
12637
12701
  await this.stopInstance(ch.instanceName).catch(() => { });
12702
+ if (profile.startsFresh)
12703
+ this.writeFreshStartMarker(ch.instanceName);
12638
12704
  await this.startClassicInstanceUnattended(ch, "classic instance after fleet reload");
12639
12705
  observe?.(ch.instanceName, "restart", "done");
12640
12706
  continue;
@@ -14247,7 +14313,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
14247
14313
  lastActivity,
14248
14314
  currentTask,
14249
14315
  idle: this.getInstanceIdle(inst.name),
14250
- state: this.getInstanceExecutionState(inst.name),
14316
+ ...this.instancePresentation(inst.name),
14251
14317
  };
14252
14318
  });
14253
14319
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -14504,6 +14570,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
14504
14570
  backend,
14505
14571
  effort,
14506
14572
  effort_source,
14573
+ ...this.instancePresentation(name),
14507
14574
  };
14508
14575
  });
14509
14576
  return {