@songsid/agend 2.3.2-beta.3 → 2.3.2-beta.5
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/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent-cli.js +21 -2
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +5 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/antigravity-usage-scan.d.ts +58 -0
- package/dist/antigravity-usage-scan.js +257 -0
- package/dist/antigravity-usage-scan.js.map +1 -0
- package/dist/antigravity-usage-worker.d.ts +1 -0
- package/dist/antigravity-usage-worker.js +10 -0
- package/dist/antigravity-usage-worker.js.map +1 -0
- package/dist/auth-api.js +2 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +45 -0
- package/dist/backend/kiro-identity.js +74 -0
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +36 -1
- package/dist/backend/kiro.js +131 -6
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +6 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-signin-guide.d.ts +1 -0
- package/dist/backend-signin-guide.js +9 -0
- package/dist/backend-signin-guide.js.map +1 -0
- package/dist/backend-signin-pending.d.ts +10 -0
- package/dist/backend-signin-pending.js +44 -0
- package/dist/backend-signin-pending.js.map +1 -0
- package/dist/browser-backend-signins.d.ts +56 -0
- package/dist/browser-backend-signins.js +189 -0
- package/dist/browser-backend-signins.js.map +1 -0
- package/dist/cache-ledger.d.ts +38 -2
- package/dist/cache-ledger.js +133 -8
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-prices.d.ts +2 -1
- package/dist/cache-prices.js +5 -2
- package/dist/cache-prices.js.map +1 -1
- package/dist/cache-service.d.ts +68 -0
- package/dist/cache-service.js +355 -13
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.js +7 -4
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +7 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/classic-channel-manager.d.ts +5 -0
- package/dist/classic-channel-manager.js +14 -1
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +21 -2
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +22 -5
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.js +31 -1
- package/dist/config.js.map +1 -1
- package/dist/cpu-quota.d.ts +23 -0
- package/dist/cpu-quota.js +86 -0
- package/dist/cpu-quota.js.map +1 -0
- package/dist/daemon.js +22 -7
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard-permissions.d.ts +20 -6
- package/dist/dashboard-permissions.js +58 -9
- package/dist/dashboard-permissions.js.map +1 -1
- package/dist/fleet-manager.d.ts +51 -1
- package/dist/fleet-manager.js +416 -32
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +5 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +14 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +24 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +24 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +6 -3
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +12 -5
- package/dist/instance-config-impact.js +10 -5
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +17 -0
- package/dist/instance-lifecycle.js +32 -4
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/kiro-db-reader.d.ts +5 -0
- package/dist/kiro-db-reader.js +18 -0
- package/dist/kiro-db-reader.js.map +1 -1
- package/dist/kiro-engine-status.d.ts +2 -0
- package/dist/kiro-engine-status.js +9 -0
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-migrate.d.ts +49 -0
- package/dist/kiro-migrate.js +105 -0
- package/dist/kiro-migrate.js.map +1 -0
- package/dist/kiro-transcript-lane.d.ts +2 -0
- package/dist/kiro-transcript-lane.js.map +1 -1
- package/dist/kiro-transcript-worker.js +1 -0
- package/dist/kiro-transcript-worker.js.map +1 -1
- package/dist/kiro-usage-run.d.ts +7 -0
- package/dist/kiro-usage-run.js +39 -0
- package/dist/kiro-usage-run.js.map +1 -0
- package/dist/kiro-usage-scan.d.ts +49 -0
- package/dist/kiro-usage-scan.js +223 -0
- package/dist/kiro-usage-scan.js.map +1 -0
- package/dist/kiro-usage-worker.d.ts +1 -0
- package/dist/kiro-usage-worker.js +10 -0
- package/dist/kiro-usage-worker.js.map +1 -0
- package/dist/kiro-usage.d.ts +58 -0
- package/dist/kiro-usage.js +310 -0
- package/dist/kiro-usage.js.map +1 -0
- package/dist/locale.js +72 -8
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/login-url.d.ts +4 -0
- package/dist/login-url.js +22 -0
- package/dist/login-url.js.map +1 -0
- package/dist/needs-you-hub.d.ts +2 -1
- package/dist/needs-you-hub.js +1 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +8 -2
- package/dist/needs-you.js +6 -0
- package/dist/needs-you.js.map +1 -1
- package/dist/notice-delivery.d.ts +15 -0
- package/dist/notice-delivery.js +37 -0
- package/dist/notice-delivery.js.map +1 -0
- package/dist/opencode-usage-scan.d.ts +28 -0
- package/dist/opencode-usage-scan.js +92 -0
- package/dist/opencode-usage-scan.js.map +1 -0
- package/dist/opencode-usage-worker.d.ts +1 -0
- package/dist/opencode-usage-worker.js +10 -0
- package/dist/opencode-usage-worker.js.map +1 -0
- package/dist/outbound-handlers.js +22 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +24 -0
- package/dist/outbound-schemas.js +21 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +1 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/quickstart-api.js +13 -2
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.js +4 -1
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-triggers.d.ts +2 -0
- package/dist/reply-triggers.js +3 -0
- package/dist/reply-triggers.js.map +1 -0
- package/dist/settings-api.js +27 -4
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +17 -14
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +7 -1
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-instance-patch.js +4 -1
- package/dist/settings-instance-patch.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +7 -1
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-host.d.ts +9 -0
- package/dist/setup-host.js +126 -6
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-signin-assets.d.ts +2 -0
- package/dist/setup-signin-assets.js +6 -0
- package/dist/setup-signin-assets.js.map +1 -0
- package/dist/setup-wizard.js +4 -0
- package/dist/setup-wizard.js.map +1 -1
- package/dist/structured-signin-transport.d.ts +8 -0
- package/dist/structured-signin-transport.js +79 -0
- package/dist/structured-signin-transport.js.map +1 -0
- package/dist/structured-signin.d.ts +131 -0
- package/dist/structured-signin.js +388 -0
- package/dist/structured-signin.js.map +1 -0
- package/dist/tool-permissions.js +12 -0
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.js +6 -2
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-sources.d.ts +26 -2
- package/dist/transcript-sources.js +87 -40
- package/dist/transcript-sources.js.map +1 -1
- package/dist/turn-reply-guard.d.ts +1 -1
- package/dist/turn-reply-guard.js +1 -1
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +19 -8
- package/dist/ui/backend-signin.js +63 -10
- package/dist/ui/fleet-office-editor.js +10 -6
- package/dist/ui/fleet-office-layout.js +34 -28
- package/dist/ui/fleet-office-model.js +6 -3
- package/dist/ui/fleet-office-overview.js +2 -2
- package/dist/ui/fleet-office-pack.d.ts +3 -1
- package/dist/ui/fleet-office-pack.js +58 -7
- package/dist/ui/fleet-office-skin.js +13 -4
- package/dist/ui/fleet-office-store.js +27 -1
- package/dist/ui/fleet-office-view.js +5 -4
- package/dist/ui/fleet-office-zones.js +155 -6
- package/dist/ui/fleet-office.js +169 -41
- package/dist/ui/fleet-usage.js +19 -15
- package/dist/ui/panel-log.js +2 -2
- package/dist/ui/panel-needs.js +4 -4
- package/dist/ui/panel-settings.js +32 -10
- package/dist/ui/settings-dialogs.js +8 -1
- package/dist/ui/settings-model.js +14 -6
- package/dist/ui/settings-registry.js +4 -0
- package/dist/ui/settings-strings.js +14 -0
- package/dist/ui/shared/app-i18n.js +4 -2
- package/dist/ui/shared/app-shell.js +14 -6
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +18 -2
- package/dist/ui/shared/side-panel.js +95 -0
- package/dist/ui/structured-signin.js +115 -0
- package/dist/usage-report.d.ts +20 -0
- package/dist/usage-report.js +54 -17
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.d.ts +9 -1
- package/dist/usage-rollup.js +20 -4
- package/dist/usage-rollup.js.map +1 -1
- package/dist/web-api.d.ts +4 -0
- package/dist/web-api.js +110 -0
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +1 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.d.ts +1 -7
- package/dist/web-backend-login.js +2 -24
- package/dist/web-backend-login.js.map +1 -1
- package/dist/web-office-acts.d.ts +173 -0
- package/dist/web-office-acts.js +285 -0
- package/dist/web-office-acts.js.map +1 -0
- package/dist/web-office-skins.d.ts +1 -1
- package/dist/web-office-skins.js +1 -1
- package/dist/web-office-skins.js.map +1 -1
- package/dist/web-settings-patch.d.ts +2 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/package.json +2 -1
- package/templates/windows-shortcuts.ps1 +18 -7
package/dist/fleet-manager.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { hasLegacyOffice, normalizeOfficeStorage } from "./dashboard-permissions.js";
|
|
2
|
+
import { loginNoticeMode, tipNoticeMode, normalizeNoticeDelivery, setNoticeMode } from "./notice-delivery.js";
|
|
3
|
+
import { DEFAULT_REPLY_TRIGGERS } from "./reply-triggers.js";
|
|
1
4
|
import { WorkingRecoveryCoordinator, WORKING_RECOVERY_KIND, buildWorkingRecoveryPrompt } from "./working-recovery.js";
|
|
2
5
|
import { PUBLIC_TUNNEL_TOKEN_ENV, PUBLIC_TUNNEL_TOKEN_HINT, normalizePublicTunnelToken } from "./public-tunnel-config.js";
|
|
3
6
|
import { humanOwnerObjectId, webHumanOrigin } from "./human-delivery-receipts.js";
|
|
@@ -28,13 +31,16 @@ import { ProfileControlServer } from "./profile-control.js";
|
|
|
28
31
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, rmSync, readdirSync, renameSync, copyFileSync, chmodSync, statSync, accessSync, realpathSync, lstatSync, constants as fsConstants, closeSync } from "node:fs";
|
|
29
32
|
import { atomicWriteFileSync } from "./atomic-write.js";
|
|
30
33
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
31
|
-
import { freemem, totalmem,
|
|
34
|
+
import { freemem, totalmem, homedir } from "node:os";
|
|
35
|
+
import { effectiveCpuCount } from "./cpu-quota.js";
|
|
32
36
|
import { createServer } from "node:http";
|
|
33
37
|
import { Worker } from "node:worker_threads";
|
|
34
38
|
import { ProbeWorkerPool } from "./probe-worker-pool.js";
|
|
35
39
|
import { join, dirname, basename, delimiter, isAbsolute, relative, resolve as resolvePath } from "node:path";
|
|
36
40
|
import { fileURLToPath } from "node:url";
|
|
41
|
+
import { isDeepStrictEqual } from "node:util";
|
|
37
42
|
import { getAgendHome, ensureWorkspaceGit } from "./paths.js";
|
|
43
|
+
import { applyKiroMigration, kiroMigrationPrompt, planKiroMigration } from "./kiro-migrate.js";
|
|
38
44
|
import { fleetLabel } from "./fleet-label.js";
|
|
39
45
|
import { beginFullRestartProgress as persistFullRestartProgress, beginUpdateProgress as persistUpdateProgress, clearUpdateMarker, isUpdateInProgress, readUpdateProgress, sameUpdateMarker, setUpdateProgressStage, updateMarkerIdentity, updateProgressOperation, updateReceiptOf, } from "./update-marker.js";
|
|
40
46
|
import { formatUpdateProgress } from "./update-progress.js";
|
|
@@ -53,7 +59,7 @@ const __dirname = dirname(__filename);
|
|
|
53
59
|
const DEFAULT_OPEN_ACCESS = { mode: "open", allowed_users: [], max_pending_codes: 0, code_expiry_minutes: 0 };
|
|
54
60
|
import { STATUS_EMOJI_CONFIG_KEYS, STATUS_EMOJI_KEYS, STATUS_EMOJI_SUGGESTIONS, TELEGRAM_REACTION_EMOJIS, builtinStatusEmojis, customEmojiValue, emojiImageUrl, normalizeEmoji, previewStatusEmojis, reactionForm, reactionMatchKey, resolveStatusEmojis, statusAvoidList, statusEmojiProblem, statusMatchKey, statusMatchKeys, textForm, } from "./status-emojis.js";
|
|
55
61
|
import { isProbeableRouteTarget } from "./fleet-context.js";
|
|
56
|
-
import { getEffectiveInstanceDefaults, loadFleetConfig, loadRawFleetConfig, DEFAULT_COST_GUARD, DEFAULT_DAILY_SUMMARY, DEFAULT_INSTANCE_CONFIG } from "./config.js";
|
|
62
|
+
import { getEffectiveInstanceDefaults, loadFleetConfig, loadRawFleetConfig, DEFAULT_COST_GUARD, DEFAULT_DAILY_SUMMARY, DEFAULT_INSTANCE_CONFIG, autoGeneralInstanceConfig } from "./config.js";
|
|
57
63
|
import { EventLog } from "./event-log.js";
|
|
58
64
|
import { binaryProbe } from "./binary-probe.js";
|
|
59
65
|
import { AVAILABILITY_STALE_MS, BackendAvailabilityStore, hostAvailabilityProbes } from "./backend-availability.js";
|
|
@@ -160,6 +166,7 @@ import { OwnerCssStore } from "./web-owner-css-store.js";
|
|
|
160
166
|
import { ThemeStore } from "./web-theme-store.js";
|
|
161
167
|
import { OfficeStore, OfficeWriteLimiter } from "./web-office-store.js";
|
|
162
168
|
import { OfficeSkinStore } from "./web-office-skins.js";
|
|
169
|
+
import { OfficeActs } from "./web-office-acts.js";
|
|
163
170
|
import { WebAppearance } from "./web-appearance.js";
|
|
164
171
|
import { WebEffectLibrary } from "./web-effect-library.js";
|
|
165
172
|
import { WebEffectTrials } from "./web-effect-trials.js";
|
|
@@ -189,7 +196,7 @@ import { discordGeneralChannelId } from "./discord-general.js";
|
|
|
189
196
|
import { parseEnvText } from "./env-file.js";
|
|
190
197
|
import { isDisallowedIntentsError, isRejectedTokenError } from "./discord-permissions.js";
|
|
191
198
|
import { filterUsageProviders, formatDiscordUsageActivity, getUsageSnapshot, handleUsageRequest, isUsagePath, usageProviderIdForBackend } from "./usage/usage-api.js";
|
|
192
|
-
import { LOGIN_FLOWS, LOGIN_BACKEND_ALIASES } from "./login-flows.js";
|
|
199
|
+
import { LOGIN_FLOWS, checkAuthStatus, LOGIN_BACKEND_ALIASES } from "./login-flows.js";
|
|
193
200
|
import { LoginSession } from "./login-manager.js";
|
|
194
201
|
import { runOnLoginShell } from "./login-shell.js";
|
|
195
202
|
import { tightenInstanceDirs } from "./private-dir.js";
|
|
@@ -209,6 +216,10 @@ import { admitProfileLogin, LoginController, LOGIN_TOKEN_RESEND_PREFIX, POST_LOG
|
|
|
209
216
|
import { WebBackendLogins, dashboardSignInSupported } from "./web-backend-login.js";
|
|
210
217
|
import { runBeforeDeadline } from "./deadline.js";
|
|
211
218
|
import { LoginWindowLock } from "./login-window-lock.js";
|
|
219
|
+
import { BrowserBackendSignIns } from "./browser-backend-signins.js";
|
|
220
|
+
import { createStructuredSignInTransport } from "./structured-signin-transport.js";
|
|
221
|
+
import { ONBOARDING_BACKENDS, readPendingBackendSignIns } from "./backend-signin-pending.js";
|
|
222
|
+
import { structuredSignInSupported } from "./structured-signin.js";
|
|
212
223
|
import { handleSettingsRequest } from "./settings-api.js";
|
|
213
224
|
import { setLocale, detectLocale, getLocale, t } from "./locale.js";
|
|
214
225
|
import { recentChatContext } from "./log-tail.js";
|
|
@@ -272,6 +283,7 @@ import { PROVIDER_SECRET_SPECS, PUBLIC_TUNNEL_SECRET_SPEC, providerSecretSpec, p
|
|
|
272
283
|
/** A self-restart is a whole service restart; 120s is the apply budget, not this. */
|
|
273
284
|
const SELF_RESTART_DEADLINE_MS = 300_000;
|
|
274
285
|
import { APPLY_FLEET_TARGET, ApplyJobStore, viewOf, } from "./apply-job.js";
|
|
286
|
+
import { kiroHome } from "./backend/kiro-kas-store.js";
|
|
275
287
|
import { CHAT_PROFILE_LOGIN_BACKENDS, credentialHomeSpec, credentialProfileHome, credentialProfileLogin, credentialProfileLoginCommand, credentialProfileStoreHome, credentialSwitchCarriesConversation, credentialSwitchStartsFresh, instanceCredentialProfile } from "./backend/credential-profile.js";
|
|
276
288
|
import { carryClaudeConversation } from "./backend/claude-carry.js";
|
|
277
289
|
import { mergeBackendOptions, profileKey, profileName, profileOf } from "./classic-bindings.js";
|
|
@@ -469,6 +481,10 @@ const OOM_RETRY_CALLBACK_PREFIX = "oom-retry:";
|
|
|
469
481
|
const NEEDS_PAUSE_TTL_MS = 10_000;
|
|
470
482
|
const HANG_CALLBACK_PREFIX = "hang:";
|
|
471
483
|
const CLEAR_CONFIRM_CALLBACK_PREFIX = "clear-confirm:";
|
|
484
|
+
/** #2598 B2: a kiro classic → TUI move's Confirm in General. "kiro-migrate:" (13) + 32 hex + ":confirm" (8) = 53 bytes. */
|
|
485
|
+
const KIRO_MIGRATE_CALLBACK_PREFIX = "kiro-migrate:";
|
|
486
|
+
/** Long enough for an admin to read the prompt; the move is judged again on the click. */
|
|
487
|
+
const KIRO_MIGRATE_TIMEOUT_MS = 10 * 60_000;
|
|
472
488
|
/**
|
|
473
489
|
* The prompts the web dashboard also offers: the ones about an instance's own health, which a dashboard
|
|
474
490
|
* user — holding the full-fleet web credential — may answer exactly as a fleet admin on the platform may.
|
|
@@ -564,6 +580,7 @@ export const CLI_ENV_PROBE_DEADLINE_MS = 16_000;
|
|
|
564
580
|
* started ten CLIs together, saturated the CPU, and healthy starts then missed
|
|
565
581
|
* their startup budget — which used to cost the user their conversation. Cores
|
|
566
582
|
* bound how many can actually make progress; memory bounds how many fit.
|
|
583
|
+
* `cores` is what this process may use (cpuset and cgroup quota, #749), not the host's count.
|
|
567
584
|
*/
|
|
568
585
|
export function deriveSpawnConcurrency(freeMemMB, cores) {
|
|
569
586
|
const byMemory = Math.floor(freeMemMB / 300);
|
|
@@ -881,6 +898,8 @@ export class FleetManager {
|
|
|
881
898
|
pendingBackendSelects = new Map();
|
|
882
899
|
/** nonce → pending button prompt (hang restart, interactive assist, clean-exit restart). */
|
|
883
900
|
pendingNonceButtons = new Map();
|
|
901
|
+
/** #2598 B2: the kiro moves waiting on a fleet admin's Confirm in General, by prompt nonce. */
|
|
902
|
+
pendingKiroMigrations = new Map();
|
|
884
903
|
/** #1386 "Needs you": the list, its live chat messages and Acknowledge. Started with the fleet (finishStartup). */
|
|
885
904
|
needsYou = null;
|
|
886
905
|
/** When the fleet first saw an instance crashed, for its "Needs you" age; cleared when it is not. */
|
|
@@ -1147,6 +1166,53 @@ export class FleetManager {
|
|
|
1147
1166
|
officeSkinWrites = new OfficeWriteLimiter(undefined, { perWindow: 10, windowMs: 60_000 });
|
|
1148
1167
|
/** How often a person may save the offices (PUT /ui/offices); memory only, as the appearance limiter. */
|
|
1149
1168
|
officeWrites = new OfficeWriteLimiter();
|
|
1169
|
+
officeActsInstance;
|
|
1170
|
+
/** #2578: the agents' acts in the Office view (office_view / office_set), memory only; every change is `office_acts`. */
|
|
1171
|
+
get officeActs() {
|
|
1172
|
+
return (this.officeActsInstance ??= new OfficeActs({
|
|
1173
|
+
offices: () => this.officeStore.list().offices,
|
|
1174
|
+
pack: id => this.officeSkins.pack(id),
|
|
1175
|
+
chart: () => { const c = this.orgChart(); return { names: Object.keys(c.instances), teams: c.teams }; },
|
|
1176
|
+
people: () => this.officePeople(),
|
|
1177
|
+
changed: acts => this.emitSseEvent("office_acts", { acts, now: Date.now() }),
|
|
1178
|
+
}));
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* #2578: each instance's state as the org chart shows it (fleet-org.js orgState, from the same fields the status
|
|
1182
|
+
* frame and the Needs-you list carry) and its display name (resolveInstanceIdentity, as getUiStatus) — read once.
|
|
1183
|
+
*/
|
|
1184
|
+
officePeople() {
|
|
1185
|
+
const needs = new Map();
|
|
1186
|
+
for (const item of this.needsYouItems())
|
|
1187
|
+
needs.set(item.instance, (needs.get(item.instance) ?? 0) + 1);
|
|
1188
|
+
const classicRooms = new Map((this.classicChannels?.getAll() ?? []).map(ch => [ch.instanceName, ch]));
|
|
1189
|
+
let profiles = null;
|
|
1190
|
+
return {
|
|
1191
|
+
stateOf: name => {
|
|
1192
|
+
if (!this.isKnownInstance(name))
|
|
1193
|
+
return "missing";
|
|
1194
|
+
const p = this.instancePresentation(name), status = this.getInstanceStatus(name);
|
|
1195
|
+
if (p.state === "awaiting_input" || (needs.get(name) ?? 0) > 0)
|
|
1196
|
+
return "needs";
|
|
1197
|
+
if (p.startup_phase === "queued" || p.startup_phase === "starting")
|
|
1198
|
+
return p.startup_phase;
|
|
1199
|
+
if (status === "crashed" || status === "paused")
|
|
1200
|
+
return status;
|
|
1201
|
+
if (status !== "running")
|
|
1202
|
+
return "stopped";
|
|
1203
|
+
return p.execution_state === "stuck" || p.execution_state === "working" ? p.execution_state : "idle";
|
|
1204
|
+
},
|
|
1205
|
+
displayName: name => {
|
|
1206
|
+
try {
|
|
1207
|
+
profiles ??= profileIdentities(this.dataDir);
|
|
1208
|
+
return resolveInstanceIdentity({ cfg: this.fleetConfig?.instances[name], classic: classicRooms.get(name), profile: profiles.get(name) }).display_name || undefined;
|
|
1209
|
+
}
|
|
1210
|
+
catch {
|
|
1211
|
+
return undefined;
|
|
1212
|
+
}
|
|
1213
|
+
},
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1150
1216
|
/** #1629: the theme tools, a person's reset and lock, and the one appearance rate limiter (shared with effects). */
|
|
1151
1217
|
get webAppearance() {
|
|
1152
1218
|
return (this.webAppearanceService ??= new WebAppearance(this.themeStore, () => this.fleetConfig?.web ?? null, () => { try {
|
|
@@ -1672,6 +1738,74 @@ export class FleetManager {
|
|
|
1672
1738
|
// No platform confirmation was reachable. Host inspection is still required.
|
|
1673
1739
|
this.logger.info({ id: view.id }, "Settings change requires local `agend settings confirm <id>`");
|
|
1674
1740
|
}
|
|
1741
|
+
kiroMigrateDeps() {
|
|
1742
|
+
return {
|
|
1743
|
+
agendHome: getAgendHome(),
|
|
1744
|
+
fleetConfig: () => this.fleetConfig,
|
|
1745
|
+
saveFleetConfig: () => this.saveFleetConfig(),
|
|
1746
|
+
status: name => this.getInstanceStatus(name),
|
|
1747
|
+
restart: name => this.restartSingleInstance(name, { explicit: true }),
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* #2598 B2, migrate_kiro_instance: judge the move, then ask in General for a fleet admin's Confirm. Nothing changes
|
|
1752
|
+
* here; the move runs on the Confirm (handleKiroMigrateCallback), judged again then.
|
|
1753
|
+
*/
|
|
1754
|
+
async requestKiroMigration(name, startFresh) {
|
|
1755
|
+
const judged = planKiroMigration(this.kiroMigrateDeps(), name, startFresh);
|
|
1756
|
+
if (!judged.ok)
|
|
1757
|
+
return judged;
|
|
1758
|
+
const general = this.findGeneralInstance();
|
|
1759
|
+
const adapterId = general ? this.getInstanceAdapterId(general) : undefined;
|
|
1760
|
+
const adapter = general ? this.getAdapterForInstance(general) : undefined;
|
|
1761
|
+
const chatId = general ? this.getGroupIdForInstance(general) : undefined;
|
|
1762
|
+
const topic = general ? String(this.fleetConfig?.instances[general]?.topic_id ?? "") : "";
|
|
1763
|
+
if (!general || !adapterId || !adapter || !chatId)
|
|
1764
|
+
return { ok: false, error: t("kiro_migrate.no_general") };
|
|
1765
|
+
const { plan } = judged;
|
|
1766
|
+
try {
|
|
1767
|
+
const nonce = await this.postNonceButtonPromptOrThrow({
|
|
1768
|
+
prefix: KIRO_MIGRATE_CALLBACK_PREFIX, alertType: "clear_confirm", instanceName: name, adapter, adapterId, chatId,
|
|
1769
|
+
...(topic ? { threadId: topic } : {}),
|
|
1770
|
+
message: kiroMigrationPrompt(plan, this.getInstanceStatus(name) === "running"),
|
|
1771
|
+
choices: [{ action: "confirm", label: t("kiro_migrate.confirm_button") }, { action: "cancel", label: t("kiro_migrate.cancel_button") }],
|
|
1772
|
+
expiredText: t("kiro_migrate.expired", name),
|
|
1773
|
+
timeoutMs: KIRO_MIGRATE_TIMEOUT_MS,
|
|
1774
|
+
});
|
|
1775
|
+
this.pendingKiroMigrations.set(nonce, plan);
|
|
1776
|
+
const expiry = setTimeout(() => this.pendingKiroMigrations.delete(nonce), KIRO_MIGRATE_TIMEOUT_MS + 60_000);
|
|
1777
|
+
expiry.unref?.();
|
|
1778
|
+
}
|
|
1779
|
+
catch (err) {
|
|
1780
|
+
return { ok: false, error: `${t("kiro_migrate.no_general")} (${err.message})` };
|
|
1781
|
+
}
|
|
1782
|
+
return { ok: true, text: t("kiro_migrate.requested"), classic_conversation: plan.classicId };
|
|
1783
|
+
}
|
|
1784
|
+
/** A fleet admin's Confirm or Cancel on a kiro move. Any fleet admin (the default nonce authorization), in General. */
|
|
1785
|
+
async handleKiroMigrateCallback(data, adapterId, adapter) {
|
|
1786
|
+
const claimed = this.consumeNonceCallback(KIRO_MIGRATE_CALLBACK_PREFIX, /^kiro-migrate:([0-9a-f]{32}):(confirm|cancel)$/, data, adapterId, adapter);
|
|
1787
|
+
if (claimed === null)
|
|
1788
|
+
return false;
|
|
1789
|
+
if (claimed === "consumed")
|
|
1790
|
+
return true;
|
|
1791
|
+
const { entry, action } = claimed;
|
|
1792
|
+
const nonce = data.callbackData.slice(KIRO_MIGRATE_CALLBACK_PREFIX.length, KIRO_MIGRATE_CALLBACK_PREFIX.length + 32);
|
|
1793
|
+
const plan = this.pendingKiroMigrations.get(nonce);
|
|
1794
|
+
this.pendingKiroMigrations.delete(nonce);
|
|
1795
|
+
const messageId = entry.messageId ?? data.messageId;
|
|
1796
|
+
if (action === "cancel" || !plan) {
|
|
1797
|
+
await this.retireNonceButtons(entry, messageId, t(plan ? "kiro_migrate.cancelled" : "kiro_migrate.expired", entry.instanceName));
|
|
1798
|
+
return true;
|
|
1799
|
+
}
|
|
1800
|
+
this.eventLog?.insert(plan.name, "kiro_migrate", { classicId: plan.classicId, userId: data.userId });
|
|
1801
|
+
await this.retireNonceButtons(entry, messageId, t("kiro_migrate.applying", plan.name));
|
|
1802
|
+
const outcome = await applyKiroMigration(this.kiroMigrateDeps(), plan);
|
|
1803
|
+
this.logger.info({ name: plan.name, ok: outcome.ok, classicId: plan.classicId }, "kiro classic → TUI move");
|
|
1804
|
+
this.notifyFleetError(outcome.text);
|
|
1805
|
+
if (outcome.ok)
|
|
1806
|
+
this.notifyInstanceTopic(plan.name, outcome.text);
|
|
1807
|
+
return true;
|
|
1808
|
+
}
|
|
1675
1809
|
async handleSettingsChangeCallback(data, adapterId, adapter) {
|
|
1676
1810
|
const result = this.consumeNonceCallback("settings-confirm:", /^settings-confirm:([0-9a-f]{32}):(confirm|reject)$/, data, adapterId, adapter, undefined, (userId, entry) => entry.confirmationAdmin?.(userId) === true);
|
|
1677
1811
|
if (!result)
|
|
@@ -2525,7 +2659,7 @@ export class FleetManager {
|
|
|
2525
2659
|
const explicit = this.fleetConfig?.defaults?.startup?.concurrency;
|
|
2526
2660
|
if (explicit != null)
|
|
2527
2661
|
return Math.max(1, Math.min(20, explicit));
|
|
2528
|
-
return deriveSpawnConcurrency(Math.round(freemem() / (1024 * 1024)),
|
|
2662
|
+
return deriveSpawnConcurrency(Math.round(freemem() / (1024 * 1024)), effectiveCpuCount());
|
|
2529
2663
|
}
|
|
2530
2664
|
/** #1685: "elevated" or "critical" while host memory pressure slows or holds new agents; otherwise null (an
|
|
2531
2665
|
* unknown macOS reading — the advisory case — is unknown, so null too). */
|
|
@@ -3308,7 +3442,7 @@ export class FleetManager {
|
|
|
3308
3442
|
await data.respond(t("permission.denied"));
|
|
3309
3443
|
return;
|
|
3310
3444
|
}
|
|
3311
|
-
this.fleetConfig
|
|
3445
|
+
setNoticeMode(this.fleetConfig, "tips", mode === "on");
|
|
3312
3446
|
this.saveFleetConfig();
|
|
3313
3447
|
await data.respond(t(mode === "on" ? "tips.enabled" : "tips.disabled"));
|
|
3314
3448
|
return;
|
|
@@ -5098,6 +5232,7 @@ export class FleetManager {
|
|
|
5098
5232
|
}
|
|
5099
5233
|
classicBehaviorUpdate(instanceName) {
|
|
5100
5234
|
return {
|
|
5235
|
+
reply_triggers: this.classicChannels?.getReplyTriggersByInstance(instanceName, this.fleetConfig?.defaults?.reply_triggers) ?? DEFAULT_REPLY_TRIGGERS,
|
|
5101
5236
|
tool_progress: this.classicChannels?.getToolProgressByInstance(instanceName, this.fleetConfig?.defaults?.tool_progress) ?? "off",
|
|
5102
5237
|
reply_completion_guard: this.classicChannels?.getReplyCompletionGuardByInstance(instanceName, this.fleetConfig?.defaults?.reply_completion_guard) ?? true,
|
|
5103
5238
|
};
|
|
@@ -6446,11 +6581,33 @@ export class FleetManager {
|
|
|
6446
6581
|
getPublicWebStatus() {
|
|
6447
6582
|
return this.publicWebLink?.status() ?? { state: "closed" };
|
|
6448
6583
|
}
|
|
6584
|
+
/** Notices use the first explicit admin of the owning connection; never a guest/paired account. */
|
|
6585
|
+
noticeAdmin(adapterId) {
|
|
6586
|
+
return this.adminListOf(adapterId)?.find(id => id.length > 0);
|
|
6587
|
+
}
|
|
6588
|
+
/** Best-effort private delivery. The caller retains its existing topic fallback and authority fences. */
|
|
6589
|
+
async tryNoticeDm(adapter, userId, text) {
|
|
6590
|
+
if (!userId || !adapter?.sendDirect)
|
|
6591
|
+
return false;
|
|
6592
|
+
try {
|
|
6593
|
+
await withinBudget(adapter.sendDirect(userId, text, { disablePreview: true }), performance.now() + 2_000);
|
|
6594
|
+
return true;
|
|
6595
|
+
}
|
|
6596
|
+
catch {
|
|
6597
|
+
this.logger.info({ adapterId: adapter.id }, "Fleet notice DM failed; falling back to the topic");
|
|
6598
|
+
return false;
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6449
6601
|
async confirmPublicWebLogin(info, current) {
|
|
6450
6602
|
if (!current() || !this.publicOwnerCurrent(info.owner))
|
|
6451
6603
|
throw new Error("public owner unavailable");
|
|
6452
6604
|
const adapter = info.owner.binding;
|
|
6453
|
-
|
|
6605
|
+
const text = t("web.public_login_notice", info.label, info.handle.slice(0, 8));
|
|
6606
|
+
if (loginNoticeMode(this.fleetConfig?.web) !== "dm" || !await this.tryNoticeDm(adapter, info.owner.userId, text)) {
|
|
6607
|
+
if (!current() || !this.publicOwnerCurrent(info.owner))
|
|
6608
|
+
throw new Error("public owner changed");
|
|
6609
|
+
await adapter.sendText(info.owner.chatId, text, { threadId: info.owner.threadId, allowedMentions: { parse: [] } });
|
|
6610
|
+
}
|
|
6454
6611
|
if (!current() || !this.publicOwnerCurrent(info.owner))
|
|
6455
6612
|
throw new Error("public owner changed");
|
|
6456
6613
|
}
|
|
@@ -6468,7 +6625,12 @@ export class FleetManager {
|
|
|
6468
6625
|
if (!open)
|
|
6469
6626
|
throw new Error("guest public sign-in unavailable");
|
|
6470
6627
|
const adapter = open.owner.binding;
|
|
6471
|
-
|
|
6628
|
+
const text = t("web.guest_public_login_notice", this.webPerson(info.account).name, info.label, this.guestScope(info.account).size, info.handle.slice(0, 8));
|
|
6629
|
+
if (loginNoticeMode(this.fleetConfig?.web) !== "dm" || !await this.tryNoticeDm(adapter, open.owner.userId, text)) {
|
|
6630
|
+
if (!admitted())
|
|
6631
|
+
throw new Error("guest public sign-in changed");
|
|
6632
|
+
await adapter.sendText(open.owner.chatId, text, { threadId: open.owner.threadId, allowedMentions: { parse: [] } });
|
|
6633
|
+
}
|
|
6472
6634
|
if (!admitted())
|
|
6473
6635
|
throw new Error("guest public sign-in changed");
|
|
6474
6636
|
}
|
|
@@ -7306,17 +7468,26 @@ export class FleetManager {
|
|
|
7306
7468
|
}
|
|
7307
7469
|
/** Called by the sign-in endpoint: a login the operator did not make should be visible to them. */
|
|
7308
7470
|
onWebLogin(info) {
|
|
7309
|
-
//
|
|
7310
|
-
|
|
7311
|
-
if (
|
|
7312
|
-
|
|
7471
|
+
// Keep the existing false policy: guest and public-link security notices remain mandatory.
|
|
7472
|
+
const guest = info.tier === "guest" && info.owner;
|
|
7473
|
+
if (!guest && this.fleetConfig?.web?.notify_login === false)
|
|
7474
|
+
return;
|
|
7475
|
+
const text = guest
|
|
7476
|
+
? t("web.guest_login_notice", this.webPerson(info.owner).name, info.label, this.guestScope(info.owner).size, info.handle.slice(0, 8))
|
|
7477
|
+
: t("web.login_notice", info.label, info.surface, info.handle.slice(0, 8));
|
|
7478
|
+
if (loginNoticeMode(this.fleetConfig?.web) === "dm") {
|
|
7479
|
+
const adapterId = info.owner?.adapterId;
|
|
7480
|
+
const adapter = adapterId ? this.adapters.get(adapterId) : undefined;
|
|
7481
|
+
// A local guest's own account must never receive the admin's security notice.
|
|
7482
|
+
const userId = guest ? (adapterId ? this.noticeAdmin(adapterId) : undefined)
|
|
7483
|
+
: info.owner && this.isFleetAdmin(info.owner.userId, adapterId) ? info.owner.userId : undefined;
|
|
7484
|
+
void this.tryNoticeDm(adapter, userId, text).then(sent => { if (!sent)
|
|
7485
|
+
this.notifyFleetError(text); })
|
|
7486
|
+
.catch(() => this.logger.warn("Web sign-in notice could not be delivered"));
|
|
7313
7487
|
return;
|
|
7314
7488
|
}
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
// The session handle makes each notice distinct: notifyFleetError throttles by text, and a second
|
|
7318
|
-
// sign-in from the same kind of browser is exactly the one the operator most needs to hear about.
|
|
7319
|
-
this.notifyFleetError(t("web.login_notice", info.label, info.surface, info.handle.slice(0, 8)));
|
|
7489
|
+
// Exactly the old topic path, including the session-handle throttle key.
|
|
7490
|
+
this.notifyFleetError(text);
|
|
7320
7491
|
}
|
|
7321
7492
|
getDashboardAccess() {
|
|
7322
7493
|
return { ready: this.healthServerListening, token: this.webToken };
|
|
@@ -7686,7 +7857,8 @@ export class FleetManager {
|
|
|
7686
7857
|
}
|
|
7687
7858
|
}
|
|
7688
7859
|
async startAllWithinDeadline(configPath, deadline) {
|
|
7689
|
-
this.
|
|
7860
|
+
if (!this.structuredLoginCleanupFailed)
|
|
7861
|
+
this.loginWindow.reopen(); // a stopAll → startAll restart must accept login windows again
|
|
7690
7862
|
this.loginController?.reopen();
|
|
7691
7863
|
const startupStartedAt = Date.now();
|
|
7692
7864
|
FleetManager.signalTarget = this;
|
|
@@ -7947,12 +8119,7 @@ export class FleetManager {
|
|
|
7947
8119
|
mkdirSync(generalDir, { recursive: true, mode: 0o700 });
|
|
7948
8120
|
const backendName = fleet.defaults.backend ?? "claude-code";
|
|
7949
8121
|
this.ensureGeneralInstructions(generalDir, backendName, name);
|
|
7950
|
-
fleet.instances[name] =
|
|
7951
|
-
...DEFAULT_INSTANCE_CONFIG,
|
|
7952
|
-
working_directory: generalDir,
|
|
7953
|
-
general_topic: true,
|
|
7954
|
-
channel_id: need.adapterId,
|
|
7955
|
-
};
|
|
8122
|
+
fleet.instances[name] = autoGeneralInstanceConfig(fleet.defaults, generalDir, need.adapterId); // #2598: its kiro_ui is the fleet's
|
|
7956
8123
|
generalsCreated = true;
|
|
7957
8124
|
}
|
|
7958
8125
|
if (generalsCreated)
|
|
@@ -11122,7 +11289,7 @@ export class FleetManager {
|
|
|
11122
11289
|
if (!ipc || !fleetRequestId)
|
|
11123
11290
|
return;
|
|
11124
11291
|
const type = String(msg.type);
|
|
11125
|
-
const responseType = isCanvasWorkerTool(type.replace(/^fleet_/, "")) ? "fleet_web_theme_response"
|
|
11292
|
+
const responseType = isCanvasWorkerTool(type.replace(/^fleet_/, "")) || type === "fleet_office_view" || type === "fleet_office_set" ? "fleet_web_theme_response"
|
|
11126
11293
|
: type.startsWith("fleet_schedule_") ? "fleet_schedule_response"
|
|
11127
11294
|
: type.startsWith("fleet_decision_") ? "fleet_decision_response"
|
|
11128
11295
|
: type === "fleet_task" ? "fleet_task_response"
|
|
@@ -11176,7 +11343,8 @@ export class FleetManager {
|
|
|
11176
11343
|
|| type === "fleet_get_web_appearance" || type === "fleet_preview_web_appearance"
|
|
11177
11344
|
|| type === "fleet_set_web_widget" || type === "fleet_remove_web_widget"
|
|
11178
11345
|
|| type === "fleet_set_web_ambient" || type === "fleet_clear_web_ambient"
|
|
11179
|
-
|| type === "fleet_export_web_mod" || type === "fleet_propose_web_mod"
|
|
11346
|
+
|| type === "fleet_export_web_mod" || type === "fleet_propose_web_mod"
|
|
11347
|
+
|| type === "fleet_office_view" || type === "fleet_office_set") {
|
|
11180
11348
|
this.handleWebTheme(name, msg);
|
|
11181
11349
|
return;
|
|
11182
11350
|
}
|
|
@@ -11197,7 +11365,8 @@ export class FleetManager {
|
|
|
11197
11365
|
const result = tool === "get_web_appearance" || tool === "preview_web_appearance"
|
|
11198
11366
|
? this.appearanceTools.handle(instanceName, tool, args)
|
|
11199
11367
|
: tool === "export_web_mod" || tool === "propose_web_mod" ? this.agentMods.handle(instanceName, tool, args)
|
|
11200
|
-
: this.
|
|
11368
|
+
: tool === "office_view" || tool === "office_set" ? this.officeActs.handle(instanceName, tool, args)
|
|
11369
|
+
: this.webAppearance.handleTool(instanceName, tool, args);
|
|
11201
11370
|
ipc.send({ type: "fleet_web_theme_response", fleetRequestId, result });
|
|
11202
11371
|
}
|
|
11203
11372
|
catch (err) {
|
|
@@ -11633,6 +11802,8 @@ export class FleetManager {
|
|
|
11633
11802
|
return this.appearanceTools.handle(instance, tool, args);
|
|
11634
11803
|
if (tool === "export_web_mod" || tool === "propose_web_mod")
|
|
11635
11804
|
return this.agentMods.handle(instance, tool, args);
|
|
11805
|
+
if (tool === "office_view" || tool === "office_set")
|
|
11806
|
+
return this.officeActs.handle(instance, tool, args); // #2578
|
|
11636
11807
|
return this.webAppearance.handleTool(instance, tool, args);
|
|
11637
11808
|
}
|
|
11638
11809
|
async handlePreviewEmojisHttp(instance, args) {
|
|
@@ -12067,6 +12238,23 @@ export class FleetManager {
|
|
|
12067
12238
|
return;
|
|
12068
12239
|
if (!this.savedFleetConfigSnapshot)
|
|
12069
12240
|
this.savedFleetConfigSnapshot = structuredClone(this.fleetConfig);
|
|
12241
|
+
normalizeNoticeDelivery(this.fleetConfig);
|
|
12242
|
+
normalizeOfficeStorage(this.fleetConfig);
|
|
12243
|
+
explicitPatches = explicitPatches.flatMap(patch => {
|
|
12244
|
+
const path = patch.path;
|
|
12245
|
+
if (path.at(-2) === "dashboard" && path.at(-1) === "office" && !patch.remove) {
|
|
12246
|
+
return [{ ...patch, path: [...path.slice(0, -2), "office_access"] }, { path, remove: true }];
|
|
12247
|
+
}
|
|
12248
|
+
if (path.at(-1) === "dashboard" && patch.value && typeof patch.value === "object" && Object.hasOwn(patch.value, "office")) {
|
|
12249
|
+
const { office, ...dashboard } = patch.value;
|
|
12250
|
+
return [{ ...patch, value: dashboard }, { path: [...path.slice(0, -1), "office_access"], value: office, remove: office === null }];
|
|
12251
|
+
}
|
|
12252
|
+
if (patch.path.join(".") === "web.notify_login" && patch.value === "dm")
|
|
12253
|
+
return [{ ...patch, value: true }, { path: ["web", "notice_delivery", "login"], value: "dm" }];
|
|
12254
|
+
if (patch.path.join(".") === "defaults.tips" && patch.value === "dm")
|
|
12255
|
+
return [{ ...patch, value: true }, { path: ["web", "notice_delivery", "tips"], value: "dm" }];
|
|
12256
|
+
return [patch];
|
|
12257
|
+
});
|
|
12070
12258
|
// Re-read immediately before patching so an unrelated concurrent/manual
|
|
12071
12259
|
// edit is retained. Invalid concurrent YAML is never overwritten.
|
|
12072
12260
|
const source = existsSync(this.configPath) ? readFileSync(this.configPath, "utf-8") : "{}\n";
|
|
@@ -12142,9 +12330,46 @@ export class FleetManager {
|
|
|
12142
12330
|
},
|
|
12143
12331
|
});
|
|
12144
12332
|
this.rawFleetConfig = loadRawFleetConfig(this.configPath);
|
|
12333
|
+
// The instance map is defaults-expanded. A defaults edit changes that view even when it writes no instance
|
|
12334
|
+
// leaves. Re-resolve from the committed YAML so inherited values cannot become stale overrides on the next save.
|
|
12335
|
+
// Keep the FleetConfig object itself: Settings and other request contexts hold it.
|
|
12336
|
+
const resolvedInstances = loadFleetConfig(this.configPath).instances;
|
|
12337
|
+
for (const [name, resolved] of Object.entries(resolvedInstances)) {
|
|
12338
|
+
const current = this.fleetConfig.instances[name];
|
|
12339
|
+
if (!current) {
|
|
12340
|
+
this.fleetConfig.instances[name] = resolved;
|
|
12341
|
+
continue;
|
|
12342
|
+
}
|
|
12343
|
+
// Lifecycle rollback fences hold these object identities across save. Refresh their values, not their owner.
|
|
12344
|
+
for (const key of Object.keys(current))
|
|
12345
|
+
if (!Object.hasOwn(resolved, key))
|
|
12346
|
+
delete current[key];
|
|
12347
|
+
Object.assign(current, resolved);
|
|
12348
|
+
}
|
|
12145
12349
|
this.savedFleetConfigSnapshot = structuredClone(this.fleetConfig);
|
|
12350
|
+
this.refreshSavedDashboardPermissions();
|
|
12146
12351
|
this.logger.info({ path: this.configPath, strippedDefaults: redundantPaths.length }, "Saved fleet config (lossless patch)");
|
|
12147
12352
|
}
|
|
12353
|
+
/** A permission tightening is effective when its save returns, independently of the later apply job or IPC. */
|
|
12354
|
+
refreshSavedDashboardPermissions() {
|
|
12355
|
+
if (!this.fleetConfig)
|
|
12356
|
+
return;
|
|
12357
|
+
const classicNames = new Set(this.classicChannels?.getAll().map(ch => ch.instanceName) ?? []);
|
|
12358
|
+
for (const [name, daemon] of this.daemons) {
|
|
12359
|
+
const next = this.fleetConfig.instances[name] ?? (classicNames.has(name) ? getEffectiveInstanceDefaults(this.fleetConfig.defaults) : undefined);
|
|
12360
|
+
const previous = daemon.getConfigSnapshot?.();
|
|
12361
|
+
if (!next || !previous)
|
|
12362
|
+
continue;
|
|
12363
|
+
if (isDeepStrictEqual(previous.dashboard, next.dashboard) && previous.office_access === next.office_access)
|
|
12364
|
+
continue;
|
|
12365
|
+
// Only permissions are adopted here. Cold fields retain the live daemon's old snapshot, so reconcile still
|
|
12366
|
+
// detects that they require a restart. MCP menus also watch the raw file; this updates the next spawn's env.
|
|
12367
|
+
daemon.applyConfigUpdate({ dashboard: next.dashboard ?? null, office_access: next.office_access ?? null });
|
|
12368
|
+
if (taskBoardSwitchedOn(previous.dashboard, next.dashboard)) {
|
|
12369
|
+
this.admitTaskBoardNotice(name, TASK_BOARD_ON_NOTICE, `task-board-on:${name}:${Date.now()}`);
|
|
12370
|
+
}
|
|
12371
|
+
}
|
|
12372
|
+
}
|
|
12148
12373
|
/** Why the config written to `candidate` must not replace fleet.yaml, or null: it does not load, or it adds errors. */
|
|
12149
12374
|
savedFleetConfigProblem(candidate) {
|
|
12150
12375
|
let after;
|
|
@@ -12182,7 +12407,43 @@ export class FleetManager {
|
|
|
12182
12407
|
this.logger.warn({ path: w.path }, `fleet.yaml: ${w.path ? `${w.path}: ` : ""}${w.message}`);
|
|
12183
12408
|
}
|
|
12184
12409
|
/** One-time upgrade migration; invalid YAML is never rewritten. */
|
|
12410
|
+
migrateNoticeDeliveryStorage() {
|
|
12411
|
+
if (this.fleetConfig && this.configPath && (this.fleetConfig.web?.notify_login === "dm" || this.fleetConfig.defaults.tips === "dm")
|
|
12412
|
+
&& validateFleetConfig(this.rawFleetConfig).valid) {
|
|
12413
|
+
try {
|
|
12414
|
+
this.writeFleetConfigBackup(readFileSync(this.configPath, "utf8"));
|
|
12415
|
+
this.saveFleetConfig();
|
|
12416
|
+
this.logger.info("Stored DM notice routing with downgrade-safe boolean fields");
|
|
12417
|
+
}
|
|
12418
|
+
catch (err) {
|
|
12419
|
+
this.logger.warn({ err }, "Could not migrate DM notice routing; keeping the original file");
|
|
12420
|
+
}
|
|
12421
|
+
}
|
|
12422
|
+
}
|
|
12423
|
+
migrateOfficeStorage() {
|
|
12424
|
+
if (!this.fleetConfig || !this.configPath || !hasLegacyOffice(this.rawFleetConfig) || !validateFleetConfig(this.rawFleetConfig).valid)
|
|
12425
|
+
return;
|
|
12426
|
+
try {
|
|
12427
|
+
this.writeFleetConfigBackup(readFileSync(this.configPath, "utf8"));
|
|
12428
|
+
const patches = [];
|
|
12429
|
+
const raw = this.rawFleetConfig;
|
|
12430
|
+
for (const [path, item] of [[["defaults"], raw.defaults], ...Object.entries(raw.instances ?? {}).map(([name, value]) => [["instances", name], value])]) {
|
|
12431
|
+
if (!item?.dashboard || !Object.hasOwn(item.dashboard, "office"))
|
|
12432
|
+
continue;
|
|
12433
|
+
patches.push({ path: [...path, "dashboard", "office"], remove: true });
|
|
12434
|
+
if (!Object.hasOwn(item, "office_access") && typeof item.dashboard.office === "boolean")
|
|
12435
|
+
patches.push({ path: [...path, "office_access"], value: item.dashboard.office });
|
|
12436
|
+
}
|
|
12437
|
+
this.saveFleetConfig(patches);
|
|
12438
|
+
this.logger.info("Stored Office access outside dashboard for downgrade compatibility");
|
|
12439
|
+
}
|
|
12440
|
+
catch (err) {
|
|
12441
|
+
this.logger.warn({ err }, "Could not migrate Office access; keeping the original file");
|
|
12442
|
+
}
|
|
12443
|
+
}
|
|
12185
12444
|
slimFleetConfigAtStartup() {
|
|
12445
|
+
this.migrateOfficeStorage();
|
|
12446
|
+
this.migrateNoticeDeliveryStorage();
|
|
12186
12447
|
const redundantPaths = collectRedundantInstanceDefaultPaths(this.rawFleetConfig);
|
|
12187
12448
|
if (redundantPaths.length === 0)
|
|
12188
12449
|
return;
|
|
@@ -12405,6 +12666,7 @@ export class FleetManager {
|
|
|
12405
12666
|
this.effectToolsService?.forget(name);
|
|
12406
12667
|
this.mods.forgetInstance(name);
|
|
12407
12668
|
this.agentMods.forget(name);
|
|
12669
|
+
this.officeActsInstance?.forget(name);
|
|
12408
12670
|
}
|
|
12409
12671
|
catch (err) {
|
|
12410
12672
|
this.logger.warn({ err: err.message, name }, "Dropping a removed instance's web theme failed");
|
|
@@ -14211,6 +14473,8 @@ export class FleetManager {
|
|
|
14211
14473
|
return true;
|
|
14212
14474
|
if (await this.handleSettingsChangeCallback(data, adapterId, adapter))
|
|
14213
14475
|
return true;
|
|
14476
|
+
if (await this.handleKiroMigrateCallback(data, adapterId, adapter))
|
|
14477
|
+
return true;
|
|
14214
14478
|
if (await this.handleDashboardCallback(data, adapterId, adapter))
|
|
14215
14479
|
return true;
|
|
14216
14480
|
if (await this.handleClassicApproval(data, adapterId, adapter))
|
|
@@ -14331,6 +14595,7 @@ export class FleetManager {
|
|
|
14331
14595
|
const fleet = Object.entries(this.fleetConfig?.instances ?? {}).map(([name, cfg]) => ({
|
|
14332
14596
|
name, backend: this.backendNameOf(name), workingDirectory: cfg.working_directory, ledgerPath: join(this.getInstanceDir(name), "cache-ledger.json"),
|
|
14333
14597
|
claudeConfigDir: this.claudeConfigDirOf(name), profile: this.usageProfileOf(name, this.backendNameOf(name)),
|
|
14598
|
+
kiroDbPath: this.kiroDbPathOf(name, this.backendNameOf(name)),
|
|
14334
14599
|
}));
|
|
14335
14600
|
const taken = new Set(fleet.map(i => i.name));
|
|
14336
14601
|
const classic = (this.classicChannels?.getAll() ?? []).filter(ch => !taken.has(ch.instanceName)).map(ch => ({
|
|
@@ -14340,6 +14605,7 @@ export class FleetManager {
|
|
|
14340
14605
|
ledgerPath: join(this.getInstanceDir(ch.instanceName), "cache-ledger.json"),
|
|
14341
14606
|
claudeConfigDir: this.claudeConfigDirOf(ch.instanceName),
|
|
14342
14607
|
profile: this.usageProfileOf(ch.instanceName, this.classicChannels?.getBackendByInstance(ch.instanceName, this.fleetConfig?.defaults?.backend) ?? this.fleetConfig?.defaults?.backend ?? "claude-code"),
|
|
14608
|
+
kiroDbPath: this.kiroDbPathOf(ch.instanceName, this.classicChannels?.getBackendByInstance(ch.instanceName, this.fleetConfig?.defaults?.backend) ?? this.fleetConfig?.defaults?.backend ?? "claude-code"),
|
|
14343
14609
|
}));
|
|
14344
14610
|
return [...fleet, ...new Map(classic.map(c => [c.name, c])).values()];
|
|
14345
14611
|
}
|
|
@@ -14360,8 +14626,19 @@ export class FleetManager {
|
|
|
14360
14626
|
return this.credentialProfileOf(name, "claude-code") ?? "default";
|
|
14361
14627
|
if (backend === "codex")
|
|
14362
14628
|
return "unrecorded";
|
|
14629
|
+
if (backend === "kiro-cli")
|
|
14630
|
+
return this.credentialProfileOf(name, "kiro-cli") ?? "default";
|
|
14631
|
+
if (backend === "grok" || backend === "muse" || backend === "opencode" || backend === "antigravity")
|
|
14632
|
+
return "default";
|
|
14363
14633
|
return undefined;
|
|
14364
14634
|
}
|
|
14635
|
+
/** #2122 §10: a Kiro instance with a credential profile writes its classic store in that profile's home. */
|
|
14636
|
+
kiroDbPathOf(name, backend) {
|
|
14637
|
+
if (backend !== "kiro-cli")
|
|
14638
|
+
return undefined;
|
|
14639
|
+
const profile = this.credentialProfileOf(name, "kiro-cli");
|
|
14640
|
+
return profile ? join(credentialProfileStoreHome(getAgendHome(), "kiro-cli", profile), "data.sqlite3") : undefined;
|
|
14641
|
+
}
|
|
14365
14642
|
/** #2122: the fleet's day boundary zone — defaults.scheduler.default_timezone, else this host's. */
|
|
14366
14643
|
usageZone() {
|
|
14367
14644
|
return this.fleetConfig?.defaults?.scheduler?.default_timezone?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
@@ -14404,6 +14681,19 @@ export class FleetManager {
|
|
|
14404
14681
|
rolloutsUnreadable: root => sharedRolloutIndex(root).unreadable,
|
|
14405
14682
|
metaPath: join(this.dataDir, "cache-codex-rollouts.json"),
|
|
14406
14683
|
zone: () => this.usageZone(),
|
|
14684
|
+
// #2122 §10: Kiro's shared classic store (XDG_DATA_HOME, as kiro-cli resolves it) and its KIRO_HOME.
|
|
14685
|
+
kiroDbPath: () => join(process.env.XDG_DATA_HOME?.trim() || join(homedir(), ".local", "share"), "kiro-cli", "data.sqlite3"),
|
|
14686
|
+
kiroHome: () => kiroHome(),
|
|
14687
|
+
// #2122 §10 step 3: grok keeps its sessions under ~/.grok (grok.ts reads them there too).
|
|
14688
|
+
grokSessionsDir: () => join(homedir(), ".grok", "sessions"),
|
|
14689
|
+
// #2122 §10 step 4: muse keeps sessions under $XDG_DATA_HOME/muse (else ~/.local/share/muse), as muse.ts reads them.
|
|
14690
|
+
// #2122 §10 step 5: OpenCode's database, under XDG_DATA_HOME as opencode resolves it.
|
|
14691
|
+
// #2122 §10 step 6: agy's conversations, under ~/.gemini as the antigravity backend reads its settings.
|
|
14692
|
+
antigravityDir: () => join(homedir(), ".gemini", "antigravity-cli", "conversations"),
|
|
14693
|
+
// The version AgEnD's CLI probe last recorded for agy: its cache file, whatever its age (never runs agy).
|
|
14694
|
+
antigravityVersion: () => this.lastRecordedCliVersion("antigravity"),
|
|
14695
|
+
opencodeDbPath: () => join(process.env.XDG_DATA_HOME?.trim() || join(homedir(), ".local", "share"), "opencode", "opencode.db"),
|
|
14696
|
+
museSessionsDir: () => join(process.env.XDG_DATA_HOME?.trim() || join(homedir(), ".local", "share"), "muse", "sessions"),
|
|
14407
14697
|
log: (level, msg, extra) => this.logger[level](extra ?? {}, msg),
|
|
14408
14698
|
});
|
|
14409
14699
|
}
|
|
@@ -14524,6 +14814,7 @@ export class FleetManager {
|
|
|
14524
14814
|
return this.deliveryOutbox.acknowledge(deliveryId, by);
|
|
14525
14815
|
},
|
|
14526
14816
|
tasks: () => this.needsYouTaskItems(),
|
|
14817
|
+
backendSignIns: () => this.needsYouBackendSignIns(),
|
|
14527
14818
|
taskAction: action => this.taskActionFromChat(action),
|
|
14528
14819
|
settings: () => ({
|
|
14529
14820
|
liveMessage: this.fleetConfig?.needs_you?.live_message !== false,
|
|
@@ -14782,14 +15073,14 @@ export class FleetManager {
|
|
|
14782
15073
|
}
|
|
14783
15074
|
}
|
|
14784
15075
|
/** Post one fresh nonce-armed tip in a known General channel. */
|
|
14785
|
-
async promptTip(generalName, adapter, chatId, threadId) {
|
|
15076
|
+
async promptTip(generalName, adapter, chatId, threadId, directUserId) {
|
|
14786
15077
|
const state = this.readTipState();
|
|
14787
15078
|
if (!state)
|
|
14788
15079
|
return "unavailable";
|
|
14789
15080
|
if (!state.advancedUnlocked
|
|
14790
15081
|
&& visibleTipLevels(true).has("advanced")
|
|
14791
15082
|
&& canUnlockAdvancedTips(state.dismissed)) {
|
|
14792
|
-
return await this.promptAdvancedTipUnlock(generalName, adapter, chatId, threadId)
|
|
15083
|
+
return await this.promptAdvancedTipUnlock(generalName, adapter, chatId, threadId, directUserId)
|
|
14793
15084
|
? "posted"
|
|
14794
15085
|
: "unavailable";
|
|
14795
15086
|
}
|
|
@@ -14805,6 +15096,7 @@ export class FleetManager {
|
|
|
14805
15096
|
chatId,
|
|
14806
15097
|
threadId,
|
|
14807
15098
|
message: this.formatTip(tip),
|
|
15099
|
+
...(directUserId ? { deliver: (choices) => withinBudget(adapter.sendDirect(directUserId, this.formatTip(tip), { choices, disablePreview: true }), performance.now() + 5_000) } : {}),
|
|
14808
15100
|
choices: [
|
|
14809
15101
|
{ action: "dismiss", label: t("tips.dismiss") },
|
|
14810
15102
|
{ action: "confused", label: t("tips.confused") },
|
|
@@ -14816,7 +15108,7 @@ export class FleetManager {
|
|
|
14816
15108
|
});
|
|
14817
15109
|
return nonce ? "posted" : "unavailable";
|
|
14818
15110
|
}
|
|
14819
|
-
async promptAdvancedTipUnlock(generalName, adapter, chatId, threadId) {
|
|
15111
|
+
async promptAdvancedTipUnlock(generalName, adapter, chatId, threadId, directUserId) {
|
|
14820
15112
|
const nonce = await this.postNonceButtonPrompt({
|
|
14821
15113
|
prefix: TIP_UNLOCK_CALLBACK_PREFIX,
|
|
14822
15114
|
alertType: "tip",
|
|
@@ -14826,6 +15118,7 @@ export class FleetManager {
|
|
|
14826
15118
|
chatId,
|
|
14827
15119
|
threadId,
|
|
14828
15120
|
message: t("tips.advanced.unlock_prompt"),
|
|
15121
|
+
...(directUserId ? { deliver: (choices) => withinBudget(adapter.sendDirect(directUserId, t("tips.advanced.unlock_prompt"), { choices, disablePreview: true }), performance.now() + 5_000) } : {}),
|
|
14829
15122
|
choices: [{ action: "unlock", label: t("tips.advanced.unlock") }],
|
|
14830
15123
|
expiredText: t("tips.advanced.expired"),
|
|
14831
15124
|
// No allowAnyUser: unlocking changes a persistent setting, which typed and slash `/tips advanced on` reserve for a
|
|
@@ -14845,6 +15138,15 @@ export class FleetManager {
|
|
|
14845
15138
|
const topicId = this.fleetConfig?.instances[generalName]?.topic_id;
|
|
14846
15139
|
if (!adapter || !chatId)
|
|
14847
15140
|
return;
|
|
15141
|
+
if (this.fleetConfig && tipNoticeMode(this.fleetConfig) === "dm" && adapter.sendDirect) {
|
|
15142
|
+
const userId = this.noticeAdmin(adapter.id);
|
|
15143
|
+
if (userId) {
|
|
15144
|
+
const result = await this.promptTip(generalName, adapter, userId, undefined, userId);
|
|
15145
|
+
if (result !== "unavailable")
|
|
15146
|
+
return;
|
|
15147
|
+
// The failed prompt disarmed its nonce; a new topic prompt is safe.
|
|
15148
|
+
}
|
|
15149
|
+
}
|
|
14848
15150
|
const result = await this.promptTip(generalName, adapter, chatId, topicId != null ? String(topicId) : undefined);
|
|
14849
15151
|
if (result === "unavailable") {
|
|
14850
15152
|
this.logger.warn({ generalName }, "Daily tip could not be posted");
|
|
@@ -16528,6 +16830,55 @@ export class FleetManager {
|
|
|
16528
16830
|
}
|
|
16529
16831
|
return this.loginController;
|
|
16530
16832
|
}
|
|
16833
|
+
browserBackendSignInsInstance = null;
|
|
16834
|
+
structuredLoginCleanupFailed = false;
|
|
16835
|
+
pendingBackendSince = new Map();
|
|
16836
|
+
browserSignInOwnerCurrent(owner) {
|
|
16837
|
+
const record = owner;
|
|
16838
|
+
return !this.shuttingDown && typeof record.handle === "string" && this.webSessions?.findByHandle(record.handle) === owner
|
|
16839
|
+
&& this.isLocalAdminSession(record.handle);
|
|
16840
|
+
}
|
|
16841
|
+
/** #2599: default-account controls share the exact writer reservation with chat/profile/install. */
|
|
16842
|
+
get browserBackendSignIns() {
|
|
16843
|
+
return this.browserBackendSignInsInstance ??= new BrowserBackendSignIns({
|
|
16844
|
+
dataDir: this.dataDir, lock: this.loginWindow,
|
|
16845
|
+
current: owner => this.browserSignInOwnerCurrent(owner),
|
|
16846
|
+
installed: async () => ONBOARDING_BACKENDS.filter(backend => this.isCliInstalled(backend)),
|
|
16847
|
+
checkAuth: backend => checkAuthStatus(LOGIN_FLOWS[backend].authCheck),
|
|
16848
|
+
checkConsoleAuth: () => checkAuthStatus(LOGIN_FLOWS.antigravity.authCheck),
|
|
16849
|
+
transport: () => createStructuredSignInTransport(),
|
|
16850
|
+
changed: (event, owner, launched) => {
|
|
16851
|
+
try {
|
|
16852
|
+
this.eventLog?.insert("login", "browser_backend_signin", event);
|
|
16853
|
+
}
|
|
16854
|
+
catch { /* safe metadata only */ }
|
|
16855
|
+
broadcastSseEvent(this.sseClients, "backend_login", {}, err => this.logger.debug({ err }, "backend_login event failed"));
|
|
16856
|
+
this.needsYou?.poke();
|
|
16857
|
+
if (!this.browserSignInOwnerCurrent(owner))
|
|
16858
|
+
return;
|
|
16859
|
+
if (event.phase === "starting") {
|
|
16860
|
+
const record = owner;
|
|
16861
|
+
this.notifyFleetError(t("login.web_dashboard_notice", event.backend, record.label, record.handle.slice(0, 8)));
|
|
16862
|
+
}
|
|
16863
|
+
if (event.phase === "done") {
|
|
16864
|
+
void this.backendAvailability.refresh([event.backend], { force: true }).catch(() => { });
|
|
16865
|
+
if (launched)
|
|
16866
|
+
void this.recoverBackendInstances(event.backend, undefined, null).catch(() => this.logger.warn({ backend: event.backend }, "Post sign-in recovery failed"));
|
|
16867
|
+
}
|
|
16868
|
+
},
|
|
16869
|
+
});
|
|
16870
|
+
}
|
|
16871
|
+
needsYouBackendSignIns() {
|
|
16872
|
+
const pending = readPendingBackendSignIns(this.dataDir).pending;
|
|
16873
|
+
for (const backend of this.pendingBackendSince?.keys() ?? [])
|
|
16874
|
+
if (!pending.includes(backend))
|
|
16875
|
+
this.pendingBackendSince.delete(backend);
|
|
16876
|
+
return pending.map(backend => {
|
|
16877
|
+
if (!this.pendingBackendSince.has(backend))
|
|
16878
|
+
this.pendingBackendSince.set(backend, Date.now());
|
|
16879
|
+
return { backend, since: this.pendingBackendSince.get(backend), browser: structuredSignInSupported(backend) };
|
|
16880
|
+
});
|
|
16881
|
+
}
|
|
16531
16882
|
/** #2233: sign-ins started from the web dashboard (local admin sessions), on the same controller as chat's. */
|
|
16532
16883
|
webBackendLoginsInstance = null;
|
|
16533
16884
|
get webBackendLogins() {
|
|
@@ -16880,6 +17231,14 @@ export class FleetManager {
|
|
|
16880
17231
|
// "cancelled" is the detail both legacy onDone handlers keep quiet about —
|
|
16881
17232
|
// a stopping fleet must not announce "login failed — fleet shutdown".
|
|
16882
17233
|
await Promise.all([
|
|
17234
|
+
bounded(this.browserBackendSignInsInstance?.close().then(confirmed => {
|
|
17235
|
+
if (confirmed)
|
|
17236
|
+
this.browserBackendSignInsInstance = null;
|
|
17237
|
+
else {
|
|
17238
|
+
this.structuredLoginCleanupFailed = true;
|
|
17239
|
+
this.logger.error("Structured login cleanup failed; sign-in admission remains closed until the host is restarted.");
|
|
17240
|
+
}
|
|
17241
|
+
}), "browser-controls"),
|
|
16883
17242
|
bounded(this.loginController?.shutdown(), "web-login"),
|
|
16884
17243
|
bounded(this.activeInstall?.session.cancel("cancelled"), "install"),
|
|
16885
17244
|
]);
|
|
@@ -16896,6 +17255,13 @@ export class FleetManager {
|
|
|
16896
17255
|
this.installHandoff = null;
|
|
16897
17256
|
return t("login.cancelled", backend);
|
|
16898
17257
|
}
|
|
17258
|
+
const browserLogin = this.browserBackendSignInsInstance, browserBackend = browserLogin?.activeBackend;
|
|
17259
|
+
if (browserLogin && browserBackend) {
|
|
17260
|
+
const confirmed = await browserLogin.close();
|
|
17261
|
+
if (confirmed)
|
|
17262
|
+
this.browserBackendSignInsInstance = null;
|
|
17263
|
+
return confirmed ? t("login.cancelled", browserBackend) : t("login.browser_cleanup_failed");
|
|
17264
|
+
}
|
|
16899
17265
|
if (this.loginController?.isActive())
|
|
16900
17266
|
return this.loginController.cancel();
|
|
16901
17267
|
return t("login.no_session");
|
|
@@ -17067,7 +17433,7 @@ export class FleetManager {
|
|
|
17067
17433
|
// session only judges that verdict; it does not read the output for URLs or prompts.
|
|
17068
17434
|
const flow = {
|
|
17069
17435
|
backend,
|
|
17070
|
-
command: info.install,
|
|
17436
|
+
command: info.unattended ?? info.install,
|
|
17071
17437
|
successPattern: /$^/,
|
|
17072
17438
|
timeoutMs: 10 * 60 * 1000,
|
|
17073
17439
|
};
|
|
@@ -18068,8 +18434,11 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
18068
18434
|
// its ping in the reference, not the text). One hop only: a reply to a message of ours that itself answered a bot
|
|
18069
18435
|
// (or the sender) is a chain of bots answering each other — a mention still carries it on. A reply to our answer
|
|
18070
18436
|
// to a person is the ordinary handoff, and is addressed.
|
|
18071
|
-
const
|
|
18072
|
-
const
|
|
18437
|
+
const replyTriggers = this.classicChannels?.getReplyTriggers(channelId, msg.adapterId, this.fleetConfig?.defaults?.reply_triggers) ?? DEFAULT_REPLY_TRIGGERS;
|
|
18438
|
+
const repliesToUs = replyTriggers && !!adapterBotUserId && msg.replyToAuthorId === adapterBotUserId && msg.replyToChain !== true;
|
|
18439
|
+
// Snapshot/quoted content is context, never the forwarder's own mention.
|
|
18440
|
+
const addressText = msg.source === "discord" ? (msg.humanOriginalBody ?? text) : text;
|
|
18441
|
+
const isMentioned = (mentionTag && addressText.includes(mentionTag)) || repliesToUs;
|
|
18073
18442
|
if (!isMentioned) {
|
|
18074
18443
|
// Bare attachment (no @mention) — already saved above; just acknowledge.
|
|
18075
18444
|
if (saved) {
|
|
@@ -18404,6 +18773,19 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
18404
18773
|
catch { /* missing / stale / corrupt */ }
|
|
18405
18774
|
return null;
|
|
18406
18775
|
}
|
|
18776
|
+
/**
|
|
18777
|
+
* #2122 §10: the version the CLI probe last recorded for a backend — its cache file read whatever its age (the usage
|
|
18778
|
+
* reader's version gate needs the last known version, not a fresh one); null when none was recorded. Never a probe.
|
|
18779
|
+
*/
|
|
18780
|
+
lastRecordedCliVersion(backend) {
|
|
18781
|
+
try {
|
|
18782
|
+
const v = JSON.parse(readFileSync(this.cliEnvPath(backend), "utf-8")).version;
|
|
18783
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
18784
|
+
}
|
|
18785
|
+
catch {
|
|
18786
|
+
return null;
|
|
18787
|
+
}
|
|
18788
|
+
}
|
|
18407
18789
|
/** Return cached CLI versions immediately. Refresh is started after /sysinfo is sent. */
|
|
18408
18790
|
getBackendCliVersionSnapshot() {
|
|
18409
18791
|
const snapshot = {};
|
|
@@ -20792,6 +21174,8 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
20792
21174
|
// mark every row done and tell the user "changes applied".
|
|
20793
21175
|
return { rejected: why };
|
|
20794
21176
|
}
|
|
21177
|
+
this.migrateOfficeStorage();
|
|
21178
|
+
this.migrateNoticeDeliveryStorage();
|
|
20795
21179
|
if (canvasWorkerEnabled(oldConfig?.web) !== this.canvasWorkerEnabled()) {
|
|
20796
21180
|
this.canvasWorkerSettingChanged();
|
|
20797
21181
|
}
|