@songsid/agend 2.2.0-beta.1 → 2.2.0-beta.11
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 +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/agent-cli-instructions.md +16 -9
- package/dist/agent-cli.js +15 -7
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +24 -9
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +47 -0
- package/dist/agent-whats-new.js +147 -0
- package/dist/agent-whats-new.js.map +1 -0
- package/dist/auth-api.d.ts +5 -3
- package/dist/auth-api.js +19 -3
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/antigravity.d.ts +8 -0
- package/dist/backend/antigravity.js +32 -9
- 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-engine-ledger.d.ts +5 -0
- package/dist/backend/kiro-engine-ledger.js +20 -0
- package/dist/backend/kiro-engine-ledger.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +6 -0
- package/dist/backend/kiro-identity.js +19 -5
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +39 -3
- package/dist/backend/kiro.js +143 -19
- 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 +47 -4
- 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 +36 -1
- package/dist/channel/adapters/discord.js +126 -4
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +42 -2
- package/dist/channel/adapters/telegram.js +142 -23
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-server.js +9 -1
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +11 -0
- package/dist/channel/mcp-tools.js +26 -13
- 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 +10 -4
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +55 -3
- package/dist/cli.js +167 -39
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +2 -2
- package/dist/command-table.js +6 -3
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +59 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +15 -2
- package/dist/cross-instance-envelope.js +29 -5
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +156 -1
- package/dist/daemon.js +773 -89
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +70 -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/fleet-context.d.ts +10 -1
- package/dist/fleet-manager.d.ts +404 -13
- package/dist/fleet-manager.js +2039 -210
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/backend-providers/SKILL.md +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +92 -21
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +27 -3
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +8 -8
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -5
- package/dist/general-knowledge/skills/fleet-health/SKILL.md +4 -4
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +13 -13
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +3 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +6 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +12 -9
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +8 -2
- package/dist/general-knowledge/skills/scheduling/SKILL.md +5 -3
- package/dist/general-knowledge/skills/session-management/SKILL.md +6 -5
- package/dist/general-knowledge/skills/tui-effort/SKILL.md +2 -2
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +8 -3
- package/dist/general-knowledge/steering/core-rules.md +8 -7
- 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/instance-avatar.d.ts +36 -0
- package/dist/instance-avatar.js +57 -0
- package/dist/instance-avatar.js.map +1 -0
- package/dist/instance-config-impact.js +6 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +29 -6
- package/dist/instance-lifecycle.js +79 -28
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +3 -1
- package/dist/instructions.js +11 -8
- package/dist/instructions.js.map +1 -1
- 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/locale.js +226 -44
- 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/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/next-steps.js +1 -1
- package/dist/next-steps.js.map +1 -1
- package/dist/outbound-handlers.d.ts +100 -0
- package/dist/outbound-handlers.js +284 -97
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +38 -15
- package/dist/outbound-schemas.js +36 -8
- package/dist/outbound-schemas.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.js +14 -4
- 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/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 -0
- package/dist/restart-progress.js +30 -0
- package/dist/restart-progress.js.map +1 -1
- package/dist/service-activation.d.ts +15 -7
- package/dist/service-activation.js +46 -29
- package/dist/service-activation.js.map +1 -1
- package/dist/service-installer.d.ts +31 -0
- package/dist/service-installer.js +41 -2
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.js +85 -25
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +72 -1
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +28 -4
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +7 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +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/status-emojis.d.ts +10 -3
- package/dist/status-emojis.js +11 -2
- package/dist/status-emojis.js.map +1 -1
- 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 +4 -4
- 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 +42 -2
- package/dist/topic-commands.js +101 -11
- 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 +80 -3
- package/dist/transcript-jsonl.js +147 -10
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +19 -0
- package/dist/transcript-monitor.js +87 -15
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +78 -0
- package/dist/transcript-sources.js +375 -64
- package/dist/transcript-sources.js.map +1 -1
- package/dist/transcript-turns.d.ts +15 -2
- package/dist/transcript-turns.js +15 -2
- package/dist/transcript-turns.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 +25 -0
- package/dist/ui/chat-commands.js +1 -0
- package/dist/ui/chat-emojis.js +33 -0
- package/dist/ui/chat-mentions.js +63 -0
- package/dist/ui/chat-render.js +61 -4
- package/dist/ui/chat-store.js +37 -3
- package/dist/ui/chat-strings.js +40 -20
- package/dist/ui/chat-thread.js +111 -12
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/panel-chat.js +41 -10
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +199 -57
- package/dist/ui/panel-log.js +428 -0
- package/dist/ui/panel-needs.js +6 -2
- package/dist/ui/panel-settings.js +184 -32
- 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 +7 -2
- package/dist/ui/settings-model.js +2 -1
- package/dist/ui/settings-strings.js +56 -4
- package/dist/ui/settings-token.js +1 -1
- package/dist/ui/shared/app-i18n.js +43 -17
- package/dist/ui/shared/app-prefs.js +250 -0
- package/dist/ui/shared/app-route.js +7 -2
- 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 +163 -22
- package/dist/ui/shared/app.js +11 -1
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/header-tools.js +55 -5
- package/dist/ui/shared/instance-nav.js +241 -45
- package/dist/ui/shared/instance-switch.js +4 -4
- package/dist/ui/shared/panel-view.js +6 -0
- package/dist/ui/shared/signin.js +7 -7
- package/dist/ui/shared/theme.js +39 -7
- package/dist/ui/shared/tokens.css +1 -1
- package/dist/ui/shared/ui-icons.js +1 -0
- package/dist/ui/shared/usage-dialog.js +23 -5
- package/dist/ui/shared/view-strings.js +32 -4
- package/dist/ui/signin.html +1 -1
- 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 +28 -2
- package/dist/update-install.js +22 -4
- package/dist/update-install.js.map +1 -1
- package/dist/update-launch.d.ts +46 -1
- package/dist/update-launch.js +146 -3
- 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/update-preflight.d.ts +100 -0
- package/dist/update-preflight.js +264 -0
- package/dist/update-preflight.js.map +1 -0
- package/dist/updater-caller.d.ts +31 -0
- package/dist/updater-caller.js +109 -0
- package/dist/updater-caller.js.map +1 -0
- 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 +34 -5
- package/dist/usage/providers.js +160 -27
- 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.d.ts +1 -1
- package/dist/view-api.js +38 -8
- package/dist/view-api.js.map +1 -1
- package/dist/wake-coordinator.d.ts +11 -0
- package/dist/wake-coordinator.js +38 -4
- package/dist/wake-coordinator.js.map +1 -1
- package/dist/web-api.d.ts +89 -4
- package/dist/web-api.js +551 -45
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +2 -2
- package/dist/web-auth.js +9 -4
- package/dist/web-auth.js.map +1 -1
- package/dist/web-chat-history.d.ts +12 -1
- package/dist/web-chat-history.js +29 -7
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +27 -4
- package/dist/web-commands.js +19 -4
- package/dist/web-commands.js.map +1 -1
- package/dist/web-host-guard.d.ts +3 -11
- package/dist/web-host-guard.js +3 -12
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-images.d.ts +47 -0
- package/dist/web-images.js +169 -0
- package/dist/web-images.js.map +1 -0
- 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 +50 -1
- package/dist/web-session.js +63 -6
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +23 -0
- package/dist/web-settings-patch.js +77 -0
- package/dist/web-settings-patch.js.map +1 -0
- package/dist/web-shell-routes.d.ts +5 -1
- package/dist/web-shell-routes.js +10 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-upload.d.ts +17 -4
- package/dist/web-upload.js +64 -13
- package/dist/web-upload.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/package.json +1 -1
- 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)
|
|
@@ -32,8 +32,11 @@ function rejectOversizedCrossInstanceMessage(ctx, message, respond) {
|
|
|
32
32
|
respond(null, t("cross_instance.message_too_large", formatByteSize(actual), formatByteSize(limit)));
|
|
33
33
|
return true;
|
|
34
34
|
}
|
|
35
|
-
function rejectOversizedCrossInstanceEnvelope(message, meta, respond
|
|
36
|
-
|
|
35
|
+
function rejectOversizedCrossInstanceEnvelope(message, meta, respond,
|
|
36
|
+
/** #1650: the footer differs by the receiver's mode — its own, or the longer of the two when targets are not known yet. */
|
|
37
|
+
mode = "mcp") {
|
|
38
|
+
const size = (m) => Buffer.byteLength(formatCrossInstanceInboundMessage(message, meta, m), "utf8");
|
|
39
|
+
const actual = mode === "either" ? Math.max(size("mcp"), size("cli")) : size(mode);
|
|
37
40
|
if (actual <= MAX_ASSEMBLED_CROSS_INSTANCE_MESSAGE_BYTES)
|
|
38
41
|
return false;
|
|
39
42
|
respond(null, t("cross_instance.envelope_too_large", formatByteSize(actual), formatByteSize(MAX_ASSEMBLED_CROSS_INSTANCE_MESSAGE_BYTES)));
|
|
@@ -165,7 +168,7 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
165
168
|
respond(null, v.error);
|
|
166
169
|
return;
|
|
167
170
|
}
|
|
168
|
-
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;
|
|
169
172
|
if (rejectOversizedCrossInstanceMessage(ctx, message, respond))
|
|
170
173
|
return;
|
|
171
174
|
const senderLabel = meta.senderSessionName ?? meta.instanceName;
|
|
@@ -221,7 +224,7 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
221
224
|
const queuedPredecessor = steerCapable
|
|
222
225
|
&& ctx.hasPendingIdleGatedDelivery?.(targetInstanceName) === true;
|
|
223
226
|
const useSteer = steerCapable && !queuedPredecessor;
|
|
224
|
-
|
|
227
|
+
let steerFallbackWarning = steer === true && !useSteer
|
|
225
228
|
? isExternalSession
|
|
226
229
|
? t("delivery.steer_external")
|
|
227
230
|
: queuedPredecessor
|
|
@@ -252,12 +255,14 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
252
255
|
ipcMeta.working_directory = working_directory;
|
|
253
256
|
if (branch)
|
|
254
257
|
ipcMeta.branch = branch;
|
|
258
|
+
if (forwards?.length)
|
|
259
|
+
ipcMeta.forwards = JSON.stringify(forwards);
|
|
255
260
|
// #77: surface the sender's display name in the recipient's [from:…] header
|
|
256
261
|
// (keeps from_instance as the machine name so the reply-back target stays valid).
|
|
257
262
|
const senderDisplay = ctx.fleetConfig?.instances[senderLabel]?.display_name;
|
|
258
263
|
if (senderDisplay && senderDisplay !== senderLabel)
|
|
259
264
|
ipcMeta.from_display = senderDisplay;
|
|
260
|
-
if (rejectOversizedCrossInstanceEnvelope(message, ipcMeta, respond))
|
|
265
|
+
if (rejectOversizedCrossInstanceEnvelope(message, ipcMeta, respond, ctx.fleetConfig?.instances[targetName]?.agent_mode === "cli" ? "cli" : "mcp"))
|
|
261
266
|
return;
|
|
262
267
|
// Fire-and-queue: hand the message to the fleet and respond immediately.
|
|
263
268
|
// deliverToInstance owns waking a paused target, the idle gate and retries;
|
|
@@ -275,6 +280,7 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
275
280
|
meta: ipcMeta,
|
|
276
281
|
};
|
|
277
282
|
let durableReceipt;
|
|
283
|
+
let effectiveUseSteer = useSteer;
|
|
278
284
|
if (ctx.admitDurableDelivery) {
|
|
279
285
|
try {
|
|
280
286
|
durableReceipt = ctx.admitDurableDelivery({
|
|
@@ -288,7 +294,13 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
288
294
|
kind: useSteer ? "steer" : "fleet_inbound",
|
|
289
295
|
correlationId,
|
|
290
296
|
payload,
|
|
297
|
+
forwards,
|
|
298
|
+
requestedKind: steer === true ? "steer" : "fleet_inbound",
|
|
291
299
|
});
|
|
300
|
+
if (durableReceipt.kind)
|
|
301
|
+
effectiveUseSteer = durableReceipt.kind === "steer";
|
|
302
|
+
if (durableReceipt.duplicate && durableReceipt.kind !== undefined && durableReceipt.kind !== payload.type)
|
|
303
|
+
steerFallbackWarning = undefined;
|
|
292
304
|
}
|
|
293
305
|
catch (err) {
|
|
294
306
|
ctx.logger.error({ err, source: meta.instanceName, target: targetInstanceName }, "Durable outbound admission failed closed");
|
|
@@ -297,6 +309,10 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
297
309
|
}
|
|
298
310
|
}
|
|
299
311
|
else {
|
|
312
|
+
if (forwards?.length) {
|
|
313
|
+
respond(null, "Forwarded message verification requires the durable delivery store");
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
300
316
|
// Standalone handler harnesses retain the legacy path; production FleetManager
|
|
301
317
|
// always provides durable admission.
|
|
302
318
|
void deliverCrossInstanceWithRetry(ctx, targetInstanceName, targetName, senderLabel, correlationId, payload, useSteer ? { isCrossInstance: true, waitForIdle: false } : undefined);
|
|
@@ -333,33 +349,43 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
333
349
|
displayName: ctx.fleetConfig?.instances[name]?.display_name,
|
|
334
350
|
})));
|
|
335
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;
|
|
336
362
|
if (groupId && ctx.adapter) {
|
|
337
|
-
const instances = ctx.fleetConfig?.instances ?? {};
|
|
338
|
-
const notificationLabel = `${displayLabel(senderLabel)} → ${displayLabel(targetName)}`;
|
|
339
|
-
const notice = { label: notificationLabel, message, requestKind, taskSummary: ipcMeta.task_summary };
|
|
340
363
|
// ── Target topic notification ──
|
|
341
|
-
const targetInstance = instances[targetInstanceName];
|
|
342
364
|
const targetTopicId = targetInstance?.topic_id;
|
|
343
|
-
|
|
344
|
-
const targetText = targetTopicNotice(crossInstanceVisibility(ctx.fleetConfig, targetInstanceName), notice);
|
|
345
|
-
if (targetText !== null && targetTopicId && !targetIsGeneral && !ctx.sessionRegistry.has(targetName)) {
|
|
365
|
+
if (targetShown && targetTopicId) {
|
|
346
366
|
const targetAdapter = ctx.getAdapterForInstance?.(targetInstanceName) ?? ctx.adapter;
|
|
347
367
|
const targetGroupId = ctx.getGroupIdForInstance?.(targetInstanceName) ?? String(groupId);
|
|
348
368
|
targetAdapter.sendText(String(targetGroupId), targetText, { threadId: String(targetTopicId) })
|
|
349
369
|
.catch(e => ctx.logger.warn({ err: e }, "Failed to post target topic notification"));
|
|
350
370
|
}
|
|
351
371
|
// ── Sender topic notification ──
|
|
352
|
-
const senderInstance = instances[meta.instanceName];
|
|
353
372
|
const senderTopicId = senderInstance?.topic_id;
|
|
354
|
-
|
|
355
|
-
const senderText = senderTopicNotice(crossInstanceVisibility(ctx.fleetConfig, meta.instanceName), notice);
|
|
356
|
-
if (senderText !== null && senderTopicId && !senderIsGeneral) {
|
|
373
|
+
if (senderShown && senderTopicId) {
|
|
357
374
|
const senderAdapter = ctx.getAdapterForInstance?.(meta.instanceName) ?? ctx.adapter;
|
|
358
375
|
const senderGroupId = ctx.getGroupIdForInstance?.(meta.instanceName) ?? String(groupId);
|
|
359
376
|
senderAdapter.sendText(senderGroupId, senderText, { threadId: String(senderTopicId) })
|
|
360
377
|
.catch(e => ctx.logger.warn({ err: e }, "Failed to post sender topic notification"));
|
|
361
378
|
}
|
|
362
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);
|
|
363
389
|
// #856: the same line, now carrying the envelope's message_id and a short
|
|
364
390
|
// content digest, so a disputed message can be matched to its outbox row
|
|
365
391
|
// and paste digests without adding a log line per delivery.
|
|
@@ -379,6 +405,7 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
379
405
|
: undefined;
|
|
380
406
|
const warnings = [steerFallbackWarning, targetStateWarning].filter((value) => !!value);
|
|
381
407
|
respond({ sent: true, queued: true, target: targetName, target_state: state,
|
|
408
|
+
...(forwards?.length ? { forwards } : {}),
|
|
382
409
|
...(durableReceipt ? {
|
|
383
410
|
durable: true,
|
|
384
411
|
delivery_id: durableReceipt.deliveryId,
|
|
@@ -387,8 +414,8 @@ const sendToInstance = async (ctx, rawArgs, respond, meta) => {
|
|
|
387
414
|
duplicate: durableReceipt.duplicate,
|
|
388
415
|
} : {}),
|
|
389
416
|
...(state === "paused" ? { waking: true } : {}), correlation_id: correlationId,
|
|
390
|
-
...(
|
|
391
|
-
...(
|
|
417
|
+
...(effectiveUseSteer ? { delivery_mode: "steer" } : {}),
|
|
418
|
+
...(steer === true && !effectiveUseSteer ? { delivery_mode: "idle_queue" } : {}),
|
|
392
419
|
...(warnings.length > 0 ? { warning: warnings.join(" ") } : {}),
|
|
393
420
|
});
|
|
394
421
|
};
|
|
@@ -760,6 +787,25 @@ const kiroEngineStatusHandler = (ctx, rawArgs, respond) => {
|
|
|
760
787
|
}
|
|
761
788
|
respond(status);
|
|
762
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
|
+
};
|
|
763
809
|
const getEffort = (ctx, rawArgs, respond, meta) => {
|
|
764
810
|
const v = validateArgs(GetEffortArgs, rawArgs, "get_effort");
|
|
765
811
|
if (!v.ok) {
|
|
@@ -918,31 +964,86 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
918
964
|
respond(null, v.error);
|
|
919
965
|
return;
|
|
920
966
|
}
|
|
921
|
-
|
|
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];
|
|
922
975
|
if (!inst) {
|
|
923
976
|
const isClassic = (ctx.classicChannels?.getAll() ?? [])
|
|
924
|
-
.some(channel => channel.instanceName ===
|
|
977
|
+
.some(channel => channel.instanceName === name);
|
|
925
978
|
respond(null, isClassic
|
|
926
979
|
? "This is a ClassicBot instance (managed in classicBot.yaml). Use set_display_name or set_description to configure it."
|
|
927
|
-
: `Instance '${
|
|
980
|
+
: `Instance '${name}' not found in fleet config`);
|
|
928
981
|
return;
|
|
929
982
|
}
|
|
930
|
-
const patch = v.data.config;
|
|
931
983
|
// Read before anything is mutated: the switch is what decides whether the
|
|
932
984
|
// next launch may resume, and by then `inst` already holds the new value.
|
|
933
|
-
|
|
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
|
+
}
|
|
934
1024
|
const profileBefore = instanceCredentialProfile(inst, ctx.fleetConfig?.defaults, backendBefore);
|
|
935
1025
|
// Snapshot enough to undo the whole patch if validation refuses it.
|
|
936
1026
|
const beforeEdit = validateFleetConfig(ctx.fleetConfig);
|
|
937
1027
|
const beforeFields = {};
|
|
938
|
-
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"]) {
|
|
939
1029
|
if (patch[key] !== undefined)
|
|
940
1030
|
beforeFields[key] = inst[key];
|
|
941
1031
|
}
|
|
942
1032
|
if (patch.backend !== undefined)
|
|
943
1033
|
inst.backend = patch.backend;
|
|
944
|
-
|
|
945
|
-
|
|
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
|
+
}
|
|
946
1047
|
if (patch.auto_pause_after !== undefined)
|
|
947
1048
|
inst.auto_pause_after = patch.auto_pause_after;
|
|
948
1049
|
if (patch.display_name !== undefined)
|
|
@@ -956,20 +1057,7 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
956
1057
|
let relaunchNeeded = false;
|
|
957
1058
|
if (patch.backend_options !== undefined) {
|
|
958
1059
|
const before = JSON.stringify(previousOptions ?? {});
|
|
959
|
-
const merged =
|
|
960
|
-
for (const [backendName, options] of Object.entries(patch.backend_options)) {
|
|
961
|
-
const namespace = { ...(merged[backendName] ?? {}), ...options };
|
|
962
|
-
// A merge cannot express removal, so null (or "") means "drop this key"
|
|
963
|
-
// — that is how an agent goes back to the fleet's default login.
|
|
964
|
-
for (const [key, value] of Object.entries(options)) {
|
|
965
|
-
if (value === null || value === "")
|
|
966
|
-
delete namespace[key];
|
|
967
|
-
}
|
|
968
|
-
if (Object.keys(namespace).length)
|
|
969
|
-
merged[backendName] = namespace;
|
|
970
|
-
else
|
|
971
|
-
delete merged[backendName];
|
|
972
|
-
}
|
|
1060
|
+
const merged = mergeBackendOptions(previousOptions, patch.backend_options);
|
|
973
1061
|
inst.backend_options = merged;
|
|
974
1062
|
relaunchNeeded = JSON.stringify(merged) !== before;
|
|
975
1063
|
}
|
|
@@ -997,7 +1085,7 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
997
1085
|
respond(null, `Rejected: ${introduced.map(issue => `${issue.path}: ${issue.message}`).join("; ")}`);
|
|
998
1086
|
return;
|
|
999
1087
|
}
|
|
1000
|
-
const backendAfter =
|
|
1088
|
+
const backendAfter = effectiveBackend();
|
|
1001
1089
|
const profileAfter = instanceCredentialProfile(inst, ctx.fleetConfig?.defaults, backendAfter);
|
|
1002
1090
|
const profileSwitched = profileAfter !== profileBefore;
|
|
1003
1091
|
// #1217: a running CLI is the backend it was launched as. Saving a new
|
|
@@ -1020,13 +1108,17 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1020
1108
|
// the escape hatch from a bad switch and must never be the thing that is
|
|
1021
1109
|
// blocked, and an unlogged shared store means the CLI was never set up at
|
|
1022
1110
|
// all — a state that predates this feature.
|
|
1023
|
-
|
|
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) {
|
|
1024
1114
|
// The same root the launcher resolves the profile under, so the store this
|
|
1025
1115
|
// probes is the store the CLI will be pointed at.
|
|
1026
1116
|
const login = credentialProfileLogin(ctx.dataDir, backendAfter, profileAfter);
|
|
1027
1117
|
if (login.state === "signed-out") {
|
|
1028
1118
|
rollback();
|
|
1029
|
-
|
|
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}`);
|
|
1030
1122
|
return;
|
|
1031
1123
|
}
|
|
1032
1124
|
}
|
|
@@ -1039,18 +1131,18 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1039
1131
|
return;
|
|
1040
1132
|
}
|
|
1041
1133
|
if (!relaunchNeeded) {
|
|
1042
|
-
respond({ success: true, name
|
|
1134
|
+
respond({ success: true, name, applied: patch });
|
|
1043
1135
|
return;
|
|
1044
1136
|
}
|
|
1045
1137
|
// Only a running instance is restarted. Switching the subscription of a
|
|
1046
1138
|
// paused or stopped agent must not start it — restartSingleInstance is
|
|
1047
1139
|
// stop-then-start and would — so the new credentials are read whenever it
|
|
1048
1140
|
// next comes up on its own.
|
|
1049
|
-
const status = targetState(ctx,
|
|
1141
|
+
const status = targetState(ctx, name);
|
|
1050
1142
|
if (status !== "running") {
|
|
1051
1143
|
respond({
|
|
1052
1144
|
success: true,
|
|
1053
|
-
name
|
|
1145
|
+
name,
|
|
1054
1146
|
applied: patch,
|
|
1055
1147
|
restarted: false,
|
|
1056
1148
|
note: `Instance is ${status}; the new ${backendSwitched ? "backend" : "backend_options"} apply when it next starts.`,
|
|
@@ -1070,19 +1162,26 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1070
1162
|
// was — taking a handover there would be describing a loss that did not
|
|
1071
1163
|
// happen, and starting fresh would be causing one.
|
|
1072
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;
|
|
1073
1171
|
const handover = switchLosesConversation
|
|
1074
|
-
? (ctx.lifecycle.daemons.get(
|
|
1172
|
+
? (ctx.lifecycle.daemons.get(name)?.collectHandoverContext() ?? "")
|
|
1075
1173
|
: "";
|
|
1076
1174
|
// Saving without restarting would report a subscription switch that has not
|
|
1077
1175
|
// happened — the running CLI keeps the credentials it was launched with.
|
|
1078
|
-
// A switch
|
|
1079
|
-
// directory, so resuming can only spend the
|
|
1080
|
-
// something that is not there.
|
|
1081
|
-
ctx.restartSingleInstance(
|
|
1082
|
-
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);
|
|
1083
1182
|
respond({
|
|
1084
1183
|
success: true,
|
|
1085
|
-
name
|
|
1184
|
+
name,
|
|
1086
1185
|
applied: patch,
|
|
1087
1186
|
restarted: true,
|
|
1088
1187
|
...(backendSwitched ? { backend_switched: true, conversation_carried_over: false } : {}),
|
|
@@ -1093,21 +1192,43 @@ const updateInstanceConfig = (ctx, rawArgs, respond) => {
|
|
|
1093
1192
|
// across accounts is the thing nobody has been able to test yet,
|
|
1094
1193
|
// so this says where the history lives rather than what will
|
|
1095
1194
|
// happen when the agent reaches for it.
|
|
1096
|
-
conversation_carried_over: !switchLosesConversation,
|
|
1097
|
-
...(
|
|
1098
|
-
? {
|
|
1099
|
-
:
|
|
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.` }),
|
|
1100
1201
|
}
|
|
1101
1202
|
: {}),
|
|
1102
1203
|
});
|
|
1103
1204
|
}, (err) => respond({
|
|
1104
1205
|
success: true,
|
|
1105
|
-
name
|
|
1206
|
+
name,
|
|
1106
1207
|
applied: patch,
|
|
1107
1208
|
restarted: false,
|
|
1108
1209
|
warning: `Config saved, but the restart failed: ${err.message}. The new backend_options take effect on the next start.`,
|
|
1109
1210
|
}));
|
|
1110
|
-
}
|
|
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
|
+
}
|
|
1111
1232
|
/** Long enough for the relaunched CLI to be listening; matches replace_instance. */
|
|
1112
1233
|
export const PROFILE_HANDOVER_SETTLE_MS = 3_000;
|
|
1113
1234
|
/**
|
|
@@ -1202,6 +1323,8 @@ function wrapAsSend(schema, toolName, buildArgs, warnMissing) {
|
|
|
1202
1323
|
request_kind: kind,
|
|
1203
1324
|
requires_reply: reply,
|
|
1204
1325
|
task_summary: summary,
|
|
1326
|
+
...(v.data.forwards !== undefined
|
|
1327
|
+
? { forwards: v.data.forwards } : {}),
|
|
1205
1328
|
};
|
|
1206
1329
|
// Re-dispatch through the handler map
|
|
1207
1330
|
return sendToInstance(ctx, newArgs, respond, meta);
|
|
@@ -1313,7 +1436,7 @@ const broadcast = async (ctx, rawArgs, respond, meta) => {
|
|
|
1313
1436
|
// Validate once before dispatching any target. The placeholders are the
|
|
1314
1437
|
// longest values generated below, so every real per-target envelope fits if
|
|
1315
1438
|
// this one does. Keeping this outside the loop prevents partial broadcasts.
|
|
1316
|
-
if (rejectOversizedCrossInstanceEnvelope(message, makeBroadcastMeta("bcast-9999999999999-zzzzzz", "bcast-9999999999999"), respond))
|
|
1439
|
+
if (rejectOversizedCrossInstanceEnvelope(message, makeBroadcastMeta("bcast-9999999999999-zzzzzz", "bcast-9999999999999"), respond, "either"))
|
|
1317
1440
|
return;
|
|
1318
1441
|
// Resolve target list: team, explicit targets, tag filter, or all running
|
|
1319
1442
|
let targetNames;
|
|
@@ -1401,30 +1524,50 @@ const broadcast = async (ctx, rawArgs, respond, meta) => {
|
|
|
1401
1524
|
});
|
|
1402
1525
|
};
|
|
1403
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
|
+
}
|
|
1404
1559
|
const createTeam = (ctx, rawArgs, respond) => {
|
|
1405
1560
|
const v = validateArgs(CreateTeamArgs, rawArgs, "create_team");
|
|
1406
1561
|
if (!v.ok) {
|
|
1407
1562
|
respond(null, v.error);
|
|
1408
1563
|
return;
|
|
1409
1564
|
}
|
|
1410
|
-
const
|
|
1411
|
-
if (!
|
|
1412
|
-
respond(null,
|
|
1413
|
-
return;
|
|
1414
|
-
}
|
|
1415
|
-
const invalid = members.filter(m => !ctx.fleetConfig.instances[m]);
|
|
1416
|
-
if (invalid.length) {
|
|
1417
|
-
respond(null, `Invalid instance names: ${invalid.join(", ")}`);
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
1420
|
-
ctx.fleetConfig.teams ??= {};
|
|
1421
|
-
if (ctx.fleetConfig.teams[name]) {
|
|
1422
|
-
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);
|
|
1423
1568
|
return;
|
|
1424
1569
|
}
|
|
1425
|
-
|
|
1426
|
-
ctx.saveFleetConfig();
|
|
1427
|
-
respond({ created: name, members });
|
|
1570
|
+
respond(r.result);
|
|
1428
1571
|
};
|
|
1429
1572
|
const deleteTeam = (ctx, rawArgs, respond) => {
|
|
1430
1573
|
const v = validateArgs(DeleteTeamArgs, rawArgs, "delete_team");
|
|
@@ -1458,31 +1601,74 @@ const listTeams = (ctx, rawArgs, respond) => {
|
|
|
1458
1601
|
}));
|
|
1459
1602
|
respond(result);
|
|
1460
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
|
+
}
|
|
1461
1660
|
const updateTeam = (ctx, rawArgs, respond) => {
|
|
1462
1661
|
const v = validateArgs(UpdateTeamArgs, rawArgs, "update_team");
|
|
1463
1662
|
if (!v.ok) {
|
|
1464
1663
|
respond(null, v.error);
|
|
1465
1664
|
return;
|
|
1466
1665
|
}
|
|
1467
|
-
const
|
|
1468
|
-
if (!
|
|
1469
|
-
respond(null,
|
|
1666
|
+
const r = commitTeamUpdate(ctx.fleetConfig, v.data, () => ctx.saveFleetConfig());
|
|
1667
|
+
if (!r.ok) {
|
|
1668
|
+
respond(null, r.error);
|
|
1470
1669
|
return;
|
|
1471
1670
|
}
|
|
1472
|
-
|
|
1473
|
-
if (add?.length) {
|
|
1474
|
-
const invalid = add.filter(m => !ctx.fleetConfig.instances[m]);
|
|
1475
|
-
if (invalid.length) {
|
|
1476
|
-
respond(null, `Invalid instance names: ${invalid.join(", ")}`);
|
|
1477
|
-
return;
|
|
1478
|
-
}
|
|
1479
|
-
team.members = [...new Set([...team.members, ...add])];
|
|
1480
|
-
}
|
|
1481
|
-
if (remove?.length) {
|
|
1482
|
-
team.members = team.members.filter(m => !remove.includes(m));
|
|
1483
|
-
}
|
|
1484
|
-
ctx.saveFleetConfig();
|
|
1485
|
-
respond({ name, members: team.members });
|
|
1671
|
+
respond(r.result);
|
|
1486
1672
|
};
|
|
1487
1673
|
// ── Fleet Templates ────────────────────────────────────────────────────
|
|
1488
1674
|
const deployTemplate = async (ctx, rawArgs, respond) => {
|
|
@@ -1783,6 +1969,7 @@ export const outboundHandlers = new Map([
|
|
|
1783
1969
|
["stop_instance", stopInstance],
|
|
1784
1970
|
["get_fleet_status", getFleetStatus],
|
|
1785
1971
|
["get_effort", getEffort],
|
|
1972
|
+
["close_poll", closePoll],
|
|
1786
1973
|
["kiro_engine_status", kiroEngineStatusHandler],
|
|
1787
1974
|
["get_usage", getUsage],
|
|
1788
1975
|
["list_models", listModels],
|