@songsid/agend 2.2.0-beta.2 → 2.2.0-beta.20
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 +40 -24
- package/README.zh-TW.md +40 -24
- package/dist/agent-cli-instructions.md +6 -2
- package/dist/agent-cli.js +15 -7
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +3 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.js +16 -4
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +3 -1
- package/dist/auth-api.js +17 -1
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/antigravity.d.ts +33 -0
- package/dist/backend/antigravity.js +126 -18
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-carry.d.ts +22 -0
- package/dist/backend/claude-carry.js +170 -0
- package/dist/backend/claude-carry.js.map +1 -0
- package/dist/backend/claude-code.d.ts +65 -2
- package/dist/backend/claude-code.js +226 -33
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-metadata.d.ts +33 -0
- package/dist/backend/codex-metadata.js +122 -18
- package/dist/backend/codex-metadata.js.map +1 -1
- package/dist/backend/codex.d.ts +3 -0
- package/dist/backend/codex.js +17 -1
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/credential-profile.d.ts +40 -3
- package/dist/backend/credential-profile.js +113 -5
- package/dist/backend/credential-profile.js.map +1 -1
- package/dist/backend/effort-metadata.d.ts +37 -1
- package/dist/backend/effort-metadata.js +86 -15
- package/dist/backend/effort-metadata.js.map +1 -1
- package/dist/backend/grok.d.ts +4 -0
- package/dist/backend/grok.js +14 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +27 -1
- package/dist/backend/kiro.js +63 -10
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/launch-effort.d.ts +61 -0
- package/dist/backend/launch-effort.js +170 -0
- package/dist/backend/launch-effort.js.map +1 -0
- package/dist/backend/muse.d.ts +84 -0
- package/dist/backend/muse.js +449 -53
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +48 -2
- package/dist/backend/types.js +12 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-command.d.ts +91 -0
- package/dist/backend-command.js +122 -0
- package/dist/backend-command.js.map +1 -0
- package/dist/background-work-ended.d.ts +18 -0
- package/dist/background-work-ended.js +30 -0
- package/dist/background-work-ended.js.map +1 -0
- package/dist/cache-service.d.ts +2 -0
- package/dist/cache-service.js +2 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +26 -1
- package/dist/channel/adapters/discord.js +103 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +35 -3
- package/dist/channel/adapters/telegram.js +122 -23
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +5 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-edit-errors.d.ts +5 -0
- package/dist/channel/message-edit-errors.js +24 -0
- package/dist/channel/message-edit-errors.js.map +1 -0
- package/dist/channel/telegram-markdown.d.ts +6 -0
- package/dist/channel/telegram-markdown.js +104 -0
- package/dist/channel/telegram-markdown.js.map +1 -0
- package/dist/channel/tool-router.d.ts +2 -0
- package/dist/channel/tool-router.js +6 -5
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +46 -4
- package/dist/cli.js +184 -34
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +2 -2
- package/dist/command-table.js +5 -2
- package/dist/command-table.js.map +1 -1
- package/dist/compact-watch.d.ts +86 -0
- package/dist/compact-watch.js +288 -0
- package/dist/compact-watch.js.map +1 -0
- package/dist/config-validator.js +55 -5
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-envelope.js +2 -0
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +129 -1
- package/dist/daemon.js +754 -94
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +64 -8
- package/dist/delivery-outbox.js +315 -24
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +18 -1
- package/dist/delivery-reconciliation.js +67 -2
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/doctor.js +7 -0
- package/dist/doctor.js.map +1 -1
- package/dist/fleet-context.d.ts +12 -1
- package/dist/fleet-manager.d.ts +392 -19
- package/dist/fleet-manager.js +2030 -275
- 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/general-knowledge/skills/credential-profiles/SKILL.md +84 -16
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +16 -1
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +4 -1
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -0
- package/dist/host-memory.d.ts +10 -0
- package/dist/host-memory.js +33 -1
- package/dist/host-memory.js.map +1 -1
- package/dist/human-delivery-receipts.d.ts +83 -0
- package/dist/human-delivery-receipts.js +194 -0
- package/dist/human-delivery-receipts.js.map +1 -0
- package/dist/install-lock.d.ts +18 -1
- package/dist/install-lock.js +30 -3
- package/dist/install-lock.js.map +1 -1
- package/dist/instance-avatar.d.ts +5 -4
- package/dist/instance-avatar.js +12 -8
- package/dist/instance-avatar.js.map +1 -1
- package/dist/instance-config-impact.js +1 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +25 -6
- package/dist/instance-lifecycle.js +76 -27
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +3 -3
- package/dist/instructions.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/kiro-log-gate.d.ts +42 -0
- package/dist/kiro-log-gate.js +39 -0
- package/dist/kiro-log-gate.js.map +1 -0
- package/dist/launchd-handoff.d.ts +43 -0
- package/dist/launchd-handoff.js +56 -0
- package/dist/launchd-handoff.js.map +1 -0
- package/dist/locale.js +268 -42
- package/dist/locale.js.map +1 -1
- package/dist/log-service.d.ts +99 -0
- package/dist/log-service.js +243 -0
- package/dist/log-service.js.map +1 -0
- package/dist/log-steps.d.ts +169 -0
- package/dist/log-steps.js +858 -0
- package/dist/log-steps.js.map +1 -0
- package/dist/login-controller.d.ts +20 -3
- package/dist/login-controller.js +71 -19
- package/dist/login-controller.js.map +1 -1
- package/dist/login-flows.d.ts +5 -2
- package/dist/login-flows.js +5 -4
- package/dist/login-flows.js.map +1 -1
- package/dist/login-shell.d.ts +21 -0
- package/dist/login-shell.js +98 -0
- package/dist/login-shell.js.map +1 -0
- package/dist/memory-pressure.d.ts +13 -1
- package/dist/memory-pressure.js +50 -10
- package/dist/memory-pressure.js.map +1 -1
- package/dist/needs-you-hub.js +2 -0
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +6 -0
- package/dist/needs-you-live.js +23 -3
- package/dist/needs-you-live.js.map +1 -1
- package/dist/outbound-handlers.d.ts +100 -0
- package/dist/outbound-handlers.js +277 -93
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +30 -15
- package/dist/outbound-schemas.js +23 -5
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/provider-probe.d.ts +29 -0
- package/dist/provider-probe.js +198 -2
- package/dist/provider-probe.js.map +1 -1
- package/dist/public-link-progress.d.ts +1 -1
- package/dist/public-link-progress.js +8 -2
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.d.ts +101 -0
- package/dist/public-preview.js +236 -0
- package/dist/public-preview.js.map +1 -0
- package/dist/public-web-gateway.d.ts +2 -0
- package/dist/public-web-gateway.js +20 -6
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +24 -1
- package/dist/public-web-link.js +63 -9
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.d.ts +1 -1
- package/dist/quickstart-api.js +8 -6
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +12 -0
- package/dist/quickstart.js +36 -6
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +89 -5
- package/dist/reply-buttons-controller.js +273 -8
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/reply-buttons.d.ts +48 -3
- package/dist/reply-buttons.js +105 -10
- package/dist/reply-buttons.js.map +1 -1
- package/dist/reply-polls.d.ts +85 -0
- package/dist/reply-polls.js +154 -0
- package/dist/reply-polls.js.map +1 -0
- package/dist/restart-guard.d.ts +6 -3
- package/dist/restart-guard.js +6 -3
- package/dist/restart-guard.js.map +1 -1
- package/dist/restart-progress.d.ts +33 -2
- package/dist/restart-progress.js +30 -2
- package/dist/restart-progress.js.map +1 -1
- package/dist/sd-notify.d.ts +22 -1
- package/dist/sd-notify.js +63 -10
- package/dist/sd-notify.js.map +1 -1
- package/dist/service-activation.d.ts +15 -7
- package/dist/service-activation.js +42 -26
- package/dist/service-activation.js.map +1 -1
- package/dist/service-installer.d.ts +55 -0
- package/dist/service-installer.js +90 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/service-plan.d.ts +11 -0
- package/dist/service-plan.js +23 -0
- package/dist/service-plan.js.map +1 -1
- package/dist/settings-api.js +14 -4
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +4 -2
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +1 -1
- package/dist/spawn-gate.js +5 -1
- package/dist/spawn-gate.js.map +1 -1
- package/dist/spawn-hold-reply.d.ts +12 -0
- package/dist/spawn-hold-reply.js +39 -0
- package/dist/spawn-hold-reply.js.map +1 -0
- package/dist/systemd-peer-bus.d.ts +95 -0
- package/dist/systemd-peer-bus.js +430 -0
- package/dist/systemd-peer-bus.js.map +1 -0
- package/dist/systemd-runtime.d.ts +27 -1
- package/dist/systemd-runtime.js +69 -7
- package/dist/systemd-runtime.js.map +1 -1
- package/dist/tips.js +1 -1
- package/dist/tips.js.map +1 -1
- package/dist/tool-permissions.js +3 -3
- package/dist/tool-permissions.js.map +1 -1
- package/dist/tool-progress.js +3 -2
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-commands.d.ts +71 -4
- package/dist/topic-commands.js +167 -34
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.d.ts +3 -3
- package/dist/transcript-delta-reader.js +8 -8
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/transcript-jsonl.d.ts +85 -3
- package/dist/transcript-jsonl.js +156 -13
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +11 -0
- package/dist/transcript-monitor.js +61 -13
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +24 -0
- package/dist/transcript-sources.js +211 -56
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +7 -15
- package/dist/tunnel/cloudflared.js +36 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/purpose-lane.d.ts +5 -1
- package/dist/tunnel/purpose-lane.js +14 -7
- package/dist/tunnel/purpose-lane.js.map +1 -1
- package/dist/tunnel/types.d.ts +5 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/turn-resume.d.ts +2 -0
- package/dist/turn-resume.js +1 -1
- package/dist/turn-resume.js.map +1 -1
- package/dist/types.d.ts +22 -0
- package/dist/ui/chat-commands.js +1 -0
- package/dist/ui/chat-mentions.js +63 -0
- package/dist/ui/chat-render.js +80 -10
- package/dist/ui/chat-store.js +70 -6
- package/dist/ui/chat-strings.js +62 -32
- package/dist/ui/chat-thread.js +199 -24
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/chat-tour.js +17 -14
- package/dist/ui/panel-chat.js +45 -21
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +213 -58
- package/dist/ui/panel-log.js +428 -0
- package/dist/ui/panel-needs.js +14 -10
- package/dist/ui/panel-settings.js +82 -17
- package/dist/ui/preview-panel.js +55 -15
- package/dist/ui/preview-status.js +28 -0
- package/dist/ui/preview.js +44 -3
- package/dist/ui/settings-dialogs.js +11 -6
- package/dist/ui/settings-model.js +3 -1
- package/dist/ui/settings-strings.js +32 -12
- package/dist/ui/settings-token.js +8 -1
- package/dist/ui/settings-wizard.js +5 -3
- package/dist/ui/shared/app-i18n.js +41 -15
- package/dist/ui/shared/app-prefs.js +250 -0
- package/dist/ui/shared/app-route.js +6 -1
- package/dist/ui/shared/app-shell.js +165 -8
- package/dist/ui/shared/app-store.js +5 -1
- package/dist/ui/shared/app-stream.js +26 -5
- package/dist/ui/shared/app.css +170 -30
- package/dist/ui/shared/app.js +11 -1
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/header-tools.js +83 -13
- package/dist/ui/shared/instance-nav.js +178 -51
- package/dist/ui/shared/instance-switch.js +8 -5
- package/dist/ui/shared/panel-view.js +21 -4
- package/dist/ui/shared/signin.js +15 -9
- package/dist/ui/shared/theme.js +39 -7
- package/dist/ui/shared/tokens.css +1 -1
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/usage-dialog.js +23 -5
- package/dist/ui/shared/view-strings.js +40 -4
- package/dist/ui/signin.html +8 -3
- package/dist/unrecognised-prompt.d.ts +23 -0
- package/dist/unrecognised-prompt.js +74 -0
- package/dist/unrecognised-prompt.js.map +1 -0
- package/dist/update-check.d.ts +31 -0
- package/dist/update-check.js +47 -0
- package/dist/update-check.js.map +1 -1
- package/dist/update-install.d.ts +13 -1
- package/dist/update-install.js +16 -4
- package/dist/update-install.js.map +1 -1
- package/dist/update-launch.d.ts +48 -1
- package/dist/update-launch.js +159 -5
- package/dist/update-launch.js.map +1 -1
- package/dist/update-marker.d.ts +51 -0
- package/dist/update-marker.js +61 -0
- package/dist/update-marker.js.map +1 -1
- package/dist/usage/i18n-keys.d.ts +1 -1
- package/dist/usage/i18n-keys.js +1 -1
- package/dist/usage/i18n-keys.js.map +1 -1
- package/dist/usage/providers.d.ts +62 -13
- package/dist/usage/providers.js +256 -68
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/statusline-usage.d.ts +6 -2
- package/dist/usage/statusline-usage.js +10 -4
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/view-api.js +31 -9
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +82 -4
- package/dist/web-api.js +632 -88
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +5 -2
- package/dist/web-auth.js.map +1 -1
- package/dist/web-chat-history.d.ts +26 -1
- package/dist/web-chat-history.js +47 -8
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +41 -5
- package/dist/web-commands.js +25 -7
- package/dist/web-commands.js.map +1 -1
- package/dist/web-login.d.ts +14 -0
- package/dist/web-login.js +4 -2
- package/dist/web-login.js.map +1 -1
- package/dist/web-mentions.d.ts +44 -0
- package/dist/web-mentions.js +75 -0
- package/dist/web-mentions.js.map +1 -0
- package/dist/web-prefs.d.ts +81 -0
- package/dist/web-prefs.js +297 -0
- package/dist/web-prefs.js.map +1 -0
- package/dist/web-preview.d.ts +14 -1
- package/dist/web-preview.js +35 -13
- package/dist/web-preview.js.map +1 -1
- package/dist/web-profile.d.ts +32 -0
- package/dist/web-profile.js +132 -0
- package/dist/web-profile.js.map +1 -0
- package/dist/web-reply-files.d.ts +45 -0
- package/dist/web-reply-files.js +223 -0
- package/dist/web-reply-files.js.map +1 -0
- package/dist/web-schedules.d.ts +12 -0
- package/dist/web-schedules.js +34 -0
- package/dist/web-schedules.js.map +1 -0
- package/dist/web-server-drain.d.ts +10 -0
- package/dist/web-server-drain.js +50 -0
- package/dist/web-server-drain.js.map +1 -0
- package/dist/web-session.d.ts +14 -0
- package/dist/web-session.js +11 -1
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +3 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -0
- package/dist/web-shell-routes.js +9 -0
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-upload.d.ts +135 -7
- package/dist/web-upload.js +315 -51
- package/dist/web-upload.js.map +1 -1
- package/launcher/install-admission.cjs +61 -6
- package/package.json +3 -3
- package/templates/launchd.plist.ejs +3 -1
- package/templates/systemd.service.ejs +2 -1
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { validateFleetConfig } from "./config-validator.js";
|
|
1
|
+
import { KNOWN_BACKENDS, validateFleetConfig } from "./config-validator.js";
|
|
2
2
|
import { resolve as pathResolve, isAbsolute } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { deliveryContentDigest, deliveryStatusSelector } from "./delivery-outbox.js";
|
|
6
6
|
import { GENERAL_PAUSE_ERROR, isGeneralInstance } from "./general-instance.js";
|
|
7
|
-
import { DEFAULT_MAX_CROSS_INSTANCE_MESSAGE_BYTES, DEFAULT_LIST_INSTANCES_OUTPUT_BUDGET, MAX_INSTANCE_LOG_LINES } from "./config.js";
|
|
7
|
+
import { DEFAULT_MAX_CROSS_INSTANCE_MESSAGE_BYTES, DEFAULT_LIST_INSTANCES_OUTPUT_BUDGET, MAX_INSTANCE_LOG_LINES, getEffectiveInstanceDefaults } from "./config.js";
|
|
8
8
|
import { t } from "./locale.js";
|
|
9
9
|
import { presentationState, interactionSummary } from "./interaction-observation.js";
|
|
10
10
|
import { truncatePreview } from "./channel/markdown-chunk.js";
|
|
11
11
|
import { crossInstanceVisibility, senderTopicNotice, targetTopicNotice } from "./cross-instance-notice.js";
|
|
12
12
|
import { instanceSupportsSteer } from "./steer-capability.js";
|
|
13
13
|
import { assignDisplayLabels, displayInstanceName, readStatuslineModel } from "./topic-commands.js";
|
|
14
|
-
import { credentialProfileLogin, credentialSwitchStartsFresh, instanceCredentialProfile } from "./backend/credential-profile.js";
|
|
14
|
+
import { CHAT_PROFILE_LOGIN_BACKENDS, credentialProfileLogin, credentialSwitchCarriesConversation, credentialSwitchStartsFresh, instanceCredentialProfile } from "./backend/credential-profile.js";
|
|
15
15
|
import { kiroEngineCandidates, kiroEngineStatus } from "./kiro-engine-status.js";
|
|
16
16
|
import { formatCrossInstanceInboundMessage, MAX_ASSEMBLED_CROSS_INSTANCE_MESSAGE_BYTES, } from "./cross-instance-envelope.js";
|
|
17
|
-
import { BroadcastArgs, CreateInstanceArgs, CreateTeamArgs, DelegateTaskArgs, DeleteInstanceArgs, DeleteTeamArgs, DeployTemplateArgs, DescribeInstanceArgs, ListDeploymentsArgs, ListInstancesArgs, ListTeamsArgs, ReplaceInstanceArgs, ReportResultArgs, DeliveryStatusArgs, RequestInformationArgs, RestartInstanceArgs, SendToInstanceArgs, PauseInstanceArgs, WakeInstanceArgs, StopInstanceArgs, GetInstanceLogsArgs, GetEffortArgs, KiroEngineStatusArgs, GetUsageArgs, ListModelsArgs, GetFleetConfigArgs, UpdateInstanceConfigArgs, UpdateFleetDefaultsArgs, StartInstanceArgs, TeardownDeploymentArgs, UpdateTeamArgs, validateArgs, } from "./outbound-schemas.js";
|
|
17
|
+
import { BroadcastArgs, CreateInstanceArgs, CreateTeamArgs, DelegateTaskArgs, DeleteInstanceArgs, DeleteTeamArgs, DeployTemplateArgs, DescribeInstanceArgs, ListDeploymentsArgs, ListInstancesArgs, ListTeamsArgs, ReplaceInstanceArgs, ReportResultArgs, DeliveryStatusArgs, RequestInformationArgs, RestartInstanceArgs, SendToInstanceArgs, PauseInstanceArgs, WakeInstanceArgs, StopInstanceArgs, GetInstanceLogsArgs, GetEffortArgs, ClosePollArgs, KiroEngineStatusArgs, GetUsageArgs, ListModelsArgs, GetFleetConfigArgs, UpdateInstanceConfigArgs, UpdateFleetDefaultsArgs, StartInstanceArgs, TeardownDeploymentArgs, UpdateTeamArgs, validateArgs, } from "./outbound-schemas.js";
|
|
18
18
|
const HOME_DIR = homedir();
|
|
19
19
|
function formatByteSize(bytes) {
|
|
20
20
|
if (bytes < 1024)
|
|
@@ -168,7 +168,7 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
168
168
|
respond(null, v.error);
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
|
-
const { instance_name: targetName, message, steer, request_kind: reqKind, requires_reply, task_summary, working_directory, branch, correlation_id: parsedCorrelationId } = v.data;
|
|
171
|
+
const { instance_name: targetName, message, forwards, steer, request_kind: reqKind, requires_reply, task_summary, working_directory, branch, correlation_id: parsedCorrelationId } = v.data;
|
|
172
172
|
if (rejectOversizedCrossInstanceMessage(ctx, message, respond))
|
|
173
173
|
return;
|
|
174
174
|
const senderLabel = meta.senderSessionName ?? meta.instanceName;
|
|
@@ -224,7 +224,7 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
224
224
|
const queuedPredecessor = steerCapable
|
|
225
225
|
&& ctx.hasPendingIdleGatedDelivery?.(targetInstanceName) === true;
|
|
226
226
|
const useSteer = steerCapable && !queuedPredecessor;
|
|
227
|
-
|
|
227
|
+
let steerFallbackWarning = steer === true && !useSteer
|
|
228
228
|
? isExternalSession
|
|
229
229
|
? t("delivery.steer_external")
|
|
230
230
|
: queuedPredecessor
|
|
@@ -255,6 +255,8 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
255
255
|
ipcMeta.working_directory = working_directory;
|
|
256
256
|
if (branch)
|
|
257
257
|
ipcMeta.branch = branch;
|
|
258
|
+
if (forwards?.length)
|
|
259
|
+
ipcMeta.forwards = JSON.stringify(forwards);
|
|
258
260
|
// #77: surface the sender's display name in the recipient's [from:…] header
|
|
259
261
|
// (keeps from_instance as the machine name so the reply-back target stays valid).
|
|
260
262
|
const senderDisplay = ctx.fleetConfig?.instances[senderLabel]?.display_name;
|
|
@@ -278,6 +280,7 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
278
280
|
meta: ipcMeta,
|
|
279
281
|
};
|
|
280
282
|
let durableReceipt;
|
|
283
|
+
let effectiveUseSteer = useSteer;
|
|
281
284
|
if (ctx.admitDurableDelivery) {
|
|
282
285
|
try {
|
|
283
286
|
durableReceipt = ctx.admitDurableDelivery({
|
|
@@ -291,7 +294,13 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
291
294
|
kind: useSteer ? "steer" : "fleet_inbound",
|
|
292
295
|
correlationId,
|
|
293
296
|
payload,
|
|
297
|
+
forwards,
|
|
298
|
+
requestedKind: steer === true ? "steer" : "fleet_inbound",
|
|
294
299
|
});
|
|
300
|
+
if (durableReceipt.kind)
|
|
301
|
+
effectiveUseSteer = durableReceipt.kind === "steer";
|
|
302
|
+
if (durableReceipt.duplicate && durableReceipt.kind !== undefined && durableReceipt.kind !== payload.type)
|
|
303
|
+
steerFallbackWarning = undefined;
|
|
295
304
|
}
|
|
296
305
|
catch (err) {
|
|
297
306
|
ctx.logger.error({ err, source: meta.instanceName, target: targetInstanceName }, "Durable outbound admission failed closed");
|
|
@@ -300,6 +309,10 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
300
309
|
}
|
|
301
310
|
}
|
|
302
311
|
else {
|
|
312
|
+
if (forwards?.length) {
|
|
313
|
+
respond(null, "Forwarded message verification requires the durable delivery store");
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
303
316
|
// Standalone handler harnesses retain the legacy path; production FleetManager
|
|
304
317
|
// always provides durable admission.
|
|
305
318
|
void deliverCrossInstanceWithRetry(ctx, targetInstanceName, targetName, senderLabel, correlationId, payload, useSteer ? { isCrossInstance: true, waitForIdle: false } : undefined);
|
|
@@ -336,33 +349,43 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
336
349
|
displayName: ctx.fleetConfig?.instances[name]?.display_name,
|
|
337
350
|
})));
|
|
338
351
|
const displayLabel = (name) => fleetLabels.get(name) ?? displayInstanceName(name, ctx.fleetConfig?.instances[name]?.display_name);
|
|
352
|
+
const instances = ctx.fleetConfig?.instances ?? {};
|
|
353
|
+
const notice = { label: `${displayLabel(senderLabel)} → ${displayLabel(targetName)}`, message, requestKind, taskSummary: ipcMeta.task_summary };
|
|
354
|
+
const targetInstance = instances[targetInstanceName];
|
|
355
|
+
const targetIsGeneral = targetInstance?.general_topic === true;
|
|
356
|
+
const targetText = targetTopicNotice(crossInstanceVisibility(ctx.fleetConfig, targetInstanceName), notice);
|
|
357
|
+
const targetShown = targetText !== null && !targetIsGeneral && !ctx.sessionRegistry.has(targetName);
|
|
358
|
+
const senderInstance = instances[meta.instanceName];
|
|
359
|
+
const senderIsGeneral = senderInstance?.general_topic === true;
|
|
360
|
+
const senderText = senderTopicNotice(crossInstanceVisibility(ctx.fleetConfig, meta.instanceName), notice);
|
|
361
|
+
const senderShown = senderText !== null && !senderIsGeneral;
|
|
339
362
|
if (groupId && ctx.adapter) {
|
|
340
|
-
const instances = ctx.fleetConfig?.instances ?? {};
|
|
341
|
-
const notificationLabel = `${displayLabel(senderLabel)} → ${displayLabel(targetName)}`;
|
|
342
|
-
const notice = { label: notificationLabel, message, requestKind, taskSummary: ipcMeta.task_summary };
|
|
343
363
|
// ── Target topic notification ──
|
|
344
|
-
const targetInstance = instances[targetInstanceName];
|
|
345
364
|
const targetTopicId = targetInstance?.topic_id;
|
|
346
|
-
|
|
347
|
-
const targetText = targetTopicNotice(crossInstanceVisibility(ctx.fleetConfig, targetInstanceName), notice);
|
|
348
|
-
if (targetText !== null && targetTopicId && !targetIsGeneral && !ctx.sessionRegistry.has(targetName)) {
|
|
365
|
+
if (targetShown && targetTopicId) {
|
|
349
366
|
const targetAdapter = ctx.getAdapterForInstance?.(targetInstanceName) ?? ctx.adapter;
|
|
350
367
|
const targetGroupId = ctx.getGroupIdForInstance?.(targetInstanceName) ?? String(groupId);
|
|
351
368
|
targetAdapter.sendText(String(targetGroupId), targetText, { threadId: String(targetTopicId) })
|
|
352
369
|
.catch(e => ctx.logger.warn({ err: e }, "Failed to post target topic notification"));
|
|
353
370
|
}
|
|
354
371
|
// ── Sender topic notification ──
|
|
355
|
-
const senderInstance = instances[meta.instanceName];
|
|
356
372
|
const senderTopicId = senderInstance?.topic_id;
|
|
357
|
-
|
|
358
|
-
const senderText = senderTopicNotice(crossInstanceVisibility(ctx.fleetConfig, meta.instanceName), notice);
|
|
359
|
-
if (senderText !== null && senderTopicId && !senderIsGeneral) {
|
|
373
|
+
if (senderShown && senderTopicId) {
|
|
360
374
|
const senderAdapter = ctx.getAdapterForInstance?.(meta.instanceName) ?? ctx.adapter;
|
|
361
375
|
const senderGroupId = ctx.getGroupIdForInstance?.(meta.instanceName) ?? String(groupId);
|
|
362
376
|
senderAdapter.sendText(senderGroupId, senderText, { threadId: String(senderTopicId) })
|
|
363
377
|
.catch(e => ctx.logger.warn({ err: e }, "Failed to post sender topic notification"));
|
|
364
378
|
}
|
|
365
379
|
}
|
|
380
|
+
// #1672: the same notices in each instance's web chat — a muted status line, never an agent's or a person's message.
|
|
381
|
+
// Not gated on a topic or a chat platform (a web-only fleet, a topic-less instance); recorded in its history (#1565).
|
|
382
|
+
const webNotice = (instance, text) => ctx.emitSseEvent?.("message", {
|
|
383
|
+
instance, sender: displayLabel(senderLabel), role: "status", text, ts: new Date().toISOString(),
|
|
384
|
+
});
|
|
385
|
+
if (targetShown)
|
|
386
|
+
webNotice(targetInstanceName, targetText);
|
|
387
|
+
if (senderShown)
|
|
388
|
+
webNotice(meta.instanceName, senderText);
|
|
366
389
|
// #856: the same line, now carrying the envelope's message_id and a short
|
|
367
390
|
// content digest, so a disputed message can be matched to its outbox row
|
|
368
391
|
// and paste digests without adding a log line per delivery.
|
|
@@ -382,6 +405,7 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
382
405
|
: undefined;
|
|
383
406
|
const warnings = [steerFallbackWarning, targetStateWarning].filter((value) => !!value);
|
|
384
407
|
respond({ sent: true, queued: true, target: targetName, target_state: state,
|
|
408
|
+
...(forwards?.length ? { forwards } : {}),
|
|
385
409
|
...(durableReceipt ? {
|
|
386
410
|
durable: true,
|
|
387
411
|
delivery_id: durableReceipt.deliveryId,
|
|
@@ -390,8 +414,8 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
390
414
|
duplicate: durableReceipt.duplicate,
|
|
391
415
|
} : {}),
|
|
392
416
|
...(state === "paused" ? { waking: true } : {}), correlation_id: correlationId,
|
|
393
|
-
...(
|
|
394
|
-
...(
|
|
417
|
+
...(effectiveUseSteer ? { delivery_mode: "steer" } : {}),
|
|
418
|
+
...(steer === true && !effectiveUseSteer ? { delivery_mode: "idle_queue" } : {}),
|
|
395
419
|
...(warnings.length > 0 ? { warning: warnings.join(" ") } : {}),
|
|
396
420
|
});
|
|
397
421
|
};
|
|
@@ -763,6 +787,25 @@ const kiroEngineStatusHandler = (ctx, rawArgs, respond) => {
|
|
|
763
787
|
}
|
|
764
788
|
respond(status);
|
|
765
789
|
};
|
|
790
|
+
/** #1823: close one of the caller's own polls before its deadline; its results then go out as at the deadline. */
|
|
791
|
+
const closePoll = async (ctx, rawArgs, respond, meta) => {
|
|
792
|
+
const v = validateArgs(ClosePollArgs, rawArgs, "close_poll");
|
|
793
|
+
if (!v.ok) {
|
|
794
|
+
respond(null, v.error);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
const fm = ctx;
|
|
798
|
+
const instance = meta?.instanceName; // whose polls: the instance's (as the reply recorded)
|
|
799
|
+
if (!instance || !fm.closePollEarly) {
|
|
800
|
+
respond(null, "close_poll is not available here");
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
const r = await fm.closePollEarly(instance, v.data.poll_id);
|
|
804
|
+
if (r.ok)
|
|
805
|
+
respond({ closed: true, poll_id: v.data.poll_id, voted: r.voted });
|
|
806
|
+
else
|
|
807
|
+
respond(null, `close_poll: ${r.error}`);
|
|
808
|
+
};
|
|
766
809
|
const getEffort = (ctx, rawArgs, respond, meta) => {
|
|
767
810
|
const v = validateArgs(GetEffortArgs, rawArgs, "get_effort");
|
|
768
811
|
if (!v.ok) {
|
|
@@ -921,31 +964,86 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
921
964
|
respond(null, v.error);
|
|
922
965
|
return;
|
|
923
966
|
}
|
|
924
|
-
|
|
967
|
+
applyInstanceConfigPatch(ctx, v.data.name, v.data.config, respond);
|
|
968
|
+
};
|
|
969
|
+
/**
|
|
970
|
+
* update_instance_config's write: one validated config write, rolled back whole on a refusal, then one relaunch when
|
|
971
|
+
* the backend (fresh, #1217), a credential profile or backend_options changed. Answers through `respond` like the tool.
|
|
972
|
+
*/
|
|
973
|
+
export function applyInstanceConfigPatch(ctx, name, patch, respond) {
|
|
974
|
+
const inst = ctx.fleetConfig?.instances[name];
|
|
925
975
|
if (!inst) {
|
|
926
976
|
const isClassic = (ctx.classicChannels?.getAll() ?? [])
|
|
927
|
-
.some(channel => channel.instanceName ===
|
|
977
|
+
.some(channel => channel.instanceName === name);
|
|
928
978
|
respond(null, isClassic
|
|
929
979
|
? "This is a ClassicBot instance (managed in classicBot.yaml). Use set_display_name or set_description to configure it."
|
|
930
|
-
: `Instance '${
|
|
980
|
+
: `Instance '${name}' not found in fleet config`);
|
|
931
981
|
return;
|
|
932
982
|
}
|
|
933
|
-
const patch = v.data.config;
|
|
934
983
|
// Read before anything is mutated: the switch is what decides whether the
|
|
935
984
|
// next launch may resume, and by then `inst` already holds the new value.
|
|
936
|
-
|
|
985
|
+
// The backend an instance runs: its own, else the fleet default (#1815 review: a Settings-created entry may omit it).
|
|
986
|
+
const effectiveBackend = () => inst.backend ?? ctx.fleetConfig?.defaults?.backend ?? "claude-code";
|
|
987
|
+
const backendBefore = effectiveBackend();
|
|
988
|
+
// #1808: a model and an effort name the backend they were chosen for. On a switch the new backend gets the ones
|
|
989
|
+
// given — if it takes them, refused here before anything is written — or none of the instance's own (the inherited
|
|
990
|
+
// value, else the CLI's default). Never the old backend's: codex launched with `--model opus` fails every turn.
|
|
991
|
+
if (patch.backend !== undefined && patch.backend !== backendBefore) {
|
|
992
|
+
const backendNew = patch.backend;
|
|
993
|
+
// Only a backend AgEnD knows gets these checks; any other name is refused by validation below, as before (and a
|
|
994
|
+
// "__proto__" or "toString" never reaches a lookup keyed by backend name).
|
|
995
|
+
const known = KNOWN_BACKENDS.includes(backendNew);
|
|
996
|
+
// The provider the new backend will run on (#1815 review): this patch's backend_options over the instance's, else
|
|
997
|
+
// the fleet defaults' — a custom provider has its own catalog, which the account's list says nothing about.
|
|
998
|
+
const options = mergeBackendOptions(inst.backend_options, patch.backend_options ?? {})[backendNew]
|
|
999
|
+
?? ctx.fleetConfig?.defaults?.backend_options?.[backendNew];
|
|
1000
|
+
const rawProvider = options?.provider;
|
|
1001
|
+
const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider.trim() : null;
|
|
1002
|
+
if (known && typeof patch.model === "string") {
|
|
1003
|
+
const verdict = ctx.backendModelVerdict?.(name, backendNew, patch.model, { provider });
|
|
1004
|
+
if (verdict && !verdict.ok) {
|
|
1005
|
+
respond(null, `Rejected: ${backendNew} does not take the model "${patch.model}". ${verdict.choices.length
|
|
1006
|
+
? `Choose one of: ${verdict.choices.join(", ")}` : `No ${backendNew} model list is known yet`} — or leave model out for its default.`);
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
if (known && typeof patch.effort === "string") {
|
|
1011
|
+
// The levels of the model that will run there (codex's differ per model): the one given, else the inherited one.
|
|
1012
|
+
const inheritedModel = getEffectiveInstanceDefaults(ctx.fleetConfig?.defaults).model;
|
|
1013
|
+
const runs = typeof patch.model === "string" ? patch.model : typeof inheritedModel === "string" ? inheritedModel : null;
|
|
1014
|
+
const levels = ctx.backendEffortLevels?.(name, backendNew, runs);
|
|
1015
|
+
if (levels && !levels.includes(patch.effort)) {
|
|
1016
|
+
respond(null, levels.length
|
|
1017
|
+
? `Rejected: ${backendNew} takes no effort "${patch.effort}" at launch on this host. Choose one of: ${levels.join(", ")} — or leave effort out for its default.`
|
|
1018
|
+
: `Rejected: ${backendNew} takes no effort at launch on this host — leave effort out.`);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
patch = { ...patch, model: patch.model === undefined ? null : patch.model, effort: patch.effort === undefined ? null : patch.effort };
|
|
1023
|
+
}
|
|
937
1024
|
const profileBefore = instanceCredentialProfile(inst, ctx.fleetConfig?.defaults, backendBefore);
|
|
938
1025
|
// Snapshot enough to undo the whole patch if validation refuses it.
|
|
939
1026
|
const beforeEdit = validateFleetConfig(ctx.fleetConfig);
|
|
940
1027
|
const beforeFields = {};
|
|
941
|
-
for (const key of ["backend", "model", "auto_pause_after", "display_name", "description"]) {
|
|
1028
|
+
for (const key of ["backend", "model", "effort", "auto_pause_after", "display_name", "description"]) {
|
|
942
1029
|
if (patch[key] !== undefined)
|
|
943
1030
|
beforeFields[key] = inst[key];
|
|
944
1031
|
}
|
|
945
1032
|
if (patch.backend !== undefined)
|
|
946
1033
|
inst.backend = patch.backend;
|
|
947
|
-
|
|
948
|
-
|
|
1034
|
+
// null: the value this instance would have with no override of its own — what loading fleet.yaml gives it.
|
|
1035
|
+
const inherited = getEffectiveInstanceDefaults(ctx.fleetConfig?.defaults);
|
|
1036
|
+
for (const key of ["model", "effort"]) {
|
|
1037
|
+
const value = patch[key];
|
|
1038
|
+
if (value === undefined)
|
|
1039
|
+
continue;
|
|
1040
|
+
if (value !== null)
|
|
1041
|
+
inst[key] = value;
|
|
1042
|
+
else if (inherited[key] !== undefined)
|
|
1043
|
+
inst[key] = inherited[key];
|
|
1044
|
+
else
|
|
1045
|
+
delete inst[key];
|
|
1046
|
+
}
|
|
949
1047
|
if (patch.auto_pause_after !== undefined)
|
|
950
1048
|
inst.auto_pause_after = patch.auto_pause_after;
|
|
951
1049
|
if (patch.display_name !== undefined)
|
|
@@ -959,20 +1057,7 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
959
1057
|
let relaunchNeeded = false;
|
|
960
1058
|
if (patch.backend_options !== undefined) {
|
|
961
1059
|
const before = JSON.stringify(previousOptions ?? {});
|
|
962
|
-
const merged =
|
|
963
|
-
for (const [backendName, options] of Object.entries(patch.backend_options)) {
|
|
964
|
-
const namespace = { ...(merged[backendName] ?? {}), ...options };
|
|
965
|
-
// A merge cannot express removal, so null (or "") means "drop this key"
|
|
966
|
-
// — that is how an agent goes back to the fleet's default login.
|
|
967
|
-
for (const [key, value] of Object.entries(options)) {
|
|
968
|
-
if (value === null || value === "")
|
|
969
|
-
delete namespace[key];
|
|
970
|
-
}
|
|
971
|
-
if (Object.keys(namespace).length)
|
|
972
|
-
merged[backendName] = namespace;
|
|
973
|
-
else
|
|
974
|
-
delete merged[backendName];
|
|
975
|
-
}
|
|
1060
|
+
const merged = mergeBackendOptions(previousOptions, patch.backend_options);
|
|
976
1061
|
inst.backend_options = merged;
|
|
977
1062
|
relaunchNeeded = JSON.stringify(merged) !== before;
|
|
978
1063
|
}
|
|
@@ -1000,7 +1085,7 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1000
1085
|
respond(null, `Rejected: ${introduced.map(issue => `${issue.path}: ${issue.message}`).join("; ")}`);
|
|
1001
1086
|
return;
|
|
1002
1087
|
}
|
|
1003
|
-
const backendAfter =
|
|
1088
|
+
const backendAfter = effectiveBackend();
|
|
1004
1089
|
const profileAfter = instanceCredentialProfile(inst, ctx.fleetConfig?.defaults, backendAfter);
|
|
1005
1090
|
const profileSwitched = profileAfter !== profileBefore;
|
|
1006
1091
|
// #1217: a running CLI is the backend it was launched as. Saving a new
|
|
@@ -1023,13 +1108,17 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1023
1108
|
// the escape hatch from a bad switch and must never be the thing that is
|
|
1024
1109
|
// blocked, and an unlogged shared store means the CLI was never set up at
|
|
1025
1110
|
// all — a state that predates this feature.
|
|
1026
|
-
|
|
1111
|
+
// #1806 review: a profile is a login of ONE backend — the same name under another backend is another store, so a
|
|
1112
|
+
// backend switch onto a named profile is checked too (claude-code "personal" says nothing about kiro-cli "personal").
|
|
1113
|
+
if ((profileSwitched || backendSwitched) && profileAfter) {
|
|
1027
1114
|
// The same root the launcher resolves the profile under, so the store this
|
|
1028
1115
|
// probes is the store the CLI will be pointed at.
|
|
1029
1116
|
const login = credentialProfileLogin(ctx.dataDir, backendAfter, profileAfter);
|
|
1030
1117
|
if (login.state === "signed-out") {
|
|
1031
1118
|
rollback();
|
|
1032
|
-
|
|
1119
|
+
// #1699 part 2: a claude-code profile can also be signed in from chat, in the browser terminal.
|
|
1120
|
+
const fromChat = CHAT_PROFILE_LOGIN_BACKENDS.has(backendAfter) ? `\n or from chat: /login ${backendAfter} ${profileAfter}` : "";
|
|
1121
|
+
respond(null, `Rejected: credential profile "${profileAfter}" has no ${backendAfter} login yet, and switching to it would leave the agent waiting at a sign-in prompt. Log it in first:\n ${login.loginCommand}${fromChat}`);
|
|
1033
1122
|
return;
|
|
1034
1123
|
}
|
|
1035
1124
|
}
|
|
@@ -1042,18 +1131,18 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1042
1131
|
return;
|
|
1043
1132
|
}
|
|
1044
1133
|
if (!relaunchNeeded) {
|
|
1045
|
-
respond({ success: true, name
|
|
1134
|
+
respond({ success: true, name, applied: patch });
|
|
1046
1135
|
return;
|
|
1047
1136
|
}
|
|
1048
1137
|
// Only a running instance is restarted. Switching the subscription of a
|
|
1049
1138
|
// paused or stopped agent must not start it — restartSingleInstance is
|
|
1050
1139
|
// stop-then-start and would — so the new credentials are read whenever it
|
|
1051
1140
|
// next comes up on its own.
|
|
1052
|
-
const status = targetState(ctx,
|
|
1141
|
+
const status = targetState(ctx, name);
|
|
1053
1142
|
if (status !== "running") {
|
|
1054
1143
|
respond({
|
|
1055
1144
|
success: true,
|
|
1056
|
-
name
|
|
1145
|
+
name,
|
|
1057
1146
|
applied: patch,
|
|
1058
1147
|
restarted: false,
|
|
1059
1148
|
note: `Instance is ${status}; the new ${backendSwitched ? "backend" : "backend_options"} apply when it next starts.`,
|
|
@@ -1073,19 +1162,26 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1073
1162
|
// was — taking a handover there would be describing a loss that did not
|
|
1074
1163
|
// happen, and starting fresh would be causing one.
|
|
1075
1164
|
const switchLosesConversation = profileSwitched && credentialSwitchStartsFresh(backendAfter);
|
|
1165
|
+
// #1753: Claude Code's conversation can go along — copied into the new profile and resumed there, between the old
|
|
1166
|
+
// CLI's stop and the new one's start (the restart does it, and says how it went). The handover is still taken now,
|
|
1167
|
+
// while the daemon that holds it is alive: it is what the new session gets if the copy cannot be made.
|
|
1168
|
+
const carry = profileSwitched && !backendSwitched && credentialSwitchCarriesConversation(backendAfter)
|
|
1169
|
+
? { backend: backendAfter, from: profileBefore, to: profileAfter, result: null }
|
|
1170
|
+
: null;
|
|
1076
1171
|
const handover = switchLosesConversation
|
|
1077
|
-
? (ctx.lifecycle.daemons.get(
|
|
1172
|
+
? (ctx.lifecycle.daemons.get(name)?.collectHandoverContext() ?? "")
|
|
1078
1173
|
: "";
|
|
1079
1174
|
// Saving without restarting would report a subscription switch that has not
|
|
1080
1175
|
// happened — the running CLI keeps the credentials it was launched with.
|
|
1081
|
-
// A switch
|
|
1082
|
-
// directory, so resuming can only spend the
|
|
1083
|
-
// something that is not there.
|
|
1084
|
-
ctx.restartSingleInstance(
|
|
1085
|
-
const
|
|
1176
|
+
// A switch that cannot take the conversation along starts fresh: the new store
|
|
1177
|
+
// has no conversation for this directory, so resuming can only spend the
|
|
1178
|
+
// resume startup budget waiting for something that is not there.
|
|
1179
|
+
ctx.restartSingleInstance(name, carry ? { carry } : switchLosesConversation || backendSwitched ? { freshStart: true } : undefined).then(async () => {
|
|
1180
|
+
const carried = carry?.result?.carried === true;
|
|
1181
|
+
const handoverChars = carried ? 0 : await deliverProfileHandover(ctx, name, profileBefore, profileAfter, handover);
|
|
1086
1182
|
respond({
|
|
1087
1183
|
success: true,
|
|
1088
|
-
name
|
|
1184
|
+
name,
|
|
1089
1185
|
applied: patch,
|
|
1090
1186
|
restarted: true,
|
|
1091
1187
|
...(backendSwitched ? { backend_switched: true, conversation_carried_over: false } : {}),
|
|
@@ -1096,21 +1192,43 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1096
1192
|
// across accounts is the thing nobody has been able to test yet,
|
|
1097
1193
|
// so this says where the history lives rather than what will
|
|
1098
1194
|
// happen when the agent reaches for it.
|
|
1099
|
-
conversation_carried_over: !switchLosesConversation,
|
|
1100
|
-
...(
|
|
1101
|
-
? {
|
|
1102
|
-
:
|
|
1195
|
+
conversation_carried_over: carried || !switchLosesConversation,
|
|
1196
|
+
...(carried
|
|
1197
|
+
? { conversation_note: "The conversation was copied into the new profile and resumed there (the old profile keeps its own copy). The next reply re-reads it from a cold cache: one slower, full-price turn." }
|
|
1198
|
+
: switchLosesConversation
|
|
1199
|
+
? { handover_chars: handoverChars, ...(carry?.result && !carry.result.carried ? { carry_failed: carry.result.reason } : {}) }
|
|
1200
|
+
: { conversation_note: `${backendAfter} keeps its history outside the login, so it is shared across profiles; switching back to a profile returns to the same history.` }),
|
|
1103
1201
|
}
|
|
1104
1202
|
: {}),
|
|
1105
1203
|
});
|
|
1106
1204
|
}, (err) => respond({
|
|
1107
1205
|
success: true,
|
|
1108
|
-
name
|
|
1206
|
+
name,
|
|
1109
1207
|
applied: patch,
|
|
1110
1208
|
restarted: false,
|
|
1111
1209
|
warning: `Config saved, but the restart failed: ${err.message}. The new backend_options take effect on the next start.`,
|
|
1112
1210
|
}));
|
|
1113
|
-
}
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* backend_options merged per backend namespace, so setting a kiro option cannot silently drop a codex one. A merge
|
|
1214
|
+
* cannot express removal, so null (or "") means "drop this key" — that is how an agent goes back to the fleet's
|
|
1215
|
+
* default login.
|
|
1216
|
+
*/
|
|
1217
|
+
function mergeBackendOptions(previous, patch) {
|
|
1218
|
+
const merged = { ...(previous ?? {}) };
|
|
1219
|
+
for (const [backendName, options] of Object.entries(patch)) {
|
|
1220
|
+
const namespace = { ...(merged[backendName] ?? {}), ...options };
|
|
1221
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1222
|
+
if (value === null || value === "")
|
|
1223
|
+
delete namespace[key];
|
|
1224
|
+
}
|
|
1225
|
+
if (Object.keys(namespace).length)
|
|
1226
|
+
merged[backendName] = namespace;
|
|
1227
|
+
else
|
|
1228
|
+
delete merged[backendName];
|
|
1229
|
+
}
|
|
1230
|
+
return merged;
|
|
1231
|
+
}
|
|
1114
1232
|
/** Long enough for the relaunched CLI to be listening; matches replace_instance. */
|
|
1115
1233
|
export const PROFILE_HANDOVER_SETTLE_MS = 3_000;
|
|
1116
1234
|
/**
|
|
@@ -1205,6 +1323,8 @@ function wrapAsSend(schema, toolName, buildArgs, warnMissing) {
|
|
|
1205
1323
|
request_kind: kind,
|
|
1206
1324
|
requires_reply: reply,
|
|
1207
1325
|
task_summary: summary,
|
|
1326
|
+
...(v.data.forwards !== undefined
|
|
1327
|
+
? { forwards: v.data.forwards } : {}),
|
|
1208
1328
|
};
|
|
1209
1329
|
// Re-dispatch through the handler map
|
|
1210
1330
|
return sendToInstance(ctx, newArgs, respond, meta);
|
|
@@ -1404,30 +1524,50 @@ const broadcast = async (ctx, rawArgs, respond, meta) => {
|
|
|
1404
1524
|
});
|
|
1405
1525
|
};
|
|
1406
1526
|
// ── Teams ────────────────────────────────────────────────────────────────
|
|
1527
|
+
/**
|
|
1528
|
+
* #1692: the one creation of a team — create_team (agents) and POST /ui/teams (the web) both come here. Members must be
|
|
1529
|
+
* instances fleet.yaml defines (own keys; repeats dropped); an existing team is never overwritten (409); a name every
|
|
1530
|
+
* object already has (`__proto__`, `constructor`, `toString`…) is not a team name. The team and its save are one: a
|
|
1531
|
+
* save that throws removes it again (500).
|
|
1532
|
+
*/
|
|
1533
|
+
export function commitTeamCreate(fleetConfig, args, save) {
|
|
1534
|
+
if (!fleetConfig)
|
|
1535
|
+
return { ok: false, status: 500, error: "Fleet config not available" };
|
|
1536
|
+
if (args.name in Object.prototype)
|
|
1537
|
+
return { ok: false, status: 400, error: `Invalid team name: ${args.name}` };
|
|
1538
|
+
const invalid = args.members.filter(m => !Object.hasOwn(fleetConfig.instances, m));
|
|
1539
|
+
if (invalid.length)
|
|
1540
|
+
return { ok: false, status: 400, error: `Invalid instance names: ${invalid.join(", ")}` };
|
|
1541
|
+
if (fleetConfig.teams && Object.hasOwn(fleetConfig.teams, args.name))
|
|
1542
|
+
return { ok: false, status: 409, error: `Team already exists: ${args.name}` };
|
|
1543
|
+
const createdMap = !fleetConfig.teams;
|
|
1544
|
+
const teams = (fleetConfig.teams ??= {});
|
|
1545
|
+
const members = [...new Set(args.members)];
|
|
1546
|
+
const description = args.description?.trim();
|
|
1547
|
+
teams[args.name] = { members, ...(description ? { description } : {}) };
|
|
1548
|
+
try {
|
|
1549
|
+
save();
|
|
1550
|
+
}
|
|
1551
|
+
catch (err) {
|
|
1552
|
+
delete teams[args.name];
|
|
1553
|
+
if (createdMap)
|
|
1554
|
+
delete fleetConfig.teams;
|
|
1555
|
+
return { ok: false, status: 500, error: `Saving fleet.yaml failed; no team was created: ${err?.message ?? String(err)}` };
|
|
1556
|
+
}
|
|
1557
|
+
return { ok: true, result: { created: args.name, members } };
|
|
1558
|
+
}
|
|
1407
1559
|
const createTeam = (ctx, rawArgs, respond) => {
|
|
1408
1560
|
const v = validateArgs(CreateTeamArgs, rawArgs, "create_team");
|
|
1409
1561
|
if (!v.ok) {
|
|
1410
1562
|
respond(null, v.error);
|
|
1411
1563
|
return;
|
|
1412
1564
|
}
|
|
1413
|
-
const
|
|
1414
|
-
if (!
|
|
1415
|
-
respond(null,
|
|
1416
|
-
return;
|
|
1417
|
-
}
|
|
1418
|
-
const invalid = members.filter(m => !ctx.fleetConfig.instances[m]);
|
|
1419
|
-
if (invalid.length) {
|
|
1420
|
-
respond(null, `Invalid instance names: ${invalid.join(", ")}`);
|
|
1421
|
-
return;
|
|
1422
|
-
}
|
|
1423
|
-
ctx.fleetConfig.teams ??= {};
|
|
1424
|
-
if (ctx.fleetConfig.teams[name]) {
|
|
1425
|
-
respond(null, `Team already exists: ${name}`);
|
|
1565
|
+
const r = commitTeamCreate(ctx.fleetConfig, v.data, () => ctx.saveFleetConfig());
|
|
1566
|
+
if (!r.ok) {
|
|
1567
|
+
respond(null, r.error);
|
|
1426
1568
|
return;
|
|
1427
1569
|
}
|
|
1428
|
-
|
|
1429
|
-
ctx.saveFleetConfig();
|
|
1430
|
-
respond({ created: name, members });
|
|
1570
|
+
respond(r.result);
|
|
1431
1571
|
};
|
|
1432
1572
|
const deleteTeam = (ctx, rawArgs, respond) => {
|
|
1433
1573
|
const v = validateArgs(DeleteTeamArgs, rawArgs, "delete_team");
|
|
@@ -1461,31 +1601,74 @@ const listTeams = (ctx, rawArgs, respond) => {
|
|
|
1461
1601
|
}));
|
|
1462
1602
|
respond(result);
|
|
1463
1603
|
};
|
|
1604
|
+
/**
|
|
1605
|
+
* #1689: the one update of a team — update_team (agents) and PATCH /ui/teams/<name> (the web) both come here. Own keys
|
|
1606
|
+
* only: a team or an instance is one fleet.yaml defines (`__proto__` and `constructor` are not); nothing changes unless
|
|
1607
|
+
* everything is valid. `description`: "" clears it. Renaming is not an update (the org chart and agents name teams).
|
|
1608
|
+
* The caller saves fleet.yaml after an ok.
|
|
1609
|
+
*/
|
|
1610
|
+
export function applyTeamUpdate(fleetConfig, args) {
|
|
1611
|
+
if (!fleetConfig)
|
|
1612
|
+
return { ok: false, status: 500, error: "Fleet config not available" };
|
|
1613
|
+
const teams = fleetConfig.teams;
|
|
1614
|
+
if (!teams || !Object.hasOwn(teams, args.name))
|
|
1615
|
+
return { ok: false, status: 404, error: `Team not found: ${args.name}` };
|
|
1616
|
+
const team = teams[args.name];
|
|
1617
|
+
const invalid = (args.add ?? []).filter(m => !Object.hasOwn(fleetConfig.instances, m));
|
|
1618
|
+
if (invalid.length)
|
|
1619
|
+
return { ok: false, status: 400, error: `Invalid instance names: ${invalid.join(", ")}` };
|
|
1620
|
+
let members = [...team.members];
|
|
1621
|
+
if (args.add?.length)
|
|
1622
|
+
members = [...new Set([...members, ...args.add])];
|
|
1623
|
+
if (args.remove?.length)
|
|
1624
|
+
members = members.filter(m => !args.remove.includes(m));
|
|
1625
|
+
team.members = members;
|
|
1626
|
+
if (args.description !== undefined) {
|
|
1627
|
+
if (args.description.trim())
|
|
1628
|
+
team.description = args.description.trim();
|
|
1629
|
+
else
|
|
1630
|
+
delete team.description;
|
|
1631
|
+
}
|
|
1632
|
+
return { ok: true, result: { name: args.name, members: team.members, description: team.description ?? null } };
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* #1689 review: the update and its save as one — a save that fails (an I/O error) puts the team back as it was, so the
|
|
1636
|
+
* live fleet never differs from fleet.yaml; that is a 500 (the input was fine), not a 400.
|
|
1637
|
+
*/
|
|
1638
|
+
export function commitTeamUpdate(fleetConfig, args, save) {
|
|
1639
|
+
const teams = fleetConfig?.teams;
|
|
1640
|
+
const team = teams && Object.hasOwn(teams, args.name) ? teams[args.name] : null;
|
|
1641
|
+
const before = team ? { members: [...team.members], hadDescription: Object.hasOwn(team, "description"), description: team.description } : null;
|
|
1642
|
+
const r = applyTeamUpdate(fleetConfig, args);
|
|
1643
|
+
if (!r.ok)
|
|
1644
|
+
return r;
|
|
1645
|
+
try {
|
|
1646
|
+
save();
|
|
1647
|
+
}
|
|
1648
|
+
catch (err) {
|
|
1649
|
+
if (team && before) {
|
|
1650
|
+
team.members = before.members;
|
|
1651
|
+
if (before.hadDescription)
|
|
1652
|
+
team.description = before.description;
|
|
1653
|
+
else
|
|
1654
|
+
delete team.description;
|
|
1655
|
+
}
|
|
1656
|
+
return { ok: false, status: 500, error: `Saving fleet.yaml failed; the team is unchanged: ${err?.message ?? String(err)}` };
|
|
1657
|
+
}
|
|
1658
|
+
return r;
|
|
1659
|
+
}
|
|
1464
1660
|
const updateTeam = (ctx, rawArgs, respond) => {
|
|
1465
1661
|
const v = validateArgs(UpdateTeamArgs, rawArgs, "update_team");
|
|
1466
1662
|
if (!v.ok) {
|
|
1467
1663
|
respond(null, v.error);
|
|
1468
1664
|
return;
|
|
1469
1665
|
}
|
|
1470
|
-
const
|
|
1471
|
-
if (!
|
|
1472
|
-
respond(null,
|
|
1666
|
+
const r = commitTeamUpdate(ctx.fleetConfig, v.data, () => ctx.saveFleetConfig());
|
|
1667
|
+
if (!r.ok) {
|
|
1668
|
+
respond(null, r.error);
|
|
1473
1669
|
return;
|
|
1474
1670
|
}
|
|
1475
|
-
|
|
1476
|
-
if (add?.length) {
|
|
1477
|
-
const invalid = add.filter(m => !ctx.fleetConfig.instances[m]);
|
|
1478
|
-
if (invalid.length) {
|
|
1479
|
-
respond(null, `Invalid instance names: ${invalid.join(", ")}`);
|
|
1480
|
-
return;
|
|
1481
|
-
}
|
|
1482
|
-
team.members = [...new Set([...team.members, ...add])];
|
|
1483
|
-
}
|
|
1484
|
-
if (remove?.length) {
|
|
1485
|
-
team.members = team.members.filter(m => !remove.includes(m));
|
|
1486
|
-
}
|
|
1487
|
-
ctx.saveFleetConfig();
|
|
1488
|
-
respond({ name, members: team.members });
|
|
1671
|
+
respond(r.result);
|
|
1489
1672
|
};
|
|
1490
1673
|
// ── Fleet Templates ────────────────────────────────────────────────────
|
|
1491
1674
|
const deployTemplate = async (ctx, rawArgs, respond) => {
|
|
@@ -1786,6 +1969,7 @@ export const outboundHandlers = new Map([
|
|
|
1786
1969
|
["stop_instance", stopInstance],
|
|
1787
1970
|
["get_fleet_status", getFleetStatus],
|
|
1788
1971
|
["get_effort", getEffort],
|
|
1972
|
+
["close_poll", closePoll],
|
|
1789
1973
|
["kiro_engine_status", kiroEngineStatusHandler],
|
|
1790
1974
|
["get_usage", getUsage],
|
|
1791
1975
|
["list_models", listModels],
|