From a6a071648601023386d2075d36cabc282f3aef57 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 14 Jul 2026 00:56:53 -0700 Subject: [PATCH] feat(setup): rename Crestodian to OpenClaw system agent User-facing name is now OpenClaw (the system speaks); internal code name is system-agent. Gateway methods crestodian.* -> openclaw.chat/openclaw.setup.*, agent tool -> openclaw, reserved agent ids openclaw + retired crestodian. openclaw setup routes: onboarding flags -> onboard, -m/--yes -> system agent, bare configured interactive -> OpenClaw chat, unconfigured -> onboarding. Hidden crestodian CLI and /crestodian TUI aliases kept; docs moved to docs/cli/openclaw.md with redirect stub. macOS/Android strings in lockstep. Refs #107237 --- .../openclaw/app/gateway/GatewayProtocol.kt | 10 +- .../app/i18n/NativeStringResources.kt | 2 +- .../app/src/main/res/values/strings.xml | 2 +- apps/macos/Sources/OpenClaw/Constants.swift | 2 +- .../GatewayDiscoveryPreferences.swift | 4 +- apps/macos/Sources/OpenClaw/MenuBar.swift | 18 +- apps/macos/Sources/OpenClaw/Onboarding.swift | 22 +- .../Sources/OpenClaw/OnboardingAISetup.swift | 86 +-- .../OpenClaw/OnboardingAISetupSupport.swift | 4 +- .../OpenClaw/OnboardingAISetupView.swift | 24 +- ....swift => OnboardingSystemAgentChat.swift} | 38 +- .../OpenClaw/OnboardingView+AISetupPage.swift | 24 +- .../OpenClaw/OnboardingView+Layout.swift | 38 +- .../OpenClaw/OnboardingView+Pages.swift | 2 +- .../Sources/OpenClaw/SettingsRootView.swift | 58 +- ...ttings.swift => SystemAgentSettings.swift} | 16 +- .../AppStateRemoteConfigTests.swift | 14 +- .../GatewayChannelConfigureTests.swift | 12 +- .../MenuContentSmokeTests.swift | 12 +- .../OnboardingAISetupTests.swift | 568 +++++++++--------- ...t => OnboardingSystemAgentChatTests.swift} | 200 +++--- .../OnboardingViewSmokeTests.swift | 102 ++-- .../SettingsViewSmokeTests.swift | 44 +- .../Sources/OpenClawKit/GatewayPush.swift | 2 +- .../OpenClawKit/Resources/tool-display.json | 4 +- .../OpenClawProtocol/GatewayModels.swift | 18 +- docs/cli/crestodian.md | 353 +---------- docs/cli/index.md | 36 +- docs/cli/onboard.md | 40 +- docs/cli/openclaw.md | 353 +++++++++++ docs/cli/setup.md | 42 +- docs/docs.json | 6 +- docs/docs_map.md | 33 +- docs/help/testing.md | 14 +- docs/platforms/macos.md | 4 +- docs/plugins/sdk-agent-harness.md | 2 +- docs/refactor/database-first.md | 6 +- docs/start/onboarding-overview.md | 32 +- docs/start/onboarding.md | 10 +- docs/start/wizard.md | 24 +- docs/tools/slash-commands.md | 2 +- docs/web/tui.md | 16 +- extensions/anthropic/cli-shared.test.ts | 12 +- extensions/anthropic/cli-shared.ts | 30 +- .../src/app-server/dynamic-tool-build.test.ts | 20 +- .../src/app-server/dynamic-tool-build.ts | 20 +- .../src/app-server/dynamic-tool-profile.ts | 6 +- .../codex/src/app-server/dynamic-tools.ts | 4 +- .../src/app-server/run-attempt-tool-setup.ts | 2 +- .../codex/src/app-server/run-attempt-tools.ts | 10 +- .../codex/src/app-server/run-attempt.test.ts | 12 +- .../thread-lifecycle.binding.test.ts | 62 +- .../src/app-server/thread-lifecycle.test.ts | 8 +- .../codex/src/app-server/thread-lifecycle.ts | 38 +- extensions/copilot/src/attempt.test.ts | 22 +- extensions/copilot/src/attempt.ts | 18 +- extensions/copilot/src/tool-bridge.test.ts | 22 +- extensions/qa-lab/src/ci-smoke-plan.ts | 2 +- .../src/suite-runtime-agent-process.test.ts | 4 +- .../src/engine/commands/command-visibility.ts | 2 +- package.json | 6 +- packages/gateway-protocol/src/index.ts | 74 +-- packages/gateway-protocol/src/schema.ts | 2 +- .../gateway-protocol/src/schema/frames.ts | 2 +- .../{crestodian.test.ts => openclaw.test.ts} | 20 +- .../src/schema/{crestodian.ts => openclaw.ts} | 46 +- .../src/schema/protocol-schemas.ts | 44 +- ...yaml => system-agent-ring-zero-setup.yaml} | 22 +- ...aml => docker-system-agent-first-run.yaml} | 22 +- .../check-database-first-legacy-stores.mjs | 2 +- scripts/deadcode-exports.baseline.mjs | 76 ++- ...er.sh => system-agent-first-run-docker.sh} | 18 +- ....json => system-agent-first-run-spec.json} | 22 +- ...s => system-agent-rescue-docker-client.ts} | 94 +-- ...ocker.sh => system-agent-rescue-docker.sh} | 20 +- scripts/lib/docker-e2e-scenarios.mjs | 6 +- scripts/test-live-shard.mjs | 6 +- scripts/test-projects.test-support.mjs | 48 +- ...tools.create-openclaw-coding-tools.test.ts | 14 +- src/agents/agent-tools.ts | 6 +- src/agents/cli-auth-epoch.test.ts | 16 +- src/agents/cli-executable-identity.ts | 2 +- src/agents/cli-runner.spawn.test.ts | 4 +- .../cli-runner/bundle-mcp.resume.test.ts | 8 +- src/agents/cli-runner/bundle-mcp.test.ts | 4 +- src/agents/cli-runner/bundle-mcp.ts | 18 +- src/agents/cli-runner/prepare.test.ts | 30 +- src/agents/cli-runner/prepare.ts | 20 +- src/agents/core-tool-factory-descriptors.ts | 2 +- src/agents/embedded-agent-runner/run.ts | 6 +- src/agents/harness/selection.test.ts | 72 +-- src/agents/harness/selection.ts | 28 +- .../harness/tool-surface-bridge.test.ts | 14 +- src/agents/tool-display-config.ts | 4 +- ...tool.test.ts => system-agent-tool.test.ts} | 140 ++--- ...restodian-tool.ts => system-agent-tool.ts} | 138 ++--- src/auto-reply/commands-registry.shared.ts | 6 +- src/auto-reply/reply/commands-crestodian.ts | 33 - .../reply/commands-handlers.runtime.ts | 4 +- src/auto-reply/reply/commands-system-agent.ts | 33 + src/channels/plugins/setup-wizard-helpers.ts | 2 +- src/cli/command-catalog.ts | 6 +- src/cli/program.smoke.test.ts | 12 +- src/cli/program.test-mocks.ts | 8 +- .../program/command-descriptor-utils.test.ts | 12 + src/cli/program/command-descriptor-utils.ts | 6 +- src/cli/program/command-group-descriptors.ts | 1 + src/cli/program/command-registry-core.ts | 7 +- src/cli/program/command-registry.test.ts | 12 +- src/cli/program/core-command-descriptors.ts | 9 +- src/cli/program/register.crestodian.ts | 41 -- src/cli/program/register.onboard.test.ts | 24 +- src/cli/program/register.onboard.ts | 10 +- src/cli/program/register.setup.test.ts | 106 +++- src/cli/program/register.setup.ts | 219 +++++-- src/commands/agents.add.test.ts | 37 ++ src/commands/agents.commands.add.ts | 13 +- ...ig-migrations.runtime.system-agent.test.ts | 38 ++ ...-config-migrations.runtime.system-agent.ts | 43 ++ .../legacy-config-migrations.runtime.ts | 2 + src/commands/onboard-guided.test.ts | 64 +- src/commands/onboard-guided.ts | 29 +- src/commands/onboard-inference.test.ts | 2 +- src/commands/onboard-inference.ts | 6 +- src/commands/onboard-interactive.ts | 10 +- src/commands/onboard-remote-gateway.test.ts | 70 +-- src/commands/onboard-remote-gateway.ts | 60 +- src/commands/onboard.ts | 2 +- ...ts => system-agent-with-inference.test.ts} | 74 +-- ...ence.ts => system-agent-with-inference.ts} | 44 +- src/config/config-misc.test.ts | 8 +- src/config/types.openclaw.ts | 6 +- ...es.crestodian.ts => types.system-agent.ts} | 10 +- src/config/zod-schema.ts | 4 +- src/crestodian/dialogue.ts | 110 ---- src/crestodian/inference-error.ts | 25 - src/crestodian/plugin-install.ts | 19 - src/gateway/local-request-context.ts | 2 +- src/gateway/methods/core-descriptors.ts | 10 +- ...r-methods.control-plane-rate-limit.test.ts | 4 +- src/gateway/server-methods.ts | 18 +- .../server-methods/agents-mutate.test.ts | 16 + src/gateway/server-methods/agents.ts | 9 +- src/gateway/server-methods/shared-types.ts | 6 +- ...restodian.test.ts => system-agent.test.ts} | 156 ++--- .../{crestodian.ts => system-agent.ts} | 156 ++--- src/gateway/server-request-context.test.ts | 2 +- src/gateway/server-request-context.ts | 4 +- .../server.auth.default-token.suite.ts | 2 +- src/gateway/server.impl.ts | 4 +- .../server/ws-connection/connect-hello.ts | 2 +- src/mcp/openclaw-tools-serve-config.ts | 50 +- src/mcp/openclaw-tools-serve.test.ts | 52 +- src/mcp/openclaw-tools-serve.ts | 20 +- src/plugins/plugin-registry-snapshot.ts | 2 +- src/process/lanes.ts | 2 +- src/system-agent/agent-id.ts | 12 + .../agent-turn.test.ts | 164 ++--- .../agent-turn.ts | 222 +++---- .../approval-intent.test.ts | 48 +- .../approval-intent.ts | 36 +- .../assistant-prompts.ts | 50 +- .../assistant.configured.test.ts | 68 +-- .../assistant.test.ts | 24 +- src/{crestodian => system-agent}/assistant.ts | 114 ++-- .../audit.test.ts | 12 +- src/{crestodian => system-agent}/audit.ts | 22 +- .../chat-engine.channel-hooks.test.ts | 18 +- .../chat-engine.test.ts | 218 +++---- .../chat-engine.ts | 231 +++---- src/system-agent/dialogue.ts | 110 ++++ src/system-agent/inference-error.ts | 27 + .../inference-route.ts | 30 +- .../onboarding-welcome.test.ts | 0 .../onboarding-welcome.ts | 12 +- .../operations.test.ts | 487 +++++++-------- .../operations.ts | 199 +++--- .../overview.test.ts | 32 +- src/{crestodian => system-agent}/overview.ts | 52 +- src/system-agent/plugin-install.ts | 19 + .../probes.test.ts | 4 +- src/{crestodian => system-agent}/probes.ts | 4 +- .../rescue-channel.live.test.ts | 34 +- .../rescue-message.test.ts | 128 ++-- .../rescue-message.ts | 100 +-- .../rescue-policy.test.ts | 12 +- .../rescue-policy.ts | 26 +- .../setup-apply.test.ts | 73 ++- .../setup-apply.ts | 74 +-- .../setup-config-snapshot.ts | 14 +- .../setup-inference-probe.ts | 0 .../setup-inference.test.ts | 136 +++-- .../setup-inference.ts | 160 ++--- .../system-agent.test-helpers.ts} | 34 +- .../system-agent.test.ts} | 124 ++-- .../system-agent.ts} | 124 ++-- .../tui-backend.test.ts | 54 +- .../tui-backend.ts | 155 ++--- .../verified-inference.test.ts | 134 ++--- .../verified-inference.ts | 146 ++--- src/tui/commands.test.ts | 16 +- src/tui/commands.ts | 5 +- src/tui/tui-command-handlers.test.ts | 14 +- src/tui/tui-command-handlers.ts | 10 +- src/tui/tui-types.ts | 4 +- src/tui/tui.ts | 2 +- src/wizard/i18n/locales/en.ts | 2 +- src/wizard/setup.test.ts | 4 +- src/wizard/setup.ts | 2 +- taxonomy.yaml | 6 +- .../runtime/docker-e2e-lane.fixture.test.ts | 2 +- .../qa-lab/runtime/docker-e2e-lane.fixture.ts | 4 +- ...> system-agent-first-run-docker-client.ts} | 60 +- test/scripts/docker-build-helper.test.ts | 8 +- test/scripts/docker-e2e-plan.test.ts | 8 +- ...est.ts => docker-e2e-system-agent.test.ts} | 42 +- test/scripts/lint-suppressions.test.ts | 4 +- test/scripts/test-live-shard.test.ts | 2 +- test/scripts/test-projects.test.ts | 82 +-- test/tsconfig/tsconfig.test.root.json | 2 +- test/vitest-unit-fast-config.test.ts | 6 +- test/vitest/vitest.unit-fast-paths.mjs | 16 +- tsdown.config.ts | 8 +- 223 files changed, 4961 insertions(+), 4484 deletions(-) rename apps/macos/Sources/OpenClaw/{OnboardingCrestodianChat.swift => OnboardingSystemAgentChat.swift} (90%) rename apps/macos/Sources/OpenClaw/{CrestodianSettings.swift => SystemAgentSettings.swift} (80%) rename apps/macos/Tests/OpenClawIPCTests/{OnboardingCrestodianChatTests.swift => OnboardingSystemAgentChatTests.swift} (77%) create mode 100644 docs/cli/openclaw.md rename packages/gateway-protocol/src/schema/{crestodian.test.ts => openclaw.test.ts} (58%) rename packages/gateway-protocol/src/schema/{crestodian.ts => openclaw.ts} (75%) rename qa/scenarios/config/{crestodian-ring-zero-setup.yaml => system-agent-ring-zero-setup.yaml} (62%) rename qa/scenarios/runtime/{docker-crestodian-first-run.yaml => docker-system-agent-first-run.yaml} (62%) rename scripts/e2e/{crestodian-first-run-docker.sh => system-agent-first-run-docker.sh} (63%) rename scripts/e2e/{crestodian-first-run-spec.json => system-agent-first-run-spec.json} (64%) rename scripts/e2e/{crestodian-rescue-docker-client.ts => system-agent-rescue-docker-client.ts} (67%) rename scripts/e2e/{crestodian-rescue-docker.sh => system-agent-rescue-docker.sh} (62%) rename src/agents/tools/{crestodian-tool.test.ts => system-agent-tool.test.ts} (74%) rename src/agents/tools/{crestodian-tool.ts => system-agent-tool.ts} (69%) delete mode 100644 src/auto-reply/reply/commands-crestodian.ts create mode 100644 src/auto-reply/reply/commands-system-agent.ts delete mode 100644 src/cli/program/register.crestodian.ts create mode 100644 src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.test.ts create mode 100644 src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.ts rename src/commands/{crestodian-with-inference.test.ts => system-agent-with-inference.test.ts} (83%) rename src/commands/{crestodian-with-inference.ts => system-agent-with-inference.ts} (67%) rename src/config/{types.crestodian.ts => types.system-agent.ts} (67%) delete mode 100644 src/crestodian/dialogue.ts delete mode 100644 src/crestodian/inference-error.ts delete mode 100644 src/crestodian/plugin-install.ts rename src/gateway/server-methods/{crestodian.test.ts => system-agent.test.ts} (79%) rename src/gateway/server-methods/{crestodian.ts => system-agent.ts} (66%) create mode 100644 src/system-agent/agent-id.ts rename src/{crestodian => system-agent}/agent-turn.test.ts (87%) rename src/{crestodian => system-agent}/agent-turn.ts (62%) rename src/{crestodian => system-agent}/approval-intent.test.ts (81%) rename src/{crestodian => system-agent}/approval-intent.ts (84%) rename src/{crestodian => system-agent}/assistant-prompts.ts (81%) rename src/{crestodian => system-agent}/assistant.configured.test.ts (82%) rename src/{crestodian => system-agent}/assistant.test.ts (79%) rename src/{crestodian => system-agent}/assistant.ts (56%) rename src/{crestodian => system-agent}/audit.test.ts (74%) rename src/{crestodian => system-agent}/audit.ts (63%) rename src/{crestodian => system-agent}/chat-engine.channel-hooks.test.ts (89%) rename src/{crestodian => system-agent}/chat-engine.test.ts (91%) rename src/{crestodian => system-agent}/chat-engine.ts (83%) create mode 100644 src/system-agent/dialogue.ts create mode 100644 src/system-agent/inference-error.ts rename src/{crestodian => system-agent}/inference-route.ts (91%) rename src/{crestodian => system-agent}/onboarding-welcome.test.ts (100%) rename src/{crestodian => system-agent}/onboarding-welcome.ts (90%) rename src/{crestodian => system-agent}/operations.test.ts (74%) rename src/{crestodian => system-agent}/operations.ts (88%) rename src/{crestodian => system-agent}/overview.test.ts (83%) rename src/{crestodian => system-agent}/overview.ts (87%) create mode 100644 src/system-agent/plugin-install.ts rename src/{crestodian => system-agent}/probes.test.ts (95%) rename src/{crestodian => system-agent}/probes.ts (94%) rename src/{crestodian => system-agent}/rescue-channel.live.test.ts (72%) rename src/{crestodian => system-agent}/rescue-message.test.ts (72%) rename src/{crestodian => system-agent}/rescue-message.ts (63%) rename src/{crestodian => system-agent}/rescue-policy.test.ts (83%) rename src/{crestodian => system-agent}/rescue-policy.ts (80%) rename src/{crestodian => system-agent}/setup-apply.test.ts (92%) rename src/{crestodian => system-agent}/setup-apply.ts (91%) rename src/{crestodian => system-agent}/setup-config-snapshot.ts (66%) rename src/{crestodian => system-agent}/setup-inference-probe.ts (100%) rename src/{crestodian => system-agent}/setup-inference.test.ts (97%) rename src/{crestodian => system-agent}/setup-inference.ts (95%) rename src/{crestodian/crestodian.test-helpers.ts => system-agent/system-agent.test-helpers.ts} (86%) rename src/{crestodian/crestodian.test.ts => system-agent/system-agent.test.ts} (75%) rename src/{crestodian/crestodian.ts => system-agent/system-agent.ts} (51%) rename src/{crestodian => system-agent}/tui-backend.test.ts (86%) rename src/{crestodian => system-agent}/tui-backend.ts (71%) rename src/{crestodian => system-agent}/verified-inference.test.ts (90%) rename src/{crestodian => system-agent}/verified-inference.ts (89%) rename test/e2e/qa-lab/runtime/{crestodian-first-run-docker-client.ts => system-agent-first-run-docker-client.ts} (84%) rename test/scripts/{docker-e2e-crestodian.test.ts => docker-e2e-system-agent.test.ts} (55%) diff --git a/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt b/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt index a689dc7b4272..d146727df927 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt @@ -135,10 +135,10 @@ enum class GatewayMethod( PluginApprovalResolve("plugin.approval.resolve"), PluginsUiDescriptors("plugins.uiDescriptors"), PluginsSessionAction("plugins.sessionAction"), - CrestodianChat("crestodian.chat"), - CrestodianSetupDetect("crestodian.setup.detect"), - CrestodianSetupActivate("crestodian.setup.activate"), - CrestodianSetupAuthStart("crestodian.setup.auth.start"), + SystemAgentChat("openclaw.chat"), + SystemAgentSetupDetect("openclaw.setup.detect"), + SystemAgentSetupActivate("openclaw.setup.activate"), + SystemAgentSetupAuthStart("openclaw.setup.auth.start"), WizardStart("wizard.start"), WizardNext("wizard.next"), WizardCancel("wizard.cancel"), @@ -360,7 +360,7 @@ enum class GatewayMethod( GatewaySuspendResume("gateway.suspend.resume"), ChatToolTitles("chat.toolTitles"), SessionsDiff("sessions.diff"), - CrestodianSetupVerify("crestodian.setup.verify"), + SystemAgentSetupVerify("openclaw.setup.verify"), EnvironmentsCreate("environments.create"), EnvironmentsDestroy("environments.destroy"), SessionsCatalogList("sessions.catalog.list"), diff --git a/apps/android/app/src/main/java/ai/openclaw/app/i18n/NativeStringResources.kt b/apps/android/app/src/main/java/ai/openclaw/app/i18n/NativeStringResources.kt index ee504a07a98f..64fe30dfc830 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/i18n/NativeStringResources.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/i18n/NativeStringResources.kt @@ -383,7 +383,7 @@ internal val nativeStringResourceIds: Map = "Create" to R.string.native_4759498ac2a719c6, "Create Goal" to R.string.native_1d5e35075a1017e0, "Created" to R.string.native_d70b9e24bca26b40, - "Crestodian" to R.string.native_8c28828d13dbb711, + "OpenClaw" to R.string.native_8c28828d13dbb711, "Cron" to R.string.native_dd9d24965dbedc02, "Cron action failed." to R.string.native_8cac4b77b9c54695, "Cron changes require operator.admin access." to R.string.native_a1bca979bc69befb, diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index d8f02224d747..1367d7bfda6e 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -790,7 +790,7 @@ "Open Screen" "Inspecting" "Scan or paste a setup code to add another gateway." - "Crestodian" + "OpenClaw" "TLS timed out" "Gateway paired. Checking node capability approval." "Motion" diff --git a/apps/macos/Sources/OpenClaw/Constants.swift b/apps/macos/Sources/OpenClaw/Constants.swift index d4ca66e68ae6..ebbd0e95d06c 100644 --- a/apps/macos/Sources/OpenClaw/Constants.swift +++ b/apps/macos/Sources/OpenClaw/Constants.swift @@ -6,7 +6,7 @@ let launchdLabel = "ai.openclaw.mac" let gatewayLaunchdLabel = "ai.openclaw.gateway" let onboardingVersionKey = "openclaw.onboardingVersion" let onboardingSeenKey = "openclaw.onboardingSeen" -let onboardingCrestodianPendingKey = "openclaw.onboardingCrestodianPending" +let onboardingSystemAgentPendingKey = "openclaw.onboardingSystemAgentPending" let currentOnboardingVersion = 8 let pauseDefaultsKey = "openclaw.pauseEnabled" let iconAnimationsEnabledKey = "openclaw.iconAnimationsEnabled" diff --git a/apps/macos/Sources/OpenClaw/GatewayDiscoveryPreferences.swift b/apps/macos/Sources/OpenClaw/GatewayDiscoveryPreferences.swift index b0d61400796f..bc39714a1b3d 100644 --- a/apps/macos/Sources/OpenClaw/GatewayDiscoveryPreferences.swift +++ b/apps/macos/Sources/OpenClaw/GatewayDiscoveryPreferences.swift @@ -61,7 +61,7 @@ enum GatewayDiscoveryPreferences { } else { defaultRemotePort } - return OnboardingCrestodianResumeStore.routeIdentity( + return OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: remoteTransport, @@ -86,7 +86,7 @@ enum GatewayDiscoveryPreferences { remoteURL: remoteURL, remoteTarget: remoteTarget) } - return OnboardingCrestodianResumeStore.routeIdentity( + return OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: connectionMode, preferredGatewayID: nil, remoteTransport: remoteTransport, diff --git a/apps/macos/Sources/OpenClaw/MenuBar.swift b/apps/macos/Sources/OpenClaw/MenuBar.swift index 651ed3bc2536..8232d02e63ad 100644 --- a/apps/macos/Sources/OpenClaw/MenuBar.swift +++ b/apps/macos/Sources/OpenClaw/MenuBar.swift @@ -552,14 +552,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate { static func shouldOpenDashboardInsteadOfOnboarding( connectionMode: AppState.ConnectionMode, onboardingSeen: Bool, - crestodianResumePending: Bool, + systemAgentResumePending: Bool, gatewayConnected: Bool, configuredInferenceModel: String?) -> Bool { let model = configuredInferenceModel?.trimmingCharacters(in: .whitespacesAndNewlines) return connectionMode != .unconfigured && !onboardingSeen && - !crestodianResumePending && + !systemAgentResumePending && gatewayConnected && model?.isEmpty == false } @@ -592,7 +592,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { private func scheduleFirstRunOnboardingIfNeeded(gatewayConnected: Bool) async { let connectionMode = AppStateStore.shared.connectionMode - let expectedRouteIdentity = OnboardingCrestodianResumeStore.selectedRouteIdentity() + let expectedRouteIdentity = OnboardingSystemAgentResumeStore.selectedRouteIdentity() var configuredInferenceModel: String? if connectionMode != .unconfigured, !AppStateStore.shared.onboardingSeen, @@ -603,7 +603,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { return } // Bind inference discovery to the connected route. A socket without a - // default-agent model cannot run Crestodian and must stay in onboarding. + // default-agent model cannot run OpenClaw and must stay in onboarding. do { configuredInferenceModel = try await GatewayConnection.shared.configuredInferenceModel( ifCurrentRoute: route) @@ -614,7 +614,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { return } let gatewayRouteIsCurrent = await GatewayConnection.shared.isCurrentRoute(route) - let currentRouteIdentity = OnboardingCrestodianResumeStore.selectedRouteIdentity() + let currentRouteIdentity = OnboardingSystemAgentResumeStore.selectedRouteIdentity() guard Self.isCurrentFirstRunInferenceProbe( expectedConnectionMode: connectionMode, currentConnectionMode: AppStateStore.shared.connectionMode, @@ -627,11 +627,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate { } } let onboardingSeen = AppStateStore.shared.onboardingSeen - let crestodianResumePending = OnboardingCrestodianResumeStore.isPending(for: expectedRouteIdentity) + let systemAgentResumePending = OnboardingSystemAgentResumeStore.isPending(for: expectedRouteIdentity) let shouldOpenDashboard = Self.shouldOpenDashboardInsteadOfOnboarding( connectionMode: connectionMode, onboardingSeen: onboardingSeen, - crestodianResumePending: crestodianResumePending, + systemAgentResumePending: systemAgentResumePending, gatewayConnected: gatewayConnected, configuredInferenceModel: configuredInferenceModel) if connectionMode != .unconfigured, onboardingSeen || shouldOpenDashboard { @@ -650,7 +650,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { private func scheduleFirstRunOnboardingRecovery() { self.scheduleFirstRunOnboardingPresentation( expectedConnectionMode: AppStateStore.shared.connectionMode, - expectedRouteIdentity: OnboardingCrestodianResumeStore.selectedRouteIdentity()) + expectedRouteIdentity: OnboardingSystemAgentResumeStore.selectedRouteIdentity()) } private func scheduleFirstRunOnboardingPresentation( @@ -661,7 +661,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { let shouldShow = seenVersion < currentOnboardingVersion || !AppStateStore.shared.onboardingSeen guard shouldShow else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { - let currentRouteIdentity = OnboardingCrestodianResumeStore.selectedRouteIdentity() + let currentRouteIdentity = OnboardingSystemAgentResumeStore.selectedRouteIdentity() guard Self.shouldPresentScheduledFirstRunOnboarding( expectedConnectionMode: expectedConnectionMode, currentConnectionMode: AppStateStore.shared.connectionMode, diff --git a/apps/macos/Sources/OpenClaw/Onboarding.swift b/apps/macos/Sources/OpenClaw/Onboarding.swift index 77a33f3da40d..9e789460a7b2 100644 --- a/apps/macos/Sources/OpenClaw/Onboarding.swift +++ b/apps/macos/Sources/OpenClaw/Onboarding.swift @@ -15,7 +15,7 @@ enum RemoteOnboardingProbeState: Equatable { case failed(String) } -enum OnboardingCrestodianResumeStore { +enum OnboardingSystemAgentResumeStore { struct ActivationOwner: Equatable { let id: String let routeFingerprint: String @@ -130,7 +130,7 @@ enum OnboardingCrestodianResumeStore { static func markPending( routeIdentity: String?, activationOwner: ActivationOwner? = nil, - activationTimeoutMs: Double = OnboardingCrestodianResumeStore.maximumActivationTimeoutMs, + activationTimeoutMs: Double = OnboardingSystemAgentResumeStore.maximumActivationTimeoutMs, defaults: UserDefaults = .standard, now: Date = Date()) -> Date? @@ -264,14 +264,14 @@ enum OnboardingCrestodianResumeStore { } static func clear(defaults: UserDefaults = .standard) { - defaults.removeObject(forKey: onboardingCrestodianPendingKey) + defaults.removeObject(forKey: onboardingSystemAgentPendingKey) } private static func loadRecords( defaults: UserDefaults, now: Date = Date()) -> [String: Record] { - guard let stored = defaults.object(forKey: onboardingCrestodianPendingKey) else { return [:] } + guard let stored = defaults.object(forKey: onboardingSystemAgentPendingKey) else { return [:] } if let legacyRoute = normalized(stored as? String) { let records = [legacyRoute: conservativeLegacyRecord(now: now)] self.writeRecords(records, defaults: defaults) @@ -400,7 +400,7 @@ enum OnboardingCrestodianResumeStore { } defaults.set( ["version": self.recordVersion, "records": payload], - forKey: onboardingCrestodianPendingKey) + forKey: onboardingSystemAgentPendingKey) } private static func date(_ value: Any?) -> Date? { @@ -609,7 +609,7 @@ struct OnboardingView: View { @State var suppressRemoteProbeReset = false @State var gatewayDiscovery: GatewayDiscoveryModel @State var onboardingSkillsModel = SkillsSettingsModel() - @State var crestodianState = OnboardingCrestodianChatState() + @State var systemAgentState = OnboardingSystemAgentChatState() @State var aiSetup = OnboardingAISetupModel() @State var configuredGatewayProbe = OnboardingConfiguredGatewayProbe() @State var didLoadOnboardingSkills = false @@ -617,7 +617,7 @@ struct OnboardingView: View { @State var defaultsToLocalGateway: Bool @Bindable var state: AppState var permissionMonitor: PermissionMonitor - let crestodianDefaults: UserDefaults + let systemAgentDefaults: UserDefaults let aiSetupRouteIdentityProvider: @MainActor () -> String? let gatewaySelectionPersister: @MainActor () -> Bool @@ -755,16 +755,16 @@ struct OnboardingView: View { localDisplayName: InstanceIdentity.displayName, filterLocalGateways: false), aiSetupGateway: GatewayConnection = .shared, - crestodianDefaults: UserDefaults = .standard, + systemAgentDefaults: UserDefaults = .standard, aiSetupRouteIdentityProvider: (@MainActor () -> String?)? = nil, configuredGatewayProbeTimeoutMs: Double = 15000, gatewaySelectionPersister: (@MainActor () -> Bool)? = nil) { self.state = state self.permissionMonitor = permissionMonitor - self.crestodianDefaults = crestodianDefaults + self.systemAgentDefaults = systemAgentDefaults let routeIdentityProvider = aiSetupRouteIdentityProvider ?? { - OnboardingCrestodianResumeStore.selectedRouteIdentity(state: state) + OnboardingSystemAgentResumeStore.selectedRouteIdentity(state: state) } self.aiSetupRouteIdentityProvider = routeIdentityProvider self.gatewaySelectionPersister = gatewaySelectionPersister ?? { @@ -775,7 +775,7 @@ struct OnboardingView: View { _gatewayDiscovery = State(initialValue: discoveryModel) _aiSetup = State(initialValue: OnboardingAISetupModel( gateway: aiSetupGateway, - defaults: crestodianDefaults, + defaults: systemAgentDefaults, routeIdentityProvider: routeIdentityProvider)) _configuredGatewayProbe = State( initialValue: OnboardingConfiguredGatewayProbe( diff --git a/apps/macos/Sources/OpenClaw/OnboardingAISetup.swift b/apps/macos/Sources/OpenClaw/OnboardingAISetup.swift index e95ae7153d77..6f8ba8cbe72c 100644 --- a/apps/macos/Sources/OpenClaw/OnboardingAISetup.swift +++ b/apps/macos/Sources/OpenClaw/OnboardingAISetup.swift @@ -6,7 +6,7 @@ import OpenClawProtocol /// Structured "Connect your AI" onboarding step. /// -/// Drives the gateway's `crestodian.setup.detect` / `crestodian.setup.activate` +/// Drives the gateway's `openclaw.setup.detect` / `openclaw.setup.activate` /// RPCs: detect reusable AI access (Claude Code, Codex, Gemini logins, API /// keys), live-test candidates in the detected order, and automatically fall /// through when one fails. Config is only written server-side after a @@ -85,7 +85,7 @@ final class OnboardingAISetupModel { } /// Once setup starts changing inference, its successful result belongs to - /// Crestodian rather than the existing-Gateway onboarding bypass. + /// OpenClaw rather than the existing-Gateway onboarding bypass. var ownsInferenceTransition: Bool { (self.phase == .detecting && !self.configuredGatewayProbeUnavailable) || self.phase == .testing || self.manualTesting || self.authBusy || self.connected || @@ -104,7 +104,7 @@ final class OnboardingAISetupModel { private var started = false private var attemptToken = UUID() @ObservationIgnored private var pendingVerification: PendingVerification? - @ObservationIgnored private var pendingActivationOwner: OnboardingCrestodianResumeStore.ActivationOwner? + @ObservationIgnored private var pendingActivationOwner: OnboardingSystemAgentResumeStore.ActivationOwner? @ObservationIgnored private var completedHandoff: CompletedHandoff? @ObservationIgnored private var pendingActivationRequiresFreshActivation = false @ObservationIgnored private var serverLease: GatewayConnection.ServerLease? @@ -131,14 +131,14 @@ final class OnboardingAISetupModel { private struct CompletedHandoff { let routeIdentity: String - let activationOwner: OnboardingCrestodianResumeStore.ActivationOwner? + let activationOwner: OnboardingSystemAgentResumeStore.ActivationOwner? } init( gateway: GatewayConnection = .shared, defaults: UserDefaults = .standard, routeIdentityProvider: @escaping @MainActor () -> String? = { - OnboardingCrestodianResumeStore.selectedRouteIdentity() + OnboardingSystemAgentResumeStore.selectedRouteIdentity() }) { self.gateway = gateway @@ -254,11 +254,11 @@ final class OnboardingAISetupModel { // repeatedly. Keep the first attempt and let every caller await it. guard !self.ownsInferenceTransition else { return } let routeIdentity = self.routeIdentityProvider() - let pendingState = OnboardingCrestodianResumeStore.pendingState( + let pendingState = OnboardingSystemAgentResumeStore.pendingState( for: routeIdentity, defaults: self.defaults) let inMemoryOwner = self.pendingActivationOwner - let restoredOwner = OnboardingCrestodianResumeStore.activationOwner( + let restoredOwner = OnboardingSystemAgentResumeStore.activationOwner( for: routeIdentity, defaults: self.defaults) let activationOwner = inMemoryOwner ?? restoredOwner @@ -338,7 +338,7 @@ final class OnboardingAISetupModel { return .notConnected } guard activationOwner.routeFingerprint == currentFingerprint else { - switch OnboardingCrestodianResumeStore.pendingState( + switch OnboardingSystemAgentResumeStore.pendingState( for: context.routeIdentity, defaults: self.defaults) { @@ -353,7 +353,7 @@ final class OnboardingAISetupModel { case .activationExpired, .completed, .none: // No live mutation remains to overlap. Retire only this // owner, then let the replacement credentials start fresh. - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: context.routeIdentity, activationOwner: activationOwner, defaults: self.defaults) @@ -367,7 +367,7 @@ final class OnboardingAISetupModel { } do { let data = try await gateway.request( - method: "crestodian.setup.verify", + method: "openclaw.setup.verify", params: [:], timeoutMs: 150_000, ifCurrentServerLease: lease) @@ -377,14 +377,14 @@ final class OnboardingAISetupModel { else { return .superseded } let result = try JSONDecoder().decode(ActivateResult.self, from: data) if result.ok, let modelRef = result.modelRef { - let pendingState = OnboardingCrestodianResumeStore.pendingState( + let pendingState = OnboardingSystemAgentResumeStore.pendingState( for: context.routeIdentity, defaults: self.defaults) switch pendingState { case let .activating(deadline), let .verified(deadline): // This proves inference works, but not that the dropped // activation stopped mutating. Preserve its deadline. - OnboardingCrestodianResumeStore.markVerified( + OnboardingSystemAgentResumeStore.markVerified( ifOwnedBy: context.routeIdentity, activationOwner: self.pendingActivationOwner, defaults: self.defaults) @@ -438,7 +438,7 @@ final class OnboardingAISetupModel { private func pendingVerificationFailureOutcome( context: AttemptContext) -> PendingVerificationOutcome { - switch OnboardingCrestodianResumeStore.pendingState( + switch OnboardingSystemAgentResumeStore.pendingState( for: context.routeIdentity, defaults: self.defaults) { @@ -446,7 +446,7 @@ final class OnboardingAISetupModel { // The dropped activation may still be writing config or credentials. // Verification may repeat, but mutation stays blocked until its lease ends. if let activationOwner = pendingActivationOwner, - !OnboardingCrestodianResumeStore.isOwned( + !OnboardingSystemAgentResumeStore.isOwned( by: activationOwner, for: context.routeIdentity, defaults: defaults) @@ -472,7 +472,7 @@ final class OnboardingAISetupModel { } private func retainCompletedReceiptForRetry(context: AttemptContext) { - self.pendingActivationOwner = OnboardingCrestodianResumeStore.activationOwner( + self.pendingActivationOwner = OnboardingSystemAgentResumeStore.activationOwner( for: context.routeIdentity, defaults: self.defaults) self.pendingActivationRequiresFreshActivation = true @@ -480,7 +480,7 @@ final class OnboardingAISetupModel { } private func activePendingActivationDeadline(for routeIdentity: String) -> Date? { - switch OnboardingCrestodianResumeStore.pendingState( + switch OnboardingSystemAgentResumeStore.pendingState( for: routeIdentity, defaults: self.defaults) { @@ -502,17 +502,17 @@ final class OnboardingAISetupModel { private func retainAmbiguousActivation( ifOwnedBy context: AttemptContext, - activationOwner: OnboardingCrestodianResumeStore.ActivationOwner, + activationOwner: OnboardingSystemAgentResumeStore.ActivationOwner, activationDeadline: Date) { guard isCurrentAttempt(context) else { return } self.pendingActivationVerification = true - switch OnboardingCrestodianResumeStore.pendingState( + switch OnboardingSystemAgentResumeStore.pendingState( for: context.routeIdentity, defaults: self.defaults) { case let .activating(deadline), let .verified(deadline): - guard OnboardingCrestodianResumeStore.isOwned( + guard OnboardingSystemAgentResumeStore.isOwned( by: activationOwner, for: context.routeIdentity, defaults: self.defaults) @@ -532,7 +532,7 @@ final class OnboardingAISetupModel { // A concurrent read-only probe can clear the marker while the // dispatched handler is still returning. Restore route ownership // before probing so failure or relaunch cannot start a duplicate. - OnboardingCrestodianResumeStore.restorePending( + OnboardingSystemAgentResumeStore.restorePending( routeIdentity: context.routeIdentity, activationOwner: activationOwner, deadline: activationDeadline, @@ -571,7 +571,7 @@ final class OnboardingAISetupModel { /// A replacement activation on the same route retains its own receipt. func clearCompletedHandoffIfOwned() { guard let completedHandoff else { return } - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: completedHandoff.routeIdentity, activationOwner: completedHandoff.activationOwner, defaults: self.defaults) @@ -583,7 +583,7 @@ final class OnboardingAISetupModel { let authSessionToCancel = self.authSessionID let authServerLease = self.serverLease if clearPendingHandoff, let routeIdentity = routeIdentityProvider() { - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: routeIdentity, activationOwner: self.pendingActivationOwner, defaults: self.defaults) @@ -662,7 +662,7 @@ extension OnboardingAISetupModel { let lease = try await gateway.acquireServerLease() guard self.isCurrentAttempt(context), !Task.isCancelled else { return } let data = try await gateway.request( - method: "crestodian.setup.detect", + method: "openclaw.setup.detect", params: [:], timeoutMs: 20000, ifCurrentServerLease: lease) @@ -748,10 +748,10 @@ extension OnboardingAISetupModel { private func clearPendingHandoff( ifOwnedBy context: AttemptContext, - activationOwner: OnboardingCrestodianResumeStore.ActivationOwner? = nil) + activationOwner: OnboardingSystemAgentResumeStore.ActivationOwner? = nil) { guard self.isCurrentAttempt(context) else { return } - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: context.routeIdentity, activationOwner: activationOwner ?? self.pendingActivationOwner, defaults: self.defaults) @@ -822,7 +822,7 @@ extension OnboardingAISetupModel { self.phase = .testing self.statuses[kind] = .testing guard let supportsExactModel = await gateway.supportsServerCapability( - .crestodianSetupModelRef, + .systemAgentSetupModelRef, ifCurrentServerLease: lease), isCurrentAttempt(context), !Task.isCancelled @@ -844,14 +844,14 @@ extension OnboardingAISetupModel { kind: kind, modelRef: candidate.modelRef, supportsExactModel: supportsExactModel) - let activationOwner = OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = OnboardingSystemAgentResumeStore.ActivationOwner( id: UUID().uuidString, routeFingerprint: routeFingerprint) self.pendingActivationOwner = activationOwner self.pendingActivationRequiresFreshActivation = true // Activation can persist before the response reaches the app. Cover the // whole ambiguous window so relaunch can inspect the actual Gateway state. - guard let activationDeadline = OnboardingCrestodianResumeStore.markPending( + guard let activationDeadline = OnboardingSystemAgentResumeStore.markPending( routeIdentity: context.routeIdentity, activationOwner: activationOwner, activationTimeoutMs: requestTimeoutMs, @@ -869,7 +869,7 @@ extension OnboardingAISetupModel { } do { let data = try await gateway.request( - method: "crestodian.setup.activate", + method: "openclaw.setup.activate", params: params, timeoutMs: requestTimeoutMs, ifCurrentServerLease: lease) @@ -877,7 +877,7 @@ extension OnboardingAISetupModel { guard self.isCurrentAttempt(context), !Task.isCancelled else { return } guard await self.gateway.isCurrentServerLease(lease) else { if result.ok, - OnboardingCrestodianResumeStore.markCompleted( + OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: context.routeIdentity, activationOwner: activationOwner, defaults: self.defaults) @@ -948,7 +948,7 @@ extension OnboardingAISetupModel { private func reconcileActivationAfterGatewayRestart( kind: String, context: AttemptContext, - activationOwner: OnboardingCrestodianResumeStore.ActivationOwner, + activationOwner: OnboardingSystemAgentResumeStore.ActivationOwner, before: PersistedActivationState?, originalServerLease: GatewayConnection.ServerLease) async -> Bool { @@ -997,7 +997,7 @@ extension OnboardingAISetupModel { private func reconcilePersistedActivation( kind: String, context: AttemptContext, - activationOwner: OnboardingCrestodianResumeStore.ActivationOwner, + activationOwner: OnboardingSystemAgentResumeStore.ActivationOwner, before: PersistedActivationState?, serverLease: GatewayConnection.ServerLease, timeoutMs: Int) async -> Bool @@ -1006,7 +1006,7 @@ extension OnboardingAISetupModel { let expectedModel = candidates.first(where: { $0.kind == kind })?.modelRef, isCurrentAttempt(context), !Task.isCancelled, - OnboardingCrestodianResumeStore.isOwned( + OnboardingSystemAgentResumeStore.isOwned( by: activationOwner, for: context.routeIdentity, defaults: defaults), @@ -1014,7 +1014,7 @@ extension OnboardingAISetupModel { activationOwner.routeFingerprint else { return false } guard let detectData = try? await gateway.request( - method: "crestodian.setup.detect", + method: "openclaw.setup.detect", params: [:], timeoutMs: Double(timeoutMs), ifCurrentServerLease: serverLease), @@ -1028,7 +1028,7 @@ extension OnboardingAISetupModel { after: detection.persistedActivationState) else { return false } guard let verifyData = try? await gateway.request( - method: "crestodian.setup.verify", + method: "openclaw.setup.verify", params: [:], timeoutMs: Double(timeoutMs), ifCurrentServerLease: serverLease), @@ -1074,7 +1074,7 @@ extension OnboardingAISetupModel { Task { do { let data = try await self.gateway.request( - method: "crestodian.setup.auth.start", + method: "openclaw.setup.auth.start", params: [ "sessionId": AnyCodable(authSessionID), "authChoice": AnyCodable(option.id), @@ -1298,7 +1298,7 @@ extension OnboardingAISetupModel { lease = replacement } guard let data = try? await gateway.request( - method: "crestodian.setup.detect", + method: "openclaw.setup.detect", params: [:], timeoutMs: 10000, ifCurrentServerLease: lease), @@ -1394,14 +1394,14 @@ extension OnboardingAISetupModel { } guard self.isCurrentAttempt(context), !Task.isCancelled else { return } let requestTimeoutMs = Self.activationRequestTimeoutMs(for: "api-key") - let activationOwner = OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = OnboardingSystemAgentResumeStore.ActivationOwner( id: UUID().uuidString, routeFingerprint: routeFingerprint) self.pendingActivationOwner = activationOwner self.pendingActivationRequiresFreshActivation = true // Manual activation has the same persist-before-response ambiguity as // detected candidates, so relaunch must inspect exact Gateway truth. - guard let activationDeadline = OnboardingCrestodianResumeStore.markPending( + guard let activationDeadline = OnboardingSystemAgentResumeStore.markPending( routeIdentity: context.routeIdentity, activationOwner: activationOwner, activationTimeoutMs: requestTimeoutMs, @@ -1417,7 +1417,7 @@ extension OnboardingAISetupModel { } do { let data = try await gateway.request( - method: "crestodian.setup.activate", + method: "openclaw.setup.activate", params: [ "kind": AnyCodable("api-key"), "authChoice": AnyCodable(provider.id), @@ -1429,7 +1429,7 @@ extension OnboardingAISetupModel { guard self.isCurrentAttempt(context), !Task.isCancelled else { return } guard await self.gateway.isCurrentServerLease(lease) else { if result.ok, - OnboardingCrestodianResumeStore.markCompleted( + OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: context.routeIdentity, activationOwner: activationOwner, defaults: self.defaults) @@ -1493,11 +1493,11 @@ extension OnboardingAISetupModel { private func finishConnected( kind: String, result: ActivateResult, - activationOwner: OnboardingCrestodianResumeStore.ActivationOwner? = nil, + activationOwner: OnboardingSystemAgentResumeStore.ActivationOwner? = nil, requireExistingReceipt: Bool = false) { let routeIdentity = self.routeIdentityProvider()?.trimmingCharacters(in: .whitespacesAndNewlines) - let completedReceipt = OnboardingCrestodianResumeStore.markCompleted( + let completedReceipt = OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: routeIdentity, activationOwner: activationOwner, defaults: self.defaults) diff --git a/apps/macos/Sources/OpenClaw/OnboardingAISetupSupport.swift b/apps/macos/Sources/OpenClaw/OnboardingAISetupSupport.swift index 286384ef1984..730a09ed4fe7 100644 --- a/apps/macos/Sources/OpenClaw/OnboardingAISetupSupport.swift +++ b/apps/macos/Sources/OpenClaw/OnboardingAISetupSupport.swift @@ -84,7 +84,7 @@ extension OnboardingAISetupModel { // Codex can spend 305s installing its runtime plugin before the 90s live probe. // Keep a bounded client deadline with room for registry refresh and finalization. kind == "codex-cli" - ? OnboardingCrestodianResumeStore.maximumActivationTimeoutMs + ? OnboardingSystemAgentResumeStore.maximumActivationTimeoutMs : 150_000 } @@ -97,7 +97,7 @@ extension OnboardingAISetupModel { return code == "UNKNOWN_METHOD" || (code == "INVALID_REQUEST" && (message.contains("unknown method") || - message.contains("invalid crestodian.setup.activate params"))) + message.contains("invalid openclaw.setup.activate params"))) } return error is GatewayConnectAuthError || error is GatewayTLSValidationError || diff --git a/apps/macos/Sources/OpenClaw/OnboardingAISetupView.swift b/apps/macos/Sources/OpenClaw/OnboardingAISetupView.swift index f449873c1f61..edada89bb9a9 100644 --- a/apps/macos/Sources/OpenClaw/OnboardingAISetupView.swift +++ b/apps/macos/Sources/OpenClaw/OnboardingAISetupView.swift @@ -69,8 +69,8 @@ enum OnboardingProviderAuthLink { struct OnboardingAISetupView: View { @Bindable var model: OnboardingAISetupModel - var crestodianChat: CrestodianOnboardingChatModel - @Binding var showCrestodianChat: Bool + var systemAgentChat: SystemAgentOnboardingChatModel + @Binding var showSystemAgentChat: Bool var retryConfiguredGatewayProbe: () -> Void @State private var openedProviderAuthURL: URL? @@ -84,8 +84,8 @@ struct OnboardingAISetupView: View { } } .frame(maxWidth: .infinity, alignment: .leading) - .sheet(isPresented: self.$showCrestodianChat) { - self.crestodianSheet + .sheet(isPresented: self.$showSystemAgentChat) { + self.systemAgentSheet } .sheet(isPresented: Binding( get: { self.model.activeAuthOption != nil }, @@ -185,13 +185,13 @@ struct OnboardingAISetupView: View { self.manualSection } - if CrestodianAvailability.shouldShow(configuredModel: self.model.connectedModelRef) { + if SystemAgentAvailability.shouldShow(configuredModel: self.model.connectedModelRef) { HStack { Spacer(minLength: 0) Button { - self.showCrestodianChat = true + self.showSystemAgentChat = true } label: { - Label("Need help? Chat with Crestodian", systemImage: "questionmark.bubble") + Label("Need help? Chat with OpenClaw", systemImage: "questionmark.bubble") .font(.caption) } .buttonStyle(.link) @@ -704,19 +704,19 @@ struct OnboardingAISetupView: View { return "\(hint). Paste it here, and OpenClaw checks it with a real test question." } - private var crestodianSheet: some View { + private var systemAgentSheet: some View { VStack(spacing: 8) { HStack { - Label("Crestodian — setup helper", systemImage: "lifepreserver") + Label("OpenClaw — setup helper", systemImage: "lifepreserver") .font(.headline) Spacer(minLength: 0) Button("Done") { - self.showCrestodianChat = false + self.showSystemAgentChat = false } } .padding([.top, .horizontal], 14) - CrestodianOnboardingChatView(model: self.crestodianChat) - .task { await self.crestodianChat.startIfNeeded() } + SystemAgentOnboardingChatView(model: self.systemAgentChat) + .task { await self.systemAgentChat.startIfNeeded() } } .frame(width: 520, height: 480) } diff --git a/apps/macos/Sources/OpenClaw/OnboardingCrestodianChat.swift b/apps/macos/Sources/OpenClaw/OnboardingSystemAgentChat.swift similarity index 90% rename from apps/macos/Sources/OpenClaw/OnboardingCrestodianChat.swift rename to apps/macos/Sources/OpenClaw/OnboardingSystemAgentChat.swift index 271a6d635dc2..fdf4a899c2df 100644 --- a/apps/macos/Sources/OpenClaw/OnboardingCrestodianChat.swift +++ b/apps/macos/Sources/OpenClaw/OnboardingSystemAgentChat.swift @@ -4,8 +4,8 @@ import SwiftUI @MainActor @Observable -final class OnboardingCrestodianChatState { - var chat = CrestodianOnboardingChatModel() +final class OnboardingSystemAgentChatState { + var chat = SystemAgentOnboardingChatModel() var isPresented = false @ObservationIgnored private var startTask: Task? @@ -31,16 +31,16 @@ final class OnboardingCrestodianChatState { self.startTask?.cancel() self.startTask = nil self.chat.invalidate() - self.chat = CrestodianOnboardingChatModel() + self.chat = SystemAgentOnboardingChatModel() } } -/// Onboarding talks to Crestodian over the gateway `crestodian.chat` RPC. +/// Onboarding talks to OpenClaw over the gateway `openclaw.chat` RPC. /// The conversation is available after structured setup establishes working /// inference, so the model-backed helper can answer reliably. @MainActor @Observable -final class CrestodianOnboardingChatModel { +final class SystemAgentOnboardingChatModel { struct Message: Identifiable, Equatable { enum Role { case assistant @@ -57,7 +57,7 @@ final class CrestodianOnboardingChatModel { private(set) var errorMessage: String? private(set) var expectsSensitiveReply = false var input = "" - /// Set when Crestodian hands off to the normal agent ("talk to agent"). + /// Set when OpenClaw hands off to the normal agent ("talk to agent"). var onAgentHandoff: (() -> Void)? /// Called after every assistant reply (setup may have applied config). var onReplyReceived: (() -> Void)? @@ -66,7 +66,7 @@ final class CrestodianOnboardingChatModel { private let sessionPrefix: String private let gateway: GatewayConnection /// "onboarding" seeds the first-run setup proposal; nil gets the - /// status/repair greeting (used by Settings → Crestodian). + /// status/repair greeting (used by Settings → OpenClaw). private let welcomeVariant: String? private var started = false private var requestGeneration: UInt64? = 0 @@ -100,7 +100,7 @@ final class CrestodianOnboardingChatModel { await self.requestReply(message: nil, generation: generation) if Task.isCancelled, self.requestGeneration == generation { self.started = false - self.errorMessage = "Crestodian was interrupted. Restart to try again." + self.errorMessage = "OpenClaw was interrupted. Restart to try again." } } @@ -195,7 +195,7 @@ final class CrestodianOnboardingChatModel { let route = try await self.sessionRoute(for: generation) guard self.isCurrentRequest(generation) else { return } let data = try await self.gateway.request( - method: "crestodian.chat", + method: "openclaw.chat", params: params, timeoutMs: 190_000, ifCurrentRoute: route) @@ -214,8 +214,8 @@ final class CrestodianOnboardingChatModel { if error is CancellationError || Task.isCancelled { self.started = false self.errorMessage = Task.isCancelled - ? "Crestodian was interrupted. Restart to try again." - : "The Gateway connection changed. Restart Crestodian to reconnect." + ? "OpenClaw was interrupted. Restart to try again." + : "The Gateway connection changed. Restart OpenClaw to reconnect." return } self.errorMessage = error.localizedDescription @@ -223,8 +223,8 @@ final class CrestodianOnboardingChatModel { } } -struct CrestodianOnboardingChatView: View { - @Bindable var model: CrestodianOnboardingChatModel +struct SystemAgentOnboardingChatView: View { + @Bindable var model: SystemAgentOnboardingChatModel var body: some View { VStack(spacing: 8) { @@ -232,14 +232,14 @@ struct CrestodianOnboardingChatView: View { ScrollView { LazyVStack(alignment: .leading, spacing: 10) { ForEach(self.model.messages) { message in - CrestodianChatBubble(message: message) + SystemAgentChatBubble(message: message) .id(message.id) } if self.model.isSending { HStack(spacing: 8) { ProgressView() .controlSize(.small) - Text("Crestodian is working…") + Text("OpenClaw is working…") .font(.caption) .foregroundStyle(.secondary) } @@ -278,7 +278,7 @@ struct CrestodianOnboardingChatView: View { SecureField("Enter secret…", text: self.$model.input) } else { TextField( - "Reply to Crestodian… (yes sets everything up)", + "Reply to OpenClaw… (yes sets everything up)", text: self.$model.input) } } @@ -301,8 +301,8 @@ struct CrestodianOnboardingChatView: View { } } -private struct CrestodianChatBubble: View { - let message: CrestodianOnboardingChatModel.Message +private struct SystemAgentChatBubble: View { + let message: SystemAgentOnboardingChatModel.Message var body: some View { HStack { @@ -326,7 +326,7 @@ private struct CrestodianChatBubble: View { } private var attributedText: AttributedString { - // Crestodian replies use light markdown (headings, bold, backticks). + // OpenClaw replies use light markdown (headings, bold, backticks). // Parse per line so multi-line replies keep their structure. var result = AttributedString() let lines = self.message.text.split(separator: "\n", omittingEmptySubsequences: false) diff --git a/apps/macos/Sources/OpenClaw/OnboardingView+AISetupPage.swift b/apps/macos/Sources/OpenClaw/OnboardingView+AISetupPage.swift index 7fa30c2d5159..35137a42aa8c 100644 --- a/apps/macos/Sources/OpenClaw/OnboardingView+AISetupPage.swift +++ b/apps/macos/Sources/OpenClaw/OnboardingView+AISetupPage.swift @@ -3,7 +3,7 @@ import SwiftUI extension OnboardingView { /// Structured AI setup: detect what's already on this machine, test the /// best option live, fall through automatically, offer an API-key form - /// when nothing works. Crestodian becomes available only after inference + /// when nothing works. OpenClaw becomes available only after inference /// has completed a live round-trip. func aiSetupPage(contentHeight: CGFloat) -> some View { VStack(spacing: 12) { @@ -19,8 +19,8 @@ extension OnboardingView { ScrollView { OnboardingAISetupView( model: self.aiSetup, - crestodianChat: self.crestodianState.chat, - showCrestodianChat: self.$crestodianState.isPresented, + systemAgentChat: self.systemAgentState.chat, + showSystemAgentChat: self.$systemAgentState.isPresented, retryConfiguredGatewayProbe: { self.retryConfiguredGatewayProbe() }) .padding(.vertical, 4) .padding(.trailing, 12) @@ -46,14 +46,14 @@ extension OnboardingView { // Local mode reaches this page only after the CLI/gateway install page, // so the gateway is up before the first RPC. guard state.connectionMode != .local || cliInstalled else { return } - self.prepareCrestodianHandoff() + self.prepareSystemAgentHandoff() // A selected/reconnected Gateway may already have a configured default // agent. Check that route before setup tries to author inference. probeConfiguredGatewayForDashboard(startAISetupWhenMissing: true) } - func prepareCrestodianHandoff() { - crestodianState.chat.onAgentHandoff = { [self] in self.finish() } + func prepareSystemAgentHandoff() { + systemAgentState.chat.onAgentHandoff = { [self] in self.finish() } aiSetup.onPendingActivationDeadline = { [self] deadline, routeIdentity in let currentRouteIdentity = self.aiSetupRouteIdentityProvider() guard currentRouteIdentity == routeIdentity else { return } @@ -65,14 +65,14 @@ extension OnboardingView { aiSetup.onConnected = { [self] in // Activation already persisted the resume marker before its RPC. self.configuredGatewayProbe.cancelPendingActivationRecheck() - self.crestodianState.presentAndStart() + self.systemAgentState.presentAndStart() } } } @discardableResult - func resumePendingCrestodian(modelRef: String) -> Task { - self.prepareCrestodianHandoff() + func resumePendingSystemAgent(modelRef: String) -> Task { + self.prepareSystemAgentHandoff() let expectedRouteIdentity = self.aiSetupRouteIdentityProvider() aiSetup.resumeConfiguredInference(modelRef: modelRef) if let page = pageOrder.firstIndex(of: aiPageIndex) { @@ -91,12 +91,12 @@ extension OnboardingView { self.configuredGatewayProbe.cancelPendingActivationRecheck() // `onConnected` already owns presentation. Await that exact start // task without starting a replacement route's chat after suspension. - await self.crestodianState.waitForStartIfNeeded() + await self.systemAgentState.waitForStartIfNeeded() } } func waitForPendingInferenceSetup() { - self.prepareCrestodianHandoff() + self.prepareSystemAgentHandoff() if let page = pageOrder.firstIndex(of: aiPageIndex) { currentPage = page } @@ -115,7 +115,7 @@ extension OnboardingView { } func resumePendingInferenceSetup() { - self.prepareCrestodianHandoff() + self.prepareSystemAgentHandoff() if let page = pageOrder.firstIndex(of: aiPageIndex) { currentPage = page } diff --git a/apps/macos/Sources/OpenClaw/OnboardingView+Layout.swift b/apps/macos/Sources/OpenClaw/OnboardingView+Layout.swift index 7aa19bbd69f1..34cfb89274d9 100644 --- a/apps/macos/Sources/OpenClaw/OnboardingView+Layout.swift +++ b/apps/macos/Sources/OpenClaw/OnboardingView+Layout.swift @@ -2,7 +2,7 @@ import AppKit import SwiftUI extension OnboardingView { - /// The inference-first flow has no full-page chat; Crestodian opens in its own sheet. + /// The inference-first flow has no full-page chat; OpenClaw opens in its own sheet. var usesCompactHero: Bool { false } @@ -90,7 +90,7 @@ extension OnboardingView { // Queued detection can otherwise proceed into a mutating activation // after the window or its selected route has gone away. aiSetup.resetForGatewayChange(clearPendingHandoff: false) - crestodianState.resetForGatewayChange() + systemAgentState.resetForGatewayChange() stopPermissionMonitoring() stopDiscovery() } @@ -136,9 +136,9 @@ extension OnboardingView { // The UI attempt belongs to one route, but its durable activation lease // must survive A -> B -> A while the old Gateway can still be mutating. aiSetup.resetForGatewayChange(clearPendingHandoff: false) - // Crestodian sessions belong to one Gateway. Dismiss and replace the chat so + // OpenClaw sessions belong to one Gateway. Dismiss and replace the chat so // changing routes cannot send an old session ID to the new endpoint. - crestodianState.resetForGatewayChange() + systemAgentState.resetForGatewayChange() } @discardableResult @@ -158,12 +158,12 @@ extension OnboardingView { guard gatewaySelectionPersister() else { return nil } let expectedMode = state.connectionMode let expectedRouteIdentity = self.aiSetupRouteIdentityProvider() - let expectedPendingState = OnboardingCrestodianResumeStore.pendingState( + let expectedPendingState = OnboardingSystemAgentResumeStore.pendingState( for: expectedRouteIdentity, - defaults: crestodianDefaults) - let expectedActivationOwner = OnboardingCrestodianResumeStore.activationOwner( + defaults: systemAgentDefaults) + let expectedActivationOwner = OnboardingSystemAgentResumeStore.activationOwner( for: expectedRouteIdentity, - defaults: crestodianDefaults) + defaults: systemAgentDefaults) let probeAttempt = configuredGatewayProbe.beginProbe() return Task { @MainActor in let outcome = await self.configuredGatewayProbe.probe( @@ -177,10 +177,10 @@ extension OnboardingView { expectedRouteIdentity: expectedRouteIdentity, knownVisible: knownVisible) else { return } - let pendingState = OnboardingCrestodianResumeStore.pendingState( + let pendingState = OnboardingSystemAgentResumeStore.pendingState( for: expectedRouteIdentity, - defaults: self.crestodianDefaults) - let crestodianResumePending = pendingState != .none + defaults: self.systemAgentDefaults) + let systemAgentResumePending = pendingState != .none self.schedulePendingActivationRecheckIfNeeded(pendingState) switch outcome { @@ -191,7 +191,7 @@ extension OnboardingView { // reconnect must not downgrade connected state or fork a // second resume operation. guard !self.aiSetup.connected else { return } - self.resumePendingCrestodian(modelRef: modelRef) + self.resumePendingSystemAgent(modelRef: modelRef) return case .verified: // Inference was observed, but the dropped activation can @@ -203,7 +203,7 @@ extension OnboardingView { // the dispatched activation is still returning. Keep the // setup-owned handoff, and prove inference on this route. if self.aiSetup.pendingActivationVerification { - self.resumePendingCrestodian(modelRef: modelRef) + self.resumePendingSystemAgent(modelRef: modelRef) return } } @@ -211,7 +211,7 @@ extension OnboardingView { onboardingVisible: self.onboardingVisible, expectedMode: expectedMode, currentMode: self.state.connectionMode, - crestodianResumePending: crestodianResumePending, + systemAgentResumePending: systemAgentResumePending, setupOwnsInferenceTransition: self.aiSetup.ownsInferenceTransition) else { return } self.onboardingVisible = false @@ -235,10 +235,10 @@ extension OnboardingView { // at probe start. A replacement attempt owns its own retry. guard expectedPendingState != .none, let expectedRouteIdentity, - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: expectedRouteIdentity, activationOwner: expectedActivationOwner, - defaults: self.crestodianDefaults) + defaults: self.systemAgentDefaults) else { return } self.resumePendingInferenceSetup() return @@ -282,7 +282,7 @@ extension OnboardingView { } private func schedulePendingActivationRecheckIfNeeded( - _ pendingState: OnboardingCrestodianResumeStore.PendingState) + _ pendingState: OnboardingSystemAgentResumeStore.PendingState) { switch pendingState { case let .activating(deadline), let .verified(deadline): @@ -298,14 +298,14 @@ extension OnboardingView { onboardingVisible: Bool, expectedMode: AppState.ConnectionMode, currentMode: AppState.ConnectionMode, - crestodianResumePending: Bool, + systemAgentResumePending: Bool, setupOwnsInferenceTransition: Bool) -> Bool { self.isCurrentConfiguredGatewayProbe( onboardingVisible: onboardingVisible, expectedMode: expectedMode, currentMode: currentMode) && - !crestodianResumePending && + !systemAgentResumePending && !setupOwnsInferenceTransition } diff --git a/apps/macos/Sources/OpenClaw/OnboardingView+Pages.swift b/apps/macos/Sources/OpenClaw/OnboardingView+Pages.swift index 19e5aa2d6b28..f495825807eb 100644 --- a/apps/macos/Sources/OpenClaw/OnboardingView+Pages.swift +++ b/apps/macos/Sources/OpenClaw/OnboardingView+Pages.swift @@ -147,7 +147,7 @@ extension OnboardingView { .disabled(self.installingCLI) .onChange(of: self.state.connectionMode) { _, newValue in // The root view's mode observer calls handleConnectionModeChange(), which - // retires route-owned AI/Crestodian state. This nested observer owns probe copy only. + // retires route-owned AI/OpenClaw state. This nested observer owns probe copy only. guard Self.shouldResetRemoteProbeFeedback( for: newValue, suppressReset: self.suppressRemoteProbeReset) diff --git a/apps/macos/Sources/OpenClaw/SettingsRootView.swift b/apps/macos/Sources/OpenClaw/SettingsRootView.swift index bb4fd0062d18..8b591fdffe55 100644 --- a/apps/macos/Sources/OpenClaw/SettingsRootView.swift +++ b/apps/macos/Sources/OpenClaw/SettingsRootView.swift @@ -11,7 +11,7 @@ struct SettingsRootView: View { @State private var inferenceConfiguration: InferenceConfiguration @State private var trackedInferenceGatewayID: String? @State private var inferenceRefreshTrigger = InferenceRefreshTrigger.invalidate(UUID()) - @State private var crestodianChatIdentity = UUID() + @State private var systemAgentChatIdentity = UUID() @State private var deferredTab: SettingsTab? @State private var columnVisibility: NavigationSplitViewVisibility = .all @State private var snapshotPaths: (configPath: String?, stateDir: String?) = (nil, nil) @@ -80,8 +80,8 @@ struct SettingsRootView: View { } } .onChange(of: self.inferenceConfiguration) { _, configuration in - if !CrestodianAvailability.shouldShow(configuredModel: configuration.configuredModel), - self.selectedTab == .crestodian + if !SystemAgentAvailability.shouldShow(configuredModel: configuration.configuredModel), + self.selectedTab == .systemAgent { self.selectedTab = .general } @@ -105,7 +105,7 @@ struct SettingsRootView: View { self.trackedInferenceGatewayID = gatewayID self.scheduleInferenceRefresh( clearPrevious: plan.clearsPrevious, - resetCrestodian: plan.resetsCrestodian) + resetSystemAgent: plan.resetsSystemAgent) } .onDisappear { self.stopPermissionMonitoring() } .task { @@ -114,7 +114,7 @@ struct SettingsRootView: View { } .onChange(of: self.state.connectionMode) { _, _ in self.trackedInferenceGatewayID = MacChatTranscriptCache.currentGatewayID() - self.scheduleInferenceRefresh(clearPrevious: true, resetCrestodian: true) + self.scheduleInferenceRefresh(clearPrevious: true, resetSystemAgent: true) } .task(id: self.inferenceRefreshTrigger) { guard !self.isPreview else { return } @@ -127,7 +127,7 @@ struct SettingsRootView: View { private var visibleGroups: [SettingsTabGroup] { SettingsTabGroup.defaultGroups( showDebug: self.state.debugPaneEnabled, - showCrestodian: CrestodianAvailability.shouldShow( + showSystemAgent: SystemAgentAvailability.shouldShow( configuredModel: self.inferenceConfiguration.configuredModel)) } @@ -224,13 +224,13 @@ struct SettingsRootView: View { showOnboarding: { DebugActions.restartOnboarding() })) case .voiceWake: AnyView(VoiceWakeSettings(state: self.state, isActive: self.selectedTab == .voiceWake)) - case .crestodian: - AnyView(CrestodianSettings( + case .systemAgent: + AnyView(SystemAgentSettings( isActive: self.selectedTab == tab, onReplyReceived: { self.scheduleInferenceRefresh(clearPrevious: false) }) - .id(self.crestodianChatIdentity)) + .id(self.systemAgentChatIdentity)) case .channels: AnyView(ChannelsSettings(isActive: self.selectedTab == tab)) case .skills: @@ -271,28 +271,28 @@ struct SettingsRootView: View { showDebug: Bool, inferenceConfiguration: InferenceConfiguration) -> TabSelection { - let showCrestodian = CrestodianAvailability.shouldShow( + let showSystemAgent = SystemAgentAvailability.shouldShow( configuredModel: inferenceConfiguration.configuredModel) - let deferred = requested == .crestodian && !showCrestodian && !inferenceConfiguration.isLoaded + let deferred = requested == .systemAgent && !showSystemAgent && !inferenceConfiguration.isLoaded ? requested : nil return TabSelection( selected: Self.normalizedTab( requested, showDebug: showDebug, - showCrestodian: showCrestodian), + showSystemAgent: showSystemAgent), deferred: deferred) } static func normalizedTab( _ requested: SettingsTab, showDebug: Bool, - showCrestodian: Bool) -> SettingsTab + showSystemAgent: Bool) -> SettingsTab { if requested == .debug, !showDebug { return .general } - if requested == .crestodian, !showCrestodian { + if requested == .systemAgent, !showSystemAgent { return .general } return requested @@ -375,7 +375,7 @@ struct SettingsRootView: View { struct ConfigRefreshPlan: Equatable { let clearsPrevious: Bool - let resetsCrestodian: Bool + let resetsSystemAgent: Bool } static func configRefreshPlan( @@ -385,8 +385,8 @@ struct SettingsRootView: View { { let routeChanged = previousGatewayID != currentGatewayID return ConfigRefreshPlan( - clearsPrevious: routeChanged || selectedTab != .crestodian, - resetsCrestodian: routeChanged) + clearsPrevious: routeChanged || selectedTab != .systemAgent, + resetsSystemAgent: routeChanged) } static func configurationAfterInferenceRefresh( @@ -399,14 +399,14 @@ struct SettingsRootView: View { } } - private func scheduleInferenceRefresh(clearPrevious: Bool, resetCrestodian: Bool = false) { - if resetCrestodian { - // Crestodian sessions are gateway-owned. Re-key the cached detail so a route + private func scheduleInferenceRefresh(clearPrevious: Bool, resetSystemAgent: Bool = false) { + if resetSystemAgent { + // OpenClaw sessions are gateway-owned. Re-key the cached detail so a route // change cannot send old conversation state to a new endpoint. - self.crestodianChatIdentity = UUID() + self.systemAgentChatIdentity = UUID() } if clearPrevious { - // Preserve an active or pending Crestodian request while config truth is revalidated. + // Preserve an active or pending OpenClaw request while config truth is revalidated. // A confirmed model restores it; a confirmed missing model leaves General selected. let requestedTab = self.deferredTab ?? self.selectedTab self.inferenceConfiguration = .loading @@ -439,9 +439,9 @@ struct SettingsTabGroup: Identifiable { self.title } - static func defaultGroups(showDebug: Bool, showCrestodian: Bool) -> [SettingsTabGroup] { - let basicTabs: [SettingsTab] = showCrestodian - ? [.general, .connection, .permissions, .voiceWake, .crestodian] + static func defaultGroups(showDebug: Bool, showSystemAgent: Bool) -> [SettingsTabGroup] { + let basicTabs: [SettingsTab] = showSystemAgent + ? [.general, .connection, .permissions, .voiceWake, .systemAgent] : [.general, .connection, .permissions, .voiceWake] var groups = [ SettingsTabGroup(title: "Basics", tabs: basicTabs), @@ -460,7 +460,7 @@ struct SettingsTabGroup: Identifiable { } enum SettingsTab: CaseIterable, Identifiable, Hashable { - case general, connection, permissions, voiceWake, crestodian, channels, skills, cron + case general, connection, permissions, voiceWake, systemAgent, channels, skills, cron case execApprovals, sessions, instances, config, debug, about static let windowWidth: CGFloat = 1120 static let windowHeight: CGFloat = 790 @@ -475,7 +475,7 @@ enum SettingsTab: CaseIterable, Identifiable, Hashable { case .connection: "Connection" case .permissions: "Permissions" case .voiceWake: "Voice & Talk" - case .crestodian: "Crestodian" + case .systemAgent: "OpenClaw" case .channels: "Channels" case .skills: "Skills" case .cron: "Cron Jobs" @@ -494,7 +494,7 @@ enum SettingsTab: CaseIterable, Identifiable, Hashable { case .connection: "point.3.connected.trianglepath.dotted" case .permissions: "lock.shield" case .voiceWake: "waveform.circle" - case .crestodian: "lifepreserver" + case .systemAgent: "lifepreserver" case .channels: "link" case .skills: "sparkles" case .cron: "calendar.badge.clock" @@ -534,7 +534,7 @@ struct SettingsRootView_Previews: PreviewProvider { state: .preview, updater: DisabledUpdaterController(), initialTab: tab, - configuredInferenceModel: tab == .crestodian ? "openai/gpt-5.6-sol" : nil) + configuredInferenceModel: tab == .systemAgent ? "openai/gpt-5.6-sol" : nil) .previewDisplayName(tab.title) .frame(width: SettingsTab.windowWidth, height: SettingsTab.windowHeight) } diff --git a/apps/macos/Sources/OpenClaw/CrestodianSettings.swift b/apps/macos/Sources/OpenClaw/SystemAgentSettings.swift similarity index 80% rename from apps/macos/Sources/OpenClaw/CrestodianSettings.swift rename to apps/macos/Sources/OpenClaw/SystemAgentSettings.swift index 35b46ea39446..b192c148830a 100644 --- a/apps/macos/Sources/OpenClaw/CrestodianSettings.swift +++ b/apps/macos/Sources/OpenClaw/SystemAgentSettings.swift @@ -1,30 +1,30 @@ import SwiftUI -enum CrestodianAvailability { +enum SystemAgentAvailability { static func shouldShow(configuredModel: String?) -> Bool { !(configuredModel?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true) } } -/// Settings pane hosting the Crestodian setup/repair chat. +/// Settings pane hosting the OpenClaw setup/repair chat. /// /// The parent settings view exposes this pane only after inference is configured. -struct CrestodianSettings: View { +struct SystemAgentSettings: View { let isActive: Bool let onReplyReceived: () -> Void - @State private var chat = CrestodianOnboardingChatModel( + @State private var chat = SystemAgentOnboardingChatModel( welcomeVariant: nil, - sessionPrefix: "mac-settings-crestodian") + sessionPrefix: "mac-settings-openclaw") var body: some View { VStack(alignment: .leading, spacing: 20) { SettingsPageHeader( - title: "Crestodian", + title: "OpenClaw", subtitle: "Your AI-powered setup helper. It can check status, fix config, " + "switch models, and connect channels.") SettingsCardGroup("Chat") { - CrestodianOnboardingChatView(model: self.chat) + SystemAgentOnboardingChatView(model: self.chat) .frame(maxWidth: .infinity, minHeight: 320, maxHeight: .infinity) } .frame(maxHeight: .infinity) @@ -45,7 +45,7 @@ struct CrestodianSettings: View { @MainActor static func configureChatCallbacks( - for chat: CrestodianOnboardingChatModel, + for chat: SystemAgentOnboardingChatModel, onReplyReceived: @escaping () -> Void) { chat.onAgentHandoff = { diff --git a/apps/macos/Tests/OpenClawIPCTests/AppStateRemoteConfigTests.swift b/apps/macos/Tests/OpenClawIPCTests/AppStateRemoteConfigTests.swift index 9bc2665cb6da..d142fe545ac3 100644 --- a/apps/macos/Tests/OpenClawIPCTests/AppStateRemoteConfigTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/AppStateRemoteConfigTests.swift @@ -229,13 +229,13 @@ struct AppStateRemoteConfigTests { @Test func `config watcher endpoint replacement clears and ignores stale discovery identity`() { let previousGatewayPreference = captureGatewayPreference() - let previousPending = UserDefaults.standard.object(forKey: onboardingCrestodianPendingKey) + let previousPending = UserDefaults.standard.object(forKey: onboardingSystemAgentPendingKey) defer { restoreGatewayPreference(previousGatewayPreference) if let previousPending { - UserDefaults.standard.set(previousPending, forKey: onboardingCrestodianPendingKey) + UserDefaults.standard.set(previousPending, forKey: onboardingSystemAgentPendingKey) } else { - OnboardingCrestodianResumeStore.clear() + OnboardingSystemAgentResumeStore.clear() } } let state = AppState(preview: true) @@ -243,7 +243,7 @@ struct AppStateRemoteConfigTests { state.remoteTransport = .direct state.remoteUrl = "wss://gateway-a.example.test" GatewayDiscoveryPreferences.setPreferredStableID("gateway-a") - OnboardingCrestodianResumeStore.markPending(routeIdentity: "remote:id:gateway-a") + OnboardingSystemAgentResumeStore.markPending(routeIdentity: "remote:id:gateway-a") let view = OnboardingView(state: state) view.preferredGatewayID = "gateway-a" @@ -260,13 +260,13 @@ struct AppStateRemoteConfigTests { #expect(state.remoteUrl == "wss://gateway-b.example.test") #expect(GatewayDiscoveryPreferences.preferredStableID() == nil) #expect(view.effectivePreferredGatewayID == nil) - let routeIdentity = OnboardingCrestodianResumeStore.selectedRouteIdentity( + let routeIdentity = OnboardingSystemAgentResumeStore.selectedRouteIdentity( state: state, preferredGatewayID: view.effectivePreferredGatewayID) #expect(routeIdentity?.hasPrefix("remote:direct:") == true) #expect(routeIdentity != "remote:id:gateway-a") - #expect(!OnboardingCrestodianResumeStore.isPending(for: routeIdentity)) - #expect(OnboardingCrestodianResumeStore.isPending(for: "remote:id:gateway-a")) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: routeIdentity)) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "remote:id:gateway-a")) } @Test diff --git a/apps/macos/Tests/OpenClawIPCTests/GatewayChannelConfigureTests.swift b/apps/macos/Tests/OpenClawIPCTests/GatewayChannelConfigureTests.swift index 17b6532d0cbe..0726e450ced2 100644 --- a/apps/macos/Tests/OpenClawIPCTests/GatewayChannelConfigureTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/GatewayChannelConfigureTests.swift @@ -89,13 +89,13 @@ struct GatewayConnectionTests { } @Test func `first connection admits hello capabilities before lease readiness`() async throws { - let session = self.makeSession(serverCapabilities: ["crestodian-setup-model-ref"]) + let session = self.makeSession(serverCapabilities: ["openclaw-setup-model-ref"]) let (conn, _) = try makeConnection(session: session) let lease = try await conn.acquireServerLease() #expect(await conn.supportsServerCapability( - .crestodianSetupModelRef, + .systemAgentSetupModelRef, ifCurrentServerLease: lease) == true) #expect(await conn.cachedGatewayVersion() == "test") #expect(session.snapshotMakeCount() == 1) @@ -105,14 +105,14 @@ struct GatewayConnectionTests { } @Test func `disconnected server lease rejects before dispatch`() async throws { - let session = self.makeSession(serverCapabilities: ["crestodian-setup-model-ref"]) + let session = self.makeSession(serverCapabilities: ["openclaw-setup-model-ref"]) let (conn, _) = try makeConnection(session: session) let lease = try await conn.acquireServerLease() await conn._test_handleDisconnect(socketGeneration: 1) do { _ = try await conn.request( - method: "crestodian.setup.detect", + method: "openclaw.setup.detect", params: [:], ifCurrentServerLease: lease) Issue.record("expected disconnected server lease rejection") @@ -146,14 +146,14 @@ struct GatewayConnectionTests { let id = task.snapshotConnectRequestID() ?? "connect" return .data(Self.connectOkData( id: id, - capabilities: ["crestodian-setup-model-ref"])) + capabilities: ["openclaw-setup-model-ref"])) }) }) let (conn, _) = try makeConnection(session: session) let lease = try await conn.acquireServerLease() let request = Task { try await conn.request( - method: "crestodian.setup.activate", + method: "openclaw.setup.activate", params: [:], timeoutMs: 5000, ifCurrentServerLease: lease) diff --git a/apps/macos/Tests/OpenClawIPCTests/MenuContentSmokeTests.swift b/apps/macos/Tests/OpenClawIPCTests/MenuContentSmokeTests.swift index aad0b782ef55..02d93b670eb2 100644 --- a/apps/macos/Tests/OpenClawIPCTests/MenuContentSmokeTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/MenuContentSmokeTests.swift @@ -161,7 +161,7 @@ struct MenuContentSmokeTests { let shouldOpen = AppDelegate.shouldOpenDashboardInsteadOfOnboarding( connectionMode: mode, onboardingSeen: false, - crestodianResumePending: false, + systemAgentResumePending: false, gatewayConnected: true, configuredInferenceModel: " openai/gpt-5.5 ") @@ -174,7 +174,7 @@ struct MenuContentSmokeTests { let shouldOpen = AppDelegate.shouldOpenDashboardInsteadOfOnboarding( connectionMode: .remote, onboardingSeen: false, - crestodianResumePending: false, + systemAgentResumePending: false, gatewayConnected: true, configuredInferenceModel: model) @@ -186,7 +186,7 @@ struct MenuContentSmokeTests { let shouldOpen = AppDelegate.shouldOpenDashboardInsteadOfOnboarding( connectionMode: .remote, onboardingSeen: false, - crestodianResumePending: false, + systemAgentResumePending: false, gatewayConnected: false, configuredInferenceModel: "openai/gpt-5.5") @@ -197,18 +197,18 @@ struct MenuContentSmokeTests { let shouldOpen = AppDelegate.shouldOpenDashboardInsteadOfOnboarding( connectionMode: .local, onboardingSeen: false, - crestodianResumePending: false, + systemAgentResumePending: false, gatewayConnected: true, configuredInferenceModel: "openai/gpt-5.5") #expect(shouldOpen) } - @Test func `pending Crestodian handoff survives relaunch and keeps onboarding`() { + @Test func `pending OpenClaw handoff survives relaunch and keeps onboarding`() { let shouldOpen = AppDelegate.shouldOpenDashboardInsteadOfOnboarding( connectionMode: .local, onboardingSeen: false, - crestodianResumePending: true, + systemAgentResumePending: true, gatewayConnected: true, configuredInferenceModel: "openai/gpt-5.5") diff --git a/apps/macos/Tests/OpenClawIPCTests/OnboardingAISetupTests.swift b/apps/macos/Tests/OpenClawIPCTests/OnboardingAISetupTests.swift index 6963cdf0e160..384a7ea102fe 100644 --- a/apps/macos/Tests/OpenClawIPCTests/OnboardingAISetupTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/OnboardingAISetupTests.swift @@ -29,15 +29,15 @@ private actor ActivationMarkerObservation { private final class ActivationOwnerObservation: @unchecked Sendable { private let lock = NSLock() - private var observedOwner: OnboardingCrestodianResumeStore.ActivationOwner? + private var observedOwner: OnboardingSystemAgentResumeStore.ActivationOwner? - func record(_ owner: OnboardingCrestodianResumeStore.ActivationOwner?) { + func record(_ owner: OnboardingSystemAgentResumeStore.ActivationOwner?) { lock.lock() defer { self.lock.unlock() } observedOwner = owner } - func value() -> OnboardingCrestodianResumeStore.ActivationOwner? { + func value() -> OnboardingSystemAgentResumeStore.ActivationOwner? { lock.lock() defer { self.lock.unlock() } return observedOwner @@ -281,7 +281,7 @@ private func respondToAISetupPreparation( if respondToAISetupHealth(task: task, request: request) { return true } - guard request.method == "crestodian.setup.detect" else { return false } + guard request.method == "openclaw.setup.detect" else { return false } let modelRef = kind == "codex-cli" ? "openai/gpt-5.5" : "claude-cli/claude-opus-4-8" task.emitReceiveSuccess(.data(detectedSetupResponse( id: request.id, @@ -384,7 +384,7 @@ private func makeAISetupSession( } await recorder.record(message) switch request.method { - case "crestodian.setup.detect": + case "openclaw.setup.detect": let modelRef = detectedKind == "codex-cli" ? "openai/gpt-5.5" : "claude-cli/claude-opus-4-8" @@ -393,7 +393,7 @@ private func makeAISetupSession( kind: detectedKind, modelRef: modelRef ))) - case "crestodian.setup.activate": + case "openclaw.setup.activate": if indeterminateActivationAfterDispatch { task.emitReceiveSuccess(.data(indeterminateActivationResponse(id: request.id))) return @@ -423,15 +423,15 @@ private func makeRestartingAISetupSession( await recorder.record(message) if generation == 0 { switch request.method { - case "crestodian.setup.detect": + case "openclaw.setup.detect": task.emitReceiveSuccess(.data(detectedSetupResponse( id: request.id, kind: "codex-cli", modelRef: "openai/gpt-5.5" ))) - case "crestodian.setup.activate": + case "openclaw.setup.activate": let owner = UserDefaults(suiteName: suiteName).flatMap { - OnboardingCrestodianResumeStore.activationOwner( + OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: $0 ) @@ -444,7 +444,7 @@ private func makeRestartingAISetupSession( return } switch request.method { - case "crestodian.setup.detect": + case "openclaw.setup.detect": let response = postRestartConfiguredModel.map { persistedDetectedSetupResponse(id: request.id, configuredModel: $0) } ?? detectedSetupResponse( @@ -453,7 +453,7 @@ private func makeRestartingAISetupSession( modelRef: "openai/gpt-5.5" ) task.emitReceiveSuccess(.data(response)) - case "crestodian.setup.verify": + case "openclaw.setup.verify": task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) default: break @@ -731,7 +731,7 @@ struct OnboardingAISetupTests { #""" {"type":"hello-ok","protocol":4, "server":{"version":"test","connId":"test"}, - "features":{"methods":[],"events":[],"capabilities":["crestodian-setup-model-ref"]}, + "features":{"methods":[],"events":[],"capabilities":["openclaw-setup-model-ref"]}, "snapshot":{"presence":[],"health":{}, "stateVersion":{"presence":0,"health":0},"uptimeMs":0}, "auth":{},"policy":{}} @@ -739,30 +739,30 @@ struct OnboardingAISetupTests { ) let hello = try JSONDecoder().decode(HelloOk.self, from: data) - #expect(hello.supportsServerCapability(.crestodianSetupModelRef)) + #expect(hello.supportsServerCapability(.systemAgentSetupModelRef)) } @Test func `only definitive failures can clear an activation marker`() { let unknownMethod = GatewayResponseError( - method: "crestodian.setup.activate", + method: "openclaw.setup.activate", code: "UNKNOWN_METHOD", message: "unknown method", details: nil ) let invalidParams = GatewayResponseError( - method: "crestodian.setup.activate", + method: "openclaw.setup.activate", code: "INVALID_REQUEST", - message: "invalid crestodian.setup.activate params: kind is required", + message: "invalid openclaw.setup.activate params: kind is required", details: nil ) let indeterminate = GatewayResponseError( - method: "crestodian.setup.activate", + method: "openclaw.setup.activate", code: "UNAVAILABLE", message: "Setup inference activation is indeterminate", details: nil ) let genericInvalidRequest = GatewayResponseError( - method: "crestodian.setup.activate", + method: "openclaw.setup.activate", code: "INVALID_REQUEST", message: "activation failed after dispatch", details: nil @@ -796,7 +796,7 @@ struct OnboardingAISetupTests { if respondToAISetupPreparation(task: task, request: request, kind: "claude-cli") { return } - guard request.method == "crestodian.setup.activate" else { return } + guard request.method == "openclaw.setup.activate" else { return } task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) }) }) @@ -818,14 +818,14 @@ struct OnboardingAISetupTests { #expect(model.connected) #expect(handedOff) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .completed) model.clearCompletedHandoffIfOwned() - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .none) @@ -862,19 +862,19 @@ struct OnboardingAISetupTests { let activationOwner = try #require(ownerObservation.value()) #expect(session.snapshotMakeCount() >= 2) #expect(await (recorder.snapshot()).methods == [ - "crestodian.setup.detect", - "crestodian.setup.activate", - "crestodian.setup.detect", - "crestodian.setup.verify", + "openclaw.setup.detect", + "openclaw.setup.activate", + "openclaw.setup.detect", + "openclaw.setup.verify", ]) #expect(model.connected) #expect(model.connectedModelRef == "openai/gpt-5.5") #expect(handoffCount == 1) - #expect(OnboardingCrestodianResumeStore.activationOwner( + #expect(OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: defaults ) == activationOwner) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .completed) @@ -913,14 +913,14 @@ struct OnboardingAISetupTests { let activationOwner = try #require(ownerObservation.value()) #expect(Array(reconciledRequests.methods.prefix(3)) == [ - "crestodian.setup.detect", - "crestodian.setup.activate", - "crestodian.setup.detect", + "openclaw.setup.detect", + "openclaw.setup.activate", + "openclaw.setup.detect", ]) - #expect(!reconciledRequests.methods.contains("crestodian.setup.verify")) + #expect(!reconciledRequests.methods.contains("openclaw.setup.verify")) #expect(!model.connected) #expect(handoffCount == 0) - #expect(OnboardingCrestodianResumeStore.isOwned( + #expect(OnboardingSystemAgentResumeStore.isOwned( by: activationOwner, for: "local", defaults: defaults @@ -939,7 +939,7 @@ struct OnboardingAISetupTests { if respondToAISetupPreparation(task: task, request: request, kind: "claude-cli") { return } - guard request.method == "crestodian.setup.activate" else { return } + guard request.method == "openclaw.setup.activate" else { return } task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) }) }) @@ -956,15 +956,15 @@ struct OnboardingAISetupTests { await model.detectAndAutoConnect() await model.activate(kind: "claude-cli") - let completedOwner = try #require(OnboardingCrestodianResumeStore.activationOwner( + let completedOwner = try #require(OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: defaults )) - let replacementOwner = OnboardingCrestodianResumeStore.ActivationOwner( + let replacementOwner = OnboardingSystemAgentResumeStore.ActivationOwner( id: "replacement-activation", routeFingerprint: completedOwner.routeFingerprint ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: replacementOwner, defaults: defaults @@ -972,12 +972,12 @@ struct OnboardingAISetupTests { model.clearCompletedHandoffIfOwned() - #expect(OnboardingCrestodianResumeStore.isOwned( + #expect(OnboardingSystemAgentResumeStore.isOwned( by: replacementOwner, for: "local", defaults: defaults )) - guard case .activating = OnboardingCrestodianResumeStore.pendingState( + guard case .activating = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) @@ -998,14 +998,14 @@ struct OnboardingAISetupTests { if respondToAISetupPreparation(task: task, request: request, kind: "claude-cli") { return } - guard request.method == "crestodian.setup.activate", + guard request.method == "openclaw.setup.activate", let callbackDefaults = UserDefaults(suiteName: suiteName), - let originalOwner = OnboardingCrestodianResumeStore.activationOwner( + let originalOwner = OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: callbackDefaults ) else { return } - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: .init( id: replacementID, @@ -1035,11 +1035,11 @@ struct OnboardingAISetupTests { #expect(!model.connected) #expect(handoffCount == 0) #expect(model.phase == .ready) - #expect(OnboardingCrestodianResumeStore.activationOwner( + #expect(OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: defaults )?.id == replacementID) - guard case .activating = OnboardingCrestodianResumeStore.pendingState( + guard case .activating = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) @@ -1060,7 +1060,7 @@ struct OnboardingAISetupTests { if respondToAISetupPreparation(task: task, request: request, kind: "codex-cli") { return } - guard request.method == "crestodian.setup.activate" else { return } + guard request.method == "openclaw.setup.activate" else { return } await configGate.armNextRead() task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) }) @@ -1091,7 +1091,7 @@ struct OnboardingAISetupTests { #expect(!model.connected) #expect(model.phase == .idle) #expect(handoffCount == 0) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "local", defaults: defaults )) @@ -1118,48 +1118,48 @@ struct OnboardingAISetupTests { onboardingVisible: true, expectedMode: .remote, currentMode: .remote, - crestodianResumePending: false, + systemAgentResumePending: false, setupOwnsInferenceTransition: false )) #expect(!OnboardingView.shouldOpenConfiguredGatewayDashboard( onboardingVisible: false, expectedMode: .remote, currentMode: .remote, - crestodianResumePending: false, + systemAgentResumePending: false, setupOwnsInferenceTransition: false )) #expect(!OnboardingView.shouldOpenConfiguredGatewayDashboard( onboardingVisible: true, expectedMode: .remote, currentMode: .local, - crestodianResumePending: false, + systemAgentResumePending: false, setupOwnsInferenceTransition: false )) #expect(!OnboardingView.shouldOpenConfiguredGatewayDashboard( onboardingVisible: true, expectedMode: .unconfigured, currentMode: .unconfigured, - crestodianResumePending: false, + systemAgentResumePending: false, setupOwnsInferenceTransition: false )) } - @Test func `fresh inference transition owns the Crestodian handoff`() { + @Test func `fresh inference transition owns the OpenClaw handoff`() { #expect(!OnboardingView.shouldOpenConfiguredGatewayDashboard( onboardingVisible: true, expectedMode: .local, currentMode: .local, - crestodianResumePending: false, + systemAgentResumePending: false, setupOwnsInferenceTransition: true )) } - @Test func `pending Crestodian handoff cannot be mistaken for an existing install`() { + @Test func `pending OpenClaw handoff cannot be mistaken for an existing install`() { #expect(!OnboardingView.shouldOpenConfiguredGatewayDashboard( onboardingVisible: true, expectedMode: .local, currentMode: .local, - crestodianResumePending: true, + systemAgentResumePending: true, setupOwnsInferenceTransition: false )) } @@ -1196,7 +1196,7 @@ struct OnboardingAISetupTests { return } await recorder.record(message) - if request.method == "crestodian.setup.verify" { + if request.method == "openclaw.setup.verify" { task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) } }) @@ -1215,7 +1215,7 @@ struct OnboardingAISetupTests { await model.verifyPendingConfiguredInference() let requests = await recorder.snapshot() - #expect(requests.methods == ["crestodian.setup.verify"]) + #expect(requests.methods == ["openclaw.setup.verify"]) #expect(model.connected) #expect(model.connectedModelRef == "openai/gpt-5.5") #expect(model.connectedLatencyMs == 42) @@ -1231,7 +1231,7 @@ struct OnboardingAISetupTests { return } await recorder.record(message) - guard request.method == "crestodian.setup.verify" else { return } + guard request.method == "openclaw.setup.verify" else { return } await gate.wait() task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) }) @@ -1252,11 +1252,11 @@ struct OnboardingAISetupTests { let second = Task { await model.verifyPendingConfiguredInference() } await Task.yield() - #expect(await (recorder.snapshot()).methods == ["crestodian.setup.verify"]) + #expect(await (recorder.snapshot()).methods == ["openclaw.setup.verify"]) await gate.release() #expect(await first.value == .connected) #expect(await second.value == .connected) - #expect(await (recorder.snapshot()).methods == ["crestodian.setup.verify"]) + #expect(await (recorder.snapshot()).methods == ["openclaw.setup.verify"]) } @Test func `pending verification revalidates route after shared task completes`() async throws { @@ -1269,7 +1269,7 @@ struct OnboardingAISetupTests { if respondToAISetupHealth(task: task, request: request) { return } - guard request.method == "crestodian.setup.verify" else { return } + guard request.method == "openclaw.setup.verify" else { return } task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) }) }) @@ -1303,10 +1303,10 @@ struct OnboardingAISetupTests { } await recorder.record(message) switch request.method { - case "crestodian.setup.detect": + case "openclaw.setup.detect": await gate.wait() task.emitReceiveSuccess(.data(actionableDetectedSetupResponse(id: request.id))) - case "crestodian.setup.activate": + case "openclaw.setup.activate": task.emitReceiveSuccess(.data(failedActivationResponse(id: request.id))) default: break @@ -1335,7 +1335,7 @@ struct OnboardingAISetupTests { await gate.release() await settleQueuedAISetupTasks() - #expect(await (recorder.snapshot()).methods == ["crestodian.setup.detect"]) + #expect(await (recorder.snapshot()).methods == ["openclaw.setup.detect"]) #expect(view.aiSetup.phase == .idle) } @@ -1343,14 +1343,14 @@ struct OnboardingAISetupTests { let suiteName = "OnboardingPendingVerificationFailureTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - OnboardingCrestodianResumeStore.markPending(routeIdentity: "local", defaults: defaults) + OnboardingSystemAgentResumeStore.markPending(routeIdentity: "local", defaults: defaults) let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, let request = aiSetupRequest(from: message) else { return } if respondToAISetupHealth(task: task, request: request) { return } - guard request.method == "crestodian.setup.verify" else { return } + guard request.method == "openclaw.setup.verify" else { return } task.emitReceiveSuccess(.data(rejectedSetupVerificationResponse(id: request.id))) }) }) @@ -1372,7 +1372,7 @@ struct OnboardingAISetupTests { #expect(model.pendingActivationVerification) #expect(model.detectError?.detail == "expired login") #expect(outcome == .notConnected) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) } @Test func `completed activation receipt survives verification transport failure`() async throws { @@ -1386,7 +1386,7 @@ struct OnboardingAISetupTests { if respondToAISetupHealth(task: task, request: request) { return } - guard request.method == "crestodian.setup.verify" else { return } + guard request.method == "openclaw.setup.verify" else { return } await recorder.record(message) let verifyCount = await recorder.snapshot().methods.count let response = verifyCount == 1 @@ -1401,16 +1401,16 @@ struct OnboardingAISetupTests { sessionBox: WebSocketSessionBox(session: session) ) let route = try #require(await gateway.captureRoute()) - let activationOwner = try OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = try OnboardingSystemAgentResumeStore.ActivationOwner( id: "completed-before-verification", routeFingerprint: #require(route.activationOwnershipFingerprint) ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: activationOwner, defaults: defaults ) - #expect(OnboardingCrestodianResumeStore.markCompleted( + #expect(OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: "local", activationOwner: activationOwner, defaults: defaults @@ -1427,7 +1427,7 @@ struct OnboardingAISetupTests { #expect(failedOutcome == .notConnected) #expect(model.pendingActivationVerification) #expect(!model.connected) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .completed) @@ -1437,19 +1437,19 @@ struct OnboardingAISetupTests { #expect(retryOutcome == .connected) #expect(model.connected) - #expect(requests.methods == ["crestodian.setup.verify", "crestodian.setup.verify"]) + #expect(requests.methods == ["openclaw.setup.verify", "openclaw.setup.verify"]) } - @Test func `pending Crestodian marker is app local and clearable`() throws { - let suiteName = "OnboardingCrestodianResumeStoreTests-\(UUID().uuidString)" + @Test func `pending OpenClaw marker is app local and clearable`() throws { + let suiteName = "OnboardingSystemAgentResumeStoreTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) - OnboardingCrestodianResumeStore.markPending(routeIdentity: "local", defaults: defaults) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) - OnboardingCrestodianResumeStore.clear(defaults: defaults) - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) + OnboardingSystemAgentResumeStore.markPending(routeIdentity: "local", defaults: defaults) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) + OnboardingSystemAgentResumeStore.clear(defaults: defaults) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) } @Test func `persisted route owner ignores tunnel URL but changes with Gateway auth`() async throws { @@ -1513,13 +1513,13 @@ struct OnboardingAISetupTests { "routeFingerprint": "password-derived-verifier", ], ], - ], forKey: onboardingCrestodianPendingKey) + ], forKey: onboardingSystemAgentPendingKey) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .none) - #expect(defaults.object(forKey: onboardingCrestodianPendingKey) == nil) + #expect(defaults.object(forKey: onboardingSystemAgentPendingKey) == nil) } @Test func `ownerless v2 completion record is scrubbed`() throws { @@ -1529,13 +1529,13 @@ struct OnboardingAISetupTests { defaults.set([ "version": 2, "records": ["local": ["phase": "completed"]], - ], forKey: onboardingCrestodianPendingKey) + ], forKey: onboardingSystemAgentPendingKey) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .none) - #expect(defaults.object(forKey: onboardingCrestodianPendingKey) == nil) + #expect(defaults.object(forKey: onboardingSystemAgentPendingKey) == nil) } @Test func `activation fails closed when Keychain binding is unavailable`() async throws { @@ -1561,8 +1561,8 @@ struct OnboardingAISetupTests { await model.detectAndAutoConnect() await model.activate(kind: "codex-cli") - #expect(await (recorder.snapshot()).methods == ["crestodian.setup.detect"]) - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(await (recorder.snapshot()).methods == ["openclaw.setup.detect"]) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) #expect(model.phase == .ready) guard case let .failed(failure) = model.statuses["codex-cli"] else { Issue.record("expected secure-storage failure") @@ -1588,15 +1588,15 @@ struct OnboardingAISetupTests { "routeFingerprint": "password-derived-verifier", ], ], - ], forKey: onboardingCrestodianPendingKey) + ], forKey: onboardingSystemAgentPendingKey) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults, now: now ) == .activating(deadline: deadline)) let migrated = try #require( - defaults.dictionary(forKey: onboardingCrestodianPendingKey) + defaults.dictionary(forKey: onboardingSystemAgentPendingKey) ) let records = try #require(migrated["records"] as? [String: Any]) let local = try #require(records["local"] as? [String: Any]) @@ -1606,13 +1606,13 @@ struct OnboardingAISetupTests { } @Test func `legacy marker relaunch migrates to a full conservative lease`() throws { - let suiteName = "OnboardingLegacyCrestodianResumeStoreTests-\(UUID().uuidString)" + let suiteName = "OnboardingLegacySystemAgentResumeStoreTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } let now = Date(timeIntervalSince1970: 1_800_000_000) - defaults.set("local", forKey: onboardingCrestodianPendingKey) + defaults.set("local", forKey: onboardingSystemAgentPendingKey) - let migrated = OnboardingCrestodianResumeStore.pendingState( + let migrated = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults, now: now @@ -1625,15 +1625,15 @@ struct OnboardingAISetupTests { let leaseDeadline = try #require(deadline) #expect(leaseDeadline == now.addingTimeInterval( - OnboardingCrestodianResumeStore.legacyActivationLeaseSeconds + OnboardingSystemAgentResumeStore.legacyActivationLeaseSeconds )) - #expect(defaults.object(forKey: onboardingCrestodianPendingKey) is [String: Any]) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(defaults.object(forKey: onboardingSystemAgentPendingKey) is [String: Any]) + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults, now: now.addingTimeInterval(484) ) == .activating(deadline: leaseDeadline)) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults, now: now.addingTimeInterval(486) @@ -1647,8 +1647,8 @@ struct OnboardingAISetupTests { let url = try #require(URL(string: "ws://localhost:18789")) let appState = AppState(preview: true) appState.connectionMode = .local - let routeIdentity = OnboardingCrestodianResumeStore.selectedRouteIdentity(state: appState) - OnboardingCrestodianResumeStore.markPending( + let routeIdentity = OnboardingSystemAgentResumeStore.selectedRouteIdentity(state: appState) + OnboardingSystemAgentResumeStore.markPending( routeIdentity: routeIdentity, activationTimeoutMs: 30000, defaults: defaults @@ -1673,7 +1673,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { routeIdentity } ) @@ -1683,7 +1683,7 @@ struct OnboardingAISetupTests { #expect(await (recorder.snapshot()).methods == ["agents.list"]) #expect(view.aiSetup.waitingForPendingActivationDeadline) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: routeIdentity, defaults: defaults )) @@ -1697,12 +1697,12 @@ struct OnboardingAISetupTests { let url = try #require(URL(string: "ws://localhost:18789")) let appState = AppState(preview: true) appState.connectionMode = .local - let routeIdentity = OnboardingCrestodianResumeStore.selectedRouteIdentity(state: appState) - let activationOwner = OnboardingCrestodianResumeStore.ActivationOwner( + let routeIdentity = OnboardingSystemAgentResumeStore.selectedRouteIdentity(state: appState) + let activationOwner = OnboardingSystemAgentResumeStore.ActivationOwner( id: "expired-owner", routeFingerprint: "selected-route" ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: routeIdentity, activationOwner: activationOwner, activationTimeoutMs: 0, @@ -1721,15 +1721,15 @@ struct OnboardingAISetupTests { switch request.method { case "agents.list": task.emitReceiveSuccess(.data(missingConfiguredModelResponse(id: request.id))) - case "crestodian.setup.detect": + case "openclaw.setup.detect": if let callbackDefaults = UserDefaults(suiteName: suiteName) { - await markerObservation.record(!OnboardingCrestodianResumeStore.isPending( + await markerObservation.record(!OnboardingSystemAgentResumeStore.isPending( for: routeIdentity, defaults: callbackDefaults )) } task.emitReceiveSuccess(.data(actionableDetectedSetupResponse(id: request.id))) - case "crestodian.setup.activate": + case "openclaw.setup.activate": task.emitReceiveSuccess(.data(failedActivationResponse(id: request.id))) default: break @@ -1743,7 +1743,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { routeIdentity } ) @@ -1753,8 +1753,8 @@ struct OnboardingAISetupTests { #expect(requests.methods == [ "agents.list", - "crestodian.setup.detect", - "crestodian.setup.activate", + "openclaw.setup.detect", + "openclaw.setup.activate", ]) #expect(await markerObservation.value()) #expect(!view.aiSetup.waitingForPendingActivationDeadline) @@ -1766,15 +1766,15 @@ struct OnboardingAISetupTests { let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } let routeIdentity = "local" - let originalOwner = OnboardingCrestodianResumeStore.ActivationOwner( + let originalOwner = OnboardingSystemAgentResumeStore.ActivationOwner( id: "expired-owner-a", routeFingerprint: "selected-route" ) - let replacementOwner = OnboardingCrestodianResumeStore.ActivationOwner( + let replacementOwner = OnboardingSystemAgentResumeStore.ActivationOwner( id: "expired-owner-b", routeFingerprint: "selected-route" ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: routeIdentity, activationOwner: originalOwner, activationTimeoutMs: 0, @@ -1789,7 +1789,7 @@ struct OnboardingAISetupTests { switch request.method { case "agents.list": if let callbackDefaults = UserDefaults(suiteName: suiteName) { - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: routeIdentity, activationOwner: replacementOwner, activationTimeoutMs: 0, @@ -1798,7 +1798,7 @@ struct OnboardingAISetupTests { ) } task.emitReceiveSuccess(.data(missingConfiguredModelResponse(id: request.id))) - case "crestodian.setup.detect": + case "openclaw.setup.detect": task.emitReceiveSuccess(.data(detectedSetupResponse(id: request.id))) default: break @@ -1815,7 +1815,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { routeIdentity } ) @@ -1824,12 +1824,12 @@ struct OnboardingAISetupTests { await settleQueuedAISetupTasks() #expect(await (recorder.snapshot()).methods == ["agents.list"]) - #expect(OnboardingCrestodianResumeStore.isOwned( + #expect(OnboardingSystemAgentResumeStore.isOwned( by: replacementOwner, for: routeIdentity, defaults: defaults )) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: routeIdentity, defaults: defaults ) == .activationExpired) @@ -1841,11 +1841,11 @@ struct OnboardingAISetupTests { let suiteName = "OnboardingStaleMissingConnectedTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", defaults: defaults ) - OnboardingCrestodianResumeStore.markCompleted( + OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: "local", defaults: defaults ) @@ -1859,7 +1859,7 @@ struct OnboardingAISetupTests { case "agents.list": await gate.wait() task.emitReceiveSuccess(.data(missingConfiguredModelResponse(id: request.id))) - case "crestodian.setup.detect": + case "openclaw.setup.detect": task.emitReceiveSuccess(.data(detectedSetupResponse(id: request.id))) default: break @@ -1876,7 +1876,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" } ) @@ -1894,7 +1894,7 @@ struct OnboardingAISetupTests { await settleQueuedAISetupTasks() #expect(view.aiSetup.connected) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .completed) @@ -1922,7 +1922,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" }, configuredGatewayProbeTimeoutMs: 1 ) @@ -1940,7 +1940,7 @@ struct OnboardingAISetupTests { #expect(view.aiSetup.phase == .ready) #expect(view.aiSetup.configuredGatewayProbeUnavailable) #expect(view.aiSetup.detectError != nil) - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) } @Test func `configured gateway probe refuses an unpersisted endpoint selection`() async throws { @@ -2009,14 +2009,14 @@ struct OnboardingAISetupTests { let suiteName = "OnboardingUnavailableGatewayMarkerTests-\(markerPhase)-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationTimeoutMs: markerPhase == "expired" ? 0 : 30000, defaults: defaults, now: markerPhase == "expired" ? Date(timeIntervalSinceNow: -10) : Date() ) if markerPhase == "completed" { - OnboardingCrestodianResumeStore.markCompleted( + OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: "local", defaults: defaults ) @@ -2042,7 +2042,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" } ) view.onboardingVisible = true @@ -2058,7 +2058,7 @@ struct OnboardingAISetupTests { #expect(await (recorder.snapshot()).methods == ["agents.list"]) #expect(view.aiSetup.phase == .ready) #expect(view.aiSetup.configuredGatewayProbeUnavailable) - let pendingState = OnboardingCrestodianResumeStore.pendingState( + let pendingState = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) @@ -2076,7 +2076,7 @@ struct OnboardingAISetupTests { #expect(retried.methods == ["agents.list", "agents.list"]) #expect(view.aiSetup.phase == .ready) #expect(view.aiSetup.configuredGatewayProbeUnavailable) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == pendingState) @@ -2096,7 +2096,7 @@ struct OnboardingAISetupTests { } await recorder.record(message) switch request.method { - case "crestodian.setup.detect": + case "openclaw.setup.detect": task.emitReceiveSuccess(.data(detectedSetupResponse(id: request.id))) case "agents.list": let probeCount = await recorder.snapshot().methods.filter { $0 == "agents.list" }.count @@ -2119,7 +2119,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" } ) view.onboardingVisible = true @@ -2143,10 +2143,10 @@ struct OnboardingAISetupTests { await settleQueuedAISetupTasks() #expect(requests.methods == [ - "crestodian.setup.detect", + "openclaw.setup.detect", "agents.list", "agents.list", - "crestodian.setup.detect", + "openclaw.setup.detect", ]) #expect(view.aiSetup.phase == .ready) #expect(!view.aiSetup.configuredGatewayProbeUnavailable) @@ -2157,7 +2157,7 @@ struct OnboardingAISetupTests { let suiteName = "OnboardingUnavailableActiveLeaseRetryTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationTimeoutMs: 30000, defaults: defaults @@ -2184,7 +2184,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" } ) @@ -2202,7 +2202,7 @@ struct OnboardingAISetupTests { #expect(await (recorder.snapshot()).methods == ["agents.list", "agents.list"]) #expect(view.aiSetup.waitingForPendingActivationDeadline) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) } @Test func `verified configured model stays read only until pending deadline`() async throws { @@ -2212,7 +2212,7 @@ struct OnboardingAISetupTests { let url = try #require(URL(string: "ws://localhost:18789")) let appState = AppState(preview: true) appState.connectionMode = .local - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationTimeoutMs: 30000, defaults: defaults @@ -2234,7 +2234,7 @@ struct OnboardingAISetupTests { ? missingConfiguredModelResponse(id: request.id) : configuredModelResponse(id: request.id) task.emitReceiveSuccess(.data(response)) - case "crestodian.setup.verify": + case "openclaw.setup.verify": task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) default: break @@ -2248,7 +2248,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" } ) @@ -2260,7 +2260,7 @@ struct OnboardingAISetupTests { ) await configuredProbe.value for _ in 0 ..< 200 { - if case .verified = OnboardingCrestodianResumeStore.pendingState( + if case .verified = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) { @@ -2273,14 +2273,14 @@ struct OnboardingAISetupTests { #expect(Array(methods.prefix(3)) == [ "agents.list", "agents.list", - "crestodian.setup.verify", + "openclaw.setup.verify", ]) - #expect(!methods.contains("crestodian.setup.detect")) - #expect(!methods.contains("crestodian.setup.activate")) + #expect(!methods.contains("openclaw.setup.detect")) + #expect(!methods.contains("openclaw.setup.activate")) #expect(!view.aiSetup.connected) #expect(view.aiSetup.waitingForPendingActivationDeadline) #expect({ - if case .verified = OnboardingCrestodianResumeStore.pendingState( + if case .verified = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) { @@ -2306,25 +2306,25 @@ struct OnboardingAISetupTests { })) ) let seedRoute = try #require(await seedGateway.captureRoute()) - let activationOwner = try OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = try OnboardingSystemAgentResumeStore.ActivationOwner( id: "active-before-auth-replacement", routeFingerprint: #require(seedRoute.activationOwnershipFingerprint) ) - _ = try #require(OnboardingCrestodianResumeStore.markPending( + _ = try #require(OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:ssh:stable-gateway", activationOwner: activationOwner, activationTimeoutMs: 30000, defaults: defaults )) if wasVerified { - OnboardingCrestodianResumeStore.markVerified( + OnboardingSystemAgentResumeStore.markVerified( ifOwnedBy: "remote:ssh:stable-gateway", activationOwner: activationOwner, defaults: defaults ) } let expectedDeadline: Date - switch OnboardingCrestodianResumeStore.pendingState( + switch OnboardingSystemAgentResumeStore.pendingState( for: "remote:ssh:stable-gateway", defaults: defaults ) { @@ -2361,11 +2361,11 @@ struct OnboardingAISetupTests { #expect(!model.pendingActivationVerification) #expect(model.waitingForPendingActivationDeadline) #expect(scheduledDeadlines == [expectedDeadline]) - #expect(OnboardingCrestodianResumeStore.activationOwner( + #expect(OnboardingSystemAgentResumeStore.activationOwner( for: "remote:ssh:stable-gateway", defaults: defaults ) == activationOwner) - let pendingState = OnboardingCrestodianResumeStore.pendingState( + let pendingState = OnboardingSystemAgentResumeStore.pendingState( for: "remote:ssh:stable-gateway", defaults: defaults ) @@ -2397,9 +2397,9 @@ struct OnboardingAISetupTests { } await recorder.record(message) switch request.method { - case "crestodian.setup.verify": + case "openclaw.setup.verify": task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) - case "crestodian.setup.detect": + case "openclaw.setup.detect": task.emitReceiveSuccess(.data(detectedSetupResponse(id: request.id))) default: break @@ -2412,18 +2412,18 @@ struct OnboardingAISetupTests { sessionBox: WebSocketSessionBox(session: session) ) let route = try #require(await gateway.captureRoute()) - let activationOwner = try OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = try OnboardingSystemAgentResumeStore.ActivationOwner( id: "expired-activation", routeFingerprint: #require(route.activationOwnershipFingerprint) ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: activationOwner, activationTimeoutMs: 0, defaults: defaults, now: Date(timeIntervalSinceNow: -10) ) - OnboardingCrestodianResumeStore.markVerified( + OnboardingSystemAgentResumeStore.markVerified( ifOwnedBy: "local", activationOwner: activationOwner, defaults: defaults @@ -2443,8 +2443,8 @@ struct OnboardingAISetupTests { #expect(outcome == .freshSetupAllowed) #expect(!model.connected) #expect(!handedOff) - #expect(requests.methods == ["crestodian.setup.verify", "crestodian.setup.detect"]) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(requests.methods == ["openclaw.setup.verify", "openclaw.setup.detect"]) + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .none) @@ -2462,16 +2462,16 @@ struct OnboardingAISetupTests { })) ) let seedRoute = try #require(await seedGateway.captureRoute()) - let activationOwner = try OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = try OnboardingSystemAgentResumeStore.ActivationOwner( id: "completed-activation", routeFingerprint: #require(seedRoute.activationOwnershipFingerprint) ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: activationOwner, defaults: defaults ) - #expect(OnboardingCrestodianResumeStore.markCompleted( + #expect(OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: "local", activationOwner: activationOwner, defaults: defaults @@ -2487,7 +2487,7 @@ struct OnboardingAISetupTests { return } await recorder.record(message) - if request.method == "crestodian.setup.detect" { + if request.method == "openclaw.setup.detect" { task.emitReceiveSuccess(.data(detectedSetupResponse(id: request.id))) } }) @@ -2505,8 +2505,8 @@ struct OnboardingAISetupTests { #expect(outcome == .freshSetupAllowed) #expect(!relaunched.connected) - #expect(requests.methods == ["crestodian.setup.detect"]) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(requests.methods == ["openclaw.setup.detect"]) + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .none) @@ -2564,24 +2564,24 @@ struct OnboardingAISetupTests { sessionBox: WebSocketSessionBox(session: seedSession) ) let seedLease = try await seedGateway.acquireServerLease() - let activationOwner = try OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = try OnboardingSystemAgentResumeStore.ActivationOwner( id: "completed-device-token-activation", routeFingerprint: #require(await seedGateway.activationOwnershipFingerprint( ifCurrentServerLease: seedLease )) ) #expect(await seedGateway.authSource() == .deviceToken) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: activationOwner, defaults: defaults ) - #expect(OnboardingCrestodianResumeStore.markCompleted( + #expect(OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: "local", activationOwner: activationOwner, defaults: defaults )) - let persistedReceipt = String(describing: defaults.object(forKey: onboardingCrestodianPendingKey)) + let persistedReceipt = String(describing: defaults.object(forKey: onboardingSystemAgentPendingKey)) #expect(!persistedReceipt.contains(originalToken)) #expect(DeviceAuthStore.loadToken( deviceId: identity.deviceId, @@ -2614,12 +2614,12 @@ struct OnboardingAISetupTests { #expect(outcome == .freshSetupAllowed) #expect(!relaunched.connected) - #expect(requests.methods == ["crestodian.setup.detect"]) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(requests.methods == ["openclaw.setup.detect"]) + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .none) - #expect(!String(describing: defaults.object(forKey: onboardingCrestodianPendingKey)) + #expect(!String(describing: defaults.object(forKey: onboardingSystemAgentPendingKey)) .contains(replacementToken)) await replacementGateway.shutdown() } @@ -2642,23 +2642,23 @@ struct OnboardingAISetupTests { } await recorder.record(message) switch request.method { - case "crestodian.setup.verify": + case "openclaw.setup.verify": if let callbackDefaults = UserDefaults(suiteName: suiteName), - let originalOwner = OnboardingCrestodianResumeStore.activationOwner( + let originalOwner = OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: callbackDefaults ) { - let replacementOwner = OnboardingCrestodianResumeStore.ActivationOwner( + let replacementOwner = OnboardingSystemAgentResumeStore.ActivationOwner( id: replacementID, routeFingerprint: originalOwner.routeFingerprint ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: replacementOwner, defaults: callbackDefaults ) - OnboardingCrestodianResumeStore.markCompleted( + OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: "local", activationOwner: replacementOwner, defaults: callbackDefaults @@ -2672,16 +2672,16 @@ struct OnboardingAISetupTests { })) ) let route = try #require(await gateway.captureRoute()) - let activationOwner = try OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = try OnboardingSystemAgentResumeStore.ActivationOwner( id: "completed-before-relaunch", routeFingerprint: #require(route.activationOwnershipFingerprint) ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: activationOwner, defaults: defaults ) - #expect(OnboardingCrestodianResumeStore.markCompleted( + #expect(OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: "local", activationOwner: activationOwner, defaults: defaults @@ -2701,12 +2701,12 @@ struct OnboardingAISetupTests { #expect(outcome == .notConnected) #expect(!relaunched.connected) #expect(handoffCount == 0) - #expect(requests.methods == ["crestodian.setup.verify"]) - #expect(OnboardingCrestodianResumeStore.activationOwner( + #expect(requests.methods == ["openclaw.setup.verify"]) + #expect(OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: defaults )?.id == replacementID) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .completed) @@ -2716,22 +2716,22 @@ struct OnboardingAISetupTests { let suiteName = "OnboardingOwnedActivationMutationTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - let activationOwner = OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = OnboardingSystemAgentResumeStore.ActivationOwner( id: "owned-activation", routeFingerprint: "owned-route" ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: activationOwner, defaults: defaults ) - OnboardingCrestodianResumeStore.markVerified( + OnboardingSystemAgentResumeStore.markVerified( ifOwnedBy: "local", defaults: defaults ) #expect({ - if case .activating = OnboardingCrestodianResumeStore.pendingState( + if case .activating = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) { @@ -2739,16 +2739,16 @@ struct OnboardingAISetupTests { } return false }()) - #expect(!OnboardingCrestodianResumeStore.markCompleted( + #expect(!OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: "local", defaults: defaults )) - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: "local", defaults: defaults ) - #expect(OnboardingCrestodianResumeStore.isOwned( + #expect(OnboardingSystemAgentResumeStore.isOwned( by: activationOwner, for: "local", defaults: defaults @@ -2756,62 +2756,62 @@ struct OnboardingAISetupTests { } @Test func `pending marker for another route is preserved`() throws { - let suiteName = "OnboardingCrestodianRouteMismatchTests-\(UUID().uuidString)" + let suiteName = "OnboardingSystemAgentRouteMismatchTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-a", defaults: defaults ) - #expect(!OnboardingCrestodianResumeStore.isPending( + #expect(!OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults )) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults )) } @Test func `A to B to A preserves first activation lease`() throws { - let suiteName = "OnboardingCrestodianMultiRouteTests-\(UUID().uuidString)" + let suiteName = "OnboardingSystemAgentMultiRouteTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } let now = Date(timeIntervalSince1970: 1_800_000_000) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-a", defaults: defaults, now: now ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-b", defaults: defaults, now: now.addingTimeInterval(1) ) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults, now: now.addingTimeInterval(2) )) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults, now: now.addingTimeInterval(2) )) - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: "remote:id:gateway-b", defaults: defaults ) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults, now: now.addingTimeInterval(2) )) - #expect(!OnboardingCrestodianResumeStore.isPending( + #expect(!OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults, now: now.addingTimeInterval(2) @@ -2819,15 +2819,15 @@ struct OnboardingAISetupTests { } @Test func `route reset clears only current route lease`() throws { - let suiteName = "OnboardingCrestodianRouteResetTests-\(UUID().uuidString)" + let suiteName = "OnboardingSystemAgentRouteResetTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } let routeIdentity = AISetupRouteIdentity("remote:id:gateway-b") - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-a", defaults: defaults ) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-b", defaults: defaults ) @@ -2838,42 +2838,42 @@ struct OnboardingAISetupTests { model.resetForGatewayChange() - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults )) - #expect(!OnboardingCrestodianResumeStore.isPending( + #expect(!OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults )) } @Test func `gateway selection reset preserves in flight lease`() throws { - let suiteName = "OnboardingCrestodianSelectionResetTests-\(UUID().uuidString)" + let suiteName = "OnboardingSystemAgentSelectionResetTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } let appState = AppState(preview: true) appState.connectionMode = .local - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", defaults: defaults ) let view = OnboardingView( state: appState, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" } ) view.resetGatewayBoundAIState() - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "local", defaults: defaults )) } @Test func `v1 route marker migrates without blocking another route`() throws { - let suiteName = "OnboardingCrestodianV1MigrationTests-\(UUID().uuidString)" + let suiteName = "OnboardingSystemAgentV1MigrationTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } let now = Date(timeIntervalSince1970: 1_800_000_000) @@ -2881,10 +2881,10 @@ struct OnboardingAISetupTests { "version": 1, "routeIdentity": "remote:id:gateway-a", "phase": "verified", - ], forKey: onboardingCrestodianPendingKey) + ], forKey: onboardingSystemAgentPendingKey) #expect({ - if case .verified = OnboardingCrestodianResumeStore.pendingState( + if case .verified = OnboardingSystemAgentResumeStore.pendingState( for: "remote:id:gateway-a", defaults: defaults, now: now @@ -2893,17 +2893,17 @@ struct OnboardingAISetupTests { } return false }()) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-b", defaults: defaults, now: now ) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults, now: now )) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults, now: now @@ -2911,28 +2911,28 @@ struct OnboardingAISetupTests { } @Test func `fallback remote route identity omits auth but preserves endpoint`() { - let authenticatedIdentity = OnboardingCrestodianResumeStore.routeIdentity( + let authenticatedIdentity = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: .direct, remoteURL: "wss://user:secret@gateway.example.test/path?tenant=team-a&token=secret#fragment", remoteTarget: "" ) - let cleanIdentity = OnboardingCrestodianResumeStore.routeIdentity( + let cleanIdentity = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: .direct, remoteURL: "wss://gateway.example.test/path?tenant=team-a", remoteTarget: "" ) - let otherEndpointIdentity = OnboardingCrestodianResumeStore.routeIdentity( + let otherEndpointIdentity = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: .direct, remoteURL: "wss://gateway.example.test/other", remoteTarget: "" ) - let otherQueryIdentity = OnboardingCrestodianResumeStore.routeIdentity( + let otherQueryIdentity = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: .direct, @@ -2949,7 +2949,7 @@ struct OnboardingAISetupTests { } @Test func `fallback route identity distinguishes local state dirs and ssh gateway ports`() { - let localA = OnboardingCrestodianResumeStore.routeIdentity( + let localA = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .local, preferredGatewayID: nil, remoteTransport: .direct, @@ -2957,7 +2957,7 @@ struct OnboardingAISetupTests { remoteTarget: "", localStateDir: URL(fileURLWithPath: "/tmp/openclaw-state-a") ) - let localB = OnboardingCrestodianResumeStore.routeIdentity( + let localB = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .local, preferredGatewayID: nil, remoteTransport: .direct, @@ -2965,7 +2965,7 @@ struct OnboardingAISetupTests { remoteTarget: "", localStateDir: URL(fileURLWithPath: "/tmp/openclaw-state-b") ) - let sshA = OnboardingCrestodianResumeStore.routeIdentity( + let sshA = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: .ssh, @@ -2973,7 +2973,7 @@ struct OnboardingAISetupTests { remoteTarget: "user@gateway.example.test", sshRemotePort: 18789 ) - let sshB = OnboardingCrestodianResumeStore.routeIdentity( + let sshB = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: .ssh, @@ -2988,14 +2988,14 @@ struct OnboardingAISetupTests { } @Test func `fallback remote route identity canonicalizes the persisted URL`() { - let beforePersistence = OnboardingCrestodianResumeStore.routeIdentity( + let beforePersistence = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: .direct, remoteURL: "ws://localhost", remoteTarget: "" ) - let afterPersistence = OnboardingCrestodianResumeStore.routeIdentity( + let afterPersistence = OnboardingSystemAgentResumeStore.routeIdentity( connectionMode: .remote, preferredGatewayID: nil, remoteTransport: .direct, @@ -3020,7 +3020,7 @@ struct OnboardingAISetupTests { let requestDefaults = UserDefaults(suiteName: suiteName) await observation.record( requestDefaults.map { - OnboardingCrestodianResumeStore.isPending( + OnboardingSystemAgentResumeStore.isPending( for: "local", defaults: $0 ) @@ -3044,7 +3044,7 @@ struct OnboardingAISetupTests { await model.activate(kind: "codex-cli") #expect(await observation.value()) - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) } @Test func `stale queued detection cannot probe a replacement Gateway`() async throws { @@ -3074,7 +3074,7 @@ struct OnboardingAISetupTests { let requests = await waitForAISetupRequests(recorder, count: 1) await settleQueuedAISetupTasks() - #expect(requests.methods == ["crestodian.setup.detect"]) + #expect(requests.methods == ["openclaw.setup.detect"]) #expect(requests.apiKeys.isEmpty) #expect(model.phase == .ready) } @@ -3105,8 +3105,8 @@ struct OnboardingAISetupTests { await settleQueuedAISetupTasks() let requests = await recorder.snapshot() - #expect(requests.methods == ["crestodian.setup.detect"]) - #expect(!OnboardingCrestodianResumeStore.isPending( + #expect(requests.methods == ["openclaw.setup.detect"]) + #expect(!OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults )) @@ -3141,9 +3141,9 @@ struct OnboardingAISetupTests { await settleQueuedAISetupTasks() let requests = await recorder.snapshot() - #expect(requests.methods == ["crestodian.setup.detect"]) + #expect(requests.methods == ["openclaw.setup.detect"]) #expect(!requests.apiKeys.contains("old-route-secret")) - #expect(!OnboardingCrestodianResumeStore.isPending( + #expect(!OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults )) @@ -3174,8 +3174,8 @@ struct OnboardingAISetupTests { config.switchToken(to: "token-b", afterReads: 2) await model.activate(kind: "codex-cli") - #expect(await (recorder.snapshot()).methods == ["crestodian.setup.detect"]) - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(await (recorder.snapshot()).methods == ["openclaw.setup.detect"]) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) #expect(!model.pendingActivationVerification) #expect(model.phase == .ready) } @@ -3208,9 +3208,9 @@ struct OnboardingAISetupTests { } let requests = await recorder.snapshot() - #expect(requests.methods == ["crestodian.setup.detect"]) + #expect(requests.methods == ["openclaw.setup.detect"]) #expect(!requests.apiKeys.contains("must-not-send")) - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) #expect(!model.pendingActivationVerification) #expect(model.detectError != nil) } @@ -3237,7 +3237,7 @@ struct OnboardingAISetupTests { ) { return } - guard request.method == "crestodian.setup.activate" else { return } + guard request.method == "openclaw.setup.activate" else { return } await gate.wait() throw CancellationError() }) @@ -3264,10 +3264,10 @@ struct OnboardingAISetupTests { await activation.value #expect(await (recorder.snapshot()).methods == [ - "crestodian.setup.detect", - "crestodian.setup.activate", + "openclaw.setup.detect", + "openclaw.setup.activate", ]) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) #expect(model.pendingActivationVerification) #expect(model.waitingForPendingActivationDeadline) #expect(model.isBusy) @@ -3308,8 +3308,8 @@ struct OnboardingAISetupTests { await model.detectAndAutoConnect() await model.activate(kind: "codex-cli") - #expect(await (recorder.snapshot()).methods == ["crestodian.setup.activate"]) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(await (recorder.snapshot()).methods == ["openclaw.setup.activate"]) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) #expect(model.pendingActivationVerification) #expect(model.waitingForPendingActivationDeadline) #expect(model.phase == .detecting) @@ -3333,14 +3333,14 @@ struct OnboardingAISetupTests { } await recorder.record(message) if let callbackDefaults = UserDefaults(suiteName: suiteName) { - let pendingState = OnboardingCrestodianResumeStore.pendingState( + let pendingState = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: callbackDefaults ) if case let .activating(deadline) = pendingState { await markerObservation.record(deadline: deadline) } - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: "local", defaults: callbackDefaults ) @@ -3356,7 +3356,7 @@ struct OnboardingAISetupTests { ) var scheduledDeadlines: [(deadline: Date, routeIdentity: String)] = [] model.onPendingActivationDeadline = { deadline, routeIdentity in - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: routeIdentity, defaults: defaults )) @@ -3366,15 +3366,15 @@ struct OnboardingAISetupTests { await model.detectAndAutoConnect() await model.activate(kind: "codex-cli") - #expect(await (recorder.snapshot()).methods == ["crestodian.setup.activate"]) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(await (recorder.snapshot()).methods == ["openclaw.setup.activate"]) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) #expect(model.pendingActivationVerification) #expect(model.waitingForPendingActivationDeadline) #expect(model.phase == .detecting) #expect(scheduledDeadlines.count == 1) #expect(scheduledDeadlines.first?.routeIdentity == "local") let originalDeadline = try #require(await markerObservation.deadline()) - let restoredState = OnboardingCrestodianResumeStore.pendingState( + let restoredState = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) @@ -3411,9 +3411,9 @@ struct OnboardingAISetupTests { } await recorder.record(message) switch request.method { - case "crestodian.setup.activate": + case "openclaw.setup.activate": if let callbackDefaults = UserDefaults(suiteName: suiteName) { - OnboardingCrestodianResumeStore.clear( + OnboardingSystemAgentResumeStore.clear( ifOwnedBy: "local", defaults: callbackDefaults ) @@ -3421,9 +3421,9 @@ struct OnboardingAISetupTests { task.emitReceiveSuccess(.data(indeterminateActivationResponse(id: request.id))) case "agents.list": task.emitReceiveSuccess(.data(configuredModelResponse(id: request.id))) - case "crestodian.setup.verify": + case "openclaw.setup.verify": task.emitReceiveSuccess(.data(verifiedSetupResponse(id: request.id))) - case "crestodian.setup.detect": + case "openclaw.setup.detect": task.emitReceiveSuccess(.data(detectedSetupResponse( id: request.id, kind: "codex-cli", @@ -3438,7 +3438,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" } ) view.onboardingVisible = true @@ -3452,7 +3452,7 @@ struct OnboardingAISetupTests { await view.aiSetup.detectAndAutoConnect() await view.aiSetup.activate(kind: "codex-cli") - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) #expect(scheduledDeadlines.count == 1) let initialRecheck = try #require(view.probeConfiguredGatewayForDashboard( @@ -3465,15 +3465,15 @@ struct OnboardingAISetupTests { await settleQueuedAISetupTasks() #expect(requests.methods == [ - "crestodian.setup.detect", - "crestodian.setup.activate", + "openclaw.setup.detect", + "openclaw.setup.activate", "agents.list", - "crestodian.setup.verify", + "openclaw.setup.verify", ]) #expect(!view.aiSetup.connected) #expect(view.aiSetup.waitingForPendingActivationDeadline) #expect({ - if case .verified = OnboardingCrestodianResumeStore.pendingState( + if case .verified = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) { @@ -3482,11 +3482,11 @@ struct OnboardingAISetupTests { return false }()) - let activationOwner = try #require(OnboardingCrestodianResumeStore.activationOwner( + let activationOwner = try #require(OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: defaults )) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: activationOwner, activationTimeoutMs: 0, @@ -3503,20 +3503,20 @@ struct OnboardingAISetupTests { await settleQueuedAISetupTasks() #expect(completedRequests.methods == [ - "crestodian.setup.detect", - "crestodian.setup.activate", + "openclaw.setup.detect", + "openclaw.setup.activate", "agents.list", - "crestodian.setup.verify", + "openclaw.setup.verify", "agents.list", - "crestodian.setup.verify", - "crestodian.setup.detect", + "openclaw.setup.verify", + "openclaw.setup.detect", ]) #expect(!view.aiSetup.connected) #expect(view.aiSetup.phase == .ready) #expect(!view.aiSetup.pendingActivationVerification) #expect(!view.aiSetup.waitingForPendingActivationDeadline) #expect(handoffCount == 0) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .none) @@ -3539,14 +3539,14 @@ struct OnboardingAISetupTests { } await recorder.record(message) switch request.method { - case "crestodian.setup.activate": + case "openclaw.setup.activate": if let requestDefaults = UserDefaults(suiteName: suiteName), - let activationOwner = OnboardingCrestodianResumeStore.activationOwner( + let activationOwner = OnboardingSystemAgentResumeStore.activationOwner( for: "local", defaults: requestDefaults ) { - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", activationOwner: activationOwner, activationTimeoutMs: 0, @@ -3557,7 +3557,7 @@ struct OnboardingAISetupTests { task.emitReceiveSuccess(.data(indeterminateActivationResponse(id: request.id))) case "agents.list": task.emitReceiveSuccess(.data(missingConfiguredModelResponse(id: request.id))) - case "crestodian.setup.detect": + case "openclaw.setup.detect": task.emitReceiveSuccess(.data(detectedSetupResponse(id: request.id))) default: break @@ -3571,7 +3571,7 @@ struct OnboardingAISetupTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" } ) var recheckTask: Task? @@ -3593,15 +3593,15 @@ struct OnboardingAISetupTests { #expect(recheckRoute == "local") #expect(requests.methods == [ - "crestodian.setup.detect", - "crestodian.setup.activate", + "openclaw.setup.detect", + "openclaw.setup.activate", "agents.list", - "crestodian.setup.detect", + "openclaw.setup.detect", ]) #expect(view.aiSetup.phase == .ready) #expect(!view.aiSetup.pendingActivationVerification) #expect(!view.aiSetup.waitingForPendingActivationDeadline) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults ) == .none) @@ -3643,10 +3643,10 @@ struct OnboardingAISetupTests { } #expect(await (recorder.snapshot()).methods == [ - "crestodian.setup.detect", - "crestodian.setup.activate", + "openclaw.setup.detect", + "openclaw.setup.activate", ]) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) #expect(model.pendingActivationVerification) #expect(model.waitingForPendingActivationDeadline) #expect(model.phase == .detecting) @@ -3680,21 +3680,21 @@ struct OnboardingAISetupTests { await model.detectAndAutoConnect() let staleActivation = Task { await model.activate(kind: "codex-cli") } - while !OnboardingCrestodianResumeStore.isPending( + while !OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults ) { await Task.yield() } model.resetForGatewayChange() - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-b", defaults: defaults ) staleActivation.cancel() await staleActivation.value - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults )) @@ -3708,13 +3708,13 @@ struct OnboardingAISetupTests { defaults: defaults, routeIdentityProvider: { "local" } ) - OnboardingCrestodianResumeStore.markPending(routeIdentity: "local", defaults: defaults) + OnboardingSystemAgentResumeStore.markPending(routeIdentity: "local", defaults: defaults) model.resumeConfiguredInference(modelRef: "openai/gpt-5.5") - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) model.resetForGatewayChange() - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) } @Test func `retired setup socket requires a fresh detection lease`() { diff --git a/apps/macos/Tests/OpenClawIPCTests/OnboardingCrestodianChatTests.swift b/apps/macos/Tests/OpenClawIPCTests/OnboardingSystemAgentChatTests.swift similarity index 77% rename from apps/macos/Tests/OpenClawIPCTests/OnboardingCrestodianChatTests.swift rename to apps/macos/Tests/OpenClawIPCTests/OnboardingSystemAgentChatTests.swift index 8c17e3f6f784..53d0cc18f322 100644 --- a/apps/macos/Tests/OpenClawIPCTests/OnboardingCrestodianChatTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/OnboardingSystemAgentChatTests.swift @@ -3,7 +3,7 @@ import OpenClawKit import Testing @testable import OpenClaw -private actor CrestodianGatewayConfig { +private actor SystemAgentGatewayConfig { private var token = "a" func snapshotToken() -> String { @@ -15,7 +15,7 @@ private actor CrestodianGatewayConfig { } } -private actor CrestodianSessionRecorder { +private actor SystemAgentSessionRecorder { private var sessionIDs: [String] = [] func record(_ sessionID: String) { @@ -27,7 +27,7 @@ private actor CrestodianSessionRecorder { } } -private actor CrestodianMethodRecorder { +private actor SystemAgentMethodRecorder { private var methods: [String] = [] func record(_ method: String) { @@ -39,7 +39,7 @@ private actor CrestodianMethodRecorder { } } -private actor CrestodianRequestGate { +private actor SystemAgentRequestGate { private var consumed = false private var released = false private var continuation: CheckedContinuation? @@ -62,7 +62,7 @@ private actor CrestodianRequestGate { } } -private func crestodianSessionID(from message: URLSessionWebSocketTask.Message) -> String? { +private func systemAgentSessionID(from message: URLSessionWebSocketTask.Message) -> String? { let data: Data? = switch message { case let .data(data): data case let .string(string): string.data(using: .utf8) @@ -70,13 +70,13 @@ private func crestodianSessionID(from message: URLSessionWebSocketTask.Message) } guard let data, let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any], - object["method"] as? String == "crestodian.chat", + object["method"] as? String == "openclaw.chat", let params = object["params"] as? [String: Any] else { return nil } return params["sessionId"] as? String } -private func crestodianRequestMethod(from message: URLSessionWebSocketTask.Message) -> String? { +private func systemAgentRequestMethod(from message: URLSessionWebSocketTask.Message) -> String? { let data: Data? = switch message { case let .data(data): data case let .string(string): string.data(using: .utf8) @@ -88,7 +88,7 @@ private func crestodianRequestMethod(from message: URLSessionWebSocketTask.Messa return object["method"] as? String } -private func respondToCrestodianHealth( +private func respondToSystemAgentHealth( task: GatewayTestWebSocketTask, id: String, method: String?) -> Bool @@ -98,7 +98,7 @@ private func respondToCrestodianHealth( return true } -private func crestodianResponse(id: String, action: String = "none") -> Data { +private func systemAgentResponse(id: String, action: String = "none") -> Data { Data( """ { @@ -165,37 +165,37 @@ private func transientVerificationErrorResponse(id: String) -> Data { @Suite(.serialized) @MainActor -struct OnboardingCrestodianChatTests { - @Test func `onboarding wires Crestodian agent handoff`() { +struct OnboardingSystemAgentChatTests { + @Test func `onboarding wires OpenClaw agent handoff`() { let state = AppState(preview: true) state.connectionMode = .local let view = OnboardingView(state: state) - view.prepareCrestodianHandoff() + view.prepareSystemAgentHandoff() - #expect(view.crestodianState.chat.onAgentHandoff != nil) + #expect(view.systemAgentState.chat.onAgentHandoff != nil) } - @Test func `relaunch with pending inference resumes Crestodian`() async throws { + @Test func `relaunch with pending inference resumes OpenClaw`() async throws { let suiteName = "OnboardingPendingInferenceResumeTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - let methods = CrestodianMethodRecorder() + let methods = SystemAgentMethodRecorder() let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, let id = GatewayWebSocketTestSupport.requestID(from: message) else { return } - let method = crestodianRequestMethod(from: message) + let method = systemAgentRequestMethod(from: message) if let method { await methods.record(method) } - if respondToCrestodianHealth(task: task, id: id, method: method) { return } + if respondToSystemAgentHealth(task: task, id: id, method: method) { return } switch method { - case "crestodian.setup.verify": + case "openclaw.setup.verify": task.emitReceiveSuccess(.data(verifiedInferenceResponse(id: id))) - case "crestodian.chat": - task.emitReceiveSuccess(.data(crestodianResponse(id: id))) + case "openclaw.chat": + task.emitReceiveSuccess(.data(systemAgentResponse(id: id))) default: break } @@ -212,25 +212,25 @@ struct OnboardingCrestodianChatTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "remote:direct:example.invalid" }) - view.crestodianState.chat = CrestodianOnboardingChatModel(gateway: gateway) + view.systemAgentState.chat = SystemAgentOnboardingChatModel(gateway: gateway) - let task = view.resumePendingCrestodian(modelRef: "openai/gpt-5.5") + let task = view.resumePendingSystemAgent(modelRef: "openai/gpt-5.5") await task.value #expect(view.aiSetup.connectedModelRef == "openai/gpt-5.5") - #expect(view.crestodianState.isPresented) - #expect(view.crestodianState.chat.messages.map(\.text) == ["ready"]) + #expect(view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat.messages.map(\.text) == ["ready"]) - let repeatedResume = view.resumePendingCrestodian(modelRef: "openai/gpt-5.5") + let repeatedResume = view.resumePendingSystemAgent(modelRef: "openai/gpt-5.5") await repeatedResume.value #expect(view.aiSetup.connectedModelRef == "openai/gpt-5.5") #expect(await methods.snapshot() == [ "health", - "crestodian.setup.verify", - "crestodian.chat", + "openclaw.setup.verify", + "openclaw.chat", ]) } @@ -238,26 +238,26 @@ struct OnboardingCrestodianChatTests { let suiteName = "OnboardingPendingVerificationRetryTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - let methods = CrestodianMethodRecorder() + let methods = SystemAgentMethodRecorder() let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, let id = GatewayWebSocketTestSupport.requestID(from: message), - let method = crestodianRequestMethod(from: message) + let method = systemAgentRequestMethod(from: message) else { return } await methods.record(method) - if respondToCrestodianHealth(task: task, id: id, method: method) { return } + if respondToSystemAgentHealth(task: task, id: id, method: method) { return } switch method { - case "crestodian.setup.verify": + case "openclaw.setup.verify": let priorVerifications = await methods.snapshot().filter { - $0 == "crestodian.setup.verify" + $0 == "openclaw.setup.verify" }.count let response = priorVerifications == 1 ? transientVerificationErrorResponse(id: id) : verifiedInferenceResponse(id: id) task.emitReceiveSuccess(.data(response)) - case "crestodian.chat": - task.emitReceiveSuccess(.data(crestodianResponse(id: id))) + case "openclaw.chat": + task.emitReceiveSuccess(.data(systemAgentResponse(id: id))) default: break } @@ -269,18 +269,18 @@ struct OnboardingCrestodianChatTests { sessionBox: WebSocketSessionBox(session: session)) let appState = AppState(preview: true) appState.connectionMode = .local - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "local", defaults: defaults) let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "local" }) - view.crestodianState.chat = CrestodianOnboardingChatModel(gateway: gateway) + view.systemAgentState.chat = SystemAgentOnboardingChatModel(gateway: gateway) - await view.resumePendingCrestodian(modelRef: "openai/gpt-5.5").value - #expect(!view.crestodianState.isPresented) + await view.resumePendingSystemAgent(modelRef: "openai/gpt-5.5").value + #expect(!view.systemAgentState.isPresented) var scheduledDeadlines: [(deadline: Date, routeIdentity: String)] = [] view.aiSetup.onPendingActivationDeadline = { deadline, routeIdentity in @@ -288,7 +288,7 @@ struct OnboardingCrestodianChatTests { } view.aiSetup.retryFromScratch() for _ in 0..<200 { - if case .verified = OnboardingCrestodianResumeStore.pendingState( + if case .verified = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults) { @@ -299,11 +299,11 @@ struct OnboardingCrestodianChatTests { #expect(!view.aiSetup.connected) #expect(view.aiSetup.waitingForPendingActivationDeadline) - #expect(!view.crestodianState.isPresented) - #expect(view.crestodianState.chat.messages.isEmpty) + #expect(!view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat.messages.isEmpty) #expect(scheduledDeadlines.count == 1) #expect(scheduledDeadlines.first?.routeIdentity == "local") - if case let .verified(deadline) = OnboardingCrestodianResumeStore.pendingState( + if case let .verified(deadline) = OnboardingSystemAgentResumeStore.pendingState( for: "local", defaults: defaults) { @@ -315,9 +315,9 @@ struct OnboardingCrestodianChatTests { #expect(scheduledDeadlines.count == 1) #expect(await methods.snapshot() == [ "health", - "crestodian.setup.verify", + "openclaw.setup.verify", "health", - "crestodian.setup.verify", + "openclaw.setup.verify", ]) } @@ -325,17 +325,17 @@ struct OnboardingCrestodianChatTests { let suiteName = "OnboardingSupersededResumeTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - let gate = CrestodianRequestGate() - let methods = CrestodianMethodRecorder() + let gate = SystemAgentRequestGate() + let methods = SystemAgentMethodRecorder() let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, let id = GatewayWebSocketTestSupport.requestID(from: message), - let method = crestodianRequestMethod(from: message) + let method = systemAgentRequestMethod(from: message) else { return } await methods.record(method) - if respondToCrestodianHealth(task: task, id: id, method: method) { return } - guard method == "crestodian.setup.verify" else { return } + if respondToSystemAgentHealth(task: task, id: id, method: method) { return } + guard method == "openclaw.setup.verify" else { return } _ = await gate.waitIfFirst() task.emitReceiveSuccess(.data(verifiedInferenceResponse(id: id))) }) @@ -351,13 +351,13 @@ struct OnboardingCrestodianChatTests { let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { "remote:direct:example.invalid" }) - view.crestodianState.chat = CrestodianOnboardingChatModel(gateway: gateway) + view.systemAgentState.chat = SystemAgentOnboardingChatModel(gateway: gateway) - let staleResume = view.resumePendingCrestodian(modelRef: "openai/gpt-5.5") + let staleResume = view.resumePendingSystemAgent(modelRef: "openai/gpt-5.5") for _ in 0..<200 { - if await methods.snapshot() == ["health", "crestodian.setup.verify"] { + if await methods.snapshot() == ["health", "openclaw.setup.verify"] { break } try? await Task.sleep(nanoseconds: 5_000_000) @@ -372,30 +372,30 @@ struct OnboardingCrestodianChatTests { await staleResume.value #expect(view.aiSetup.connected) - #expect(!view.crestodianState.isPresented) - #expect(view.crestodianState.chat.messages.isEmpty) + #expect(!view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat.messages.isEmpty) } @Test func `cold launch resumes a completed activation immediately`() async throws { let suiteName = "OnboardingColdPendingHandoffTests-\(UUID().uuidString)" let defaults = try #require(UserDefaults(suiteName: suiteName)) defer { defaults.removePersistentDomain(forName: suiteName) } - let methods = CrestodianMethodRecorder() + let methods = SystemAgentMethodRecorder() let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, let id = GatewayWebSocketTestSupport.requestID(from: message), - let method = crestodianRequestMethod(from: message) + let method = systemAgentRequestMethod(from: message) else { return } await methods.record(method) - if respondToCrestodianHealth(task: task, id: id, method: method) { return } + if respondToSystemAgentHealth(task: task, id: id, method: method) { return } switch method { case "agents.list": task.emitReceiveSuccess(.data(configuredAgentsResponse(id: id))) - case "crestodian.setup.verify": + case "openclaw.setup.verify": task.emitReceiveSuccess(.data(verifiedInferenceResponse(id: id))) - case "crestodian.chat": - task.emitReceiveSuccess(.data(crestodianResponse(id: id))) + case "openclaw.chat": + task.emitReceiveSuccess(.data(systemAgentResponse(id: id))) default: break } @@ -407,66 +407,66 @@ struct OnboardingCrestodianChatTests { sessionBox: WebSocketSessionBox(session: session)) let appState = AppState(preview: true) appState.connectionMode = .local - let routeIdentity = OnboardingCrestodianResumeStore.selectedRouteIdentity(state: appState) + let routeIdentity = OnboardingSystemAgentResumeStore.selectedRouteIdentity(state: appState) let route = try #require(await gateway.captureRoute()) - let activationOwner = try OnboardingCrestodianResumeStore.ActivationOwner( + let activationOwner = try OnboardingSystemAgentResumeStore.ActivationOwner( id: "completed-before-relaunch", routeFingerprint: #require(route.activationOwnershipFingerprint)) - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: routeIdentity, activationOwner: activationOwner, defaults: defaults) - OnboardingCrestodianResumeStore.markCompleted( + OnboardingSystemAgentResumeStore.markCompleted( ifOwnedBy: routeIdentity, activationOwner: activationOwner, defaults: defaults) let view = OnboardingView( state: appState, aiSetupGateway: gateway, - crestodianDefaults: defaults, + systemAgentDefaults: defaults, aiSetupRouteIdentityProvider: { routeIdentity }) - view.crestodianState.chat = CrestodianOnboardingChatModel(gateway: gateway) + view.systemAgentState.chat = SystemAgentOnboardingChatModel(gateway: gateway) let aiSetup = view.aiSetup - let crestodianState = view.crestodianState + let systemAgentState = view.systemAgentState let initialProbe = try #require(view.onboardingDidAppear()) await initialProbe.value for _ in 0..<200 { - if crestodianState.chat.messages.map(\.text) == ["ready"] { + if systemAgentState.chat.messages.map(\.text) == ["ready"] { break } try? await Task.sleep(nanoseconds: 5_000_000) } #expect(aiSetup.connected) - #expect(crestodianState.isPresented) - #expect(crestodianState.chat.messages.map(\.text) == ["ready"]) - #expect(OnboardingCrestodianResumeStore.pendingState( + #expect(systemAgentState.isPresented) + #expect(systemAgentState.chat.messages.map(\.text) == ["ready"]) + #expect(OnboardingSystemAgentResumeStore.pendingState( for: routeIdentity, defaults: defaults) == .completed) #expect(await methods.snapshot() == [ "agents.list", "health", - "crestodian.setup.verify", - "crestodian.chat", + "openclaw.setup.verify", + "openclaw.chat", ]) } - @Test func `fresh inference presents and starts Crestodian immediately`() async throws { + @Test func `fresh inference presents and starts OpenClaw immediately`() async throws { let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, let id = GatewayWebSocketTestSupport.requestID(from: message) else { return } - task.emitReceiveSuccess(.data(crestodianResponse(id: id))) + task.emitReceiveSuccess(.data(systemAgentResponse(id: id))) }) }) let url = try #require(URL(string: "ws://example.invalid")) let gateway = GatewayConnection( configProvider: { (url: url, token: nil, password: nil) }, sessionBox: WebSocketSessionBox(session: session)) - let state = OnboardingCrestodianChatState() - state.chat = CrestodianOnboardingChatModel(gateway: gateway) + let state = OnboardingSystemAgentChatState() + state.chat = SystemAgentOnboardingChatModel(gateway: gateway) let task = state.presentAndStart() await task.value @@ -483,16 +483,16 @@ struct OnboardingCrestodianChatTests { guard sendIndex > 0, let id = GatewayWebSocketTestSupport.requestID(from: message) else { return } - task.emitReceiveSuccess(.data(crestodianResponse(id: id))) + task.emitReceiveSuccess(.data(systemAgentResponse(id: id))) }) }) let url = try #require(URL(string: "ws://example.invalid")) let gateway = GatewayConnection( configProvider: { (url: url, token: nil, password: nil) }, sessionBox: WebSocketSessionBox(session: session)) - let chat = CrestodianOnboardingChatModel(gateway: gateway) + let chat = SystemAgentOnboardingChatModel(gateway: gateway) var refreshCount = 0 - CrestodianSettings.configureChatCallbacks( + SystemAgentSettings.configureChatCallbacks( for: chat, onReplyReceived: { refreshCount += 1 }) @@ -508,8 +508,8 @@ struct OnboardingCrestodianChatTests { let gateway = GatewayConnection( configProvider: { (url: url, token: nil, password: nil) }, sessionBox: WebSocketSessionBox(session: session)) - let chat = CrestodianOnboardingChatModel(gateway: gateway) - let state = OnboardingCrestodianChatState() + let chat = SystemAgentOnboardingChatModel(gateway: gateway) + let state = OnboardingSystemAgentChatState() state.chat = chat var replyCount = 0 var handoffCount = 0 @@ -535,17 +535,17 @@ struct OnboardingCrestodianChatTests { } @Test func `chat session stays bound to its original gateway route`() async throws { - let config = CrestodianGatewayConfig() - let recorder = CrestodianSessionRecorder() + let config = SystemAgentGatewayConfig() + let recorder = SystemAgentSessionRecorder() let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, let id = GatewayWebSocketTestSupport.requestID(from: message) else { return } - if let sessionID = crestodianSessionID(from: message) { + if let sessionID = systemAgentSessionID(from: message) { await recorder.record(sessionID) } - task.emitReceiveSuccess(.data(crestodianResponse(id: id))) + task.emitReceiveSuccess(.data(systemAgentResponse(id: id))) }) }) let url = try #require(URL(string: "ws://example.invalid")) @@ -555,7 +555,7 @@ struct OnboardingCrestodianChatTests { return (url: url, token: token, password: nil) }, sessionBox: WebSocketSessionBox(session: session)) - let chat = CrestodianOnboardingChatModel(gateway: gateway) + let chat = SystemAgentOnboardingChatModel(gateway: gateway) await chat.startIfNeeded() #expect(chat.messages.map(\.text) == ["ready"]) @@ -573,7 +573,7 @@ struct OnboardingCrestodianChatTests { #expect(session.snapshotMakeCount() == 1) #expect(session.latestTask()?.snapshotSendCount() == 2) #expect(chat.messages.map(\.text) == ["ready", "must stay on route a"]) - #expect(chat.errorMessage == "The Gateway connection changed. Restart Crestodian to reconnect.") + #expect(chat.errorMessage == "The Gateway connection changed. Restart OpenClaw to reconnect.") #expect(await recorder.snapshot() == [routeASessionID]) let restartTask = try #require(chat.restartAfterError()) @@ -590,15 +590,15 @@ struct OnboardingCrestodianChatTests { } @Test func `route change while reply is in flight discards reply and action`() async throws { - let config = CrestodianGatewayConfig() - let requestGate = CrestodianRequestGate() + let config = SystemAgentGatewayConfig() + let requestGate = SystemAgentRequestGate() let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, let id = GatewayWebSocketTestSupport.requestID(from: message) else { return } _ = await requestGate.waitIfFirst() - task.emitReceiveSuccess(.data(crestodianResponse(id: id, action: "open-agent"))) + task.emitReceiveSuccess(.data(systemAgentResponse(id: id, action: "open-agent"))) }) }) let url = try #require(URL(string: "ws://example.invalid")) @@ -608,7 +608,7 @@ struct OnboardingCrestodianChatTests { return (url: url, token: token, password: nil) }, sessionBox: WebSocketSessionBox(session: session)) - let chat = CrestodianOnboardingChatModel(gateway: gateway) + let chat = SystemAgentOnboardingChatModel(gateway: gateway) var replyCount = 0 var handoffCount = 0 chat.onReplyReceived = { replyCount += 1 } @@ -631,11 +631,11 @@ struct OnboardingCrestodianChatTests { #expect(chat.messages.isEmpty) #expect(replyCount == 0) #expect(handoffCount == 0) - #expect(chat.errorMessage == "The Gateway connection changed. Restart Crestodian to reconnect.") + #expect(chat.errorMessage == "The Gateway connection changed. Restart OpenClaw to reconnect.") } @Test func `cancelled initial request exposes restart and recovers`() async throws { - let requestGate = CrestodianRequestGate() + let requestGate = SystemAgentRequestGate() let session = GatewayTestWebSocketSession(taskFactory: { GatewayTestWebSocketTask(sendHook: { task, message, sendIndex in guard sendIndex > 0, @@ -644,14 +644,14 @@ struct OnboardingCrestodianChatTests { if sendIndex == 1, await requestGate.waitIfFirst() { throw CancellationError() } - task.emitReceiveSuccess(.data(crestodianResponse(id: id))) + task.emitReceiveSuccess(.data(systemAgentResponse(id: id))) }) }) let url = try #require(URL(string: "ws://example.invalid")) let gateway = GatewayConnection( configProvider: { (url: url, token: nil, password: nil) }, sessionBox: WebSocketSessionBox(session: session)) - let chat = CrestodianOnboardingChatModel(gateway: gateway) + let chat = SystemAgentOnboardingChatModel(gateway: gateway) let startTask = Task { await chat.startIfNeeded() } var requestStarted = false @@ -667,7 +667,7 @@ struct OnboardingCrestodianChatTests { await requestGate.release() await startTask.value - #expect(chat.errorMessage == "Crestodian was interrupted. Restart to try again.") + #expect(chat.errorMessage == "OpenClaw was interrupted. Restart to try again.") #expect(!chat.isSending) #expect(chat.messages.isEmpty) diff --git a/apps/macos/Tests/OpenClawIPCTests/OnboardingViewSmokeTests.swift b/apps/macos/Tests/OpenClawIPCTests/OnboardingViewSmokeTests.swift index 4aa91182cc38..89d74f6facec 100644 --- a/apps/macos/Tests/OpenClawIPCTests/OnboardingViewSmokeTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/OnboardingViewSmokeTests.swift @@ -73,7 +73,7 @@ struct OnboardingViewSmokeTests { #expect(short < preferred) } - @Test func `page order delegates setup after inference to Crestodian`() { + @Test func `page order delegates setup after inference to OpenClaw`() { let order = OnboardingView.pageOrder( for: .local, requiresCLIInstall: false) @@ -197,17 +197,17 @@ struct OnboardingViewSmokeTests { let state = AppState(preview: true) let view = OnboardingView(state: state) var monitoredPage: Int? - let previousCrestodianChat = view.crestodianState.chat + let previousSystemAgentChat = view.systemAgentState.chat view.aiSetup.manualKey = "route-bound" - view.crestodianState.isPresented = true + view.systemAgentState.isPresented = true view.handleConnectionModeChange { pageIndex in monitoredPage = pageIndex } #expect(view.aiSetup.manualKey.isEmpty) - #expect(!view.crestodianState.isPresented) - #expect(view.crestodianState.chat !== previousCrestodianChat) + #expect(!view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat !== previousSystemAgentChat) #expect(monitoredPage == view.activePageIndex) } @@ -275,7 +275,7 @@ struct OnboardingViewSmokeTests { defaults.removePersistentDomain(forName: suiteName) } GatewayDiscoveryPreferences.setPreferredStableID("gateway-a") - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-a", defaults: defaults) @@ -286,10 +286,10 @@ struct OnboardingViewSmokeTests { state: state, permissionMonitor: PermissionMonitor.shared, discoveryModel: GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName), - crestodianDefaults: defaults) - let priorChat = view.crestodianState.chat + systemAgentDefaults: defaults) + let priorChat = view.systemAgentState.chat view.aiSetup.manualKey = "route-a-secret" - view.crestodianState.isPresented = true + view.systemAgentState.isPresented = true let gateway = GatewayDiscoveryModel.DiscoveredGateway( displayName: "Gateway B", serviceHost: nil, @@ -307,12 +307,12 @@ struct OnboardingViewSmokeTests { #expect(state.connectionMode == .remote) #expect(view.aiSetup.manualKey.isEmpty) - #expect(!view.crestodianState.isPresented) - #expect(view.crestodianState.chat !== priorChat) - #expect(!OnboardingCrestodianResumeStore.isPending( + #expect(!view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat !== priorChat) + #expect(!OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-b", defaults: defaults)) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults)) } @@ -326,7 +326,7 @@ struct OnboardingViewSmokeTests { defaults.removePersistentDomain(forName: suiteName) } GatewayDiscoveryPreferences.setPreferredStableID("gateway-a") - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-a", defaults: defaults) let state = AppState(preview: true) @@ -341,36 +341,36 @@ struct OnboardingViewSmokeTests { let view = OnboardingView( state: state, aiSetupGateway: gateway, - crestodianDefaults: defaults) + systemAgentDefaults: defaults) view.preferredGatewayID = "gateway-a" view.aiSetup.manualKey = "route-a-secret" view.aiSetup.resumeConfiguredInference(modelRef: "openai/gpt-5.5") view.aiSetup.acceptVerifiedPendingInference(modelRef: "openai/gpt-5.5") - let priorChat = view.crestodianState.chat - view.crestodianState.isPresented = true + let priorChat = view.systemAgentState.chat + view.systemAgentState.isPresented = true view.remoteProbeState = .ok(RemoteGatewayProbeSuccess(authSource: .sharedToken)) view.remoteAuthIssue = .tokenMismatch view.updateManualRemoteURL("wss://gateway-b.example.test") - let editedRouteIdentity = OnboardingCrestodianResumeStore.selectedRouteIdentity( + let editedRouteIdentity = OnboardingSystemAgentResumeStore.selectedRouteIdentity( state: state, preferredGatewayID: view.preferredGatewayID ?? GatewayDiscoveryPreferences.preferredStableID()) #expect(view.preferredGatewayID == nil) #expect(GatewayDiscoveryPreferences.preferredStableID() == nil) #expect(editedRouteIdentity?.hasPrefix("remote:direct:") == true) #expect(editedRouteIdentity != "remote:id:gateway-a") - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults)) - #expect(!OnboardingCrestodianResumeStore.isPending( + #expect(!OnboardingSystemAgentResumeStore.isPending( for: editedRouteIdentity, defaults: defaults)) #expect(view.aiSetup.phase == .idle) #expect(!view.aiSetup.connected) #expect(view.aiSetup.manualKey.isEmpty) - #expect(!view.crestodianState.isPresented) - #expect(view.crestodianState.chat !== priorChat) + #expect(!view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat !== priorChat) #expect(view.remoteProbeState == .idle) #expect(view.remoteAuthIssue == nil) #expect(gatewaySession.snapshotMakeCount() == 0) @@ -388,7 +388,7 @@ struct OnboardingViewSmokeTests { defaults.removePersistentDomain(forName: suiteName) } GatewayDiscoveryPreferences.setPreferredStableID("gateway-a") - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-a", defaults: defaults) @@ -399,10 +399,10 @@ struct OnboardingViewSmokeTests { state: state, permissionMonitor: PermissionMonitor.shared, discoveryModel: GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName), - crestodianDefaults: defaults) - let priorChat = view.crestodianState.chat + systemAgentDefaults: defaults) + let priorChat = view.systemAgentState.chat view.aiSetup.manualKey = "pending-secret" - view.crestodianState.isPresented = true + view.systemAgentState.isPresented = true let gateway = GatewayDiscoveryModel.DiscoveredGateway( displayName: "Gateway A", serviceHost: nil, @@ -419,9 +419,9 @@ struct OnboardingViewSmokeTests { view.selectRemoteGateway(gateway) #expect(view.aiSetup.manualKey == "pending-secret") - #expect(view.crestodianState.isPresented) - #expect(view.crestodianState.chat === priorChat) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat === priorChat) + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults)) } @@ -435,24 +435,24 @@ struct OnboardingViewSmokeTests { defaults.removePersistentDomain(forName: suiteName) } GatewayDiscoveryPreferences.setPreferredStableID("gateway-a") - OnboardingCrestodianResumeStore.markPending( + OnboardingSystemAgentResumeStore.markPending( routeIdentity: "remote:id:gateway-a", defaults: defaults) let state = AppState(preview: true) state.connectionMode = .remote - let view = OnboardingView(state: state, crestodianDefaults: defaults) - let priorChat = view.crestodianState.chat + let view = OnboardingView(state: state, systemAgentDefaults: defaults) + let priorChat = view.systemAgentState.chat view.aiSetup.manualKey = "route-a-secret" - view.crestodianState.isPresented = true + view.systemAgentState.isPresented = true view.selectLocalGateway() #expect(state.connectionMode == .local) #expect(view.aiSetup.manualKey.isEmpty) - #expect(!view.crestodianState.isPresented) - #expect(view.crestodianState.chat !== priorChat) - #expect(!OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) - #expect(OnboardingCrestodianResumeStore.isPending( + #expect(!view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat !== priorChat) + #expect(!OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) + #expect(OnboardingSystemAgentResumeStore.isPending( for: "remote:id:gateway-a", defaults: defaults)) } @@ -460,40 +460,40 @@ struct OnboardingViewSmokeTests { @Test func `same local selection preserves pending gateway setup state`() throws { let (defaults, suiteName) = try makeOnboardingResumeDefaults() defer { defaults.removePersistentDomain(forName: suiteName) } - OnboardingCrestodianResumeStore.markPending(routeIdentity: "local", defaults: defaults) + OnboardingSystemAgentResumeStore.markPending(routeIdentity: "local", defaults: defaults) let state = AppState(preview: true) state.connectionMode = .local - let view = OnboardingView(state: state, crestodianDefaults: defaults) - let priorChat = view.crestodianState.chat + let view = OnboardingView(state: state, systemAgentDefaults: defaults) + let priorChat = view.systemAgentState.chat view.aiSetup.manualKey = "pending-secret" - view.crestodianState.isPresented = true + view.systemAgentState.isPresented = true view.selectLocalGateway() #expect(view.aiSetup.manualKey == "pending-secret") - #expect(view.crestodianState.isPresented) - #expect(view.crestodianState.chat === priorChat) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat === priorChat) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) } @Test func `configure later preserves in flight activation lease`() throws { let (defaults, suiteName) = try makeOnboardingResumeDefaults() defer { defaults.removePersistentDomain(forName: suiteName) } - OnboardingCrestodianResumeStore.markPending(routeIdentity: "local", defaults: defaults) + OnboardingSystemAgentResumeStore.markPending(routeIdentity: "local", defaults: defaults) let state = AppState(preview: true) state.connectionMode = .local - let view = OnboardingView(state: state, crestodianDefaults: defaults) - let priorChat = view.crestodianState.chat + let view = OnboardingView(state: state, systemAgentDefaults: defaults) + let priorChat = view.systemAgentState.chat view.aiSetup.manualKey = "local-secret" - view.crestodianState.isPresented = true + view.systemAgentState.isPresented = true view.selectUnconfiguredGateway() #expect(state.connectionMode == .unconfigured) #expect(view.aiSetup.manualKey.isEmpty) - #expect(!view.crestodianState.isPresented) - #expect(view.crestodianState.chat !== priorChat) - #expect(OnboardingCrestodianResumeStore.isPending(for: "local", defaults: defaults)) + #expect(!view.systemAgentState.isPresented) + #expect(view.systemAgentState.chat !== priorChat) + #expect(OnboardingSystemAgentResumeStore.isPending(for: "local", defaults: defaults)) } @Test diff --git a/apps/macos/Tests/OpenClawIPCTests/SettingsViewSmokeTests.swift b/apps/macos/Tests/OpenClawIPCTests/SettingsViewSmokeTests.swift index 81e8b706d10d..a9da37d8a106 100644 --- a/apps/macos/Tests/OpenClawIPCTests/SettingsViewSmokeTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/SettingsViewSmokeTests.swift @@ -202,39 +202,39 @@ struct SettingsViewSmokeTests { _ = view.body } - @Test func `Crestodian settings require configured inference`() { - #expect(!CrestodianAvailability.shouldShow(configuredModel: nil)) - #expect(!CrestodianAvailability.shouldShow(configuredModel: " ")) - #expect(CrestodianAvailability.shouldShow(configuredModel: "openai/gpt-5.5")) + @Test func `OpenClaw settings require configured inference`() { + #expect(!SystemAgentAvailability.shouldShow(configuredModel: nil)) + #expect(!SystemAgentAvailability.shouldShow(configuredModel: " ")) + #expect(SystemAgentAvailability.shouldShow(configuredModel: "openai/gpt-5.5")) - let hiddenTabs = SettingsTabGroup.defaultGroups(showDebug: false, showCrestodian: false) + let hiddenTabs = SettingsTabGroup.defaultGroups(showDebug: false, showSystemAgent: false) .flatMap(\.tabs) - let visibleTabs = SettingsTabGroup.defaultGroups(showDebug: false, showCrestodian: true) + let visibleTabs = SettingsTabGroup.defaultGroups(showDebug: false, showSystemAgent: true) .flatMap(\.tabs) - #expect(!hiddenTabs.contains(.crestodian)) - #expect(visibleTabs.contains(.crestodian)) + #expect(!hiddenTabs.contains(.systemAgent)) + #expect(visibleTabs.contains(.systemAgent)) #expect(SettingsRootView.normalizedTab( - .crestodian, + .systemAgent, showDebug: false, - showCrestodian: false) == .general) + showSystemAgent: false) == .general) #expect(SettingsRootView.normalizedTab( - .crestodian, + .systemAgent, showDebug: false, - showCrestodian: true) == .crestodian) + showSystemAgent: true) == .systemAgent) let loadingSelection = SettingsRootView.tabSelection( - requested: .crestodian, + requested: .systemAgent, showDebug: false, inferenceConfiguration: .loading) #expect(loadingSelection.selected == .general) - #expect(loadingSelection.deferred == .crestodian) + #expect(loadingSelection.deferred == .systemAgent) let configuredSelection = SettingsRootView.tabSelection( requested: loadingSelection.deferred ?? .general, showDebug: false, inferenceConfiguration: .loaded("openai/gpt-5.5")) - #expect(configuredSelection.selected == .crestodian) + #expect(configuredSelection.selected == .systemAgent) #expect(configuredSelection.deferred == nil) let unconfiguredSelection = SettingsRootView.tabSelection( - requested: .crestodian, + requested: .systemAgent, showDebug: false, inferenceConfiguration: .loaded(nil)) #expect(unconfiguredSelection.selected == .general) @@ -247,7 +247,7 @@ struct SettingsViewSmokeTests { result: .confirmed(nil)) == .loaded(nil)) } - @Test func `Crestodian preserves same route and resets for gateway changes`() { + @Test func `OpenClaw preserves same route and resets for gateway changes`() { let stateDir = URL(fileURLWithPath: "/Users/tester/.openclaw") let directA = MacChatTranscriptCache.gatewayID( mode: .remote, @@ -266,17 +266,17 @@ struct SettingsViewSmokeTests { #expect(directA != directB) #expect(SettingsRootView.configRefreshPlan( - selectedTab: .crestodian, + selectedTab: .systemAgent, previousGatewayID: directA, - currentGatewayID: directA) == .init(clearsPrevious: false, resetsCrestodian: false)) + currentGatewayID: directA) == .init(clearsPrevious: false, resetsSystemAgent: false)) #expect(SettingsRootView.configRefreshPlan( selectedTab: .general, previousGatewayID: directA, - currentGatewayID: directA) == .init(clearsPrevious: true, resetsCrestodian: false)) + currentGatewayID: directA) == .init(clearsPrevious: true, resetsSystemAgent: false)) #expect(SettingsRootView.configRefreshPlan( - selectedTab: .crestodian, + selectedTab: .systemAgent, previousGatewayID: directA, - currentGatewayID: directB) == .init(clearsPrevious: true, resetsCrestodian: true)) + currentGatewayID: directB) == .init(clearsPrevious: true, resetsSystemAgent: true)) } @Test func `about settings builds body`() { diff --git a/apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayPush.swift b/apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayPush.swift index 9cff2c38bc1d..a67fe7fd09c4 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayPush.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayPush.swift @@ -2,7 +2,7 @@ import OpenClawProtocol public enum GatewayServerCapability: String, CaseIterable, Sendable { case chatSendRoutingContract = "chat-send-routing-contract" - case crestodianSetupModelRef = "crestodian-setup-model-ref" + case systemAgentSetupModelRef = "openclaw-setup-model-ref" } extension HelloOk { diff --git a/apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json b/apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json index e3f7dda92a5f..0f2fa1ba70eb 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json +++ b/apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json @@ -447,9 +447,9 @@ "proposal_id" ] }, - "crestodian": { + "openclaw": { "emoji": "🦀", - "title": "Crestodian", + "title": "OpenClaw", "detailKeys": [ "action", "path", diff --git a/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift b/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift index 4baaaa8a7865..8c2d4feebd93 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift @@ -6335,7 +6335,7 @@ public struct ConfigSchemaLookupResult: Codable, Sendable { } } -public struct CrestodianChatParams: Codable, Sendable { +public struct SystemAgentChatParams: Codable, Sendable { public let sessionid: String public let message: String? public let welcomevariant: AnyCodable? @@ -6361,7 +6361,7 @@ public struct CrestodianChatParams: Codable, Sendable { } } -public struct CrestodianChatResult: Codable, Sendable { +public struct SystemAgentChatResult: Codable, Sendable { public let sessionid: String public let reply: String public let sensitive: Bool? @@ -6387,9 +6387,9 @@ public struct CrestodianChatResult: Codable, Sendable { } } -public struct CrestodianSetupDetectParams: Codable, Sendable {} +public struct SystemAgentSetupDetectParams: Codable, Sendable {} -public struct CrestodianSetupDetectResult: Codable, Sendable { +public struct SystemAgentSetupDetectResult: Codable, Sendable { public let candidates: [[String: AnyCodable]] public let manualproviders: [[String: AnyCodable]] public let authoptions: [[String: AnyCodable]]? @@ -6427,9 +6427,9 @@ public struct CrestodianSetupDetectResult: Codable, Sendable { } } -public struct CrestodianSetupVerifyParams: Codable, Sendable {} +public struct SystemAgentSetupVerifyParams: Codable, Sendable {} -public struct CrestodianSetupActivateParams: Codable, Sendable { +public struct SystemAgentSetupActivateParams: Codable, Sendable { public let kind: AnyCodable public let modelref: String? public let authchoice: String? @@ -6459,7 +6459,7 @@ public struct CrestodianSetupActivateParams: Codable, Sendable { } } -public struct CrestodianSetupActivateResult: Codable, Sendable { +public struct SystemAgentSetupActivateResult: Codable, Sendable { public let ok: Bool public let modelref: String? public let latencyms: Double? @@ -6493,7 +6493,7 @@ public struct CrestodianSetupActivateResult: Codable, Sendable { } } -public struct CrestodianSetupAuthStartParams: Codable, Sendable { +public struct SystemAgentSetupAuthStartParams: Codable, Sendable { public let sessionid: String public let authchoice: String public let workspace: String? @@ -6515,7 +6515,7 @@ public struct CrestodianSetupAuthStartParams: Codable, Sendable { } } -public struct CrestodianSetupAuthStartResult: Codable, Sendable { +public struct SystemAgentSetupAuthStartResult: Codable, Sendable { public let sessionid: String public let done: Bool public let step: WizardStep? diff --git a/docs/cli/crestodian.md b/docs/cli/crestodian.md index 2be247a6abfe..aef8be2dec57 100644 --- a/docs/cli/crestodian.md +++ b/docs/cli/crestodian.md @@ -1,352 +1,9 @@ --- -summary: "CLI reference and security model for the inference-backed Crestodian setup and repair helper" +summary: "Redirect to the OpenClaw system-agent reference" read_when: - - You finished inference setup and want Crestodian to configure the rest - - You need to inspect or repair OpenClaw with the local setup agent - - You are designing or enabling message-channel rescue mode -title: "Crestodian" + - You followed an older Crestodian documentation link +title: "Crestodian (redirect)" +redirect: /cli/openclaw --- -# `openclaw crestodian` - -Conversational Crestodian is OpenClaw's local setup, repair, and configuration -agent. It starts only after the effective default model completes a real turn. -Fresh installs establish inference first; malformed config stays on the -classic doctor path. - -## When it starts - -Running `openclaw` with no subcommand routes based on config state: - -- Config missing, or exists with no authored settings (empty, or only `$schema`/`meta` keys): starts guided onboarding with live AI verification. -- Config exists but fails validation: starts classic onboarding, which reports the issues and directs you to `openclaw doctor`. -- Config exists and is valid: opens the normal agent TUI. A reachable - configured Gateway whose default agent has a model goes directly to that UI - without onboarding or Crestodian. Use `/crestodian` inside the TUI, or run - `openclaw crestodian` directly, to reach Crestodian later. - -Running `openclaw crestodian` first live-tests the configured default model. A passing turn starts Crestodian. An interactive failure opens guided inference setup and hands off to Crestodian after a candidate passes. One-shot, JSON, and other noninteractive requests fail with instructions to run `openclaw onboard` when inference is unavailable. `openclaw --help` and `openclaw --version` keep their normal fast paths. - -Noninteractive bare `openclaw` (no TTY) exits with a short message instead of printing root help: it points to non-interactive onboarding on a fresh or invalid install, or to `openclaw agent --local ...` when config is valid. - -`openclaw onboard --modern` remains a compatibility alias for Crestodian, but uses the same inference gate: working inference opens the chat, interactive failures start guided inference setup, and noninteractive failures exit with onboarding guidance. `openclaw onboard --classic` opens the full step-by-step wizard. - -## What Crestodian shows - -Interactive Crestodian opens the same TUI shell as `openclaw tui`, with a Crestodian chat backend. The startup greeting covers: - -- config validity and the default agent -- the verified model Crestodian is using -- Gateway reachability from the first startup probe -- the next recommended debug action - -It does not dump secrets or load plugin CLI commands just to start. - -Use `status` for the detailed inventory: config path, docs/source paths, local CLI probes, key/token presence, agents, model, and Gateway details. - -Crestodian uses the same reference discovery as regular agents: in a Git checkout it points at local `docs/` and the source tree; in an npm install it uses bundled docs and links to [https://github.com/openclaw/openclaw](https://github.com/openclaw/openclaw), with guidance to check source when docs are not enough. - -## Examples - -```bash -openclaw -openclaw crestodian -openclaw crestodian --json -openclaw crestodian --message "models" -openclaw crestodian --message "validate config" -openclaw crestodian --message "setup workspace ~/Projects/work" --yes -openclaw crestodian --message "set default model openai/gpt-5.6" --yes -openclaw onboard --modern -``` - -Inside the Crestodian TUI: - -```text -status -health -doctor -validate config -setup -setup workspace ~/Projects/work -config set gateway.port 19001 -config set-ref gateway.auth.token env OPENCLAW_GATEWAY_TOKEN -gateway status -restart gateway -agents -create agent work workspace ~/Projects/work -models -configure model provider -set default model openai/gpt-5.6 -channels -channel info slack -connect slack -open channel wizard for slack -plugins list -plugins search slack -plugin install clawhub:openclaw-codex-app-server -talk to work agent -talk to agent for ~/Projects/work -audit -quit -``` - -## Operations and approval - -Crestodian uses typed operations instead of editing config ad hoc. - -Read-only operations run immediately: show overview, list agents, list installed plugins, search ClawHub plugins, show model/backend status, run status/health checks, check Gateway reachability, run doctor without interactive fixes, validate config, show the audit-log path. - -Starting guided channel setup (`connect telegram`) also runs immediately. Its wizard collects explicit answers and owns the resulting writes. - -Persistent operations require conversational approval (or `--yes` for a direct command): write config, `config set`, `config set-ref`, setup/onboarding bootstrap, change the default model, start/stop/restart the Gateway, create agents, and install plugins. - -Crestodian installs only ClawHub, bundled, or official-catalog plugins. Install any other executable source from a trusted shell with `openclaw plugins install `, where the normal source warning and acknowledgement flow applies. - -Doctor repairs are unavailable inside Crestodian because they can rewrite the provider, authentication, or default-agent inference route powering the session. Exit Crestodian and run `openclaw doctor --fix` in a terminal. Read-only `doctor` remains available inside Crestodian. - -New agents inherit the live-verified default inference route. The agent id `crestodian` is reserved for the privileged virtual custodian and cannot be created as a normal agent. - -`config set` and `config set-ref` cannot change inference-route state, -including inference-provider credentials, top-level `auth.*`, model catalogs, -CLI backends, default/per-agent model routes, agent params/tools, or root -`tools.*`. Raw writes under `env.*`, `secrets.*`, `plugins.*`, and `$include` -are also refused because they can replace credential resolution or provider -activation. Gateway and channel auth remain normal config surfaces. Use typed plugin/channel workflows and -`set default model ` for an already -configured route; it live-tests the route before saving it. To configure or -repair provider/auth access, exit Crestodian and run `openclaw onboard`. - -Plugin uninstall is refused inside Crestodian because removing a provider -plugin could disable the inference route powering the session. Exit Crestodian -and run `openclaw plugins uninstall ` from a terminal. - -Approval is given in your own words: unambiguous replies ("yes", "sure", "go ahead", "not now") resolve from a closed deterministic list. When the configured route supports a separate completion call, other replies can be classified from only your message and the pending proposal — never by the conversation model itself, which cannot self-approve. Unclassified or ambiguous replies keep the proposal pending and the conversation asks again. - -Applied writes are recorded in `~/.openclaw/audit/crestodian.jsonl`. Discovery is not audited; only applied operations and writes are. - -Channel setup can run as a hosted conversation until it reaches a secret. The -local Crestodian TUI does not accept sensitive wizard answers because terminal -chat input is visible. It offers `open channel wizard` immediately, carrying -the selected channel into the masked terminal wizard; you can also run -`openclaw channels add --channel ` later. - -### Switching to masked channel setup - -The local chat can hand control to the masked channel wizard: - -```text -open channel wizard for slack -channel info slack -``` - -`open channel wizard for ` opens masked channel setup after the chat -TUI closes. Use `channel info ` first for the channel label, setup -state, prerequisites summary, and docs link. - -Crestodian never changes provider/auth access from inside its own session: the -session already depends on that inference route. For model-provider setup or -repair, `configure model provider` returns exit/onboarding guidance without -starting a wizard or writing config. Exit Crestodian and run `openclaw -onboard`; onboarding stages the credentials and saves only a route that -completes a real live turn. Start Crestodian again after onboarding succeeds. - -## Setup bootstrap - -`setup` configures the remaining workspace and Gateway state after guided onboarding has already established inference. It writes only through typed config operations and asks for approval first. - -```text -setup -setup workspace ~/Projects/work -``` - -`setup` preserves the verified effective model. It does not configure or -replace inference. - -If inference is missing or its live check fails, leave Crestodian and run `openclaw onboard`. Guided onboarding detects configured models, API keys, and authenticated local CLIs, asks each candidate for a real reply, and persists only a passing route. Crestodian starts immediately after that boundary and can then configure the workspace, Gateway, channels, agents, plugins, and other optional features. - -The macOS app skips this ladder entirely when it reaches a configured Gateway -whose default agent already has a configured model; it opens the normal agent -UI. -For a fresh or incomplete Gateway, the app drives the inference ladder through -the `crestodian.setup.detect` and `crestodian.setup.activate` Gateway methods: -detect lists every candidate backend it finds, activate live-tests one -candidate (a real "reply with OK" completion), and only persists the model, -credential, and provider/runtime state needed for that route after the test passes. Workspace and Gateway defaults remain for Crestodian. A failing candidate -never changes config; the app automatically walks down the ladder and finally -offers a manual key/token step populated from the Gateway's active -text-inference provider plugins. The selected provider owns its starter model -and config, and the credential is verified the same way before it is saved. - -Codex supervision and other optional plugin features stay outside this -inference activation transaction. Configure them only after inference is -working and Crestodian has started; existing plugin policy and explicit -supervision opt-outs remain untouched during inference setup. - -## AI conversation - -Interactive Crestodian's free-form conversation runs through the same agent loop as regular OpenClaw agents, restricted to one ring-zero OpenClaw authority tool, `crestodian`, that wraps the typed operations. Read actions run freely, mutations require your conversational approval for that exact operation (see Operations and approval), and every applied write is audited and re-validated. The agent session persists, so Crestodian has real multi-turn memory. If the verified inference route later stops working, return to `openclaw onboard` and repair it before continuing. - -The host does not parse natural-language requests into operations. Free-form -messages — including command-looking text and questions such as "why did my -gateway stop?" — go to the AI, which can map the request to a typed operation -through the `crestodian` tool. - -When a mutation is pending, only unambiguous approval or decline phrases from a -closed list are resolved without inference. Ambiguous consent goes to a -separate configured completion call and otherwise fails closed. Structured -wizard fields and exact host navigation are UI controls, not natural-language -operation parsing. One secret-hygiene exception is especially important: an -exact `config set` on a sensitive path (tokens, keys, passwords) never reaches -a model. The host creates a redacted proposal, and the value is masked in the -AI-visible history. Prefer `config set-ref env ` for secrets. - -Message-channel rescue mode never uses the model-assisted planner. Remote rescue stays deterministic so a broken or compromised normal agent path cannot be used as a config editor. - -### CLI harness trust model - -Embedded runtimes and the Codex app-server harness enforce the ring-zero -restriction directly: the run carries an OpenClaw tool allow-list with only -the `crestodian` tool. For Codex, OpenClaw also disables environments, native -execution, multi-agent, goal, app/plugin, skill/MCP, web-search, and -`request_user_input` surfaces for that run. Codex still injects its inert native `update_plan` -utility; it can update the model's temporary checklist but cannot write files -or OpenClaw configuration. CLI harnesses do not consume OpenClaw's allow-list, -so Crestodian admits only backends whose own tool-selection contract can prove -the same restriction: - -- Selectable backends, including Claude Code, launch with an empty native-tool - selection and one MCP tool, `crestodian`. Claude's generated MCP config is - applied with `--strict-mcp-config`, so no other MCP servers are loaded. -- Backends that declare no native tools receive the same dedicated Crestodian - MCP server. -- Always-on or unknown native-tool backends fail closed before inference; they - cannot host a Crestodian session. - -Only Crestodian sessions get the crestodian MCP server; normal agent runs -never see this tool. Selectable/no-native CLI backends and API-key models -therefore enforce the literal single-tool loop. Codex app-server models enforce -a single OpenClaw authority tool plus the inert native planning utility. In all -three cases, setup writes remain confined to Crestodian's audited approval -contract. - -Gemini CLI remains available for normal agents, but it cannot enforce the -tool-free probe required by the inference gate, so it cannot host Crestodian. - -## Switching to an agent - -Use a natural-language selector to leave Crestodian and open the normal TUI: - -```text -talk to agent -talk to work agent -switch to main agent -``` - -`openclaw tui`, `openclaw chat`, and `openclaw terminal` open the normal agent TUI directly; they do not start Crestodian. After switching into the normal TUI, `/crestodian` returns to Crestodian, optionally with a follow-up request: - -```text -/crestodian -/crestodian restart gateway -``` - -## Message rescue mode - -Message rescue mode is the message-channel entrypoint for Crestodian: use it when your normal agent is dead but a trusted channel (for example WhatsApp) still receives commands. - -This is a deterministic emergency command handler, not the conversational -Crestodian agent. It does not bootstrap a fresh setup or relax the inference -gate for Crestodian chat. - -Supported command: `/crestodian `. Rescue accepts the exact typed command grammar only — natural language is rejected with a hint, never guessed into an operation, and no model is ever consulted. - -```text -You, in a trusted owner DM: /crestodian status -OpenClaw: Crestodian rescue mode. Gateway reachable: no. Config valid: no. -You: /crestodian restart gateway -OpenClaw: Plan: restart the Gateway. Reply /crestodian yes to apply. -You: /crestodian yes -OpenClaw: Applied. Audit entry written. -``` - -Agent creation can also be queued locally or via rescue: - -```text -create agent work workspace ~/Projects/work model openai/gpt-5.6-sol -/crestodian create agent work workspace ~/Projects/work -``` - -Agent creation may name only the current live-verified default model. Omit the -model to inherit that route. - -Remote rescue is an admin surface and must be treated like remote config repair, not normal chat. - -Security contract for remote rescue: - -- Disabled when sandboxing is active for the agent/session; Crestodian refuses remote rescue and points to local CLI repair. -- Default effective state is `auto`: allow remote rescue only in trusted YOLO operation, where the runtime already has unsandboxed local authority (`tools.exec.security` resolves to `full` and `tools.exec.ask` resolves to `off`, with sandbox mode `off`). -- Requires an explicit owner identity; no wildcard sender rules, open group policy, unauthenticated webhooks, or anonymous channels. -- Owner DMs only by default; group/channel rescue needs explicit opt-in. -- Plugin search and list are read-only. Plugin install is always local-only (blocked in rescue, even when otherwise enabled) because it downloads executable code. Plugin uninstall is refused in both local Crestodian and rescue; run `openclaw plugins uninstall ` from a terminal. -- Remote rescue cannot open the local TUI or switch into an interactive agent session; use local `openclaw` for agent handoff. -- Persistent writes still require approval, even in rescue mode. -- Every applied rescue operation is audited. Message-channel rescue records channel, account, sender, and source-address metadata; config-mutating operations also record config hashes before and after. -- Secrets are never echoed. SecretRef inspection reports availability, not values. -- If the Gateway is alive, rescue prefers Gateway typed operations; if it is dead, rescue uses only the minimal local repair surface that does not depend on the normal agent loop. - -Config shape: - -```jsonc -{ - "crestodian": { - "rescue": { - "enabled": "auto", - "ownerDmOnly": true, - "pendingTtlMinutes": 15, - }, - }, -} -``` - -- `enabled`: `"auto"` (default) allows rescue only when the effective runtime is YOLO and sandboxing is off; `false` never allows message-channel rescue; `true` explicitly allows rescue when owner/channel checks pass (still subject to the sandboxing denial). -- `ownerDmOnly`: restrict rescue to owner direct messages. Default `true`. -- `pendingTtlMinutes`: how long a pending rescue write stays open for `/crestodian yes` approval before expiring. Default `15`. - -Remote rescue is covered by the Docker lane: - -```bash -pnpm test:docker:crestodian-rescue -``` - -An opt-in live channel command-surface smoke checks `/crestodian status` plus a persistent approval roundtrip through the rescue handler: - -```bash -pnpm test:live:crestodian-rescue-channel -``` - -Inference-gated packaged one-shot setup is covered by: - -```bash -pnpm test:docker:crestodian-first-run -``` - -That packaged-CLI lane starts with an empty state dir and proves Crestodian -fails closed without inference. It then tests and activates fake Claude through -the packaged activation module. Only afterward does a fuzzy request reach the -planner and resolve to typed setup, followed by one-shot commands that create an -additional agent, configure Discord through a plugin enablement plus token -SecretRef, validate config, and check the audit log. This lane is supporting -gate/operation evidence; it does not exercise interactive onboarding or the -Crestodian agent/tool/approval conversation. The QA Lab scenario below redirects -to the same Docker lane: - -```bash -pnpm openclaw qa suite --scenario crestodian-ring-zero-setup -``` - -## Related - -- [CLI reference](/cli) -- [Doctor](/cli/doctor) -- [TUI](/cli/tui) -- [Sandbox](/cli/sandbox) -- [Security](/cli/security) +This page moved to [OpenClaw setup and repair](/cli/openclaw). diff --git a/docs/cli/index.md b/docs/cli/index.md index d8f1d96a7bee..e6ee212424b2 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -12,29 +12,29 @@ the commands, global flags, and output styling rules that apply across the CLI. Setup commands by intent: -- `openclaw setup` and `openclaw onboard` verify inference first, then start Crestodian for Gateway, workspace, channels, skills, and health setup. +- `openclaw setup` and `openclaw onboard` verify inference first, then start OpenClaw for Gateway, workspace, channels, skills, and health setup. - `openclaw setup --baseline` creates the baseline config and workspace without walking the guided onboarding flow. - `openclaw configure` changes targeted parts of an existing setup: model auth, gateway, channels, plugins, or skills. - `openclaw channels add` configures channel accounts after the baseline exists; run without flags for guided setup, or with channel-specific flags for scripts. ## Command pages -| Area | Commands | -| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Setup and onboarding | [`crestodian`](/cli/crestodian) · [`setup`](/cli/setup) · [`onboard`](/cli/onboard) · [`configure`](/cli/configure) · [`config`](/cli/config) · [`completion`](/cli/completion) · [`doctor`](/cli/doctor) · [`dashboard`](/cli/dashboard) | -| Reset, backup, and migration | [`backup`](/cli/backup) · [`migrate`](/cli/migrate) · [`reset`](/cli/reset) · [`uninstall`](/cli/uninstall) · [`update`](/cli/update) | -| Messaging and agents | [`message`](/cli/message) · [`agent`](/cli/agent) · [`agents`](/cli/agents) · [`attach`](/cli/attach) · [`acp`](/cli/acp) · [`mcp`](/cli/mcp) | -| Health and sessions | [`status`](/cli/status) · [`health`](/cli/health) · [`sessions`](/cli/sessions) · [`audit`](/cli/audit) | -| Gateway and logs | [`gateway`](/cli/gateway) · [`logs`](/cli/logs) · [`system`](/cli/system) | -| Models and inference | [`models`](/cli/models) · [`promos`](/cli/promos) · [`infer`](/cli/infer) · `capability` (alias for [`infer`](/cli/infer)) · [`memory`](/cli/memory) · [`commitments`](/cli/commitments) · [`wiki`](/cli/wiki) | -| Network and nodes | [`directory`](/cli/directory) · [`nodes`](/cli/nodes) · [`devices`](/cli/devices) · [`node`](/cli/node) · [`worker`](/cli/worker) | -| Runtime and sandbox | [`approvals`](/cli/approvals) · `exec-policy` (see [`approvals`](/cli/approvals)) · [`sandbox`](/cli/sandbox) · [`tui`](/cli/tui) · `chat`/`terminal` (aliases for [`tui --local`](/cli/tui)) · [`browser`](/cli/browser) | -| Automation | [`cron`](/cli/cron) · [`tasks`](/cli/tasks) · [`hooks`](/cli/hooks) · [`webhooks`](/cli/webhooks) · [`transcripts`](/cli/transcripts) | -| Discovery and docs | [`dns`](/cli/dns) · [`docs`](/cli/docs) | -| Pairing and channels | [`pairing`](/cli/pairing) · [`qr`](/cli/qr) · [`channels`](/cli/channels) | -| Security and plugins | [`security`](/cli/security) · [`secrets`](/cli/secrets) · [`skills`](/cli/skills) · [`plugins`](/cli/plugins) · [`proxy`](/cli/proxy) | -| Legacy aliases | [`daemon`](/cli/daemon) (gateway service) · [`clawbot`](/cli/clawbot) (namespace) | -| Plugins (optional) | [`path`](/cli/path) · [`policy`](/cli/policy) · [`voicecall`](/cli/voicecall) · [`workboard`](/cli/workboard) (if installed) | +| Area | Commands | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup and onboarding | [`openclaw`](/cli/openclaw) · [`setup`](/cli/setup) · [`onboard`](/cli/onboard) · [`configure`](/cli/configure) · [`config`](/cli/config) · [`completion`](/cli/completion) · [`doctor`](/cli/doctor) · [`dashboard`](/cli/dashboard) | +| Reset, backup, and migration | [`backup`](/cli/backup) · [`migrate`](/cli/migrate) · [`reset`](/cli/reset) · [`uninstall`](/cli/uninstall) · [`update`](/cli/update) | +| Messaging and agents | [`message`](/cli/message) · [`agent`](/cli/agent) · [`agents`](/cli/agents) · [`attach`](/cli/attach) · [`acp`](/cli/acp) · [`mcp`](/cli/mcp) | +| Health and sessions | [`status`](/cli/status) · [`health`](/cli/health) · [`sessions`](/cli/sessions) · [`audit`](/cli/audit) | +| Gateway and logs | [`gateway`](/cli/gateway) · [`logs`](/cli/logs) · [`system`](/cli/system) | +| Models and inference | [`models`](/cli/models) · [`promos`](/cli/promos) · [`infer`](/cli/infer) · `capability` (alias for [`infer`](/cli/infer)) · [`memory`](/cli/memory) · [`commitments`](/cli/commitments) · [`wiki`](/cli/wiki) | +| Network and nodes | [`directory`](/cli/directory) · [`nodes`](/cli/nodes) · [`devices`](/cli/devices) · [`node`](/cli/node) · [`worker`](/cli/worker) | +| Runtime and sandbox | [`approvals`](/cli/approvals) · `exec-policy` (see [`approvals`](/cli/approvals)) · [`sandbox`](/cli/sandbox) · [`tui`](/cli/tui) · `chat`/`terminal` (aliases for [`tui --local`](/cli/tui)) · [`browser`](/cli/browser) | +| Automation | [`cron`](/cli/cron) · [`tasks`](/cli/tasks) · [`hooks`](/cli/hooks) · [`webhooks`](/cli/webhooks) · [`transcripts`](/cli/transcripts) | +| Discovery and docs | [`dns`](/cli/dns) · [`docs`](/cli/docs) | +| Pairing and channels | [`pairing`](/cli/pairing) · [`qr`](/cli/qr) · [`channels`](/cli/channels) | +| Security and plugins | [`security`](/cli/security) · [`secrets`](/cli/secrets) · [`skills`](/cli/skills) · [`plugins`](/cli/plugins) · [`proxy`](/cli/proxy) | +| Legacy aliases | [`daemon`](/cli/daemon) (gateway service) · [`clawbot`](/cli/clawbot) (namespace) | +| Plugins (optional) | [`path`](/cli/path) · [`policy`](/cli/policy) · [`voicecall`](/cli/voicecall) · [`workboard`](/cli/workboard) (if installed) | ## Global flags @@ -83,7 +83,7 @@ independently; run ` --help` for the authoritative, current list. ``` openclaw [--dev] [--profile ] - crestodian + openclaw setup onboard configure diff --git a/docs/cli/onboard.md b/docs/cli/onboard.md index a28ba0a35fe2..55b0ba9e3d2b 100644 --- a/docs/cli/onboard.md +++ b/docs/cli/onboard.md @@ -1,7 +1,7 @@ --- summary: "CLI reference for `openclaw onboard` (interactive onboarding)" read_when: - - You want to establish inference, then finish setup with Crestodian + - You want to establish inference, then finish setup with OpenClaw title: "Onboard" --- @@ -9,8 +9,10 @@ title: "Onboard" Guided setup that establishes inference first: it detects existing AI access, requires a live completion, persists only the working route, and then starts -Crestodian to configure the rest. `openclaw setup` is the same entry point; -`openclaw setup --baseline` only writes the baseline config/workspace. +OpenClaw to configure the rest. `openclaw setup` reaches this flow on fresh +systems or whenever an onboarding option is present; configured systems use +bare `openclaw setup` for system-agent chat. `openclaw setup --baseline` only +writes the baseline config/workspace. @@ -51,8 +53,8 @@ openclaw onboard --mode remote --remote-url wss://gateway-host:18789 - `--flow manual` (alias `advanced`): opens the classic wizard with full prompts for port, bind, and auth. - `--flow import`: runs a detected migration provider (for example Hermes via `--import-from hermes`), previews the plan, then applies after confirmation. Import only runs against a fresh OpenClaw setup - reset config, credentials, sessions, and workspace state first if any exist. Use [`openclaw migrate`](/cli/migrate) for dry-run plans, overwrite mode, reports, and exact mappings. -- `--modern` is a compatibility alias for the Crestodian conversational setup - assistant. It uses the same live-inference gate as `openclaw crestodian` and +- `--modern` is a compatibility alias for the OpenClaw conversational setup + assistant. It uses the same live-inference gate as `openclaw setup` and accepts only `--workspace`, `--accept-risk`, `--non-interactive`, and `--json`. Other setup flags are rejected instead of being silently ignored. @@ -72,21 +74,21 @@ then appear in a second menu. Supported browser or device sign-in and masked API-key or token methods use the same live completion path. OpenClaw persists only the verified model route and its credential after the test succeeds; a failed candidate does not replace the configured model or save the attempted -credential. Choose **Skip for now** to exit without starting Crestodian and +credential. Choose **Skip for now** to exit without starting OpenClaw and rerun `openclaw onboard` when you are ready. Workspace and Gateway setup remain -unchanged until Crestodian starts. +unchanged until OpenClaw starts. -In guided mode, `--workspace ` supplies Crestodian's proposed workspace +In guided mode, `--workspace ` supplies OpenClaw's proposed workspace and the isolated inference context. It is not persisted until you approve the -Crestodian setup proposal. Classic and noninteractive onboarding persist their +OpenClaw setup proposal. Classic and noninteractive onboarding persist their workspace through their normal setup flow. -After inference passes, guided onboarding immediately starts Crestodian with -the verified model. Crestodian can then configure the workspace, Gateway, -channels, agents, plugins, and other optional features. Inside Crestodian, use +After inference passes, guided onboarding immediately starts OpenClaw with +the verified model. OpenClaw can then configure the workspace, Gateway, +channels, agents, plugins, and other optional features. Inside OpenClaw, use `open channel wizard for ` to hand channel credential collection to a masked terminal wizard. To change the model provider or its authentication, -exit Crestodian and run `openclaw onboard`; Crestodian does not open the guided +exit OpenClaw and run `openclaw onboard`; OpenClaw does not open the guided or classic provider flows. On a configured install, running `openclaw onboard` again verifies the current @@ -97,10 +99,10 @@ workspace, so a model provided by a workspace plugin can fail here while still working in the agent. Use `openclaw onboard --classic` for provider-specific auth, channels, skills, remote Gateway setup, imports, or full Gateway controls. For conversational -non-inference setup and repair, run `openclaw crestodian`; `openclaw onboard +non-inference setup and repair, run `openclaw setup`; `openclaw onboard --modern` is a compatibility alias through the same inference gate. The classic wizard can optionally verify the default model with a live completion, but -Crestodian will not start until its own live inference check passes. +OpenClaw will not start until its own live inference check passes. In an interactive terminal, bare `openclaw` (no subcommand) routes by config state: @@ -108,12 +110,12 @@ state: - If the active config file is missing or has no authored settings (empty or metadata-only), it starts guided onboarding. - If the config file exists but fails validation, it starts the classic - onboarding path with `openclaw doctor` guidance. Crestodian needs working + onboarding path with `openclaw doctor` guidance. OpenClaw needs working inference and is not used to repair this pre-inference state. - If the config file is valid, it opens the normal agent TUI. A reachable configured Gateway with an agent and model goes directly to that UI without - onboarding or Crestodian. On a configured install, reach Crestodian with - `/crestodian` inside the TUI or `openclaw crestodian`. + onboarding or OpenClaw. On a configured install, reach OpenClaw with + `/openclaw` inside the TUI or `openclaw setup`. Plaintext `ws://` is accepted for loopback, private IP literals, `.local`, and Tailnet `*.ts.net` gateway URLs. For other trusted private-DNS names, set `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1` in the onboarding process environment. @@ -274,7 +276,7 @@ Output: `--suppress-gateway-token-output` suppresses token-bearing Gateway/UI ou `--json` does not imply non-interactive mode in guided or classic onboarding. -With `--modern`, JSON is a one-shot Crestodian overview and exits after that +With `--modern`, JSON is a one-shot OpenClaw overview and exits after that single result. Use `--non-interactive` for other scripts. diff --git a/docs/cli/openclaw.md b/docs/cli/openclaw.md new file mode 100644 index 000000000000..6bc2cfed8e31 --- /dev/null +++ b/docs/cli/openclaw.md @@ -0,0 +1,353 @@ +--- +summary: "CLI reference and security model for the inference-backed OpenClaw setup and repair helper" +read_when: + - You finished inference setup and want OpenClaw to configure the rest + - You need to inspect or repair OpenClaw with the local setup agent + - You are designing or enabling message-channel rescue mode +title: "OpenClaw setup agent" +--- + +# `openclaw setup` + +OpenClaw ships with a built-in system agent — it speaks as "OpenClaw" — for +local setup, repair, and configuration (formerly called Crestodian). It starts only after the effective default model completes a real turn. +Fresh installs establish inference first; malformed config stays on the +classic doctor path. + +## When it starts + +Running `openclaw` with no subcommand routes based on config state: + +- Config missing, or exists with no authored settings (empty, or only `$schema`/`meta` keys): starts guided onboarding with live AI verification. +- Config exists but fails validation: starts classic onboarding, which reports the issues and directs you to `openclaw doctor`. +- Config exists and is valid: opens the normal agent TUI. A reachable + configured Gateway whose default agent has a model goes directly to that UI + without onboarding or OpenClaw. Use `/openclaw` inside the TUI, or run + `openclaw setup` directly, to reach OpenClaw later. + +Running `openclaw setup` first live-tests the configured default model. A passing turn starts OpenClaw. An interactive failure opens guided inference setup and hands off to OpenClaw after a candidate passes. One-shot, JSON, and other noninteractive requests fail with instructions to run `openclaw onboard` when inference is unavailable. `openclaw --help` and `openclaw --version` keep their normal fast paths. + +Noninteractive bare `openclaw` (no TTY) exits with a short message instead of printing root help: it points to non-interactive onboarding on a fresh or invalid install, or to `openclaw agent --local ...` when config is valid. + +`openclaw onboard --modern` remains a compatibility alias for OpenClaw, but uses the same inference gate: working inference opens the chat, interactive failures start guided inference setup, and noninteractive failures exit with onboarding guidance. `openclaw onboard --classic` opens the full step-by-step wizard. + +## What OpenClaw shows + +Interactive OpenClaw opens the same TUI shell as `openclaw tui`, with an OpenClaw chat backend. The startup greeting covers: + +- config validity and the default agent +- the verified model OpenClaw is using +- Gateway reachability from the first startup probe +- the next recommended debug action + +It does not dump secrets or load plugin CLI commands just to start. + +Use `status` for the detailed inventory: config path, docs/source paths, local CLI probes, key/token presence, agents, model, and Gateway details. + +OpenClaw uses the same reference discovery as regular agents: in a Git checkout it points at local `docs/` and the source tree; in an npm install it uses bundled docs and links to [https://github.com/openclaw/openclaw](https://github.com/openclaw/openclaw), with guidance to check source when docs are not enough. + +## Examples + +```bash +openclaw +openclaw setup +openclaw setup --json +openclaw setup --message "models" +openclaw setup --message "validate config" +openclaw setup --message "setup workspace ~/Projects/work" --yes +openclaw setup --message "set default model openai/gpt-5.6" --yes +openclaw onboard --modern +``` + +Inside the OpenClaw TUI: + +```text +status +health +doctor +validate config +setup +setup workspace ~/Projects/work +config set gateway.port 19001 +config set-ref gateway.auth.token env OPENCLAW_GATEWAY_TOKEN +gateway status +restart gateway +agents +create agent work workspace ~/Projects/work +models +configure model provider +set default model openai/gpt-5.6 +channels +channel info slack +connect slack +open channel wizard for slack +plugins list +plugins search slack +plugin install clawhub:openclaw-codex-app-server +talk to work agent +talk to agent for ~/Projects/work +audit +quit +``` + +## Operations and approval + +OpenClaw uses typed operations instead of editing config ad hoc. + +Read-only operations run immediately: show overview, list agents, list installed plugins, search ClawHub plugins, show model/backend status, run status/health checks, check Gateway reachability, run doctor without interactive fixes, validate config, show the audit-log path. + +Starting guided channel setup (`connect telegram`) also runs immediately. Its wizard collects explicit answers and owns the resulting writes. + +Persistent operations require conversational approval (or `--yes` for a direct command): write config, `config set`, `config set-ref`, setup/onboarding bootstrap, change the default model, start/stop/restart the Gateway, create agents, and install plugins. + +Doctor repairs are unavailable inside OpenClaw because they can rewrite the provider, authentication, or default-agent inference route powering the session. Exit OpenClaw and run `openclaw doctor --fix` in a terminal. Read-only `doctor` remains available inside OpenClaw. + +New agents inherit the live-verified default inference route. The agent ids `openclaw` and `crestodian` are reserved for the system agent and cannot be created as normal agents. The retired id remains blocked so an old config cannot claim it. + +`config set` and `config set-ref` cannot change inference-route state, +including inference-provider credentials, top-level `auth.*`, model catalogs, +CLI backends, default/per-agent model routes, agent params/tools, or root +`tools.*`. Raw writes under `env.*`, `secrets.*`, `plugins.*`, and `$include` +are also refused because they can replace credential resolution or provider +activation. Gateway and channel auth remain normal config surfaces. Use typed plugin/channel workflows and +`set default model ` for an already +configured route; it live-tests the route before saving it. To configure or +repair provider/auth access, exit OpenClaw and run `openclaw onboard`. + +Plugin uninstall is refused inside OpenClaw because removing a provider +plugin could disable the inference route powering the session. Exit OpenClaw +and run `openclaw plugins uninstall ` from a terminal. + +Approval is given in your own words: unambiguous replies ("yes", "sure", "go ahead", "not now") resolve from a closed deterministic list. When the configured route supports a separate completion call, other replies can be classified from only your message and the pending proposal — never by the conversation model itself, which cannot self-approve. Unclassified or ambiguous replies keep the proposal pending and the conversation asks again. + +Applied writes are recorded in `~/.openclaw/audit/system-agent.jsonl`. Discovery is not audited; only applied operations and writes are. + +Channel setup can run as a hosted conversation until it reaches a secret. The +local OpenClaw TUI does not accept sensitive wizard answers because terminal +chat input is visible. It offers `open channel wizard` immediately, carrying +the selected channel into the masked terminal wizard; you can also run +`openclaw channels add --channel ` later. + +### Switching to masked channel setup + +The local chat can hand control to the masked channel wizard: + +```text +open channel wizard for slack +channel info slack +``` + +`open channel wizard for ` opens masked channel setup after the chat +TUI closes. Use `channel info ` first for the channel label, setup +state, prerequisites summary, and docs link. + +OpenClaw never changes provider/auth access from inside its own session: the +session already depends on that inference route. For model-provider setup or +repair, `configure model provider` returns exit/onboarding guidance without +starting a wizard or writing config. Exit OpenClaw and run `openclaw +onboard`; onboarding stages the credentials and saves only a route that +completes a real live turn. Start OpenClaw again after onboarding succeeds. + +## Setup bootstrap + +`setup` configures the remaining workspace and Gateway state after guided onboarding has already established inference. It writes only through typed config operations and asks for approval first. + +```text +setup +setup workspace ~/Projects/work +``` + +`setup` preserves the verified effective model. It does not configure or +replace inference. + +If inference is missing or its live check fails, leave OpenClaw and run `openclaw onboard`. Guided onboarding detects configured models, API keys, and authenticated local CLIs, asks each candidate for a real reply, and persists only a passing route. OpenClaw starts immediately after that boundary and can then configure the workspace, Gateway, channels, agents, plugins, and other optional features. + +The macOS app skips this ladder entirely when it reaches a configured Gateway +whose default agent already has a configured model; it opens the normal agent +UI. +For a fresh or incomplete Gateway, the app drives the inference ladder through +the `openclaw.setup.detect` and `openclaw.setup.activate` Gateway methods: +detect lists every candidate backend it finds, activate live-tests one +candidate (a real "reply with OK" completion), and only persists the model, +credential, and provider/runtime state needed for that route after the test passes. Workspace and Gateway defaults remain for OpenClaw. A failing candidate +never changes config; the app automatically walks down the ladder and finally +offers a manual key/token step populated from the Gateway's active +text-inference provider plugins. The selected provider owns its starter model +and config, and the credential is verified the same way before it is saved. + +Codex supervision and other optional plugin features stay outside this +inference activation transaction. Configure them only after inference is +working and OpenClaw has started; existing plugin policy and explicit +supervision opt-outs remain untouched during inference setup. + +## AI conversation + +Interactive OpenClaw's free-form conversation runs through the same agent loop as regular OpenClaw agents, restricted to one ring-zero OpenClaw authority tool, `openclaw`, that wraps the typed operations. Read actions run freely, mutations require your conversational approval for that exact operation (see Operations and approval), and every applied write is audited and re-validated. The agent session persists, so OpenClaw has real multi-turn memory. If the verified inference route later stops working, return to `openclaw onboard` and repair it before continuing. + +The host does not parse natural-language requests into operations. Free-form +messages — including command-looking text and questions such as "why did my +gateway stop?" — go to the AI, which can map the request to a typed operation +through the `openclaw` tool. + +When a mutation is pending, only unambiguous approval or decline phrases from a +closed list are resolved without inference. Ambiguous consent goes to a +separate configured completion call and otherwise fails closed. Structured +wizard fields and exact host navigation are UI controls, not natural-language +operation parsing. One secret-hygiene exception is especially important: an +exact `config set` on a sensitive path (tokens, keys, passwords) never reaches +a model. The host creates a redacted proposal, and the value is masked in the +AI-visible history. Prefer `config set-ref env ` for secrets. + +Message-channel rescue mode never uses the model-assisted planner. Remote rescue stays deterministic so a broken or compromised normal agent path cannot be used as a config editor. + +### CLI harness trust model + +Embedded runtimes and the Codex app-server harness enforce the ring-zero +restriction directly: the run carries an OpenClaw tool allow-list with only +the `openclaw` tool. For Codex, OpenClaw also disables environments, native +execution, multi-agent, goal, app/plugin, skill/MCP, web-search, and +`request_user_input` surfaces for that run. Codex still injects its inert native `update_plan` +utility; it can update the model's temporary checklist but cannot write files +or OpenClaw configuration. CLI harnesses do not consume OpenClaw's allow-list, +so OpenClaw admits only backends whose own tool-selection contract can prove +the same restriction: + +- Selectable backends, including Claude Code, launch with an empty native-tool + selection and one MCP tool, `openclaw`. Claude's generated MCP config is + applied with `--strict-mcp-config`, so no other MCP servers are loaded. +- Backends that declare no native tools receive the same dedicated OpenClaw + MCP server. +- Always-on or unknown native-tool backends fail closed before inference; they + cannot host an OpenClaw session. + +Only OpenClaw sessions get the openclaw MCP server; normal agent runs +never see this tool. Selectable/no-native CLI backends and API-key models +therefore enforce the literal single-tool loop. Codex app-server models enforce +a single OpenClaw authority tool plus the inert native planning utility. In all +three cases, setup writes remain confined to OpenClaw's audited approval +contract. + +Gemini CLI remains available for normal agents, but it cannot enforce the +tool-free probe required by the inference gate, so it cannot host OpenClaw. + +## Switching to an agent + +Use a natural-language selector to leave OpenClaw and open the normal TUI: + +```text +talk to agent +talk to work agent +switch to main agent +``` + +`openclaw tui`, `openclaw chat`, and `openclaw terminal` open the normal agent TUI directly; they do not start OpenClaw. After switching into the normal TUI, `/openclaw` returns to OpenClaw, optionally with a follow-up request: + +```text +/openclaw +/openclaw restart gateway +``` + +## Message rescue mode + +Message rescue mode is the message-channel entrypoint for OpenClaw: use it when your normal agent is dead but a trusted channel (for example WhatsApp) still receives commands. + +This is a deterministic emergency command handler, not the conversational +OpenClaw agent. It does not bootstrap a fresh setup or relax the inference +gate for OpenClaw chat. + +Supported command: `/openclaw `. Rescue accepts the exact typed command grammar only — natural language is rejected with a hint, never guessed into an operation, and no model is ever consulted. + +```text +You, in a trusted owner DM: /openclaw status +OpenClaw: OpenClaw rescue mode. Gateway reachable: no. Config valid: no. +You: /openclaw restart gateway +OpenClaw: Plan: restart the Gateway. Reply /openclaw yes to apply. +You: /openclaw yes +OpenClaw: Applied. Audit entry written. +``` + +Agent creation can also be queued locally or via rescue: + +```text +create agent work workspace ~/Projects/work model openai/gpt-5.6-sol +/openclaw create agent work workspace ~/Projects/work +``` + +Agent creation may name only the current live-verified default model. Omit the +model to inherit that route. + +Remote rescue is an admin surface and must be treated like remote config repair, not normal chat. + +Security contract for remote rescue: + +- Disabled when sandboxing is active for the agent/session; OpenClaw refuses remote rescue and points to local CLI repair. +- Default effective state is `auto`: allow remote rescue only in trusted YOLO operation, where the runtime already has unsandboxed local authority (`tools.exec.security` resolves to `full` and `tools.exec.ask` resolves to `off`, with sandbox mode `off`). +- Requires an explicit owner identity; no wildcard sender rules, open group policy, unauthenticated webhooks, or anonymous channels. +- Owner DMs only by default; group/channel rescue needs explicit opt-in. +- Plugin search and list are read-only. Plugin install is always local-only (blocked in rescue, even when otherwise enabled) because it downloads executable code. Plugin uninstall is refused in both local OpenClaw and rescue; run `openclaw plugins uninstall ` from a terminal. +- Remote rescue cannot open the local TUI or switch into an interactive agent session; use local `openclaw` for agent handoff. +- Persistent writes still require approval, even in rescue mode. +- Every applied rescue operation is audited. Message-channel rescue records channel, account, sender, and source-address metadata; config-mutating operations also record config hashes before and after. +- Secrets are never echoed. SecretRef inspection reports availability, not values. +- If the Gateway is alive, rescue prefers Gateway typed operations; if it is dead, rescue uses only the minimal local repair surface that does not depend on the normal agent loop. + +Config shape: + +```jsonc +{ + "systemAgent": { + "rescue": { + "enabled": "auto", + "ownerDmOnly": true, + "pendingTtlMinutes": 15, + }, + }, +} +``` + +- `enabled`: `"auto"` (default) allows rescue only when the effective runtime is YOLO and sandboxing is off; `false` never allows message-channel rescue; `true` explicitly allows rescue when owner/channel checks pass (still subject to the sandboxing denial). +- `ownerDmOnly`: restrict rescue to owner direct messages. Default `true`. +- `pendingTtlMinutes`: how long a pending rescue write stays open for `/openclaw yes` approval before expiring. Default `15`. + +`openclaw doctor --fix` migrates the legacy `crestodian` config block to +`systemAgent`. Runtime reads only the canonical block. + +Remote rescue is covered by the Docker lane: + +```bash +pnpm test:docker:system-agent-rescue +``` + +An opt-in live channel command-surface smoke checks `/openclaw status` plus a persistent approval roundtrip through the rescue handler: + +```bash +pnpm test:live:system-agent-rescue-channel +``` + +Inference-gated packaged one-shot setup is covered by: + +```bash +pnpm test:docker:system-agent-first-run +``` + +That packaged-CLI lane starts with an empty state dir and proves OpenClaw +fails closed without inference. It then tests and activates fake Claude through +the packaged activation module. Only afterward does a fuzzy request reach the +planner and resolve to typed setup, followed by one-shot commands that create an +additional agent, configure Discord through a plugin enablement plus token +SecretRef, validate config, and check the audit log. This lane is supporting +gate/operation evidence; it does not exercise interactive onboarding or the +OpenClaw agent/tool/approval conversation. The QA Lab scenario below redirects +to the same Docker lane: + +```bash +pnpm openclaw qa suite --scenario system-agent-ring-zero-setup +``` + +## Related + +- [CLI reference](/cli) +- [Doctor](/cli/doctor) +- [TUI](/cli/tui) +- [Sandbox](/cli/sandbox) +- [Security](/cli/security) diff --git a/docs/cli/setup.md b/docs/cli/setup.md index 5020b96b6d51..0f34722aca37 100644 --- a/docs/cli/setup.md +++ b/docs/cli/setup.md @@ -1,7 +1,8 @@ --- -summary: "CLI reference for `openclaw setup` (alias for onboarding, with baseline setup available by flag)" +summary: "CLI reference for `openclaw setup` (system-agent chat with onboarding fallback)" read_when: - - You're doing first-run setup with the CLI onboarding wizard + - You want to chat with OpenClaw for setup or repair + - You're doing first-run setup with the onboarding wizard - You want to set the default workspace path - You need the baseline-only setup flag for scripts title: "Setup" @@ -9,12 +10,23 @@ title: "Setup" # `openclaw setup` -`openclaw setup` runs the same guided onboarding flow as `openclaw onboard`: -it verifies and persists inference first, then starts Crestodian to configure -the workspace, Gateway, channels, skills, and health. Use `--baseline` when you -only need to initialize config/workspace folders without the wizard. +`openclaw setup` is the system-agent entry point. On a configured system, bare +`openclaw setup` opens an interactive OpenClaw chat. On a fresh system, it +falls through to guided onboarding. Use `-m`/`--message` for one request or +`--baseline` to initialize config/workspace folders without the wizard. -In guided mode, `--workspace ` is the workspace proposed to Crestodian; +Routing order: + +1. Any onboarding option (`--wizard`, `--baseline`, workspace, reset, + non-interactive, flow, mode, Gateway, daemon, skip, import, remote, or auth + options) runs onboarding exactly as `openclaw onboard` does. +2. `-m`/`--message` or `--yes` runs the system agent. +3. With no routing option, a configured interactive system opens OpenClaw. A + fresh system runs onboarding. On a configured system, `--json` prints the + system overview even without a TTY; an onboarding option keeps onboarding's + JSON summary. + +In guided mode, `--workspace ` is the workspace proposed to OpenClaw; it is persisted only after you approve that proposal. Baseline, classic, and noninteractive setup persist the supplied workspace through their normal flow. @@ -26,8 +38,8 @@ Tailscale (`--tailscale`), reset (`--reset`, `--reset-scope`), flow (`--skip-channels`, `--skip-skills`, `--skip-bootstrap`, `--skip-search`, `--skip-health`, `--skip-ui`, `--skip-hooks`). See [Onboard](/cli/onboard) and [CLI automation](/start/wizard-cli-automation) for the full flag reference and -non-interactive examples. `openclaw onboard --modern` is the compatibility -alias for the inference-gated Crestodian assistant and has no `setup` equivalent. +non-interactive examples. `openclaw onboard --modern` remains a compatibility +entry for the same inference-gated OpenClaw assistant. `openclaw setup` is for mutable config installs. In Nix mode (`OPENCLAW_NIX_MODE=1`) OpenClaw refuses setup writes because the config file is managed by Nix. Use the first-party [nix-openclaw Quick Start](https://github.com/openclaw/nix-openclaw#quick-start) or the equivalent source config for another Nix package. @@ -37,9 +49,11 @@ alias for the inference-gated Crestodian assistant and has no `setup` equivalent | Flag | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------- | +| `-m, --message ` | Run one OpenClaw request. | +| `--yes` | Approve persistent config writes for one `--message` request. | | `--workspace ` | Workspace proposal in guided mode; persisted directly by baseline, classic, and noninteractive setup. | | `--baseline` | Create baseline config/workspace/session folders without onboarding. | -| `--wizard` | Accepted for compatibility; setup runs onboarding by default. | +| `--wizard` | Force interactive onboarding. | | `--non-interactive` | Run onboarding without prompts. | | `--accept-risk` | Acknowledge full-system agent access risk; required with `--non-interactive`. | | `--mode ` | Onboarding mode: `local` or `remote`. | @@ -51,7 +65,7 @@ alias for the inference-gated Crestodian assistant and has no `setup` equivalent | `--import-secrets` | Import supported secrets during onboarding migration. | | `--remote-url ` | Remote Gateway WebSocket URL. | | `--remote-token ` | Remote Gateway token (optional). | -| `--json` | Output a JSON summary. | +| `--json` | Configured system: OpenClaw overview. Onboarding route: onboarding summary. | `--classic` and `--non-interactive` are mutually exclusive: classic opens the prompted wizard, while noninteractive setup uses the automation path. @@ -66,6 +80,10 @@ running onboarding. ```bash openclaw setup +openclaw setup -m "status" +openclaw setup -m "restart gateway" --yes +openclaw setup --json +openclaw setup --wizard openclaw setup --baseline openclaw setup --workspace ~/.openclaw/workspace openclaw setup --import-from hermes --import-source ~/.hermes @@ -74,7 +92,7 @@ openclaw setup --non-interactive --accept-risk --mode remote --remote-url wss:// ## Notes -- After baseline setup, run `openclaw setup` or `openclaw onboard` for the full guided journey, `openclaw configure` for targeted changes, or `openclaw channels add` to add channel accounts. +- After baseline setup, run `openclaw onboard` for the full guided journey, `openclaw configure` for targeted changes, or `openclaw channels add` to add channel accounts. - If Hermes state is detected, interactive onboarding can offer migration automatically. Import onboarding requires a fresh setup; use [Migrate](/cli/migrate) for dry-run plans, backups, and overwrite mode outside onboarding. ## Related diff --git a/docs/docs.json b/docs/docs.json index 5b96e2994be9..be227c88364d 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -52,6 +52,10 @@ ] }, "redirects": [ + { + "source": "/cli/crestodian", + "destination": "/cli/openclaw" + }, { "source": "/channels/bluebubbles", "destination": "/channels/imessage-from-bluebubbles" @@ -1751,7 +1755,7 @@ "group": "Gateway and service", "pages": [ "cli/backup", - "cli/crestodian", + "cli/openclaw", "cli/daemon", "cli/doctor", "cli/fleet", diff --git a/docs/docs_map.md b/docs/docs_map.md index 7cc44b9b5d8b..3ae8e66af9d2 100644 --- a/docs/docs_map.md +++ b/docs/docs_map.md @@ -1383,19 +1383,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`. ## cli/crestodian.md - Route: /cli/crestodian -- Headings: - - H1: openclaw crestodian - - H2: When it starts - - H2: What Crestodian shows - - H2: Examples - - H2: Operations and approval - - H3: Switching to masked channel setup - - H2: Setup bootstrap - - H2: AI conversation - - H3: CLI harness trust model - - H2: Switching to an agent - - H2: Message rescue mode - - H2: Related +- Headings: none ## cli/cron.md @@ -1794,6 +1782,23 @@ Do not edit it by hand; run `pnpm docs:map:gen`. - H2: Other behaviors - H2: Common follow-up commands +## cli/openclaw.md + +- Route: /cli/openclaw +- Headings: + - H1: openclaw setup + - H2: When it starts + - H2: What OpenClaw shows + - H2: Examples + - H2: Operations and approval + - H3: Switching to masked channel setup + - H2: Setup bootstrap + - H2: AI conversation + - H3: CLI harness trust model + - H2: Switching to an agent + - H2: Message rescue mode + - H2: Related + ## cli/pairing.md - Route: /cli/pairing @@ -10436,7 +10441,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`. - H2: Keyboard shortcuts - H2: Slash commands - H2: Local shell commands - - H2: Crestodian setup and repair helper + - H2: OpenClaw setup and repair helper - H2: Tool output - H2: Terminal colors - H2: History + streaming diff --git a/docs/help/testing.md b/docs/help/testing.md index f4553a986958..dd113142c608 100644 --- a/docs/help/testing.md +++ b/docs/help/testing.md @@ -131,21 +131,21 @@ When debugging real providers/models (requires real creds): through `npm-pack:`, verifies the dependency under the managed npm project root, then asks a live OpenAI model to call the plugin tool and return the hidden slug. -- Crestodian rescue command smoke: `pnpm test:live:crestodian-rescue-channel` +- OpenClaw rescue command smoke: `pnpm test:live:system-agent-rescue-channel` - Opt-in belt-and-suspenders check for the message-channel rescue command - surface. Exercises `/crestodian status`, queues a persistent model - change, replies `/crestodian yes`, and verifies the audit/config write + surface. Exercises `/openclaw status`, queues a persistent model + change, replies `/openclaw yes`, and verifies the audit/config write path. -- Crestodian first-run Docker smoke: `pnpm test:docker:crestodian-first-run` +- OpenClaw first-run Docker smoke: `pnpm test:docker:system-agent-first-run` - Starts from an empty OpenClaw state dir and first proves the packaged - `openclaw crestodian` CLI fails closed without inference. It then + `openclaw setup` CLI fails closed without inference. It then tests and activates fake Claude through the packaged activation module. Only afterward does a fuzzy packaged CLI request reach the planner and resolve to typed setup, followed by one-shot model, agent, Discord plugin, and SecretRef operations. It validates config and audit entries. This is supporting gate/operation evidence, not an interactive onboarding or - Crestodian agent/tool/approval proof. The same lane is exposed in QA Lab by - `pnpm openclaw qa suite --scenario crestodian-ring-zero-setup`. + OpenClaw agent/tool/approval proof. The same lane is exposed in QA Lab by + `pnpm openclaw qa suite --scenario system-agent-ring-zero-setup`. - Moonshot/Kimi cost smoke: with `MOONSHOT_API_KEY` set, run `openclaw models list --provider moonshot --json`, then run an isolated `openclaw agent --local --session-id live-kimi-cost --message 'Reply exactly: KIMI_LIVE_OK' --thinking off --json` diff --git a/docs/platforms/macos.md b/docs/platforms/macos.md index e1a3ecaeeb02..495f6152daf3 100644 --- a/docs/platforms/macos.md +++ b/docs/platforms/macos.md @@ -31,13 +31,13 @@ has no macOS app asset, use the newest one that does, or build from source with 2. Pick **This Mac** for a local Gateway, or connect to a remote Gateway. 3. Wait while the app installs the matching CLI runtime. In local mode it also installs and starts the Gateway. -4. Establish inference with a live model check. After it passes, Crestodian +4. Establish inference with a live model check. After it passes, OpenClaw handles the remaining setup. 5. Complete the macOS permission checklist and send the onboarding test message. If the app reaches an existing Gateway whose default agent has a configured model, it treats that Gateway as already set up, skips provider onboarding and -Crestodian, and opens the dashboard. If the Gateway cannot connect or its +OpenClaw, and opens the dashboard. If the Gateway cannot connect or its default agent has no model, inference onboarding remains available for recovery. diff --git a/docs/plugins/sdk-agent-harness.md b/docs/plugins/sdk-agent-harness.md index 6335a137cbd6..766a537a0f88 100644 --- a/docs/plugins/sdk-agent-harness.md +++ b/docs/plugins/sdk-agent-harness.md @@ -67,7 +67,7 @@ implementation that completed the probe. When matching `runtimeArtifact.validate(...)` capability that rechecks that binding without loading a different harness or scanning unrelated plugins. -Verified Crestodian continuations also pass `params.expectedRuntimeArtifact`. +Verified OpenClaw continuations also pass `params.expectedRuntimeArtifact`. The harness must compare it with the exact native process it acquired and fail before starting or resuming a native thread if they differ. Ordinary agent turns omit both fields, so content hashing stays out of the normal request hot diff --git a/docs/refactor/database-first.md b/docs/refactor/database-first.md index 58098a6c969b..ba92be85ffdd 100644 --- a/docs/refactor/database-first.md +++ b/docs/refactor/database-first.md @@ -894,7 +894,7 @@ sessionId}` and session key context. - Active-memory blocking subagent runs use SQLite transcript rows instead of creating temporary or persisted `session.jsonl` files under plugin state. The old `transcriptDir` option is removed. -- One-off slug generation and Crestodian planner runs use SQLite transcript rows +- One-off slug generation and system-agent planner runs use SQLite transcript rows instead of creating temporary `session.jsonl` files. - `llm-task` helper runs and hidden commitment extraction also use SQLite transcript rows, so these model-only helper sessions no longer create @@ -1030,7 +1030,7 @@ sessionId})`; create, branch, continue, list, and fork flows live in their - Memory Wiki no longer creates `.openclaw-wiki/state.json` or the unused `.openclaw-wiki/locks` directory. The migration provider removes those retired plugin metadata files if an older vault still has them. -- Crestodian audit entries now use core SQLite plugin state instead of +- System-agent audit entries now use core SQLite plugin state instead of `audit/crestodian.jsonl`. Doctor imports the legacy JSONL audit log and removes it after successful import. - Config write/observe audit entries now use core SQLite plugin state instead @@ -1040,7 +1040,7 @@ sessionId})`; create, branch, continue, list, and fork flows live in their `logs/config-health.json` sidecars while editing `openclaw.json`. The config file remains file-backed, recovery snapshots stay next to the config file, and durable config audit/health state belongs to the Gateway SQLite store. -- Crestodian rescue pending approvals now use core SQLite plugin state instead +- System-agent rescue pending approvals now use core SQLite plugin state instead of `crestodian/rescue-pending/*.json`. Doctor imports legacy pending approval files and removes them after successful import. - Phone Control temporary arm state now uses SQLite plugin state instead of diff --git a/docs/start/onboarding-overview.md b/docs/start/onboarding-overview.md index 707ba19dcb7c..a5be3adc4813 100644 --- a/docs/start/onboarding-overview.md +++ b/docs/start/onboarding-overview.md @@ -9,20 +9,20 @@ sidebarTitle: "Onboarding Overview" OpenClaw has terminal and macOS app onboarding. Both establish inference first: they detect existing AI access, require a live completion, and only then start -Crestodian to configure the remaining setup. A reachable, configured Gateway +OpenClaw to configure the remaining setup. A reachable, configured Gateway whose default agent already has a configured model skips onboarding and opens the normal agent UI. The terminal flow also offers the full classic wizard for detailed setup. ## Which path should I use? -| | CLI onboarding | macOS app onboarding | -| -------------- | -------------------------------------- | -------------------------------- | -| **Platforms** | macOS, Linux, Windows (native or WSL2) | macOS only | -| **Interface** | Inference setup, then Crestodian | Inference setup, then Crestodian | -| **Best for** | Servers, headless, full control | Desktop Mac, visual setup | -| **Automation** | `--non-interactive` for scripts | Manual only | -| **Command** | `openclaw onboard` | Launch the app | +| | CLI onboarding | macOS app onboarding | +| -------------- | -------------------------------------- | ------------------------------ | +| **Platforms** | macOS, Linux, Windows (native or WSL2) | macOS only | +| **Interface** | Inference setup, then OpenClaw | Inference setup, then OpenClaw | +| **Best for** | Servers, headless, full control | Desktop Mac, visual setup | +| **Automation** | `--non-interactive` for scripts | Manual only | +| **Command** | `openclaw onboard` | Launch the app | Most users should start with **CLI onboarding** — it works everywhere and gives you the most control. @@ -36,7 +36,7 @@ The guided inference phase establishes only: 2. **Verified inference** — a real completion on the default agent's effective model -After that completion passes, Crestodian can configure the workspace, Gateway, +After that completion passes, OpenClaw can configure the workspace, Gateway, Gateway service, channels, agents, plugins, and other optional features. The classic CLI wizard can additionally configure: @@ -59,20 +59,20 @@ and falls through on failure. If detection is exhausted, it shows OpenAI, Anthropic, xAI (Grok), Google, and OpenRouter first. **More…** contains the remaining providers in provider groups, with regions, plans, and supported browser, device, API-key, or token methods in a second menu. It saves the model -and credential only after a passing completion, then starts Crestodian to +and credential only after a passing completion, then starts OpenClaw to configure the workspace, Gateway, channels, agents, plugins, and other optional -features. **Skip for now** exits without starting Crestodian. There is no +features. **Skip for now** exits without starting OpenClaw. There is no in-flow classic handoff; exit and run `openclaw onboard --classic` when you want the classic wizard instead. -After inference passes, Crestodian can hand channel setup to a masked terminal -wizard. It does not open guided or classic provider setup; exit Crestodian and +After inference passes, OpenClaw can hand channel setup to a masked terminal +wizard. It does not open guided or classic provider setup; exit OpenClaw and run `openclaw onboard` to change the model provider or its authentication. Use `openclaw onboard --classic` for detailed model/auth, channel, skill, remote Gateway, or import setup. Adding `--install-daemon` also selects the classic flow and installs the background service in one step. Use `openclaw -crestodian` for conversational non-inference setup and repair. `openclaw +openclaw` for conversational non-inference setup and repair. `openclaw onboard --modern` is a compatibility alias that uses the same live-inference gate. @@ -83,13 +83,13 @@ CLI command docs: [`openclaw onboard`](/cli/onboard) Open the OpenClaw app. If its configured local or remote Gateway is reachable and the default agent already has a configured model, the app skips onboarding -and Crestodian and opens the normal agent UI immediately. +and OpenClaw and opens the normal agent UI immediately. For a fresh or incomplete Gateway, the first-run flow detects existing AI access (Claude Code, Codex, or API keys), live-tests the best option, and saves it only after a real reply — falling back automatically and offering a verified manual API-key step when nothing is found. Sensitive -credentials use masked input. Once inference passes, Crestodian starts and +credentials use masked input. Once inference passes, OpenClaw starts and helps configure the rest. Gemini CLI remains available for normal agents after setup, but it is not diff --git a/docs/start/onboarding.md b/docs/start/onboarding.md index 0fd91263b5b0..a13bac6fcca9 100644 --- a/docs/start/onboarding.md +++ b/docs/start/onboarding.md @@ -67,7 +67,7 @@ Where does the **Gateway** run? A connected Gateway that already has a configured agent model skips this - page entirely and opens the normal agent UI. Crestodian and provider setup + page entirely and opens the normal agent UI. OpenClaw and provider setup only run for a fresh or incomplete Gateway. Once the Gateway is ready, onboarding looks for AI access you already have: @@ -90,9 +90,9 @@ The manual key/token picker uses the same provider registry. In every route, the provider supplies its starter model and configuration; OpenClaw verifies the credential with the same live test before storing its auth profile. Next remains locked until one backend has passed, so the first agent chat cannot -start without working inference. After that live check passes, Crestodian becomes +start without working inference. After that live check passes, OpenClaw becomes available to help configure the remaining workspace, Gateway, channels, and -other optional features; it is also available later under Settings → Crestodian. +other optional features; it is also available later under Settings → OpenClaw. @@ -104,10 +104,10 @@ Onboarding requests TCC permissions for: Automation (AppleScript), Notifications - After inference passes, Crestodian owns the remaining optional setup and can + After inference passes, OpenClaw owns the remaining optional setup and can hand you off to the normal agent chat. Finishing the permission walkthrough opens that same chat; the app does not create a workspace or launch a separate - agent setup conversation before Crestodian. See + agent setup conversation before OpenClaw. See [Bootstrapping](/start/bootstrapping) for what happens on the gateway host during the agent's first real turn. diff --git a/docs/start/wizard.md b/docs/start/wizard.md index a9e68268249d..ce6972af3224 100644 --- a/docs/start/wizard.md +++ b/docs/start/wizard.md @@ -1,5 +1,5 @@ --- -summary: "CLI onboarding: verify inference, then hand remaining setup to Crestodian" +summary: "CLI onboarding: verify inference, then hand remaining setup to OpenClaw" read_when: - Running or configuring CLI onboarding - Setting up a new machine @@ -13,23 +13,23 @@ openclaw onboard CLI onboarding is the recommended terminal setup path on macOS, Linux, and Windows (native or WSL2). By default it detects AI access already available on -the machine, verifies it with a real completion, and starts Crestodian to +the machine, verifies it with a real completion, and starts OpenClaw to configure the workspace, Gateway, and optional features. `openclaw setup` runs the same flow ([Setup](/cli/setup) covers the `--baseline` config-only variant). Windows desktop users can also start from [Windows Hub](/platforms/windows). Guided onboarding establishes inference first. It detects available AI access, -requires a real completion, and only then starts [Crestodian](/cli/crestodian) +requires a real completion, and only then starts [OpenClaw](/cli/openclaw) to configure the rest of OpenClaw. Choosing **Skip for now** exits onboarding -without starting Crestodian. +without starting OpenClaw. The classic wizard remains available for custom providers, remote Gateway setup, channel pairing, daemon controls, skills, and imports. Run it explicitly with `openclaw onboard --classic`; the guided inference picker does not delegate -into it. After inference passes, Crestodian can use `open channel wizard for +into it. After inference passes, OpenClaw can use `open channel wizard for ` to hand channel setup that needs secrets to a masked terminal wizard. -To change the model provider or its authentication, exit Crestodian and run -`openclaw onboard`; Crestodian does not open guided or classic provider flows. +To change the model provider or its authentication, exit OpenClaw and run +`openclaw onboard`; OpenClaw does not open guided or classic provider flows. Fastest first chat: finish guided setup, run `openclaw dashboard`, and chat in @@ -81,10 +81,10 @@ Plain `openclaw onboard` follows this path: OpenRouter, or choose **More…** for the remaining providers. Each provider's regions, plans, and supported browser, device, API-key, or token methods appear in a second menu and are tested with the same real completion. - Choose **Skip for now** to exit without starting Crestodian. + Choose **Skip for now** to exit without starting OpenClaw. 5. Persist only the verified model route and any credential/plugin state it requires. Workspace and Gateway settings remain untouched. -6. Start Crestodian with the verified model so it can configure the workspace, +6. Start OpenClaw with the verified model so it can configure the workspace, Gateway, channels, agents, plugins, and the remaining optional setup. Re-running the command on a configured installation tests the current default @@ -146,7 +146,7 @@ Local mode (default) walks through these steps: `exec`), with a fast preflight check before saving. After model/auth setup, the wizard offers an optional live completion test; a failure can return to model/auth setup once or be ignored without blocking the rest of the - classic wizard. Ignoring it does not unlock Crestodian; conversational setup + classic wizard. Ignoring it does not unlock OpenClaw; conversational setup still requires a passing inference check. 2. **Workspace** - directory for agent files (default `~/.openclaw/workspace`). Seeds bootstrap files. 3. **Gateway** - port, bind address, auth mode, Tailscale exposure. In @@ -178,8 +178,8 @@ config is invalid or contains legacy keys, onboarding asks you to run `--flow import` runs a detected migration flow (for example Hermes) in the classic wizard instead of fresh setup; see [Migrate](/cli/migrate) and the migration guides under [Install](/install/migrating-hermes). `openclaw onboard --modern` is a -compatibility alias for [Crestodian](/cli/crestodian). It uses the same -inference gate as `openclaw crestodian`: verified inference starts the +compatibility alias for [OpenClaw](/cli/openclaw). It uses the same +inference gate as `openclaw setup`: verified inference starts the assistant, while an interactive failure returns to guided inference setup. ## Add another agent diff --git a/docs/tools/slash-commands.md b/docs/tools/slash-commands.md index e45aace59939..dc7e21fb386d 100644 --- a/docs/tools/slash-commands.md +++ b/docs/tools/slash-commands.md @@ -240,7 +240,7 @@ plugins. | `/status plugins` | Show detailed plugin health: load errors, quarantines, channel plugin failures, dependency issues, compatibility notices. Requires `commands.plugins: true` | | `/goal [status\|start\|edit\|pause\|resume\|complete\|block\|clear] ...` | Manage the current session's durable [goal](/tools/goal) | | `/diagnostics [note]` | Owner-only support-report flow. Asks for exec approval every time | - | `/crestodian ` | Run the Crestodian setup and repair helper from an owner DM | + | `/openclaw ` | Run the OpenClaw setup and repair helper from an owner DM | | `/tasks` | List active/recent background tasks for the current session | | `/context [list\|detail\|map\|json]` | Explain how context is assembled | | `/whoami` | Show your sender id. Alias: `/id` | diff --git a/docs/web/tui.md b/docs/web/tui.md index dd7b8b605bb8..30a78fc81f67 100644 --- a/docs/web/tui.md +++ b/docs/web/tui.md @@ -138,9 +138,9 @@ Local mode only: - `/auth [provider]` opens the provider auth/login flow inside the TUI. -Crestodian: +OpenClaw: -- `/crestodian [request]` returns from the normal agent TUI to the [Crestodian](#crestodian-setup-and-repair-helper) setup/repair chat, optionally forwarding one request. +- `/openclaw [request]` returns from the normal agent TUI to the [OpenClaw](#openclaw-setup-and-repair-helper) setup/repair chat, optionally forwarding one request. Other Gateway slash commands (for example, `/context`) are forwarded to the Gateway and shown as system output. See [Slash commands](/tools/slash-commands). @@ -152,19 +152,19 @@ Other Gateway slash commands (for example, `/context`) are forwarded to the Gate - Local shell commands receive `OPENCLAW_SHELL=tui-local` in their environment. - A lone `!` is sent as a normal message; leading spaces do not trigger local exec. -## Crestodian setup and repair helper +## OpenClaw setup and repair helper -Crestodian is the ring-zero setup/repair assistant, exposed as `openclaw crestodian` after the configured default model passes a live inference check. If inference is unavailable, an interactive invocation returns to inference onboarding and automation fails with repair guidance. It runs inside the same local TUI shell as `openclaw tui --local`, backed by an AI agent restricted to Crestodian's typed, approval-gated operations: +OpenClaw is the ring-zero setup/repair assistant, exposed as `openclaw setup` after the configured default model passes a live inference check. If inference is unavailable, an interactive invocation returns to inference onboarding and automation fails with repair guidance. It runs inside the same local TUI shell as `openclaw tui --local`, backed by an AI agent restricted to OpenClaw's typed, approval-gated operations: ```bash -openclaw crestodian # start interactively -openclaw crestodian -m "status" # run one request and exit -openclaw crestodian -m "set default model openai/gpt-5.2" --yes # apply a config write +openclaw setup # start interactively +openclaw setup -m "status" # run one request and exit +openclaw setup -m "set default model openai/gpt-5.2" --yes # apply a config write ``` - Persistent config writes need approval: either confirm interactively or pass `--yes`. - `--json` prints the startup overview as JSON instead of starting the chat. -- From inside Crestodian, an `open-tui` request (for example, asking to talk to a normal agent) exits Crestodian and opens the regular agent TUI; use `/crestodian` there to come back. +- From inside OpenClaw, an `open-tui` request (for example, asking to talk to a normal agent) exits OpenClaw and opens the regular agent TUI; use `/openclaw` there to come back. Use local mode when the current config already validates and you want the embedded agent to inspect it on the same machine, compare it against the docs, and help repair drift without depending on a running Gateway. diff --git a/extensions/anthropic/cli-shared.test.ts b/extensions/anthropic/cli-shared.test.ts index 3434e6fba12a..19b4d24e1a52 100644 --- a/extensions/anthropic/cli-shared.test.ts +++ b/extensions/anthropic/cli-shared.test.ts @@ -114,7 +114,7 @@ describe("Claude CLI model aliases", () => { }); describe("resolveClaudeCliExecutionArgs", () => { - it("isolates Crestodian from Claude user customizations while preserving exact MCP", () => { + it("isolates OpenClaw from Claude user customizations while preserving exact MCP", () => { expect( resolveClaudeCliExecutionArgs({ workspaceDir: "/tmp", @@ -156,7 +156,7 @@ describe("resolveClaudeCliExecutionArgs", () => { "--ide", "--strict-mcp-config", "--mcp-config", - "/tmp/openclaw-crestodian-mcp.json", + "/tmp/openclaw-openclaw-mcp.json", "--resume", "native-session", "--tools", @@ -168,7 +168,7 @@ describe("resolveClaudeCliExecutionArgs", () => { ], toolAvailability: { native: [], - mcp: ["mcp__openclaw__crestodian"], + mcp: ["mcp__openclaw__openclaw"], }, }), ).toEqual([ @@ -176,7 +176,7 @@ describe("resolveClaudeCliExecutionArgs", () => { "--output-format", "stream-json", "--mcp-config", - "/tmp/openclaw-crestodian-mcp.json", + "/tmp/openclaw-openclaw-mcp.json", "--resume", "native-session", "--setting-sources", @@ -189,11 +189,11 @@ describe("resolveClaudeCliExecutionArgs", () => { "--tools", "", "--allowedTools", - "mcp__openclaw__crestodian", + "mcp__openclaw__openclaw", ]); }); - it("leaves non-Crestodian customization args intact under generic tool availability", () => { + it("leaves non-OpenClaw customization args intact under generic tool availability", () => { expect( resolveClaudeCliExecutionArgs({ workspaceDir: "/tmp", diff --git a/extensions/anthropic/cli-shared.ts b/extensions/anthropic/cli-shared.ts index 5844dd31e23d..b437661d6342 100644 --- a/extensions/anthropic/cli-shared.ts +++ b/extensions/anthropic/cli-shared.ts @@ -93,8 +93,8 @@ const CLAUDE_BYPASS_PERMISSION_MODE = "bypassPermissions"; const CLAUDE_DEFAULT_PERMISSION_MODE = "default"; const CLAUDE_NO_TOOLS_VALUE = ""; const CLAUDE_DENY_MCP_TOOLS_VALUE = "mcp__*"; -const CLAUDE_CRESTODIAN_MCP_TOOL = "mcp__openclaw__crestodian"; -const CLAUDE_CRESTODIAN_SETTINGS = +const CLAUDE_SYSTEM_AGENT_MCP_TOOL = "mcp__openclaw__openclaw"; +const CLAUDE_SYSTEM_AGENT_SETTINGS = '{"disableAllHooks":true,"enabledPlugins":{},"autoMemoryEnabled":false,"claudeMdExcludes":["**/CLAUDE.md","**/CLAUDE.local.md","**/.claude/rules/**"]}'; type ClaudeCliEffort = "low" | "medium" | "high" | "xhigh" | "max"; @@ -304,13 +304,13 @@ const CLAUDE_TOOL_AVAILABILITY_ARGS = new Set([ "--disallowed-tools", ]); -const CLAUDE_CRESTODIAN_VARIADIC_VALUE_ARGS = new Set([ +const CLAUDE_SYSTEM_AGENT_VARIADIC_VALUE_ARGS = new Set([ ...CLAUDE_TOOL_AVAILABILITY_ARGS, "--add-dir", "--file", ]); -const CLAUDE_CRESTODIAN_VALUE_ARGS = new Set([ +const CLAUDE_SYSTEM_AGENT_VALUE_ARGS = new Set([ CLAUDE_PERMISSION_MODE_ARG, CLAUDE_SETTING_SOURCES_ARG, CLAUDE_SETTINGS_ARG, @@ -326,7 +326,7 @@ const CLAUDE_CRESTODIAN_VALUE_ARGS = new Set([ "--append-system-prompt-file", ]); -const CLAUDE_CRESTODIAN_BARE_ARGS = new Set([ +const CLAUDE_SYSTEM_AGENT_BARE_ARGS = new Set([ CLAUDE_BARE_ARG, CLAUDE_SAFE_MODE_ARG, CLAUDE_DISABLE_SLASH_COMMANDS_ARG, @@ -436,17 +436,17 @@ function resolveClaudeCliToolAvailabilityArgs( return normalized; } -function isCrestodianToolAvailability( +function isSystemAgentToolAvailability( availability: NonNullable, ): boolean { - return availability.mcp.length === 1 && availability.mcp[0] === CLAUDE_CRESTODIAN_MCP_TOOL; + return availability.mcp.length === 1 && availability.mcp[0] === CLAUDE_SYSTEM_AGENT_MCP_TOOL; } -function resolveClaudeCliCrestodianExecutionArgs(baseArgs: readonly string[]): string[] { +function resolveClaudeCliSystemAgentExecutionArgs(baseArgs: readonly string[]): string[] { const normalized = stripClaudeArgs(baseArgs, { - bare: CLAUDE_CRESTODIAN_BARE_ARGS, - variadicValue: CLAUDE_CRESTODIAN_VARIADIC_VALUE_ARGS, - value: CLAUDE_CRESTODIAN_VALUE_ARGS, + bare: CLAUDE_SYSTEM_AGENT_BARE_ARGS, + variadicValue: CLAUDE_SYSTEM_AGENT_VARIADIC_VALUE_ARGS, + value: CLAUDE_SYSTEM_AGENT_VALUE_ARGS, }); // Safe mode also suppresses explicit MCP, while bare mode drops OAuth. Empty // setting sources plus restrictive flag settings isolate user customizations; @@ -455,14 +455,14 @@ function resolveClaudeCliCrestodianExecutionArgs(baseArgs: readonly string[]): s CLAUDE_SETTING_SOURCES_ARG, "", CLAUDE_SETTINGS_ARG, - CLAUDE_CRESTODIAN_SETTINGS, + CLAUDE_SYSTEM_AGENT_SETTINGS, CLAUDE_DISABLE_SLASH_COMMANDS_ARG, CLAUDE_NO_CHROME_ARG, CLAUDE_STRICT_MCP_CONFIG_ARG, CLAUDE_TOOLS_ARG, CLAUDE_NO_TOOLS_VALUE, CLAUDE_ALLOWED_TOOLS_ARG, - CLAUDE_CRESTODIAN_MCP_TOOL, + CLAUDE_SYSTEM_AGENT_MCP_TOOL, ); return normalized; } @@ -490,8 +490,8 @@ export function resolveClaudeCliExecutionArgs( if (!context.toolAvailability) { return executionArgs; } - return isCrestodianToolAvailability(context.toolAvailability) - ? resolveClaudeCliCrestodianExecutionArgs(executionArgs) + return isSystemAgentToolAvailability(context.toolAvailability) + ? resolveClaudeCliSystemAgentExecutionArgs(executionArgs) : resolveClaudeCliToolAvailabilityArgs(executionArgs, context.toolAvailability); } diff --git a/extensions/codex/src/app-server/dynamic-tool-build.test.ts b/extensions/codex/src/app-server/dynamic-tool-build.test.ts index 8b8778b17be0..a486836e2dca 100644 --- a/extensions/codex/src/app-server/dynamic-tool-build.test.ts +++ b/extensions/codex/src/app-server/dynamic-tool-build.test.ts @@ -266,30 +266,30 @@ describe("Codex app-server dynamic tool build", () => { }); }); - it("preserves the host-provided Crestodian tool through the Codex allowlist", async () => { + it("preserves the host-provided OpenClaw tool through the Codex allowlist", async () => { const workspaceDir = path.join(tempDir, "workspace"); const params = createParams(path.join(tempDir, "session.jsonl"), workspaceDir); params.disableTools = false; params.runtimePlan = createCodexRuntimePlanFixture(); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; setOpenClawCodingToolsFactoryForTests(() => [ - { ...createRuntimeDynamicTool("crestodian"), catalogMode: "direct-only" }, + { ...createRuntimeDynamicTool("openclaw"), catalogMode: "direct-only" }, ]); const tools = await buildDynamicToolsForTest(params, workspaceDir, { - isHostScopedToolActive: (toolName) => toolName === "crestodian", - pluginConfig: { codexDynamicToolsExclude: ["crestodian"] }, + isHostScopedToolActive: (toolName) => toolName === "openclaw", + pluginConfig: { codexDynamicToolsExclude: ["openclaw"] }, }); - expect(tools.map((tool) => tool.name)).toEqual(["crestodian"]); + expect(tools.map((tool) => tool.name)).toEqual(["openclaw"]); }); it.each([ - { label: "host scope is inactive", hostActive: false, toolsAllow: ["crestodian"] }, + { label: "host scope is inactive", hostActive: false, toolsAllow: ["openclaw"] }, { label: "the public allowlist is not exact", hostActive: true, - toolsAllow: ["crestodian", "read"], + toolsAllow: ["openclaw", "read"], }, ])("does not bypass Codex excludes when $label", async ({ hostActive, toolsAllow }) => { const workspaceDir = path.join(tempDir, "workspace"); @@ -298,12 +298,12 @@ describe("Codex app-server dynamic tool build", () => { params.runtimePlan = createCodexRuntimePlanFixture(); params.toolsAllow = toolsAllow; setOpenClawCodingToolsFactoryForTests(() => [ - { ...createRuntimeDynamicTool("crestodian"), catalogMode: "direct-only" }, + { ...createRuntimeDynamicTool("openclaw"), catalogMode: "direct-only" }, ]); const tools = await buildDynamicToolsForTest(params, workspaceDir, { isHostScopedToolActive: () => hostActive, - pluginConfig: { codexDynamicToolsExclude: ["crestodian"] }, + pluginConfig: { codexDynamicToolsExclude: ["openclaw"] }, }); expect(tools).toEqual([]); diff --git a/extensions/codex/src/app-server/dynamic-tool-build.ts b/extensions/codex/src/app-server/dynamic-tool-build.ts index c2024fc9eb26..9eb9d09dfc89 100644 --- a/extensions/codex/src/app-server/dynamic-tool-build.ts +++ b/extensions/codex/src/app-server/dynamic-tool-build.ts @@ -24,7 +24,7 @@ import { readCodexPluginConfig, type CodexPluginConfig } from "./config.js"; import { dynamicToolBuildState } from "./dynamic-tool-build-state.js"; import { filterCodexDynamicTools, - isCrestodianOnlyCodexDynamicToolAllowlist, + isSystemAgentOnlyCodexDynamicToolAllowlist, isForcedPrivateQaCodexRuntime, normalizeCodexDynamicToolName, } from "./dynamic-tool-profile.js"; @@ -63,17 +63,17 @@ const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = new Set(["read", "write"]); const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec"; const CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME = "node_process"; const CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES = new Set(["host", "security", "ask"]); -function preserveRingZeroCrestodianTool( +function preserveRingZeroSystemAgentTool( allTools: T[], filteredTools: T[], ): T[] { - const crestodian = allTools.find( - (tool) => tool.name === "crestodian" && tool.catalogMode === "direct-only", + const openclaw = allTools.find( + (tool) => tool.name === "openclaw" && tool.catalogMode === "direct-only", ); - if (!crestodian) { + if (!openclaw) { return filteredTools; } - return [crestodian, ...filteredTools.filter((tool) => tool.name !== "crestodian")]; + return [openclaw, ...filteredTools.filter((tool) => tool.name !== "openclaw")]; } /** Runtime inputs needed to derive the exact Codex dynamic tool surface for a turn. */ type DynamicToolBuildParams = { @@ -339,11 +339,11 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) { }); const readableAllTools = [...readableAllToolProjection.tools]; const normallyProfiledTools = filterCodexDynamicTools(readableAllTools, input.pluginConfig); - const hostCrestodianActive = - input.isHostScopedToolActive?.("crestodian") ?? isHostScopedAgentToolActive("crestodian"); + const hostSystemAgentActive = + input.isHostScopedToolActive?.("openclaw") ?? isHostScopedAgentToolActive("openclaw"); const profileFilteredTools = - hostCrestodianActive && isCrestodianOnlyCodexDynamicToolAllowlist(params.toolsAllow) - ? preserveRingZeroCrestodianTool(readableAllTools, normallyProfiledTools) + hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) + ? preserveRingZeroSystemAgentTool(readableAllTools, normallyProfiledTools) : normallyProfiledTools; const codexFilteredTools = addNodeShellDynamicToolsIfNeeded( addSandboxShellDynamicToolsIfAvailable( diff --git a/extensions/codex/src/app-server/dynamic-tool-profile.ts b/extensions/codex/src/app-server/dynamic-tool-profile.ts index 238d32483c96..2383bbbb8502 100644 --- a/extensions/codex/src/app-server/dynamic-tool-profile.ts +++ b/extensions/codex/src/app-server/dynamic-tool-profile.ts @@ -38,12 +38,12 @@ export function normalizeCodexDynamicToolName(name: string): string { return DYNAMIC_TOOL_NAME_ALIASES[normalized] ?? normalized; } -/** True only for the host-scoped Crestodian run's exact tool contract. */ -export function isCrestodianOnlyCodexDynamicToolAllowlist( +/** True only for the host-scoped OpenClaw run's exact tool contract. */ +export function isSystemAgentOnlyCodexDynamicToolAllowlist( toolsAllow: readonly string[] | undefined, ): boolean { return ( - toolsAllow?.length === 1 && normalizeCodexDynamicToolName(toolsAllow[0] ?? "") === "crestodian" + toolsAllow?.length === 1 && normalizeCodexDynamicToolName(toolsAllow[0] ?? "") === "openclaw" ); } diff --git a/extensions/codex/src/app-server/dynamic-tools.ts b/extensions/codex/src/app-server/dynamic-tools.ts index bf91be74f089..4eb7713925cc 100644 --- a/extensions/codex/src/app-server/dynamic-tools.ts +++ b/extensions/codex/src/app-server/dynamic-tools.ts @@ -887,8 +887,8 @@ function createCodexDynamicToolSpecs(params: { const directOnlyNamespaceTools: CodexDynamicToolFunctionSpec[] = []; for (const entry of params.entries) { const functionSpec = createCodexDynamicToolFunctionSpec({ entry }); - if (entry.name === "crestodian" && params.directToolNames.has(entry.name)) { - // Crestodian is ring-zero and its whole turn surface. Keep its canonical + if (entry.name === "openclaw" && params.directToolNames.has(entry.name)) { + // OpenClaw is ring-zero and its whole turn surface. Keep its canonical // root name even though generic direct-only tools use a model namespace. specs.push(functionSpec); continue; diff --git a/extensions/codex/src/app-server/run-attempt-tool-setup.ts b/extensions/codex/src/app-server/run-attempt-tool-setup.ts index 0482dd55aff1..a50205e921ec 100644 --- a/extensions/codex/src/app-server/run-attempt-tool-setup.ts +++ b/extensions/codex/src/app-server/run-attempt-tool-setup.ts @@ -224,7 +224,7 @@ export async function prepareCodexAttemptTools(runtime: CodexAttemptRuntime) { }), directToolNames: resolveCodexDynamicToolDirectNames( params, - isHostScopedAgentToolActive("crestodian"), + isHostScopedAgentToolActive("openclaw"), ), hookContext: { agentId: sessionAgentId, diff --git a/extensions/codex/src/app-server/run-attempt-tools.ts b/extensions/codex/src/app-server/run-attempt-tools.ts index e8dfd0859e4b..8f5500c71247 100644 --- a/extensions/codex/src/app-server/run-attempt-tools.ts +++ b/extensions/codex/src/app-server/run-attempt-tools.ts @@ -4,7 +4,7 @@ import type { } from "openclaw/plugin-sdk/agent-harness-runtime"; import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime"; import { handleCodexAppServerApprovalRequest } from "./approval-bridge.js"; -import { isCrestodianOnlyCodexDynamicToolAllowlist } from "./dynamic-tool-profile.js"; +import { isSystemAgentOnlyCodexDynamicToolAllowlist } from "./dynamic-tool-profile.js"; import type { CodexDynamicToolCallParams, CodexDynamicToolCallResponse, @@ -108,15 +108,15 @@ export function handleApprovalRequest(params: { export function resolveCodexDynamicToolDirectNames( params: EmbeddedRunAttemptParams, - hostCrestodianActive = false, + hostSystemAgentActive = false, ): string[] { // Tools with catalogMode=direct-only use the model-only namespace. This list // remains for control tools that intentionally live at the dynamic-tool root. const names: string[] = []; - // Crestodian is the run's only tool and must stay callable when Codex tool + // OpenClaw is the run's only tool and must stay callable when Codex tool // search is unavailable. Exact toolsAllow is the public harness contract. - if (hostCrestodianActive && isCrestodianOnlyCodexDynamicToolAllowlist(params.toolsAllow)) { - names.push("crestodian"); + if (hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) { + names.push("openclaw"); } if (params.sourceReplyDeliveryMode === "message_tool_only") { names.push("message"); diff --git a/extensions/codex/src/app-server/run-attempt.test.ts b/extensions/codex/src/app-server/run-attempt.test.ts index e59f683f72f6..1e0f2be2183b 100644 --- a/extensions/codex/src/app-server/run-attempt.test.ts +++ b/extensions/codex/src/app-server/run-attempt.test.ts @@ -108,15 +108,15 @@ const testing = { filterCodexDynamicTools, resolveCodexDynamicToolDirectNames( params: EmbeddedRunAttemptParams, - hostCrestodianActive = false, + hostSystemAgentActive = false, ): string[] { const names: string[] = []; if ( - hostCrestodianActive && + hostSystemAgentActive && params.toolsAllow?.length === 1 && - params.toolsAllow[0] === "crestodian" + params.toolsAllow[0] === "openclaw" ) { - names.push("crestodian"); + names.push("openclaw"); } if (params.sourceReplyDeliveryMode === "message_tool_only") { names.push("message"); @@ -419,14 +419,14 @@ function createCodexToolBridgeForTest( params: EmbeddedRunAttemptParams, tools: RuntimeDynamicToolForTest[], registeredTools: RuntimeDynamicToolForTest[] = tools, - hostCrestodianActive = false, + hostSystemAgentActive = false, ) { const signal = new AbortController().signal; return createCodexDynamicToolBridge({ tools, registeredTools, signal, - directToolNames: testing.resolveCodexDynamicToolDirectNames(params, hostCrestodianActive), + directToolNames: testing.resolveCodexDynamicToolDirectNames(params, hostSystemAgentActive), }); } diff --git a/extensions/codex/src/app-server/thread-lifecycle.binding.test.ts b/extensions/codex/src/app-server/thread-lifecycle.binding.test.ts index 793dbb951b85..f08a03238115 100644 --- a/extensions/codex/src/app-server/thread-lifecycle.binding.test.ts +++ b/extensions/codex/src/app-server/thread-lifecycle.binding.test.ts @@ -265,7 +265,7 @@ function createTwoCalendarAppPolicyContext() { setupRunAttemptTestHooks(); describe("Codex app-server thread lifecycle bindings", () => { - it("resumes the same restricted Crestodian thread so turn two retains native memory", async () => { + it("resumes the same restricted OpenClaw thread so turn two retains native memory", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); await writeCodexAppServerBinding(sessionFile, { @@ -276,7 +276,7 @@ describe("Codex app-server thread lifecycle bindings", () => { dynamicToolsFingerprint: "[]", }); const params = createParams(sessionFile, workspaceDir); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; let nextThread = 1; const request = vi.fn(async (method: string, _requestParams?: unknown) => { if (method === "config/read") { @@ -308,11 +308,11 @@ describe("Codex app-server thread lifecycle bindings", () => { client: { request } as never, params, cwd: workspaceDir, - dynamicTools: [createNamedDynamicTool("crestodian")], + dynamicTools: [createNamedDynamicTool("openclaw")], appServer: createThreadLifecycleAppServerOptions(), nativeCodeModeEnabled: false, userMcpServersEnabled: false, - hostCrestodianActive: true, + hostSystemAgentActive: true, }; const first = await startOrResumeThread(common); @@ -347,11 +347,11 @@ describe("Codex app-server thread lifecycle bindings", () => { expect(binding?.ringZeroClientInstanceId).toEqual(expect.any(String)); }); - it("starts a fresh restricted Crestodian thread for a new app-server client", async () => { + it("starts a fresh restricted OpenClaw thread for a new app-server client", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); const params = createParams(sessionFile, workspaceDir); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; let nextThread = 1; const request = vi.fn(async (method: string, _requestParams?: unknown) => { if (method === "config/read") { @@ -371,11 +371,11 @@ describe("Codex app-server thread lifecycle bindings", () => { const common = { params, cwd: workspaceDir, - dynamicTools: [createNamedDynamicTool("crestodian")], + dynamicTools: [createNamedDynamicTool("openclaw")], appServer: createThreadLifecycleAppServerOptions(), nativeCodeModeEnabled: false, userMcpServersEnabled: false, - hostCrestodianActive: true, + hostSystemAgentActive: true, }; const first = await startOrResumeThread({ ...common, client: { request } as never }); @@ -393,11 +393,11 @@ describe("Codex app-server thread lifecycle bindings", () => { expect((await readCodexAppServerBinding(sessionFile))?.threadId).toBe("thread-ring-zero-2"); }); - it("retires a warm Crestodian binding when resume MCP attestation fails", async () => { + it("retires a warm OpenClaw binding when resume MCP attestation fails", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); const params = createParams(sessionFile, workspaceDir); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; let attestationCount = 0; const request = vi.fn(async (method: string) => { if (method === "config/read") { @@ -424,11 +424,11 @@ describe("Codex app-server thread lifecycle bindings", () => { abandonClient, params, cwd: workspaceDir, - dynamicTools: [createNamedDynamicTool("crestodian")], + dynamicTools: [createNamedDynamicTool("openclaw")], appServer: createThreadLifecycleAppServerOptions(), nativeCodeModeEnabled: false, userMcpServersEnabled: false, - hostCrestodianActive: true, + hostSystemAgentActive: true, }; await startOrResumeThread(common); @@ -451,7 +451,7 @@ describe("Codex app-server thread lifecycle bindings", () => { expect(await readCodexAppServerBinding(sessionFile)).toBeUndefined(); }); - it("fails closed before starting Crestodian when inherited MCP enumeration fails", async () => { + it("fails closed before starting OpenClaw when inherited MCP enumeration fails", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); await writeCodexAppServerBinding(sessionFile, { @@ -462,7 +462,7 @@ describe("Codex app-server thread lifecycle bindings", () => { dynamicToolsFingerprint: "[]", }); const params = createParams(sessionFile, workspaceDir); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; const request = vi.fn(async (method: string) => { if (method === "config/read") { throw new Error("config unavailable"); @@ -475,11 +475,11 @@ describe("Codex app-server thread lifecycle bindings", () => { client: { request } as never, params, cwd: workspaceDir, - dynamicTools: [createNamedDynamicTool("crestodian")], + dynamicTools: [createNamedDynamicTool("openclaw")], appServer: createThreadLifecycleAppServerOptions(), nativeCodeModeEnabled: false, userMcpServersEnabled: false, - hostCrestodianActive: true, + hostSystemAgentActive: true, }), ).rejects.toThrow("config unavailable"); expect(request.mock.calls.map(([method]) => method)).toEqual(["config/read"]); @@ -491,11 +491,11 @@ describe("Codex app-server thread lifecycle bindings", () => { { name: "legacy managed MDM", layer: { name: { type: "legacyManagedConfigTomlFromMdm" } } }, { name: "unknown future", layer: { name: { type: "futureManaged" } } }, { name: "malformed", layer: { name: {} } }, - ])("fails closed on $name config layers before Crestodian thread/start", async ({ layer }) => { + ])("fails closed on $name config layers before OpenClaw thread/start", async ({ layer }) => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); const params = createParams(sessionFile, workspaceDir); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; const request = vi.fn(async (method: string) => { if (method === "config/read") { return { config: {}, layers: [layer] }; @@ -508,23 +508,23 @@ describe("Codex app-server thread lifecycle bindings", () => { client: { request } as never, params, cwd: workspaceDir, - dynamicTools: [createNamedDynamicTool("crestodian")], + dynamicTools: [createNamedDynamicTool("openclaw")], appServer: createThreadLifecycleAppServerOptions(), nativeCodeModeEnabled: false, userMcpServersEnabled: false, - hostCrestodianActive: true, + hostSystemAgentActive: true, }), ).rejects.toThrow(/config layer|config layers/u); expect(request.mock.calls.map(([method]) => method)).toEqual(["config/read"]); }); it.each(["hooks", "managed_hooks"] as const)( - "fails closed on non-empty %s requirements before Crestodian thread/start", + "fails closed on non-empty %s requirements before OpenClaw thread/start", async (requirementsKey) => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); const params = createParams(sessionFile, workspaceDir); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; const request = vi.fn(async (method: string) => { if (method === "config/read") { return { config: {}, layers: [] }; @@ -546,11 +546,11 @@ describe("Codex app-server thread lifecycle bindings", () => { client: { request } as never, params, cwd: workspaceDir, - dynamicTools: [createNamedDynamicTool("crestodian")], + dynamicTools: [createNamedDynamicTool("openclaw")], appServer: createThreadLifecycleAppServerOptions(), nativeCodeModeEnabled: false, userMcpServersEnabled: false, - hostCrestodianActive: true, + hostSystemAgentActive: true, }), ).rejects.toThrow("cannot override managed hooks"); expect(request.mock.calls.map(([method]) => method)).toEqual([ @@ -564,7 +564,7 @@ describe("Codex app-server thread lifecycle bindings", () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); const params = createParams(sessionFile, workspaceDir); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; const request = vi.fn(async (method: string) => { if (method === "config/read") { return { config: {}, layers: [] }; @@ -580,11 +580,11 @@ describe("Codex app-server thread lifecycle bindings", () => { client: { request } as never, params, cwd: workspaceDir, - dynamicTools: [createNamedDynamicTool("crestodian")], + dynamicTools: [createNamedDynamicTool("openclaw")], appServer: createThreadLifecycleAppServerOptions(), nativeCodeModeEnabled: false, userMcpServersEnabled: false, - hostCrestodianActive: true, + hostSystemAgentActive: true, }), ).rejects.toThrow("cannot override required feature hooks"); expect(request.mock.calls.map(([method]) => method)).toEqual([ @@ -597,7 +597,7 @@ describe("Codex app-server thread lifecycle bindings", () => { { name: "a newly raced server", attestation: { data: [{ name: "raced" }] } }, { name: "a malformed inventory", attestation: { data: "invalid" } }, { name: "an inventory RPC failure", attestation: new Error("inventory failed") }, - ])("retires the cold Crestodian thread when attestation finds $name", async ({ attestation }) => { + ])("retires the cold OpenClaw thread when attestation finds $name", async ({ attestation }) => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); await writeCodexAppServerBinding(sessionFile, { @@ -608,7 +608,7 @@ describe("Codex app-server thread lifecycle bindings", () => { dynamicToolsFingerprint: "[]", }); const params = createParams(sessionFile, workspaceDir); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; const abandonClient = vi.fn(async () => {}); const request = vi.fn(async (method: string) => { if (method === "config/read") { @@ -635,11 +635,11 @@ describe("Codex app-server thread lifecycle bindings", () => { abandonClient, params, cwd: workspaceDir, - dynamicTools: [createNamedDynamicTool("crestodian")], + dynamicTools: [createNamedDynamicTool("openclaw")], appServer: createThreadLifecycleAppServerOptions(), nativeCodeModeEnabled: false, userMcpServersEnabled: false, - hostCrestodianActive: true, + hostSystemAgentActive: true, }), ).rejects.toThrow(); expect(abandonClient).toHaveBeenCalledTimes(1); diff --git a/extensions/codex/src/app-server/thread-lifecycle.test.ts b/extensions/codex/src/app-server/thread-lifecycle.test.ts index 7ec9e04caada..8c7bb6c3a747 100644 --- a/extensions/codex/src/app-server/thread-lifecycle.test.ts +++ b/extensions/codex/src/app-server/thread-lifecycle.test.ts @@ -39,19 +39,19 @@ type CodexThreadLifecycleTimingLogger = NonNullable< describe("Codex ring-zero thread config", () => { it("applies the restriction to both thread start and resume", () => { const params = createAttemptParams({ provider: "openai" }); - params.toolsAllow = ["crestodian"]; + params.toolsAllow = ["openclaw"]; const appServer = createAppServerOptions() as never; const start = buildThreadStartParams(params, { appServer, cwd: "/repo", dynamicTools: [], - hostCrestodianActive: true, + hostSystemAgentActive: true, nativeCodeModeEnabled: false, }); const resume = buildThreadResumeParams(params, { appServer, dynamicTools: [], - hostCrestodianActive: true, + hostSystemAgentActive: true, nativeCodeModeEnabled: false, threadId: "thread-1", }); @@ -78,7 +78,7 @@ describe("Codex ring-zero thread config", () => { appServer, cwd: "/repo", dynamicTools: [], - hostCrestodianActive: false, + hostSystemAgentActive: false, }); expect(normal.baseInstructions).toBeUndefined(); }); diff --git a/extensions/codex/src/app-server/thread-lifecycle.ts b/extensions/codex/src/app-server/thread-lifecycle.ts index c89859b0d01c..a316543c5120 100644 --- a/extensions/codex/src/app-server/thread-lifecycle.ts +++ b/extensions/codex/src/app-server/thread-lifecycle.ts @@ -39,7 +39,7 @@ import { resolveCodexContextEngineProjectionReserveTokens, } from "./context-engine-projection.js"; import { - isCrestodianOnlyCodexDynamicToolAllowlist, + isSystemAgentOnlyCodexDynamicToolAllowlist, normalizeCodexDynamicToolName, shouldDisableCodexToolSearchForModel, } from "./dynamic-tool-profile.js"; @@ -431,7 +431,7 @@ export async function startOrResumeThread(params: { contextEngineProjection?: CodexContextEngineThreadBootstrapProjection; signal?: AbortSignal; timing?: CodexThreadLifecycleTimingOptions; - hostCrestodianActive?: boolean; + hostSystemAgentActive?: boolean; }): Promise { const bindingIdentity: CodexAppServerBindingIdentity = sessionBindingIdentity({ sessionId: params.params.sessionId, @@ -490,10 +490,10 @@ export async function startOrResumeThread(params: { const environmentSelectionFingerprint = fingerprintEnvironmentSelection( params.environmentSelection, ); - const hostCrestodianActive = - params.hostCrestodianActive ?? isHostScopedAgentToolActive("crestodian"); + const hostSystemAgentActive = + params.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw"); const ringZeroActive = - hostCrestodianActive && isCrestodianOnlyCodexDynamicToolAllowlist(params.params.toolsAllow); + hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow); if (ringZeroActive && params.nativeCodeModeEnabled !== false) { throw new Error("Codex ring-zero requires native code mode to be disabled"); } @@ -1004,7 +1004,7 @@ export async function startOrResumeThread(params: { nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport, nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled, webSearchAllowed: params.webSearchAllowed, - hostCrestodianActive, + hostSystemAgentActive, ringZeroInheritedMcpServerNames, }), ); @@ -1215,7 +1215,7 @@ export async function startOrResumeThread(params: { environmentSelection: params.environmentSelection, model: startModelSelection.model, modelProvider: startModelProvider, - hostCrestodianActive, + hostSystemAgentActive, ringZeroInheritedMcpServerNames, }), ); @@ -2161,13 +2161,13 @@ export function buildThreadStartParams( environmentSelection?: CodexTurnEnvironmentParams[]; model?: string | null; modelProvider?: string | null; - hostCrestodianActive?: boolean; + hostSystemAgentActive?: boolean; ringZeroInheritedMcpServerNames?: readonly string[]; }, ): CodexThreadStartParams { const ringZeroActive = - (options.hostCrestodianActive ?? isHostScopedAgentToolActive("crestodian")) && - isCrestodianOnlyCodexDynamicToolAllowlist(params.toolsAllow); + (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && + isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow); const resolvedModelProvider = resolveCodexAppServerModelProvider({ provider: params.provider, authProfileId: params.authProfileId, @@ -2203,7 +2203,7 @@ export function buildThreadStartParams( directOnlyToolNamespaces: resolveDirectOnlyToolNamespaces(options.dynamicTools), webSearchAllowed: options.webSearchAllowed, appServer: options.appServer, - hostCrestodianActive: options.hostCrestodianActive, + hostSystemAgentActive: options.hostSystemAgentActive, ringZeroInheritedMcpServerNames: options.ringZeroInheritedMcpServerNames, }), ...resolveCodexThreadEnvironmentSelection(options), @@ -2232,7 +2232,7 @@ export function buildThreadResumeParams( nativeCodeModeOnlyEnabled?: boolean; webSearchAllowed?: boolean; model?: string | null; - hostCrestodianActive?: boolean; + hostSystemAgentActive?: boolean; ringZeroInheritedMcpServerNames?: readonly string[]; preserveNativeModel?: boolean; }, @@ -2277,7 +2277,7 @@ export function buildThreadResumeParams( directOnlyToolNamespaces: resolveDirectOnlyToolNamespaces(options.dynamicTools), webSearchAllowed: options.webSearchAllowed, appServer: options.appServer, - hostCrestodianActive: options.hostCrestodianActive, + hostSystemAgentActive: options.hostSystemAgentActive, ringZeroInheritedMcpServerNames: options.ringZeroInheritedMcpServerNames, }), developerInstructions: @@ -2463,13 +2463,13 @@ function buildCodexRuntimeThreadConfigForRun( directOnlyToolNamespaces?: readonly string[]; webSearchAllowed?: boolean; appServer?: Pick; - hostCrestodianActive?: boolean; + hostSystemAgentActive?: boolean; ringZeroInheritedMcpServerNames?: readonly string[]; } = {}, ): JsonObject { const ringZeroActive = - (options.hostCrestodianActive ?? isHostScopedAgentToolActive("crestodian")) && - isCrestodianOnlyCodexDynamicToolAllowlist(params.toolsAllow); + (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && + isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow); const configMcpServers = config?.mcp_servers; if (ringZeroActive && configMcpServers !== undefined && !isJsonObject(configMcpServers)) { throw new Error("Codex ring-zero received invalid thread mcp_servers config"); @@ -2498,7 +2498,7 @@ function buildCodexRuntimeThreadConfigForRun( : undefined, buildCodexRingZeroThreadConfigPatch( params, - options.hostCrestodianActive, + options.hostSystemAgentActive, ringZeroMcpServerNames, ), ) ?? baseConfig; @@ -2515,10 +2515,10 @@ function buildCodexRuntimeThreadConfigForRun( function buildCodexRingZeroThreadConfigPatch( params: Pick, - hostCrestodianActive = isHostScopedAgentToolActive("crestodian"), + hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames: readonly string[] = [], ): JsonObject | undefined { - if (!hostCrestodianActive || !isCrestodianOnlyCodexDynamicToolAllowlist(params.toolsAllow)) { + if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) { return undefined; } // Narrow OpenClaw allowlists already send environments: [] and disable diff --git a/extensions/copilot/src/attempt.test.ts b/extensions/copilot/src/attempt.test.ts index 2a418ca448b1..3ff42a331fee 100644 --- a/extensions/copilot/src/attempt.test.ts +++ b/extensions/copilot/src/attempt.test.ts @@ -3398,19 +3398,19 @@ describe("runCopilotAttempt", () => { ]); }); - it("keeps a host-scoped Crestodian create-session surface ring-zero", async () => { + it("keeps a host-scoped OpenClaw create-session surface ring-zero", async () => { const sdk = makeFakeSdk(); const pool = makeFakePool(sdk); - const sdkTools = [makeFakeSdkTool("crestodian")]; + const sdkTools = [makeFakeSdkTool("openclaw")]; const createToolBridge = vi.fn(async () => ({ sdkTools, sourceTools: [] })); - await runCopilotAttempt(makeParams({ toolsAllow: ["crestodian"] }), { + await runCopilotAttempt(makeParams({ toolsAllow: ["openclaw"] }), { createToolBridge, - isHostScopedToolActive: (toolName) => toolName === "crestodian", + isHostScopedToolActive: (toolName) => toolName === "openclaw", pool, }); - expect(readAvailableTools(sdk.createSession.mock.calls[0])).toEqual(["crestodian"]); + expect(readAvailableTools(sdk.createSession.mock.calls[0])).toEqual(["openclaw"]); }); it("forwards `[]` to the SDK when the bridge returns no tools (disable / raw / fully filtered)", async () => { @@ -3481,31 +3481,31 @@ describe("runCopilotAttempt", () => { expect(resumeCfg?.availableTools).toEqual(["read", "builtin:ask_user"]); }); - it("keeps a host-scoped Crestodian resume-session surface ring-zero", async () => { + it("keeps a host-scoped OpenClaw resume-session surface ring-zero", async () => { const sdk = makeFakeSdk({ onResumeSession: (session) => { session.sendAndWait.mockResolvedValueOnce(makeAssistantMessageEvent("resumed")); }, }); const pool = makeFakePool(sdk); - const sdkTools = [makeFakeSdkTool("crestodian")]; + const sdkTools = [makeFakeSdkTool("openclaw")]; const createToolBridge = vi.fn(async () => ({ sdkTools, sourceTools: [] })); await runCopilotAttempt( makeParams({ - initialReplayState: { sdkSessionId: "sess-crestodian" }, - toolsAllow: ["crestodian"], + initialReplayState: { sdkSessionId: "sess-openclaw" }, + toolsAllow: ["openclaw"], } as never), { createToolBridge, - isHostScopedToolActive: (toolName) => toolName === "crestodian", + isHostScopedToolActive: (toolName) => toolName === "openclaw", pool, }, ); const resumeCall = sdk.resumeSession.mock.calls[0] as unknown[] | undefined; const resumeCfg = resumeCall?.[1] as { availableTools?: string[] }; - expect(resumeCfg?.availableTools).toEqual(["crestodian"]); + expect(resumeCfg?.availableTools).toEqual(["openclaw"]); }); it("forwards `[]` to resumeSession when the bridge returns no tools", async () => { diff --git a/extensions/copilot/src/attempt.ts b/extensions/copilot/src/attempt.ts index 90055a8a3aa4..53d874bcbddb 100644 --- a/extensions/copilot/src/attempt.ts +++ b/extensions/copilot/src/attempt.ts @@ -364,10 +364,10 @@ export async function runCopilotAttempt( const attemptStartedAt = now(); const input = params as AttemptParamsLike; const createToolBridge = deps.createToolBridge ?? createCopilotToolBridge; - const hostCrestodianActive = - deps.isHostScopedToolActive?.("crestodian") ?? isHostScopedAgentToolActive("crestodian"); - const ringZeroCrestodianRun = - hostCrestodianActive && isCrestodianOnlyToolAllowlist(input.toolsAllow); + const hostSystemAgentActive = + deps.isHostScopedToolActive?.("openclaw") ?? isHostScopedAgentToolActive("openclaw"); + const ringZeroSystemAgentRun = + hostSystemAgentActive && isSystemAgentOnlyToolAllowlist(input.toolsAllow); const messages = getMessagesSnapshotInput(input); const modelRef = resolveModelRef(input); const resolvedWorkspaceForSandbox = @@ -772,7 +772,7 @@ export async function runCopilotAttempt( emitLlmInput(prompt, additionalContext), } : undefined, - includeAskUser: !ringZeroCrestodianRun, + includeAskUser: !ringZeroSystemAgentRun, }, ); const compactionSessionConfig = byokProxy @@ -793,7 +793,7 @@ export async function runCopilotAttempt( emitLlmInput(prompt, additionalContext), } : undefined, - includeAskUser: !ringZeroCrestodianRun, + includeAskUser: !ringZeroSystemAgentRun, }, ) : sessionConfig; @@ -1372,7 +1372,7 @@ function createSessionConfig( tools: sdkTools, // Restrict the SDK's tool catalog to the bridged tool names returned // by `createCopilotToolBridge`, plus the built-in `ask_user` tool for - // normal runs. Ring-zero Crestodian runs expose only Crestodian. Without this, the SDK + // normal runs. Ring-zero OpenClaw runs expose only OpenClaw. Without this, the SDK // would still expose its native read/write/shell/url/mcp/memory/ // hook tools to the model alongside our overrides, which would // bypass OpenClaw's wrapped-tool enforcement under any permissive @@ -1441,8 +1441,8 @@ function buildCopilotAvailableTools(sdkTools: SdkTool[], includeAskUser: boolean return [...new Set(availableTools)]; } -function isCrestodianOnlyToolAllowlist(toolsAllow: readonly string[] | undefined): boolean { - return toolsAllow?.length === 1 && toolsAllow[0]?.trim().toLowerCase() === "crestodian"; +function isSystemAgentOnlyToolAllowlist(toolsAllow: readonly string[] | undefined): boolean { + return toolsAllow?.length === 1 && toolsAllow[0]?.trim().toLowerCase() === "openclaw"; } async function createMessageOptions( diff --git a/extensions/copilot/src/tool-bridge.test.ts b/extensions/copilot/src/tool-bridge.test.ts index a4fb0b5d255a..f66b5bae52c4 100644 --- a/extensions/copilot/src/tool-bridge.test.ts +++ b/extensions/copilot/src/tool-bridge.test.ts @@ -188,27 +188,27 @@ describe("createCopilotToolBridge", () => { expect(result.sdkTools.map((tool) => tool.name)).toEqual(["tool-a", "tool-b"]); }); - it("preserves direct-only Crestodian through the exact Copilot allowlist", async () => { - const crestodianTool = makeTool({ - name: "crestodian", + it("preserves direct-only OpenClaw through the exact Copilot allowlist", async () => { + const systemAgentTool = makeTool({ + name: "openclaw", catalogMode: "direct-only", } as never); const result = await createCopilotToolBridge({ - agentId: "crestodian", + agentId: "openclaw", attemptParams: { - runId: "crestodian-turn-1", - sessionKey: "agent:crestodian:main", - toolsAllow: ["crestodian"], + runId: "openclaw-turn-1", + sessionKey: "agent:openclaw:main", + toolsAllow: ["openclaw"], } as never, - createOpenClawCodingTools: async () => [crestodianTool], + createOpenClawCodingTools: async () => [systemAgentTool], modelId: "gpt-4.1", modelProvider: "github-copilot", - sessionId: "crestodian-session", + sessionId: "openclaw-session", }); - expect(result.sourceTools).toEqual([crestodianTool]); - expect(result.sdkTools.map((tool) => tool.name)).toEqual(["crestodian"]); + expect(result.sourceTools).toEqual([systemAgentTool]); + expect(result.sdkTools.map((tool) => tool.name)).toEqual(["openclaw"]); }); it("compacts the Copilot tool surface behind tool_search controls when enabled", async () => { diff --git a/extensions/qa-lab/src/ci-smoke-plan.ts b/extensions/qa-lab/src/ci-smoke-plan.ts index e01444b9f731..488265cd8ddb 100644 --- a/extensions/qa-lab/src/ci-smoke-plan.ts +++ b/extensions/qa-lab/src/ci-smoke-plan.ts @@ -10,7 +10,7 @@ const QA_SMOKE_CI_PARTS = ["profile-1", "profile-2"] as const; const QA_SMOKE_CI_CHANNELS = ["matrix", OPENCLAW_CRABLINE_DEFAULT_CHANNEL] as const; const QA_SMOKE_CI_SCENARIO_IDS = new Set([ "control-ui-chat-flow-playwright", - "crestodian-ring-zero-setup", + "system-agent-ring-zero-setup", "dreaming-shadow-trial-report", "gateway-smoke", "luna-thinking-visibility-switch", diff --git a/extensions/qa-lab/src/suite-runtime-agent-process.test.ts b/extensions/qa-lab/src/suite-runtime-agent-process.test.ts index 0c3dd7df4dc1..642e173eb78e 100644 --- a/extensions/qa-lab/src/suite-runtime-agent-process.test.ts +++ b/extensions/qa-lab/src/suite-runtime-agent-process.test.ts @@ -257,7 +257,7 @@ describe("qa suite runtime agent process helpers", () => { alternateModel: "openai/gpt-5.6-luna-mini", providerMode: "mock-openai", } as never, - ["crestodian", "-m", "overview"], + ["openclaw", "-m", "overview"], { env: { OPENCLAW_STATE_DIR: "/tmp/isolated-state", @@ -275,7 +275,7 @@ describe("qa suite runtime agent process helpers", () => { expect(spawnCall?.[0]).toBe("/usr/bin/node"); expect(spawnCall?.[1]).toEqual([ path.join("/repo", "dist", "index.js"), - "crestodian", + "openclaw", "-m", "overview", ]); diff --git a/extensions/qqbot/src/engine/commands/command-visibility.ts b/extensions/qqbot/src/engine/commands/command-visibility.ts index 58d1297caac9..17b4f42c078d 100644 --- a/extensions/qqbot/src/engine/commands/command-visibility.ts +++ b/extensions/qqbot/src/engine/commands/command-visibility.ts @@ -32,7 +32,7 @@ const PRIVATE_ONLY_CORE_COMMANDS = new Set([ "tools", "skill", "diagnostics", - "crestodian", + "openclaw", "tasks", "allowlist", "approve", diff --git a/package.json b/package.json index 8d4075facc2d..fe277f64c40b 100644 --- a/package.json +++ b/package.json @@ -1818,8 +1818,8 @@ "test:docker:cleanup": "bash scripts/test-cleanup-docker.sh", "test:docker:commitments-safety": "bash scripts/e2e/commitments-safety-docker.sh", "test:docker:config-reload": "bash scripts/e2e/config-reload-source-docker.sh", - "test:docker:crestodian-first-run": "bash scripts/e2e/crestodian-first-run-docker.sh", - "test:docker:crestodian-rescue": "bash scripts/e2e/crestodian-rescue-docker.sh", + "test:docker:system-agent-first-run": "bash scripts/e2e/system-agent-first-run-docker.sh", + "test:docker:system-agent-rescue": "bash scripts/e2e/system-agent-rescue-docker.sh", "test:docker:cron-cli": "bash scripts/e2e/cron-cli-docker.sh", "test:docker:cron-mcp-cleanup": "bash scripts/e2e/cron-mcp-cleanup-docker.sh", "test:docker:codex-media-path": "bash scripts/e2e/codex-media-path-docker.sh", @@ -1922,7 +1922,7 @@ "test:live": "node scripts/test-live.mjs", "test:live:cache": "node scripts/run-with-env.mjs OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_CACHE_TEST=1 -- node --import tsx scripts/check-live-cache.ts", "test:live:codex-harness": "node scripts/test-live.mjs --codex-harness -- src/gateway/gateway-codex-harness.live.test.ts", - "test:live:crestodian-rescue-channel": "node scripts/run-with-env.mjs OPENCLAW_LIVE_CRESTODIAN_RESCUE_CHANNEL=1 -- node scripts/test-live.mjs -- src/crestodian/rescue-channel.live.test.ts", + "test:live:system-agent-rescue-channel": "node scripts/run-with-env.mjs OPENCLAW_LIVE_SYSTEM_AGENT_RESCUE_CHANNEL=1 -- node scripts/test-live.mjs -- src/system-agent/rescue-channel.live.test.ts", "test:live:gateway-profiles": "node scripts/test-live.mjs -- src/gateway/gateway-models.profiles.live.test.ts", "test:live:media": "node --import tsx test/e2e/qa-lab/media/hosted-media-provider-live.ts", "test:live:media:image": "node --import tsx test/e2e/qa-lab/media/hosted-media-provider-live.ts image", diff --git a/packages/gateway-protocol/src/index.ts b/packages/gateway-protocol/src/index.ts index 8d872737a11c..f7e33b8c4164 100644 --- a/packages/gateway-protocol/src/index.ts +++ b/packages/gateway-protocol/src/index.ts @@ -446,16 +446,16 @@ import { WakeParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, - CrestodianChatParamsSchema, - CrestodianChatResultSchema, - CrestodianSetupDetectParamsSchema, - CrestodianSetupDetectResultSchema, - CrestodianSetupVerifyParamsSchema, - CrestodianSetupVerifyResultSchema, - CrestodianSetupActivateParamsSchema, - CrestodianSetupActivateResultSchema, - CrestodianSetupAuthStartParamsSchema, - CrestodianSetupAuthStartResultSchema, + SystemAgentChatParamsSchema, + SystemAgentChatResultSchema, + SystemAgentSetupDetectParamsSchema, + SystemAgentSetupDetectResultSchema, + SystemAgentSetupVerifyParamsSchema, + SystemAgentSetupVerifyResultSchema, + SystemAgentSetupActivateParamsSchema, + SystemAgentSetupActivateResultSchema, + SystemAgentSetupAuthStartParamsSchema, + SystemAgentSetupAuthStartResultSchema, WizardCancelParamsSchema, WizardNextParamsSchema, WizardNextResultSchema, @@ -680,14 +680,14 @@ export const validateConfigPatchParams = lazyCompile(ConfigPatchParamsSchema); export const validateConfigSchemaParams = lazyCompile(ConfigSchemaParamsSchema); export const validateConfigSchemaLookupParams = lazyCompile(ConfigSchemaLookupParamsSchema); export const validateConfigSchemaLookupResult = lazyCompile(ConfigSchemaLookupResultSchema); -export const validateCrestodianChatParams = lazyCompile(CrestodianChatParamsSchema); -export const validateCrestodianSetupDetectParams = lazyCompile(CrestodianSetupDetectParamsSchema); -export const validateCrestodianSetupVerifyParams = lazyCompile(CrestodianSetupVerifyParamsSchema); -export const validateCrestodianSetupActivateParams = lazyCompile( - CrestodianSetupActivateParamsSchema, +export const validateSystemAgentChatParams = lazyCompile(SystemAgentChatParamsSchema); +export const validateSystemAgentSetupDetectParams = lazyCompile(SystemAgentSetupDetectParamsSchema); +export const validateSystemAgentSetupVerifyParams = lazyCompile(SystemAgentSetupVerifyParamsSchema); +export const validateSystemAgentSetupActivateParams = lazyCompile( + SystemAgentSetupActivateParamsSchema, ); -export const validateCrestodianSetupAuthStartParams = lazyCompile( - CrestodianSetupAuthStartParamsSchema, +export const validateSystemAgentSetupAuthStartParams = lazyCompile( + SystemAgentSetupAuthStartParamsSchema, ); export const validateWizardStartParams = lazyCompile(WizardStartParamsSchema); export const validateWizardNextParams = lazyCompile(WizardNextParamsSchema); @@ -1049,16 +1049,16 @@ export { ConfigSchemaResponseSchema, ConfigSchemaLookupResultSchema, UpdateStatusParamsSchema, - CrestodianChatParamsSchema, - CrestodianChatResultSchema, - CrestodianSetupDetectParamsSchema, - CrestodianSetupDetectResultSchema, - CrestodianSetupVerifyParamsSchema, - CrestodianSetupVerifyResultSchema, - CrestodianSetupActivateParamsSchema, - CrestodianSetupActivateResultSchema, - CrestodianSetupAuthStartParamsSchema, - CrestodianSetupAuthStartResultSchema, + SystemAgentChatParamsSchema, + SystemAgentChatResultSchema, + SystemAgentSetupDetectParamsSchema, + SystemAgentSetupDetectResultSchema, + SystemAgentSetupVerifyParamsSchema, + SystemAgentSetupVerifyResultSchema, + SystemAgentSetupActivateParamsSchema, + SystemAgentSetupActivateResultSchema, + SystemAgentSetupAuthStartParamsSchema, + SystemAgentSetupAuthStartResultSchema, WizardStartParamsSchema, WizardNextParamsSchema, WizardCancelParamsSchema, @@ -1340,16 +1340,16 @@ export type { ConfigPatchParams, ConfigSchemaParams, ConfigSchemaResponse, - CrestodianChatParams, - CrestodianChatResult, - CrestodianSetupDetectParams, - CrestodianSetupDetectResult, - CrestodianSetupVerifyParams, - CrestodianSetupVerifyResult, - CrestodianSetupActivateParams, - CrestodianSetupActivateResult, - CrestodianSetupAuthStartParams, - CrestodianSetupAuthStartResult, + SystemAgentChatParams, + SystemAgentChatResult, + SystemAgentSetupDetectParams, + SystemAgentSetupDetectResult, + SystemAgentSetupVerifyParams, + SystemAgentSetupVerifyResult, + SystemAgentSetupActivateParams, + SystemAgentSetupActivateResult, + SystemAgentSetupAuthStartParams, + SystemAgentSetupAuthStartResult, WizardStartParams, WizardNextParams, WizardCancelParams, diff --git a/packages/gateway-protocol/src/schema.ts b/packages/gateway-protocol/src/schema.ts index 167bb5ee07e3..2d9f43d4e01f 100644 --- a/packages/gateway-protocol/src/schema.ts +++ b/packages/gateway-protocol/src/schema.ts @@ -16,7 +16,7 @@ export * from "./schema/channels.js"; export * from "./schema/talk-marks.js"; export * from "./schema/commands.js"; export * from "./schema/config.js"; -export * from "./schema/crestodian.js"; +export * from "./schema/openclaw.js"; export * from "./schema/cron.js"; export * from "./schema/error-codes.js"; export * from "./schema/environments.js"; diff --git a/packages/gateway-protocol/src/schema/frames.ts b/packages/gateway-protocol/src/schema/frames.ts index a42b23b52b5a..0c9af9564cf1 100644 --- a/packages/gateway-protocol/src/schema/frames.ts +++ b/packages/gateway-protocol/src/schema/frames.ts @@ -7,7 +7,7 @@ import { SnapshotSchema, StateVersionSchema } from "./snapshot.js"; export const GATEWAY_SERVER_CAPS = { CHAT_SEND_ROUTING_CONTRACT: "chat-send-routing-contract", - CRESTODIAN_SETUP_MODEL_REF: "crestodian-setup-model-ref", + SYSTEM_AGENT_SETUP_MODEL_REF: "openclaw-setup-model-ref", } as const; /** diff --git a/packages/gateway-protocol/src/schema/crestodian.test.ts b/packages/gateway-protocol/src/schema/openclaw.test.ts similarity index 58% rename from packages/gateway-protocol/src/schema/crestodian.test.ts rename to packages/gateway-protocol/src/schema/openclaw.test.ts index fd41c38dc919..2a7e1a0b4840 100644 --- a/packages/gateway-protocol/src/schema/crestodian.test.ts +++ b/packages/gateway-protocol/src/schema/openclaw.test.ts @@ -1,24 +1,24 @@ import { Value } from "typebox/value"; import { describe, expect, it } from "vitest"; -import { validateCrestodianSetupVerifyParams } from "../index.js"; -import { CrestodianSetupVerifyResultSchema } from "./crestodian.js"; +import { validateSystemAgentSetupVerifyParams } from "../index.js"; +import { SystemAgentSetupVerifyResultSchema } from "./openclaw.js"; -describe("Crestodian setup verification protocol", () => { +describe("OpenClaw setup verification protocol", () => { it("accepts only an empty request", () => { - expect(validateCrestodianSetupVerifyParams({})).toBe(true); - expect(validateCrestodianSetupVerifyParams({ modelRef: "openai/gpt-5.5" })).toBe(false); + expect(validateSystemAgentSetupVerifyParams({})).toBe(true); + expect(validateSystemAgentSetupVerifyParams({ modelRef: "openai/gpt-5.5" })).toBe(false); }); it("accepts the structured success and failure results", () => { expect( - Value.Check(CrestodianSetupVerifyResultSchema, { + Value.Check(SystemAgentSetupVerifyResultSchema, { ok: true, modelRef: "openai/gpt-5.5", latencyMs: 25, }), ).toBe(true); expect( - Value.Check(CrestodianSetupVerifyResultSchema, { + Value.Check(SystemAgentSetupVerifyResultSchema, { ok: false, status: "unavailable", error: "no configured model", @@ -28,7 +28,7 @@ describe("Crestodian setup verification protocol", () => { it("rejects mixed or incomplete results", () => { expect( - Value.Check(CrestodianSetupVerifyResultSchema, { + Value.Check(SystemAgentSetupVerifyResultSchema, { ok: true, modelRef: "openai/gpt-5.5", latencyMs: 25, @@ -36,14 +36,14 @@ describe("Crestodian setup verification protocol", () => { }), ).toBe(false); expect( - Value.Check(CrestodianSetupVerifyResultSchema, { + Value.Check(SystemAgentSetupVerifyResultSchema, { ok: false, status: "ok", error: "contradictory result", }), ).toBe(false); expect( - Value.Check(CrestodianSetupVerifyResultSchema, { + Value.Check(SystemAgentSetupVerifyResultSchema, { ok: false, status: "unavailable", }), diff --git a/packages/gateway-protocol/src/schema/crestodian.ts b/packages/gateway-protocol/src/schema/openclaw.ts similarity index 75% rename from packages/gateway-protocol/src/schema/crestodian.ts rename to packages/gateway-protocol/src/schema/openclaw.ts index 14bd726925f5..2b483d65379d 100644 --- a/packages/gateway-protocol/src/schema/crestodian.ts +++ b/packages/gateway-protocol/src/schema/openclaw.ts @@ -1,4 +1,4 @@ -// Gateway Protocol schema module defines Crestodian chat payloads. +// Gateway Protocol schema module defines OpenClaw chat payloads. import type { Static } from "typebox"; import { Type } from "typebox"; import { closedObject } from "./closed-object.js"; @@ -6,12 +6,12 @@ import { NonEmptyString } from "./primitives.js"; import { WizardStartResultSchema } from "./wizard.js"; /** - * Crestodian chat lets clients (macOS app onboarding, future UIs) hold the + * OpenClaw chat lets clients (macOS app onboarding, future UIs) hold the * setup/repair conversation over the gateway. The gateway live-tests the * configured inference route before creating a session. Omitting `message` * returns the welcome/greeting for a verified fresh session without input. */ -export const CrestodianChatParamsSchema = closedObject({ +export const SystemAgentChatParamsSchema = closedObject({ sessionId: NonEmptyString, message: Type.Optional(Type.String()), /** "onboarding" seeds the first-run setup proposal in the greeting. */ @@ -20,8 +20,8 @@ export const CrestodianChatParamsSchema = closedObject({ reset: Type.Optional(Type.Boolean()), }); -/** One Crestodian reply; `action` tells clients about conversation handoffs. */ -export const CrestodianChatResultSchema = closedObject({ +/** One OpenClaw reply; `action` tells clients about conversation handoffs. */ +export const SystemAgentChatResultSchema = closedObject({ sessionId: NonEmptyString, reply: NonEmptyString, /** The next reply is a hosted-wizard secret and clients must mask its input/echo. */ @@ -42,7 +42,7 @@ export const CrestodianChatResultSchema = closedObject({ * client can walk the ladder candidate-by-candidate without ever leaving a * broken default model behind. */ -export const CrestodianSetupDetectParamsSchema = closedObject({}); +export const SystemAgentSetupDetectParamsSchema = closedObject({}); const SetupInferenceKind = Type.Union([ Type.Literal("existing-model"), @@ -74,7 +74,7 @@ const SetupInferenceFailureStatus = Type.Union([ Type.Literal("unknown"), ]); -export const CrestodianSetupDetectResultSchema = closedObject({ +export const SystemAgentSetupDetectResultSchema = closedObject({ candidates: Type.Array( closedObject({ kind: SetupInferenceKind, @@ -115,9 +115,9 @@ export const CrestodianSetupDetectResultSchema = closedObject({ }); /** Live verification of the Gateway's current default-agent inference route. */ -export const CrestodianSetupVerifyParamsSchema = closedObject({}); +export const SystemAgentSetupVerifyParamsSchema = closedObject({}); -export const CrestodianSetupVerifyResultSchema = Type.Union([ +export const SystemAgentSetupVerifyResultSchema = Type.Union([ closedObject({ ok: Type.Literal(true), modelRef: NonEmptyString, @@ -130,7 +130,7 @@ export const CrestodianSetupVerifyResultSchema = Type.Union([ }), ]); -export const CrestodianSetupActivateParamsSchema = closedObject({ +export const SystemAgentSetupActivateParamsSchema = closedObject({ kind: Type.Union([ Type.Literal("existing-model"), Type.Literal("openai-api-key"), @@ -149,7 +149,7 @@ export const CrestodianSetupActivateParamsSchema = closedObject({ workspace: Type.Optional(Type.String()), }); -export const CrestodianSetupActivateResultSchema = closedObject({ +export const SystemAgentSetupActivateResultSchema = closedObject({ ok: Type.Boolean(), /** Present on success: the model ref that answered the live test. */ modelRef: Type.Optional(Type.String()), @@ -162,24 +162,24 @@ export const CrestodianSetupActivateResultSchema = closedObject({ }); /** Starts one provider-owned interactive login as a gateway wizard session. */ -export const CrestodianSetupAuthStartParamsSchema = closedObject({ +export const SystemAgentSetupAuthStartParamsSchema = closedObject({ /** Client-generated so cancellation remains possible if the start reply is lost. */ sessionId: NonEmptyString, authChoice: NonEmptyString, workspace: Type.Optional(Type.String()), }); -export const CrestodianSetupAuthStartResultSchema = WizardStartResultSchema; +export const SystemAgentSetupAuthStartResultSchema = WizardStartResultSchema; // Wire types derive directly from local schema consts so public d.ts graphs never // pull in the ProtocolSchemas registry. -export type CrestodianChatParams = Static; -export type CrestodianChatResult = Static; -export type CrestodianSetupDetectParams = Static; -export type CrestodianSetupDetectResult = Static; -export type CrestodianSetupActivateParams = Static; -export type CrestodianSetupActivateResult = Static; -export type CrestodianSetupVerifyParams = Static; -export type CrestodianSetupVerifyResult = Static; -export type CrestodianSetupAuthStartParams = Static; -export type CrestodianSetupAuthStartResult = Static; +export type SystemAgentChatParams = Static; +export type SystemAgentChatResult = Static; +export type SystemAgentSetupDetectParams = Static; +export type SystemAgentSetupDetectResult = Static; +export type SystemAgentSetupActivateParams = Static; +export type SystemAgentSetupActivateResult = Static; +export type SystemAgentSetupVerifyParams = Static; +export type SystemAgentSetupVerifyResult = Static; +export type SystemAgentSetupAuthStartParams = Static; +export type SystemAgentSetupAuthStartResult = Static; diff --git a/packages/gateway-protocol/src/schema/protocol-schemas.ts b/packages/gateway-protocol/src/schema/protocol-schemas.ts index a7a5c21564d3..4f45e16c66e5 100644 --- a/packages/gateway-protocol/src/schema/protocol-schemas.ts +++ b/packages/gateway-protocol/src/schema/protocol-schemas.ts @@ -193,18 +193,6 @@ import { UpdateStatusParamsSchema, UpdateRunParamsSchema, } from "./config.js"; -import { - CrestodianChatParamsSchema, - CrestodianChatResultSchema, - CrestodianSetupActivateParamsSchema, - CrestodianSetupActivateResultSchema, - CrestodianSetupAuthStartParamsSchema, - CrestodianSetupAuthStartResultSchema, - CrestodianSetupDetectParamsSchema, - CrestodianSetupDetectResultSchema, - CrestodianSetupVerifyParamsSchema, - CrestodianSetupVerifyResultSchema, -} from "./crestodian.js"; import { CronAddParamsSchema, CronAddResultSchema, @@ -322,6 +310,18 @@ import { NodeSkillsUpdateParamsSchema, NodeRenameParamsSchema, } from "./nodes.js"; +import { + SystemAgentChatParamsSchema, + SystemAgentChatResultSchema, + SystemAgentSetupActivateParamsSchema, + SystemAgentSetupActivateResultSchema, + SystemAgentSetupAuthStartParamsSchema, + SystemAgentSetupAuthStartResultSchema, + SystemAgentSetupDetectParamsSchema, + SystemAgentSetupDetectResultSchema, + SystemAgentSetupVerifyParamsSchema, + SystemAgentSetupVerifyResultSchema, +} from "./openclaw.js"; import { PluginApprovalRequestParamsSchema, PluginApprovalResolveParamsSchema, @@ -694,16 +694,16 @@ export const ProtocolSchemas = { ConfigSchemaLookupParams: ConfigSchemaLookupParamsSchema, ConfigSchemaResponse: ConfigSchemaResponseSchema, ConfigSchemaLookupResult: ConfigSchemaLookupResultSchema, - CrestodianChatParams: CrestodianChatParamsSchema, - CrestodianChatResult: CrestodianChatResultSchema, - CrestodianSetupDetectParams: CrestodianSetupDetectParamsSchema, - CrestodianSetupDetectResult: CrestodianSetupDetectResultSchema, - CrestodianSetupVerifyParams: CrestodianSetupVerifyParamsSchema, - CrestodianSetupVerifyResult: CrestodianSetupVerifyResultSchema, - CrestodianSetupActivateParams: CrestodianSetupActivateParamsSchema, - CrestodianSetupActivateResult: CrestodianSetupActivateResultSchema, - CrestodianSetupAuthStartParams: CrestodianSetupAuthStartParamsSchema, - CrestodianSetupAuthStartResult: CrestodianSetupAuthStartResultSchema, + SystemAgentChatParams: SystemAgentChatParamsSchema, + SystemAgentChatResult: SystemAgentChatResultSchema, + SystemAgentSetupDetectParams: SystemAgentSetupDetectParamsSchema, + SystemAgentSetupDetectResult: SystemAgentSetupDetectResultSchema, + SystemAgentSetupVerifyParams: SystemAgentSetupVerifyParamsSchema, + SystemAgentSetupVerifyResult: SystemAgentSetupVerifyResultSchema, + SystemAgentSetupActivateParams: SystemAgentSetupActivateParamsSchema, + SystemAgentSetupActivateResult: SystemAgentSetupActivateResultSchema, + SystemAgentSetupAuthStartParams: SystemAgentSetupAuthStartParamsSchema, + SystemAgentSetupAuthStartResult: SystemAgentSetupAuthStartResultSchema, WizardStartParams: WizardStartParamsSchema, WizardNextParams: WizardNextParamsSchema, WizardCancelParams: WizardCancelParamsSchema, diff --git a/qa/scenarios/config/crestodian-ring-zero-setup.yaml b/qa/scenarios/config/system-agent-ring-zero-setup.yaml similarity index 62% rename from qa/scenarios/config/crestodian-ring-zero-setup.yaml rename to qa/scenarios/config/system-agent-ring-zero-setup.yaml index 2abc0285826b..05acb2e304f8 100644 --- a/qa/scenarios/config/crestodian-ring-zero-setup.yaml +++ b/qa/scenarios/config/system-agent-ring-zero-setup.yaml @@ -1,33 +1,33 @@ -title: Crestodian ring-zero setup +title: OpenClaw ring-zero setup scenario: - id: crestodian-ring-zero-setup + id: system-agent-ring-zero-setup surface: config coverage: secondary: - - config.crestodian-setup + - config.system-agent-setup - channels.discord-config - agents.create - objective: Verify the package-installed Crestodian inference gate and supporting one-shot setup operations, without claiming the interactive agent/tool/approval flow. + objective: Verify the package-installed OpenClaw inference gate and supporting one-shot setup operations, without claiming the interactive agent/tool/approval flow. successCriteria: - - Crestodian fails closed in an empty state dir and directs the user to inference onboarding. - - The packaged activation module tests a fake Claude inference backend before persisting its model, with remaining setup deferred to Crestodian. + - OpenClaw fails closed in an empty state dir and directs the user to inference onboarding. + - The packaged activation module tests a fake Claude inference backend before persisting its model, with remaining setup deferred to OpenClaw. - A fuzzy packaged CLI request reaches the verified planner and resolves to a typed setup operation only after activation. - Supporting one-shot commands write the workspace/model and create a non-main agent. - Discord is enabled and configured through an env SecretRef without persisting the raw token. - Config validation passes and audit entries exist for every applied write. docsRefs: - - docs/cli/crestodian.md + - docs/cli/setup.md - docs/channels/discord.md - docs/help/testing.md codeRefs: - - scripts/e2e/crestodian-first-run-docker.sh - - scripts/e2e/crestodian-first-run-spec.json - - test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts + - scripts/e2e/system-agent-first-run-docker.sh + - scripts/e2e/system-agent-first-run-spec.json + - test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts execution: kind: script path: test/e2e/qa-lab/runtime/docker-e2e-lane.ts summary: Redirects to the packaged inference-gate and typed-operation Docker lane; coverage remains secondary until a real interactive agent/tool/approval flow is exercised. args: - --lane - - crestodian-first-run + - system-agent-first-run diff --git a/qa/scenarios/runtime/docker-crestodian-first-run.yaml b/qa/scenarios/runtime/docker-system-agent-first-run.yaml similarity index 62% rename from qa/scenarios/runtime/docker-crestodian-first-run.yaml rename to qa/scenarios/runtime/docker-system-agent-first-run.yaml index 1c77d9af8240..2cd532f1f44e 100644 --- a/qa/scenarios/runtime/docker-crestodian-first-run.yaml +++ b/qa/scenarios/runtime/docker-system-agent-first-run.yaml @@ -1,33 +1,33 @@ -title: Docker packaged Crestodian inference gate +title: Docker packaged OpenClaw inference gate scenario: - id: docker-crestodian-first-run + id: docker-system-agent-first-run surface: docker-podman-hosting category: docker-podman-hosting.container-setup coverage: secondary: - docker.first-run-onboarding - raspberry-pi.first-run-verification - objective: Verify a fresh package-installed Docker state gates Crestodian on a tested inference route, then supports planner-backed and typed setup operations without leaking secrets. + objective: Verify a fresh package-installed Docker state gates OpenClaw on a tested inference route, then supports planner-backed and typed setup operations without leaking secrets. successCriteria: - - The bare-command routing policy selects onboarding, and the packaged Crestodian CLI exits with onboarding guidance while inference is unavailable. + - The bare-command routing policy selects onboarding, and the packaged OpenClaw CLI exits with onboarding guidance while inference is unavailable. - The packaged activation module tests fake Claude before persisting its model, while workspace and Gateway setup remain untouched. - The modern compatibility entrypoint becomes available after activation. - A fuzzy request uses the verified planner for typed setup; subsequent one-shot commands write the default workspace/model and create the configured agent. - Discord setup is written through a SecretRef without persisting the raw token. - - Config validation and expected Crestodian audit entries succeed. + - Config validation and expected OpenClaw audit entries succeed. docsRefs: - docs/install/docker.md - - docs/cli/crestodian.md + - docs/cli/setup.md - docs/help/testing.md codeRefs: - - scripts/e2e/crestodian-first-run-docker.sh - - test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts - - scripts/e2e/crestodian-first-run-spec.json + - scripts/e2e/system-agent-first-run-docker.sh + - test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts + - scripts/e2e/system-agent-first-run-spec.json execution: kind: script path: test/e2e/qa-lab/runtime/docker-e2e-lane.ts - summary: Runs supporting packaged-CLI evidence for the inference gate, activation module, planner, and typed operations; it does not claim an interactive onboarding or Crestodian agent/tool/approval flow. + summary: Runs supporting packaged-CLI evidence for the inference gate, activation module, planner, and typed operations; it does not claim an interactive onboarding or OpenClaw agent/tool/approval flow. args: - --lane - - crestodian-first-run + - system-agent-first-run diff --git a/scripts/check-database-first-legacy-stores.mjs b/scripts/check-database-first-legacy-stores.mjs index 7c241abe1c49..7406fdae9bb7 100644 --- a/scripts/check-database-first-legacy-stores.mjs +++ b/scripts/check-database-first-legacy-stores.mjs @@ -92,7 +92,7 @@ const legacyStorePatterns = [ /\b(?:credentials\/oauth|github-copilot\.token|openrouter-models|auth-profiles|auth-state|exec-approvals|workspace-state)\.json\b/u, /\bcron\/(?:runs\/[^"'`]+\.jsonl|jobs\.json|jobs-state\.json)\b/u, /\b(?:process-leases|session-toggles|known-users|msteams-conversations|msteams-polls|msteams-sso-tokens|bot-storage|sync-store|thread-bindings|inbound-dedupe|startup-verification|storage-meta|crypto-idb-snapshot|command-deploy-cache|plugin-binding-approvals|plugins\/installs|config-health|port-guard|restart-sentinel|gateway-restart-intent|gateway-supervisor-restart-handoff)\.json\b/u, - /\b(?:calls|ref-index|audit\/file-transfer|audit\/crestodian)\.jsonl\b/u, + /\b(?:calls|ref-index|audit\/file-transfer|audit\/openclaw)\.jsonl\b/u, /\b(?:reply-cache|sent-echoes|events|claims)\.jsonl\b/u, /\bplugin-state\/state\.sqlite\b/u, /\btasks\/(?:runs\.sqlite|flows\/registry\.sqlite)\b/u, diff --git a/scripts/deadcode-exports.baseline.mjs b/scripts/deadcode-exports.baseline.mjs index 59ed0910354c..3503bb9ee3c0 100644 --- a/scripts/deadcode-exports.baseline.mjs +++ b/scripts/deadcode-exports.baseline.mjs @@ -335,10 +335,12 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/agents/mcp-ui-resource.ts: testing", "src/agents/media-generation-task-status-shared.ts: resetRecentMediaGenerationDuplicateGuardsForTests", "src/agents/model-fallback-observation.ts: resetModelFallbackDecisionLogCoalescingForTest", + "src/agents/model-fallback.ts: FallbackSummaryError", "src/agents/model-fallback.ts: testing", "src/agents/model-suppression.ts: clearModelSuppressionResolverCacheForTest", "src/agents/models-config-state.ts: resetModelsJsonReadyCacheForTest", "src/agents/models-config.plan.ts: planOpenClawModelsJsonWithDeps", + "src/agents/models-config.plan.ts: ResolveImplicitProvidersForModelsJson", "src/agents/models-config.plan.ts: resolveProvidersForModelsJsonWithDeps", "src/agents/models-config.providers.implicit.ts: resolvePluginMetadataProviderOwnersForTest", "src/agents/models-config.providers.implicit.ts: resolveProviderDiscoveryFilterForTest", @@ -354,10 +356,22 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/agents/session-suspension.ts: testing", "src/agents/session-write-lock.ts: resetSessionWriteLockStateForTest", "src/agents/session-write-lock.ts: testing", + "src/agents/sessions/keybindings.ts: AppKeybindings", + "src/agents/sessions/keybindings.ts: Keybinding", + "src/agents/sessions/keybindings.ts: KEYBINDINGS", + "src/agents/sessions/keybindings.ts: KeyId", + "src/agents/sessions/prompt-templates.ts: parseCommandArgs", + "src/agents/sessions/prompt-templates.ts: substituteArgs", "src/agents/sessions/tools/bash.ts: resolveBashTimeoutMs", "src/agents/shell-snapshot.ts: resetShellSnapshotCacheForTests", "src/agents/shell-snapshot.ts: resolveShellSnapshotDir", + "src/agents/shell-utils.ts: getPosixShellArgs", + "src/agents/shell-utils.ts: resolvePowerShellPath", + "src/agents/shell-utils.ts: resolveShellFromPath", + "src/agents/shell-utils.ts: resolveShellFromWhich", + "src/agents/shell-utils.ts: resolveWindowsBashPath", "src/agents/subagent-announce-delivery.ts: testing", + "src/agents/subagent-announce-dispatch.ts: mapSteerOutcomeToDeliveryResult", "src/agents/subagent-announce-output.ts: testing", "src/agents/subagent-attachments.ts: decodeStrictBase64", "src/agents/subagent-control.ts: testing", @@ -381,6 +395,11 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/agents/subagent-registry.ts: testing", "src/agents/subagent-spawn.ts: testing", "src/agents/system-prompt-config.ts: resolveAgentSystemPromptConfig", + "src/agents/tool-loop-detection.ts: CRITICAL_THRESHOLD", + "src/agents/tool-loop-detection.ts: GLOBAL_CIRCUIT_BREAKER_THRESHOLD", + "src/agents/tool-loop-detection.ts: hashToolCall", + "src/agents/tool-loop-detection.ts: TOOL_CALL_HISTORY_SIZE", + "src/agents/tool-loop-detection.ts: WARNING_THRESHOLD", "src/agents/tool-policy-pipeline.ts: resetToolPolicyWarningCacheForTest", "src/agents/tool-search.ts: testing", "src/agents/tool-search.ts: ToolSearchCatalogSession", @@ -389,6 +408,7 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/agents/tools/image-tool.ts: resolveImageModelConfigForTool", "src/agents/tools/image-tool.ts: testing", "src/agents/tools/model-config.helpers.ts: hasDirectProviderApiKeyAuthForTool", + "src/agents/tools/sessions-resolution.ts: looksLikeSessionKey", "src/agents/tools/sessions-resolution.ts: testing", "src/agents/tools/sessions-send-tool.a2a.ts: testing", "src/agents/tools/video-generate-tool.ts: resolveVideoGenerationModelConfigForTool", @@ -407,21 +427,45 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/auto-reply/reply/acp-reset-target.ts: testing", "src/auto-reply/reply/agent-runner-execution.ts: buildContextOverflowRecoveryText", "src/auto-reply/reply/agent-runner-execution.ts: computeContextAwareReserveTokensFloor", + "src/auto-reply/reply/agent-runner-execution.ts: MAX_LIVE_SWITCH_RETRIES", "src/auto-reply/reply/agent-runner-memory.ts: setAgentRunnerMemoryTestDeps", "src/auto-reply/reply/agent-runner-session-reset.ts: setAgentRunnerSessionResetTestDeps", "src/auto-reply/reply/commands-login.ts: testing", + "src/auto-reply/reply/dispatch-from-config.ts: getDispatcherFinalOutcomeCounts", "src/auto-reply/reply/dispatch-from-config.ts: testing", + "src/auto-reply/reply/get-reply-directives-apply.ts: formatModelOverrideResetEvent", + "src/auto-reply/reply/get-reply-fast-path.ts: markCompleteReplyConfig", + "src/auto-reply/reply/get-reply-fast-path.ts: withFastReplyConfig", + "src/auto-reply/reply/get-reply-run.ts: buildExecOverridePromptHint", + "src/auto-reply/reply/get-reply-run.ts: resolvePromptSessionContextForSystemEvent", + "src/auto-reply/reply/get-reply-run.ts: resolvePromptSilentReplyConversationType", + "src/auto-reply/reply/inbound-dedupe.ts: buildInboundDedupeKey", + "src/auto-reply/reply/progress-narrator.ts: createProgressNarrator", + "src/auto-reply/reply/prompt-prelude.ts: buildReplyPromptBodies", "src/auto-reply/reply/queue/cleanup.ts: testing", + "src/auto-reply/reply/queue/drain.ts: resolveFollowupAuthorizationKey", "src/auto-reply/reply/queue/enqueue.ts: resetRecentQueuedMessageIdDedupe", "src/auto-reply/reply/reply-run-registry.ts: testing", + "src/auto-reply/reply/stage-sandbox-media.ts: appendScpStderrTail", + "src/auto-reply/reply/stage-sandbox-media.ts: SCP_STDERR_TAIL_CHARS", "src/auto-reply/reply/stage-sandbox-media.ts: testing", "src/auto-reply/usage-bar/template.ts: clearUsageBarTemplateCacheForTest", "src/cli/channel-options.ts: testing", + "src/cli/command-path-policy.ts: resolveCliCatalogCommandPath", "src/cli/command-secret-gateway.ts: testing", + "src/cli/cron-cli/register.cron-simple.ts: loadCronJobForShow", + "src/cli/daemon-cli/response.ts: buildDaemonHintItems", "src/cli/gateway-cli/run.ts: testing", + "src/cli/nodes-camera.ts: CameraClipTarget", + "src/cli/nodes-camera.ts: CameraSnapTarget", + "src/cli/nodes-camera.ts: writeUrlToFile", "src/cli/plugins-install-command.ts: loadConfigForInstall", "src/cli/plugins-list-format.ts: formatPluginLine", + "src/cli/ports.ts: listPortListeners", + "src/cli/ports.ts: parseLsofOutput", + "src/cli/ports.ts: probePortFree", "src/cli/startup-metadata.ts: testing", + "src/cli/update-cli/progress.ts: inferUpdateFailureHints", "src/cli/update-cli/update-command-plugins.ts: buildInvalidConfigPostCoreUpdateResult", "src/cli/update-cli/update-command-plugins.ts: collectMissingPluginInstallPayloads", "src/cli/update-cli/update-command-plugins.ts: resolvePostSyncPluginUpdateSkipIds", @@ -467,20 +511,40 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/context-engine/registry.ts: ContextEngineRegistrationResult", "src/context-engine/registry.ts: getContextEngineFactory", "src/context-engine/registry.ts: listContextEngineIds", - "src/crestodian/agent-turn.ts: CrestodianAgentTurnDeps", - "src/crestodian/agent-turn.ts: CrestodianAgentTurnDirective", - "src/crestodian/agent-turn.ts: runCrestodianAgentTurnWithDeps", + "src/system-agent/agent-turn.ts: SystemAgentTurnDeps", + "src/system-agent/agent-turn.ts: SystemAgentTurnDirective", + "src/system-agent/agent-turn.ts: runSystemAgentTurnWithDeps", + "src/cron/command-output-summary.ts: cronCommandSummaryNeedsExternalRedaction", + "src/cron/delivery-context.ts: cronDeliveryFromContext", + "src/cron/delivery-preview.ts: resolveCronDeliveryPreview", + "src/cron/isolated-agent.ts: RunCronAgentTurnResult", "src/cron/isolated-agent/delivery-dispatch.ts: getCompletedDirectCronDeliveriesCountForTests", "src/cron/isolated-agent/delivery-dispatch.ts: resetCompletedDirectCronDeliveriesForTests", + "src/cron/isolated-agent/helpers.ts: pickDeliverablePayloads", + "src/cron/isolated-agent/helpers.ts: pickLastDeliverablePayload", + "src/cron/isolated-agent/helpers.ts: pickSummaryFromPayloads", + "src/cron/isolated-agent/run-executor.ts: createCronPromptExecutor", + "src/cron/isolated-agent/run.ts: resolveCronDeliveryContext", + "src/cron/isolated-agent/run.ts: RunCronAgentTurnResult", + "src/cron/job-session-bindings.ts: CronJobSessionBinding", + "src/cron/run-diagnostics.ts: MISSING_WEB_SEARCH_PROVIDER_DIAGNOSTIC_MESSAGE", "src/cron/schedule.ts: clearCronScheduleCacheForTest", "src/cron/schedule.ts: getCronScheduleCacheMaxForTest", "src/cron/schedule.ts: getCronScheduleCacheSizeForTest", "src/cron/schedule.ts: hasCronInCacheForTest", "src/cron/service/active-run-cancellation.ts: cancelActiveCronTaskRun", + "src/cron/service/active-run-cancellation.ts: CRON_TASK_RUN_SETTLEMENT_TRACKING_MAX_MS", "src/cron/service/active-run-cancellation.ts: resetActiveCronTaskRunsForTests", + "src/cron/service/timeout-policy.ts: AGENT_TURN_SAFETY_TIMEOUT_MS", + "src/cron/service/timeout-policy.ts: DEFAULT_JOB_TIMEOUT_MS", "src/cron/service/timer.ts: executeJobCore", "src/cron/service/timer.ts: onTimer", "src/cron/session-reaper.ts: resetReaperThrottle", + "src/cron/session-reaper.ts: resolveRetentionMs", + "src/cron/stagger.ts: isRecurringTopOfHourCronExpr", + "src/cron/store/row-codec.ts: bindScheduleColumns", + "src/cron/store/row-codec.ts: scheduleFromRow", + "src/cron/task-run-history.ts: ReadCronTaskRunHistoryPageOptions", "src/entry.compile-cache.ts: buildOpenClawCompileCacheRespawnPlan", "src/entry.compile-cache.ts: isNodeVersionAffectedByCompileCacheDeadlock", "src/entry.compile-cache.ts: isSourceCheckoutInstallRoot", @@ -509,10 +573,10 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/mcp/channel-bridge.ts: shouldRetryInitialMcpGatewayConnect", "src/mcp/channel-server.ts: createOpenClawChannelMcpServer", "src/mcp/channel-server.ts: OpenClawChannelBridge", - "src/mcp/openclaw-tools-serve-config.ts: OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV", + "src/mcp/openclaw-tools-serve-config.ts: OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV", "src/mcp/openclaw-tools-serve-config.ts: OpenClawToolsMcpToolId", - "src/mcp/openclaw-tools-serve-config.ts: resolveOpenClawToolsMcpCrestodianApproval", - "src/mcp/openclaw-tools-serve-config.ts: resolveOpenClawToolsMcpCrestodianSurface", + "src/mcp/openclaw-tools-serve-config.ts: resolveOpenClawToolsMcpSystemAgentApproval", + "src/mcp/openclaw-tools-serve-config.ts: resolveOpenClawToolsMcpSystemAgentSurface", "src/mcp/openclaw-tools-serve-config.ts: resolveOpenClawToolsMcpToolSelection", "src/media/input-files.ts: fetchWithGuard", "src/media/parse.ts: SplitMediaFromOutputOptions", diff --git a/scripts/e2e/crestodian-first-run-docker.sh b/scripts/e2e/system-agent-first-run-docker.sh similarity index 63% rename from scripts/e2e/crestodian-first-run-docker.sh rename to scripts/e2e/system-agent-first-run-docker.sh index e93b4a169c78..28e32f764ba9 100644 --- a/scripts/e2e/crestodian-first-run-docker.sh +++ b/scripts/e2e/system-agent-first-run-docker.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash -# Runs the Crestodian first-run Docker smoke against the package-installed +# Runs the OpenClaw first-run Docker smoke against the package-installed # functional E2E image, with only the test harness mounted from the checkout. set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh" -IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-crestodian-first-run-e2e" OPENCLAW_CRESTODIAN_FIRST_RUN_E2E_IMAGE)" -CONTAINER_NAME="openclaw-crestodian-first-run-e2e-$$" -RUN_LOG="$(mktemp -t openclaw-crestodian-first-run-log.XXXXXX)" +IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-system-agent-first-run-e2e" OPENCLAW_SYSTEM_AGENT_FIRST_RUN_E2E_IMAGE)" +CONTAINER_NAME="openclaw-system-agent-first-run-e2e-$$" +RUN_LOG="$(mktemp -t openclaw-system-agent-first-run-log.XXXXXX)" cleanup() { docker_e2e_docker_cmd rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true @@ -15,10 +15,10 @@ cleanup() { } trap cleanup EXIT -docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-first-run -OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 crestodian-first-run empty)" +docker_e2e_build_or_reuse "$IMAGE_NAME" system-agent-first-run +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 system-agent-first-run empty)" -echo "Running in-container Crestodian first-run smoke..." +echo "Running in-container OpenClaw first-run smoke..." # Harness files are mounted read-only; the app under test comes from /app/dist. set +e docker_e2e_run_with_harness \ @@ -28,13 +28,13 @@ docker_e2e_run_with_harness \ bash -lc "set -euo pipefail source scripts/lib/openclaw-e2e-instance.sh openclaw_e2e_eval_test_state_from_b64 \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" - tsx test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts + tsx test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts " >"$RUN_LOG" 2>&1 status=${PIPESTATUS[0]} set -e if [ "$status" -ne 0 ]; then - echo "Docker Crestodian first-run smoke failed" + echo "Docker OpenClaw first-run smoke failed" docker_e2e_print_log "$RUN_LOG" exit "$status" fi diff --git a/scripts/e2e/crestodian-first-run-spec.json b/scripts/e2e/system-agent-first-run-spec.json similarity index 64% rename from scripts/e2e/crestodian-first-run-spec.json rename to scripts/e2e/system-agent-first-run-spec.json index 68d5ab166d43..2210ab1576a0 100644 --- a/scripts/e2e/crestodian-first-run-spec.json +++ b/scripts/e2e/system-agent-first-run-spec.json @@ -1,43 +1,43 @@ { - "stateDirName": "crestodian-ring-zero-state", - "defaultWorkspaceName": "crestodian-main-workspace", - "agentWorkspaceName": "crestodian-reef-workspace", - "dockerDefaultWorkspace": "/tmp/openclaw-first-run", + "stateDirName": "system-agent-ring-zero-state", + "defaultWorkspaceName": "openclaw-main-workspace", + "agentWorkspaceName": "openclaw-reef-workspace", + "dockerDefaultWorkspace": "/tmp/system-agent-first-run", "dockerAgentWorkspace": "/tmp/openclaw-reef", "agentId": "reef", "model": "claude-cli/claude-opus-4-8", "telegramEnv": "TELEGRAM_BOT_TOKEN", - "telegramToken": "123456:openclaw-crestodian-telegram-e2e-token", + "telegramToken": "123456:openclaw-openclaw-telegram-e2e-token", "commands": [ { "id": "setup", "message": "please finish basic setup with workspace {defaultWorkspace}", - "expectOutput": "[crestodian] done: crestodian.setup", + "expectOutput": "[openclaw] done: openclaw.setup", "approve": true, "planner": true }, { "id": "default-model", "message": "set default model {model}", - "expectOutput": "[crestodian] done: config.setDefaultModel", + "expectOutput": "[openclaw] done: config.setDefaultModel", "approve": true }, { "id": "agent", "message": "create agent {agentId} workspace {agentWorkspace}", - "expectOutput": "[crestodian] done: agents.create", + "expectOutput": "[openclaw] done: agents.create", "approve": true }, { "id": "telegram-token", "message": "config set-ref channels.telegram.botToken env {telegramEnv}", - "expectOutput": "[crestodian] done: config.setRef", + "expectOutput": "[openclaw] done: config.setRef", "approve": true }, { "id": "telegram-enabled", "message": "config set channels.telegram.enabled true", - "expectOutput": "[crestodian] done: config.set", + "expectOutput": "[openclaw] done: config.set", "approve": true }, { @@ -48,7 +48,7 @@ } ], "auditOperations": [ - "crestodian.setup", + "openclaw.setup", "config.setDefaultModel", "agents.create", "config.setRef", diff --git a/scripts/e2e/crestodian-rescue-docker-client.ts b/scripts/e2e/system-agent-rescue-docker-client.ts similarity index 67% rename from scripts/e2e/crestodian-rescue-docker-client.ts rename to scripts/e2e/system-agent-rescue-docker-client.ts index 7de76b7091f5..6c137e4d82b9 100644 --- a/scripts/e2e/crestodian-rescue-docker-client.ts +++ b/scripts/e2e/system-agent-rescue-docker-client.ts @@ -1,15 +1,15 @@ -// Crestodian rescue-message Docker harness. +// OpenClaw rescue-message Docker harness. // Imports packaged dist modules so the Docker lane verifies the npm tarball, // while this small test driver stays mounted from the checkout. import fs from "node:fs/promises"; import path from "node:path"; -import { handleCrestodianCommand } from "../../dist/auto-reply/reply/commands-crestodian.js"; +import { handleSystemAgentCommand } from "../../dist/auto-reply/reply/commands-system-agent.js"; import { clearConfigCache } from "../../dist/config/config.js"; import type { OpenClawConfig } from "../../dist/config/types.openclaw.js"; -import { runCrestodianRescueMessage } from "../../dist/crestodian/rescue-message.js"; +import { runSystemAgentRescueMessage } from "../../dist/system-agent/rescue-message.js"; import { createE2eStateDir } from "./lib/temp-state-dir.ts"; -type CommandResult = Awaited>; +type CommandResult = Awaited>; function assert(condition: unknown, message: string): asserts condition { if (!condition) { @@ -35,11 +35,11 @@ function makeParams(commandBody: string, cfg: OpenClawConfig, isGroup = false) { }, agentId: "default", isGroup, - } as Parameters[0]; + } as Parameters[0]; } async function invoke(commandBody: string, cfg: OpenClawConfig, isGroup = false): Promise { - const result: CommandResult = await handleCrestodianCommand( + const result: CommandResult = await handleSystemAgentCommand( makeParams(commandBody, cfg, isGroup), true, ); @@ -51,7 +51,7 @@ async function invoke(commandBody: string, cfg: OpenClawConfig, isGroup = false) } async function main() { - const tempState = await createE2eStateDir("openclaw-crestodian-"); + const tempState = await createE2eStateDir("openclaw-openclaw-"); tempState.registerExitCleanup(); const stateDir = tempState.stateDir; const configPath = process.env.OPENCLAW_CONFIG_PATH ?? path.join(stateDir, "openclaw.json"); @@ -71,71 +71,71 @@ async function main() { ); clearConfigCache(); - const denied = await invoke("/crestodian status", { - crestodian: { rescue: { enabled: true } }, + const denied = await invoke("/openclaw status", { + systemAgent: { rescue: { enabled: true } }, agents: { defaults: { sandbox: { mode: "all" } } }, }); assert(denied.includes("sandboxing is active"), "sandboxed rescue was not denied"); const cfg: OpenClawConfig = {}; - const refusedTui = await invoke("/crestodian talk to agent", cfg); + const refusedTui = await invoke("/openclaw talk to agent", cfg); assert( refusedTui.includes("cannot open the local TUI"), "remote rescue TUI handoff was not refused", ); - const plan = await invoke("/crestodian set default model openai/gpt-5.2", cfg); + const plan = await invoke("/openclaw set default model openai/gpt-5.2", cfg); assert( - plan.includes("Reply /crestodian yes to apply"), + plan.includes("Reply /openclaw yes to apply"), "persistent change did not require approval", ); - const applied = await invoke("/crestodian yes", cfg); + const applied = await invoke("/openclaw yes", cfg); assert(applied.includes("Default model: openai/gpt-5.2"), "approved change did not apply"); - const configValid = await invoke("/crestodian validate config", cfg); + const configValid = await invoke("/openclaw validate config", cfg); assert(configValid.includes("Config valid:"), "config validation did not report valid config"); - const configSetPlan = await invoke("/crestodian config set gateway.port 19001", cfg); + const configSetPlan = await invoke("/openclaw config set gateway.port 19001", cfg); assert( - configSetPlan.includes("Reply /crestodian yes to apply"), + configSetPlan.includes("Reply /openclaw yes to apply"), "generic config set did not require approval", ); - const configSetApplied = await invoke("/crestodian yes", cfg); - assert(configSetApplied.includes("[crestodian] done: config.set"), "generic config set failed"); + const configSetApplied = await invoke("/openclaw yes", cfg); + assert(configSetApplied.includes("[openclaw] done: config.set"), "generic config set failed"); const refPlan = await invoke( - "/crestodian config set-ref gateway.auth.token env OPENCLAW_GATEWAY_TOKEN", + "/openclaw config set-ref gateway.auth.token env OPENCLAW_GATEWAY_TOKEN", cfg, ); assert( - refPlan.includes("Reply /crestodian yes to apply"), + refPlan.includes("Reply /openclaw yes to apply"), "SecretRef set did not require approval", ); - const refApplied = await invoke("/crestodian yes", cfg); - assert(refApplied.includes("[crestodian] done: config.setRef"), "SecretRef set failed"); + const refApplied = await invoke("/openclaw yes", cfg); + assert(refApplied.includes("[openclaw] done: config.setRef"), "SecretRef set failed"); - const agentPlan = await invoke("/crestodian create agent work workspace /tmp/openclaw-work", cfg); + const agentPlan = await invoke("/openclaw create agent work workspace /tmp/openclaw-work", cfg); assert( - agentPlan.includes("Reply /crestodian yes to apply"), + agentPlan.includes("Reply /openclaw yes to apply"), "agent creation did not require approval", ); - const agentApplied = await invoke("/crestodian yes", cfg); - assert(agentApplied.includes("[crestodian] done: agents.create"), "agent creation did not apply"); + const agentApplied = await invoke("/openclaw yes", cfg); + assert(agentApplied.includes("[openclaw] done: agents.create"), "agent creation did not apply"); const setupPlan = await invoke( - "/crestodian setup workspace /tmp/openclaw-setup model openai/gpt-5.2", + "/openclaw setup workspace /tmp/openclaw-setup model openai/gpt-5.2", cfg, ); - assert(setupPlan.includes("Reply /crestodian yes to apply"), "setup did not require approval"); - const setupApplied = await invoke("/crestodian yes", cfg); - assert(setupApplied.includes("[crestodian] done: crestodian.setup"), "setup did not apply"); + assert(setupPlan.includes("Reply /openclaw yes to apply"), "setup did not require approval"); + const setupApplied = await invoke("/openclaw yes", cfg); + assert(setupApplied.includes("[openclaw] done: openclaw.setup"), "setup did not apply"); const gatewayRestarts: string[] = []; - const gatewayCommand = makeParams("/crestodian restart gateway", cfg).command; - const gatewayPlan = await runCrestodianRescueMessage({ + const gatewayCommand = makeParams("/openclaw restart gateway", cfg).command; + const gatewayPlan = await runSystemAgentRescueMessage({ cfg, command: gatewayCommand, - commandBody: "/crestodian restart gateway", + commandBody: "/openclaw restart gateway", agentId: "default", isGroup: false, deps: { @@ -145,13 +145,13 @@ async function main() { }, }); assert( - gatewayPlan?.includes("Reply /crestodian yes to apply"), + gatewayPlan?.includes("Reply /openclaw yes to apply"), "gateway restart did not require approval", ); - const gatewayApplied = await runCrestodianRescueMessage({ + const gatewayApplied = await runSystemAgentRescueMessage({ cfg, command: gatewayCommand, - commandBody: "/crestodian yes", + commandBody: "/openclaw yes", agentId: "default", isGroup: false, deps: { @@ -161,17 +161,17 @@ async function main() { }, }); assert( - gatewayApplied?.includes("[crestodian] done: gateway.restart"), + gatewayApplied?.includes("[openclaw] done: gateway.restart"), "gateway restart did not apply", ); assert(gatewayRestarts.length === 1, "gateway restart dependency was not invoked once"); const doctorRuns: string[] = []; - const doctorCommand = makeParams("/crestodian doctor fix", cfg).command; - const doctorPlan = await runCrestodianRescueMessage({ + const doctorCommand = makeParams("/openclaw doctor fix", cfg).command; + const doctorPlan = await runSystemAgentRescueMessage({ cfg, command: doctorCommand, - commandBody: "/crestodian doctor fix", + commandBody: "/openclaw doctor fix", agentId: "default", isGroup: false, deps: { @@ -181,13 +181,13 @@ async function main() { }, }); assert( - doctorPlan?.includes("Reply /crestodian yes to apply"), + doctorPlan?.includes("Reply /openclaw yes to apply"), "doctor fix did not require approval", ); - const doctorApplied = await runCrestodianRescueMessage({ + const doctorApplied = await runSystemAgentRescueMessage({ cfg, command: doctorCommand, - commandBody: "/crestodian yes", + commandBody: "/openclaw yes", agentId: "default", isGroup: false, deps: { @@ -196,7 +196,7 @@ async function main() { }, }, }); - assert(doctorApplied?.includes("[crestodian] done: doctor.fix"), "doctor fix did not apply"); + assert(doctorApplied?.includes("[openclaw] done: doctor.fix"), "doctor fix did not apply"); assert(doctorRuns.join(",") === "repair", "doctor repair dependency was not invoked once"); const updatedConfig = JSON.parse(await fs.readFile(configPath, "utf8")) as OpenClawConfig; @@ -226,7 +226,7 @@ async function main() { "agent config was not updated", ); - const auditPath = path.join(stateDir, "audit", "crestodian.jsonl"); + const auditPath = path.join(stateDir, "audit", "system-agent.jsonl"); const auditLines = (await fs.readFile(auditPath, "utf8")).trim().split("\n"); assert(auditLines.length >= 2, "audit log did not record both operations"); const audits = auditLines.map((line) => JSON.parse(line)); @@ -243,7 +243,7 @@ async function main() { "SecretRef config audit missing", ); assert( - audits.some((audit) => audit.operation === "crestodian.setup"), + audits.some((audit) => audit.operation === "openclaw.setup"), "setup audit missing", ); const agentAudit = audits.find((audit) => audit.operation === "agents.create"); @@ -261,7 +261,7 @@ async function main() { "doctor fix audit missing", ); - console.log("Crestodian rescue Docker E2E passed"); + console.log("OpenClaw rescue Docker E2E passed"); } main().catch((err: unknown) => { diff --git a/scripts/e2e/crestodian-rescue-docker.sh b/scripts/e2e/system-agent-rescue-docker.sh similarity index 62% rename from scripts/e2e/crestodian-rescue-docker.sh rename to scripts/e2e/system-agent-rescue-docker.sh index ad23a36e526c..9edf13b327e7 100755 --- a/scripts/e2e/crestodian-rescue-docker.sh +++ b/scripts/e2e/system-agent-rescue-docker.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash -# Runs the Crestodian rescue-message Docker smoke against the package-installed +# Runs the OpenClaw rescue-message Docker smoke against the package-installed # functional E2E image, with only the test harness mounted from the checkout. set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh" -IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-crestodian-rescue-e2e" OPENCLAW_CRESTODIAN_RESCUE_E2E_IMAGE)" -CONTAINER_NAME="openclaw-crestodian-rescue-e2e-$$" -RUN_LOG="$(mktemp -t openclaw-crestodian-rescue-log.XXXXXX)" +IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-system-agent-rescue-e2e" OPENCLAW_SYSTEM_AGENT_RESCUE_E2E_IMAGE)" +CONTAINER_NAME="openclaw-system-agent-rescue-e2e-$$" +RUN_LOG="$(mktemp -t openclaw-system-agent-rescue-log.XXXXXX)" cleanup() { docker_e2e_docker_cmd rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true @@ -15,27 +15,27 @@ cleanup() { } trap cleanup EXIT -docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-rescue -OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 crestodian-rescue empty)" +docker_e2e_build_or_reuse "$IMAGE_NAME" system-agent-rescue +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 system-agent-rescue empty)" -echo "Running in-container Crestodian rescue smoke..." +echo "Running in-container OpenClaw rescue smoke..." # Harness files are mounted read-only; the app under test comes from /app/dist. set +e docker_e2e_run_with_harness \ --name "$CONTAINER_NAME" \ -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ - -e "OPENCLAW_GATEWAY_TOKEN=crestodian-rescue-token" \ + -e "OPENCLAW_GATEWAY_TOKEN=system-agent-rescue-token" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail source scripts/lib/openclaw-e2e-instance.sh openclaw_e2e_eval_test_state_from_b64 \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" - tsx scripts/e2e/crestodian-rescue-docker-client.ts + tsx scripts/e2e/system-agent-rescue-docker-client.ts " >"$RUN_LOG" 2>&1 status=${PIPESTATUS[0]} set -e if [ "$status" -ne 0 ]; then - echo "Docker Crestodian rescue smoke failed" + echo "Docker OpenClaw rescue smoke failed" docker_e2e_print_log "$RUN_LOG" exit "$status" fi diff --git a/scripts/lib/docker-e2e-scenarios.mjs b/scripts/lib/docker-e2e-scenarios.mjs index 123d0f14f0bc..ce7bf4ba6736 100644 --- a/scripts/lib/docker-e2e-scenarios.mjs +++ b/scripts/lib/docker-e2e-scenarios.mjs @@ -456,7 +456,7 @@ export const mainLanes = [ stateScenario: "empty", }, ), - lane("crestodian-rescue", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-rescue", { + lane("system-agent-rescue", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:system-agent-rescue", { stateScenario: "empty", }), serviceLane( @@ -523,8 +523,8 @@ export const mainLanes = [ stateScenario: "empty", }), lane( - "crestodian-first-run", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-first-run", + "system-agent-first-run", + "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:system-agent-first-run", { stateScenario: "empty" }, ), lane( diff --git a/scripts/test-live-shard.mjs b/scripts/test-live-shard.mjs index 61ad4bf6c5bd..f02a9b4d75d0 100644 --- a/scripts/test-live-shard.mjs +++ b/scripts/test-live-shard.mjs @@ -30,7 +30,7 @@ const OPTIONAL_LIVE_SHARD_FILE_ENVS = new Map([ ["src/agents/tools/image-tool.ollama.live.test.ts", ["OPENCLAW_LIVE_OLLAMA_IMAGE"]], ["src/agents/tools/image-tool.providers.live.test.ts", ["OPENCLAW_LIVE_IMAGE_TOOL_TEST"]], ["src/skills/workshop/experience-review.live.test.ts", ["OPENCLAW_LIVE_SKILL_EXPERIENCE_REVIEW"]], - ["src/crestodian/rescue-channel.live.test.ts", ["OPENCLAW_LIVE_CRESTODIAN_RESCUE_CHANNEL"]], + ["src/system-agent/rescue-channel.live.test.ts", ["OPENCLAW_LIVE_SYSTEM_AGENT_RESCUE_CHANNEL"]], ["src/gateway/android-node.capabilities.live.test.ts", ["OPENCLAW_LIVE_ANDROID_NODE"]], ["src/gateway/gateway-acp-bind.live.test.ts", ["OPENCLAW_LIVE_ACP_BIND"]], ["src/gateway/gateway-acp-spawn-defaults.live.test.ts", ["OPENCLAW_LIVE_ACP_SPAWN_DEFAULTS"]], @@ -267,12 +267,12 @@ export function selectLiveShardFiles(shard, files = collectAllLiveTestFiles()) { return files.filter((file) => file === "src/agents/zai.live.test.ts"); case "native-live-src-gateway": return files.filter( - (file) => file.startsWith("src/gateway/") || file.startsWith("src/crestodian/"), + (file) => file.startsWith("src/gateway/") || file.startsWith("src/system-agent/"), ); case "native-live-src-gateway-core": return files.filter( (file) => - (file.startsWith("src/gateway/") || file.startsWith("src/crestodian/")) && + (file.startsWith("src/gateway/") || file.startsWith("src/system-agent/")) && !isGatewayBackendLiveTest(file) && !isGatewayProfilesLiveTest(file), ); diff --git a/scripts/test-projects.test-support.mjs b/scripts/test-projects.test-support.mjs index cadac34e1c8a..cbc26018f732 100644 --- a/scripts/test-projects.test-support.mjs +++ b/scripts/test-projects.test-support.mjs @@ -855,54 +855,54 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([ ["test/scripts/docker-build-helper.test.ts", "test/scripts/docker-e2e-plan.test.ts"], ], [ - "scripts/e2e/crestodian-first-run-docker.sh", + "scripts/e2e/system-agent-first-run-docker.sh", [ "test/scripts/docker-build-helper.test.ts", "test/scripts/docker-e2e-plan.test.ts", - "test/scripts/docker-e2e-crestodian.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", ], ], [ - "test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts", + "test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts", [ - "test/scripts/docker-e2e-crestodian.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", "src/cli/program/register.onboard.test.ts", "src/cli/run-main.test.ts", "src/cli/run-main.exit.test.ts", - "src/commands/crestodian-with-inference.test.ts", - "src/crestodian/assistant.configured.test.ts", - "src/crestodian/assistant.test.ts", - "src/crestodian/crestodian.test.ts", - "src/crestodian/operations.test.ts", - "src/crestodian/overview.test.ts", - "src/crestodian/setup-inference.test.ts", - "src/crestodian/audit.test.ts", + "src/commands/system-agent-with-inference.test.ts", + "src/system-agent/assistant.configured.test.ts", + "src/system-agent/assistant.test.ts", + "src/system-agent/system-agent.test.ts", + "src/system-agent/operations.test.ts", + "src/system-agent/overview.test.ts", + "src/system-agent/setup-inference.test.ts", + "src/system-agent/audit.test.ts", ], ], [ - "scripts/e2e/crestodian-first-run-spec.json", + "scripts/e2e/system-agent-first-run-spec.json", [ - "test/scripts/docker-e2e-crestodian.test.ts", - "src/crestodian/operations.test.ts", - "src/crestodian/audit.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", + "src/system-agent/operations.test.ts", + "src/system-agent/audit.test.ts", ], ], [ - "scripts/e2e/crestodian-rescue-docker.sh", + "scripts/e2e/system-agent-rescue-docker.sh", [ "test/scripts/docker-build-helper.test.ts", "test/scripts/docker-e2e-plan.test.ts", - "test/scripts/docker-e2e-crestodian.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", ], ], [ - "scripts/e2e/crestodian-rescue-docker-client.ts", + "scripts/e2e/system-agent-rescue-docker-client.ts", [ - "test/scripts/docker-e2e-crestodian.test.ts", - "src/crestodian/rescue-policy.test.ts", - "src/crestodian/rescue-message.test.ts", - "src/crestodian/operations.test.ts", - "src/crestodian/audit.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", + "src/system-agent/rescue-policy.test.ts", + "src/system-agent/rescue-message.test.ts", + "src/system-agent/operations.test.ts", + "src/system-agent/audit.test.ts", ], ], [ diff --git a/src/agents/agent-tools.create-openclaw-coding-tools.test.ts b/src/agents/agent-tools.create-openclaw-coding-tools.test.ts index 71dab6a4378f..1c79dde1f8f0 100644 --- a/src/agents/agent-tools.create-openclaw-coding-tools.test.ts +++ b/src/agents/agent-tools.create-openclaw-coding-tools.test.ts @@ -418,14 +418,14 @@ describe("createOpenClawCodingTools", () => { it("keeps the injected ring-zero tool under policy and rejects a same-name replacement", () => { const injectedTool = { - ...stubTool("crestodian"), - label: "Crestodian", + ...stubTool("openclaw"), + label: "OpenClaw", description: "trusted ring-zero tool", execute: async () => ({ content: [], details: {} }), }; const duplicateTool = { - ...stubTool("crestodian"), - label: "Crestodian", + ...stubTool("openclaw"), + label: "OpenClaw", description: "duplicate plugin tool", execute: async () => ({ content: [], details: {} }), }; @@ -433,8 +433,8 @@ describe("createOpenClawCodingTools", () => { const tools = runWithAgentRingZeroTools([injectedTool], () => createOpenClawCodingTools({ - config: { tools: { allow: ["read"], deny: ["crestodian"] } }, - runtimeToolAllowlist: ["crestodian"], + config: { tools: { allow: ["read"], deny: ["openclaw"] } }, + runtimeToolAllowlist: ["openclaw"], toolConstructionPlan: { includeBaseCodingTools: false, includeShellTools: false, @@ -446,7 +446,7 @@ describe("createOpenClawCodingTools", () => { ); expect(tools).toHaveLength(1); - expect(tools[0]?.name).toBe("crestodian"); + expect(tools[0]?.name).toBe("openclaw"); expect(tools[0]?.description).toBe("trusted ring-zero tool"); }); diff --git a/src/agents/agent-tools.ts b/src/agents/agent-tools.ts index 888cb89e5ac7..adc1a65e9658 100644 --- a/src/agents/agent-tools.ts +++ b/src/agents/agent-tools.ts @@ -424,8 +424,8 @@ type OpenClawCodingToolsOptions = { toolSearchCatalogRef?: ToolSearchCatalogRef; /** Limits which tool families are materialized before the shared policy pipeline runs. */ toolConstructionPlan?: OpenClawCodingToolConstructionPlan; - /** Ring-zero Crestodian tool; set only by the Crestodian agent runner. */ - crestodianTool?: import("./tools/crestodian-tool.js").CrestodianToolOptions; + /** Ring-zero OpenClaw tool; set only by the OpenClaw agent runner. */ + systemAgentTool?: import("./tools/system-agent-tool.js").SystemAgentToolOptions; /** Trusted sender identity bit for command/channel-action auth and owner-gated plugin tools. */ senderIsOwner?: boolean; /** Auth profiles already loaded for this run; used for prompt-time tool availability. */ @@ -928,7 +928,7 @@ function createOpenClawCodingToolsInternal(options?: OpenClawCodingToolsOptions) ? mergeAgentRingZeroTools( ringZeroTools, createOpenClawTools({ - ...(options?.crestodianTool ? { crestodianTool: options.crestodianTool } : {}), + ...(options?.systemAgentTool ? { systemAgentTool: options.systemAgentTool } : {}), sandboxBrowserBridgeUrl: sandbox?.browser?.bridgeUrl, allowHostBrowserControl: sandbox ? sandbox.browserAllowHostControl : true, agentSessionKey: options?.sessionKey, diff --git a/src/agents/cli-auth-epoch.test.ts b/src/agents/cli-auth-epoch.test.ts index 3bb8b4b174fd..beac61a4368b 100644 --- a/src/agents/cli-auth-epoch.test.ts +++ b/src/agents/cli-auth-epoch.test.ts @@ -1001,7 +1001,7 @@ describe("resolveCliAuthEpoch", () => { const fingerprint = await resolveCliRuntimeOwnerFingerprint({ provider: "claude-cli", config, - agentId: "crestodian", + agentId: "openclaw", }); expectCliAuthEpoch(fingerprint); @@ -1009,7 +1009,7 @@ describe("resolveCliAuthEpoch", () => { resolveCliRuntimeOwnerFingerprint({ provider: "claude-cli", config, - agentId: "crestodian", + agentId: "openclaw", runtimeOwnerId: "replacement-backend", }), ).resolves.toBeUndefined(); @@ -1029,13 +1029,13 @@ describe("resolveCliAuthEpoch", () => { const first = await resolveCliRuntimeOwnerFingerprint({ provider: "claude-cli", config, - agentId: "crestodian", + agentId: "openclaw", env: { PATH: firstBin }, }); const second = await resolveCliRuntimeOwnerFingerprint({ provider: "claude-cli", config, - agentId: "crestodian", + agentId: "openclaw", env: { PATH: secondBin }, }); @@ -1056,7 +1056,7 @@ describe("resolveCliAuthEpoch", () => { const first = await resolveCliRuntimeOwnerFingerprint({ provider: "claude-cli", config, - agentId: "crestodian", + agentId: "openclaw", }); copyNativeExecutable(executable, nativeUtility("false")); if (nativeUtility("true") === nativeUtility("false")) { @@ -1065,7 +1065,7 @@ describe("resolveCliAuthEpoch", () => { const second = await resolveCliRuntimeOwnerFingerprint({ provider: "claude-cli", config, - agentId: "crestodian", + agentId: "openclaw", }); expectCliAuthEpoch(first); @@ -1110,7 +1110,7 @@ describe("resolveCliAuthEpoch", () => { resolveCliRuntimeOwnerFingerprint({ provider: "claude-cli", config: cliConfig("./claude"), - agentId: "crestodian", + agentId: "openclaw", cwd: dir, }), ).resolves.toBeUndefined(); @@ -1128,7 +1128,7 @@ describe("resolveCliAuthEpoch", () => { resolveCliRuntimeOwnerFingerprint({ provider: "claude-cli", config: cliConfig(process.execPath), - agentId: "crestodian", + agentId: "openclaw", authProfileId: "anthropic:missing", }), ).resolves.toBeUndefined(); diff --git a/src/agents/cli-executable-identity.ts b/src/agents/cli-executable-identity.ts index db7addfbba69..85fd16177fde 100644 --- a/src/agents/cli-executable-identity.ts +++ b/src/agents/cli-executable-identity.ts @@ -161,7 +161,7 @@ function resolveCommandPath(params: { return undefined; } if (hasPathSeparator(params.command) && !isDurableRootedCommand(params.command)) { - // The setup probe and later Crestodian turns intentionally use different + // The setup probe and later OpenClaw turns intentionally use different // workspaces. A cwd-relative executable cannot name one durable owner. return undefined; } diff --git a/src/agents/cli-runner.spawn.test.ts b/src/agents/cli-runner.spawn.test.ts index e252011a2cb6..b389a1dd6c28 100644 --- a/src/agents/cli-runner.spawn.test.ts +++ b/src/agents/cli-runner.spawn.test.ts @@ -1058,7 +1058,7 @@ describe("runCliAgent spawn path", () => { mockSuccessfulClaudeJsonlRun(); const toolAvailability: NonNullable = { native: [], - mcp: ["mcp__openclaw__crestodian"], + mcp: ["mcp__openclaw__openclaw"], }; const resolveExecutionArgs = vi.fn(({ baseArgs }) => baseArgs); @@ -1088,7 +1088,7 @@ describe("runCliAgent spawn path", () => { runId: "run-claude-tool-policy-refused", cliToolAvailability: { native: [], - mcp: ["mcp__openclaw__crestodian"], + mcp: ["mcp__openclaw__openclaw"], }, resolveExecutionArgs, }), diff --git a/src/agents/cli-runner/bundle-mcp.resume.test.ts b/src/agents/cli-runner/bundle-mcp.resume.test.ts index f377429a0b24..0bb71917e0dd 100644 --- a/src/agents/cli-runner/bundle-mcp.resume.test.ts +++ b/src/agents/cli-runner/bundle-mcp.resume.test.ts @@ -1,6 +1,6 @@ /** Tests bundle-MCP resume hash stability across loopback endpoint changes. */ import { describe, expect, it } from "vitest"; -import { buildCrestodianToolsMcpServerConfig } from "../../mcp/openclaw-tools-serve-config.js"; +import { buildSystemAgentToolsMcpServerConfig } from "../../mcp/openclaw-tools-serve-config.js"; import { resolveCliSessionReuse } from "../cli-session.js"; import { prepareCliBundleMcpConfig } from "./bundle-mcp.js"; import { @@ -83,14 +83,14 @@ describe("prepareCliBundleMcpConfig resume hash", () => { await second.cleanup?.(); }); - it("keeps Crestodian approval state out of the resume identity", async () => { - const prepare = async (options: Parameters[0]) => + it("keeps OpenClaw approval state out of the resume identity", async () => { + const prepare = async (options: Parameters[0]) => await prepareCliBundleMcpConfig({ enabled: true, mode: "claude-config-file", backend: { command: "node", args: ["./fake-claude.mjs"] }, workspaceDir: cliBundleMcpHarness.bundleProbeWorkspaceDir, - exclusiveConfig: buildCrestodianToolsMcpServerConfig(options), + exclusiveConfig: buildSystemAgentToolsMcpServerConfig(options), }); const proposed = "proposal-sha256"; const first = await prepare({ surface: "cli", proposalRef: {} }); diff --git a/src/agents/cli-runner/bundle-mcp.test.ts b/src/agents/cli-runner/bundle-mcp.test.ts index ef8f98398703..d9066b0f9d01 100644 --- a/src/agents/cli-runner/bundle-mcp.test.ts +++ b/src/agents/cli-runner/bundle-mcp.test.ts @@ -62,7 +62,7 @@ describe("prepareCliBundleMcpConfig", () => { workspaceDir, config: { plugins: { enabled: false } }, exclusiveConfig: { - mcpServers: { openclaw: { command: "node", args: ["crestodian.mjs"] } }, + mcpServers: { openclaw: { command: "node", args: ["openclaw.mjs"] } }, }, }); @@ -72,7 +72,7 @@ describe("prepareCliBundleMcpConfig", () => { mcpServers?: Record; }; expect(Object.keys(raw.mcpServers ?? {})).toEqual(["openclaw"]); - expect(raw.mcpServers?.openclaw?.args).toEqual(["crestodian.mjs"]); + expect(raw.mcpServers?.openclaw?.args).toEqual(["openclaw.mjs"]); expect(prepared.mcpConfigHash).toMatch(/^[0-9a-f]{64}$/); await prepared.cleanup?.(); diff --git a/src/agents/cli-runner/bundle-mcp.ts b/src/agents/cli-runner/bundle-mcp.ts index 3c1edf26c6f6..257019008b65 100644 --- a/src/agents/cli-runner/bundle-mcp.ts +++ b/src/agents/cli-runner/bundle-mcp.ts @@ -11,8 +11,8 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { formatErrorMessage } from "../../infra/errors.js"; import { tryReadJson } from "../../infra/json-files.js"; import { - OPENCLAW_TOOLS_MCP_CRESTODIAN_APPROVAL_ARMED_ENV, - OPENCLAW_TOOLS_MCP_CRESTODIAN_PROPOSAL_ENV, + OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV, + OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV, OPENCLAW_TOOLS_MCP_TOOLS_ENV, } from "../../mcp/openclaw-tools-serve-config.js"; import { extractMcpServerMap, type BundleMcpConfig } from "../../plugins/bundle-mcp.js"; @@ -69,10 +69,10 @@ function normalizeOpenClawLoopbackUrl(value: string): string { return `${match[1]}:${match[2]}`; } -function canonicalizeCrestodianTurnStateForResume( +function canonicalizeSystemAgentTurnStateForResume( server: BundleMcpConfig["mcpServers"][string], ): BundleMcpConfig["mcpServers"][string] { - if (!isRecord(server.env) || server.env[OPENCLAW_TOOLS_MCP_TOOLS_ENV] !== "crestodian") { + if (!isRecord(server.env) || server.env[OPENCLAW_TOOLS_MCP_TOOLS_ENV] !== "openclaw") { return server; } // The host reissues approval authority through a fresh stdio server each turn. @@ -81,8 +81,8 @@ function canonicalizeCrestodianTurnStateForResume( ...server, env: { ...server.env, - [OPENCLAW_TOOLS_MCP_CRESTODIAN_APPROVAL_ARMED_ENV]: "", - [OPENCLAW_TOOLS_MCP_CRESTODIAN_PROPOSAL_ENV]: "", + [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV]: "", + [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV]: "", }, }; } @@ -92,7 +92,7 @@ function canonicalizeBundleMcpConfigForResume(config: BundleMcpConfig): BundleMc // hashing so resume compatibility tracks config shape, not ephemeral ports. const canonicalServers = Object.fromEntries( Object.entries(config.mcpServers).map(([name, server]) => { - const canonicalServer = canonicalizeCrestodianTurnStateForResume(server); + const canonicalServer = canonicalizeSystemAgentTurnStateForResume(server); if (name !== "openclaw" || typeof canonicalServer.url !== "string") { return [name, sortJsonValue(canonicalServer)]; } @@ -212,8 +212,8 @@ export async function prepareCliBundleMcpConfig(params: { additionalConfig?: BundleMcpConfig; /** * Serve exactly these servers, skipping user/plugin/additional merges. - * Ring-zero Crestodian runs use this so the CLI harness sees only the - * crestodian MCP server instead of the normal openclaw tool surface. + * Ring-zero OpenClaw runs use this so the CLI harness sees only the + * openclaw MCP server instead of the normal openclaw tool surface. */ exclusiveConfig?: BundleMcpConfig; env?: Record; diff --git a/src/agents/cli-runner/prepare.test.ts b/src/agents/cli-runner/prepare.test.ts index c30a1eb8bd32..8392e0fd732d 100644 --- a/src/agents/cli-runner/prepare.test.ts +++ b/src/agents/cli-runner/prepare.test.ts @@ -39,7 +39,7 @@ import { resetContextWindowCacheForTest } from "../context.js"; import { buildActiveImageGenerationTaskPromptContextForSession } from "../image-generation-task-status.js"; import { buildActiveMusicGenerationTaskPromptContextForSession } from "../music-generation-task-status.js"; import type { SandboxWorkspaceInfo } from "../sandbox/types.js"; -import type { CrestodianToolOptions } from "../tools/crestodian-tool.js"; +import type { SystemAgentToolOptions } from "../tools/system-agent-tool.js"; import { buildActiveVideoGenerationTaskPromptContextForSession } from "../video-generation-task-status.js"; import { prepareCliRunContext, @@ -500,7 +500,7 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { it("honors an explicit auth agent directory independently of session identity", async () => { const { dir, sessionFile } = createSessionFile(); const modelOwnerAgentDir = path.join(dir, "ops-agent"); - const crestodianAgentDir = path.join(dir, "crestodian-agent"); + const systemAgentDir = path.join(dir, "openclaw-agent"); const prepareExecution = vi.fn(async () => undefined); fs.mkdirSync(modelOwnerAgentDir, { recursive: true }); cliBackendsTesting.setDepsForTest({ @@ -525,8 +525,8 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { try { const context = await prepareCliRunContext({ sessionId: "session-test", - sessionKey: "agent:crestodian:main", - agentId: "crestodian", + sessionKey: "agent:openclaw:main", + agentId: "openclaw", sessionFile, workspaceDir: dir, agentDir: modelOwnerAgentDir, @@ -540,7 +540,7 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { agents: { list: [ { id: "ops", default: true, agentDir: modelOwnerAgentDir }, - { id: "crestodian", agentDir: crestodianAgentDir }, + { id: "openclaw", agentDir: systemAgentDir }, ], }, }, @@ -3561,7 +3561,7 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { } }); - it("serves only the crestodian MCP server for ring-zero runs", async () => { + it("serves only the openclaw MCP server for ring-zero runs", async () => { const { dir, sessionFile } = createSessionFile(); try { const getActiveMcpLoopbackRuntime = vi.fn(() => undefined); @@ -3601,7 +3601,7 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { ], }); - const params: RunCliAgentParams & { crestodianTool: CrestodianToolOptions } = { + const params: RunCliAgentParams & { systemAgentTool: SystemAgentToolOptions } = { sessionId: "session-test", sessionFile, workspaceDir: dir, @@ -3609,12 +3609,12 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { provider: "claude-cli", model: "test-model", timeoutMs: 1_000, - runId: "run-test-crestodian-mcp", + runId: "run-test-openclaw-mcp", config: createCliBackendConfig(), - crestodianTool: { surface: "cli" }, + systemAgentTool: { surface: "cli" }, cliToolAvailability: { native: [], - mcp: ["mcp__openclaw__crestodian"], + mcp: ["mcp__openclaw__openclaw"], }, }; const context = await prepareCliRunContext(params); @@ -3632,7 +3632,7 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { expect(resolveExecutionArgs).not.toHaveBeenCalled(); expect(context.params.cliToolAvailability).toEqual({ native: [], - mcp: ["mcp__openclaw__crestodian"], + mcp: ["mcp__openclaw__openclaw"], }); const mcpConfigPath = expectDefined( args[args.indexOf("--mcp-config") + 1], @@ -3643,8 +3643,8 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { }; expect(Object.keys(raw.mcpServers ?? {})).toEqual(["openclaw"]); expect(raw.mcpServers?.openclaw?.env).toMatchObject({ - OPENCLAW_TOOLS_MCP_TOOLS: "crestodian", - OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE: "cli", + OPENCLAW_TOOLS_MCP_TOOLS: "openclaw", + OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE: "cli", }); await context.preparedBackend.cleanup?.(); @@ -3969,14 +3969,14 @@ describe("shouldSkipLocalCliCredentialEpoch", () => { const context = await prepareCliRunContext({ sessionId: "session-test", - sessionKey: "agent:crestodian:main", + sessionKey: "agent:openclaw:main", sessionFile, workspaceDir: dir, prompt: "approve the proposal", provider: "claude-cli", model: "opus", timeoutMs: 1_000, - runId: "run-crestodian-process-per-turn", + runId: "run-openclaw-process-per-turn", cliSessionBinding: { sessionId: "native-claude-sid" }, config: createCliBackendConfig(), disableCliLiveSession: true, diff --git a/src/agents/cli-runner/prepare.ts b/src/agents/cli-runner/prepare.ts index 4a432568eade..3a40a3eaa2bd 100644 --- a/src/agents/cli-runner/prepare.ts +++ b/src/agents/cli-runner/prepare.ts @@ -27,7 +27,7 @@ import { getActiveMcpLoopbackRuntime, } from "../../gateway/mcp-http.loopback-runtime.js"; import { resolveMcpLoopbackScopedTools } from "../../gateway/mcp-http.runtime.js"; -import { buildCrestodianToolsMcpServerConfig } from "../../mcp/openclaw-tools-serve-config.js"; +import { buildSystemAgentToolsMcpServerConfig } from "../../mcp/openclaw-tools-serve-config.js"; import { isClaudeCliProvider } from "../../plugin-sdk/anthropic-cli.js"; import type { CliBackendAuthEpochMode, @@ -122,8 +122,8 @@ function resolveClaudeCliContextModelId(modelId: string): string { return CLAUDE_CLI_CONTEXT_MODEL_ALIASES[lower] ?? trimmed; } type RunCliAgentPrepareParams = RunCliAgentParams & { - /** Ring-zero tool transport supplied only by the Crestodian orchestrator. */ - crestodianTool?: import("../tools/crestodian-tool.js").CrestodianToolOptions; + /** Ring-zero tool transport supplied only by the OpenClaw orchestrator. */ + systemAgentTool?: import("../tools/system-agent-tool.js").SystemAgentToolOptions; }; const prepareDeps = { @@ -775,16 +775,16 @@ export async function prepareCliRunContext( bootstrapMode === "none" ? toolBoundExtraSystemPromptHash : hashCliSessionText(JSON.stringify([toolBoundExtraSystemPromptHash ?? null, bootstrapMode])); - // Ring-zero Crestodian runs replace the bundle MCP surface entirely: no + // Ring-zero OpenClaw runs replace the bundle MCP surface entirely: no // loopback server, no plugin/user servers. A selectable backend also removes - // its native tools, leaving only this crestodian stdio server. - const crestodianMcpConfig = internalParams.crestodianTool - ? buildCrestodianToolsMcpServerConfig(internalParams.crestodianTool) + // its native tools, leaving only this openclaw stdio server. + const systemAgentMcpConfig = internalParams.systemAgentTool + ? buildSystemAgentToolsMcpServerConfig(internalParams.systemAgentTool) : undefined; const bundleMcpEnabled = !nodeClaudePlacement && !isSideQuestion && - !crestodianMcpConfig && + !systemAgentMcpConfig && backendResolved.bundleMcp && params.disableTools !== true; let mcpLoopbackRuntime = bundleMcpEnabled ? prepareDeps.getActiveMcpLoopbackRuntime() : undefined; @@ -856,13 +856,13 @@ export async function prepareCliRunContext( : undefined; cleanupPreparedResources = cleanupMcpClientGrant; const preparedBackend = await prepareCliBundleMcpConfig({ - enabled: bundleMcpEnabled || crestodianMcpConfig !== undefined, + enabled: bundleMcpEnabled || systemAgentMcpConfig !== undefined, mode: backendResolved.bundleMcpMode, backend: backendResolved.config, workspaceDir, config: params.config, agentDir, - ...(crestodianMcpConfig ? { exclusiveConfig: crestodianMcpConfig } : {}), + ...(systemAgentMcpConfig ? { exclusiveConfig: systemAgentMcpConfig } : {}), additionalConfig: mcpLoopbackRuntime ? prepareDeps.createMcpLoopbackServerConfig(mcpLoopbackRuntime.port) : undefined, diff --git a/src/agents/core-tool-factory-descriptors.ts b/src/agents/core-tool-factory-descriptors.ts index f86dde4740cb..a688a3d79403 100644 --- a/src/agents/core-tool-factory-descriptors.ts +++ b/src/agents/core-tool-factory-descriptors.ts @@ -17,7 +17,7 @@ export const CORE_TOOL_FACTORY_DESCRIPTORS = [ { name: "exec", family: "shell" }, { name: "process", family: "shell" }, { name: "agents_list", family: "openclaw" }, - { name: "crestodian", family: "openclaw" }, + { name: "openclaw", family: "openclaw" }, { name: "computer", family: "openclaw" }, { name: "cron", family: "openclaw" }, { name: "gateway", family: "openclaw" }, diff --git a/src/agents/embedded-agent-runner/run.ts b/src/agents/embedded-agent-runner/run.ts index 6454ace13e29..c8da6ca749b5 100644 --- a/src/agents/embedded-agent-runner/run.ts +++ b/src/agents/embedded-agent-runner/run.ts @@ -284,8 +284,8 @@ type RunEmbeddedAgentInternalParams = RunEmbeddedAgentParams & { binding: import("../execution-auth-binding.js").AgentExecutionAuthBinding, ) => void; authProfileStateMode?: "read-write" | "read-only"; - /** Ring-zero tool override, supplied only by the Crestodian orchestrator. */ - crestodianTool?: import("../tools/crestodian-tool.js").CrestodianToolOptions; + /** Ring-zero tool override, supplied only by the OpenClaw orchestrator. */ + systemAgentTool?: import("../tools/system-agent-tool.js").SystemAgentToolOptions; }; type RunEmbeddedAgentParamsWithSessionFile = RunEmbeddedAgentInternalParams & { sessionFile: string; @@ -1857,7 +1857,7 @@ async function runEmbeddedAgentInternal( bootstrapContextRunKind: params.bootstrapContextRunKind, jobId: params.jobId, toolsAllow: params.toolsAllow, - ...(params.crestodianTool ? { crestodianTool: params.crestodianTool } : {}), + ...(params.systemAgentTool ? { systemAgentTool: params.systemAgentTool } : {}), cleanupBundleMcpOnRunEnd: params.cleanupBundleMcpOnRunEnd, disableMessageTool: params.disableMessageTool, forceRestartSafeTools: params.forceRestartSafeTools, diff --git a/src/agents/harness/selection.test.ts b/src/agents/harness/selection.test.ts index c95b77323524..0ad220dc6188 100644 --- a/src/agents/harness/selection.test.ts +++ b/src/agents/harness/selection.test.ts @@ -12,7 +12,7 @@ import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult, } from "../embedded-agent-runner/run/types.js"; -import type { CrestodianToolOptions } from "../tools/crestodian-tool.js"; +import type { SystemAgentToolOptions } from "../tools/system-agent-tool.js"; import { maybeCompactAgentHarnessSession } from "./compaction.js"; import { clearAgentHarnesses, registerAgentHarness } from "./registry.js"; import { @@ -380,18 +380,18 @@ function registerTestCompactor( describe("runAgentHarnessAttempt", () => { it.each(["codex", "copilot"] as const)( - "binds the host Crestodian tool to the %s SDK construction path without leaking authority", + "binds the host OpenClaw tool to the %s SDK construction path without leaking authority", async (harnessId) => { let receivedPrivateAuthority = true; let hostScopeActive = false; let toolNames: string[] = []; const pluginRunAttempt = vi.fn(async (attemptParams) => { - receivedPrivateAuthority = "crestodianTool" in attemptParams; + receivedPrivateAuthority = "systemAgentTool" in attemptParams; await Promise.resolve(); - hostScopeActive = isHostScopedAgentToolActive("crestodian"); + hostScopeActive = isHostScopedAgentToolActive("openclaw"); toolNames = createOpenClawCodingTools({ - config: { tools: { allow: ["read"], deny: ["crestodian"], toolSearch: true } }, - runtimeToolAllowlist: ["crestodian"], + config: { tools: { allow: ["read"], deny: ["openclaw"], toolSearch: true } }, + runtimeToolAllowlist: ["openclaw"], toolConstructionPlan: { includeBaseCodingTools: false, includeShellTools: false, @@ -413,25 +413,25 @@ describe("runAgentHarnessAttempt", () => { ); const params = createAttemptParams( providerRuntimeConfig("codex", harnessId), - ) as EmbeddedRunAttemptParams & { crestodianTool?: CrestodianToolOptions }; - params.toolsAllow = ["crestodian"]; - params.crestodianTool = { surface: "cli", proposalRef: {}, directiveRef: {} }; + ) as EmbeddedRunAttemptParams & { systemAgentTool?: SystemAgentToolOptions }; + params.toolsAllow = ["openclaw"]; + params.systemAgentTool = { surface: "cli", proposalRef: {}, directiveRef: {} }; await runAgentHarnessAttempt(params); expect(pluginRunAttempt).toHaveBeenCalledTimes(1); expect(receivedPrivateAuthority).toBe(false); expect(hostScopeActive).toBe(true); - expect(toolNames).toEqual(["crestodian"]); - expect(createOpenClawCodingTools().some((tool) => tool.name === "crestodian")).toBe(false); - expect(isHostScopedAgentToolActive("crestodian")).toBe(false); + expect(toolNames).toEqual(["openclaw"]); + expect(createOpenClawCodingTools().some((tool) => tool.name === "openclaw")).toBe(false); + expect(isHostScopedAgentToolActive("openclaw")).toBe(false); }, ); it.each([ { name: "missing", toolsAllow: undefined }, - { name: "broad", toolsAllow: ["crestodian", "read"] }, - ])("rejects $name allowlists for private Crestodian authority", async ({ toolsAllow }) => { + { name: "broad", toolsAllow: ["openclaw", "read"] }, + ])("rejects $name allowlists for private OpenClaw authority", async ({ toolsAllow }) => { const pluginRunAttempt = vi.fn(async () => createAttemptResult("codex"), ); @@ -446,18 +446,18 @@ describe("runAgentHarnessAttempt", () => { ); const params = createAttemptParams( providerRuntimeConfig("codex", "codex"), - ) as EmbeddedRunAttemptParams & { crestodianTool?: CrestodianToolOptions }; + ) as EmbeddedRunAttemptParams & { systemAgentTool?: SystemAgentToolOptions }; params.toolsAllow = toolsAllow; - params.crestodianTool = { surface: "cli", proposalRef: {}, directiveRef: {} }; + params.systemAgentTool = { surface: "cli", proposalRef: {}, directiveRef: {} }; await expect(runAgentHarnessAttempt(params)).rejects.toThrow( - 'Crestodian host authority requires toolsAllow: ["crestodian"]', + 'OpenClaw host authority requires toolsAllow: ["openclaw"]', ); expect(pluginRunAttempt).not.toHaveBeenCalled(); - expect(isHostScopedAgentToolActive("crestodian")).toBe(false); + expect(isHostScopedAgentToolActive("openclaw")).toBe(false); }); - it("keeps the host Crestodian allowlist across global, agent, and sandbox deny-all policy", async () => { + it("keeps the host OpenClaw allowlist across global, agent, and sandbox deny-all policy", async () => { const received: Array<{ toolsAllow: string[] | undefined; extraSystemPrompt: string | undefined; @@ -467,7 +467,7 @@ describe("runAgentHarnessAttempt", () => { received.push({ toolsAllow: attemptParams.toolsAllow, extraSystemPrompt: attemptParams.extraSystemPrompt, - hostScopeActive: isHostScopedAgentToolActive("crestodian"), + hostScopeActive: isHostScopedAgentToolActive("openclaw"), }); return createAttemptResult("codex"); }); @@ -503,32 +503,32 @@ describe("runAgentHarnessAttempt", () => { for (const [index, testCase] of cases.entries()) { const params = createAttemptParams(testCase.config) as EmbeddedRunAttemptParams & { - crestodianTool?: CrestodianToolOptions; + systemAgentTool?: SystemAgentToolOptions; }; params.sessionId = `session-${index}`; params.agentHarnessRuntimeOverride = "codex"; params.agentId = testCase.agentId; params.sessionKey = testCase.sessionKey; - params.toolsAllow = ["crestodian"]; - params.crestodianTool = { surface: "cli", proposalRef: {}, directiveRef: {} }; + params.toolsAllow = ["openclaw"]; + params.systemAgentTool = { surface: "cli", proposalRef: {}, directiveRef: {} }; await runAgentHarnessAttempt(params); } expect(received).toEqual([ - { toolsAllow: ["crestodian"], extraSystemPrompt: undefined, hostScopeActive: true }, - { toolsAllow: ["crestodian"], extraSystemPrompt: undefined, hostScopeActive: true }, - { toolsAllow: ["crestodian"], extraSystemPrompt: undefined, hostScopeActive: true }, + { toolsAllow: ["openclaw"], extraSystemPrompt: undefined, hostScopeActive: true }, + { toolsAllow: ["openclaw"], extraSystemPrompt: undefined, hostScopeActive: true }, + { toolsAllow: ["openclaw"], extraSystemPrompt: undefined, hostScopeActive: true }, ]); - expect(isHostScopedAgentToolActive("crestodian")).toBe(false); + expect(isHostScopedAgentToolActive("openclaw")).toBe(false); }); - it("binds the same host Crestodian scope to the built-in OpenClaw harness", async () => { + it("binds the same host OpenClaw scope to the built-in OpenClaw harness", async () => { let toolNames: string[] = []; agentRunAttempt.mockImplementationOnce(async () => { await Promise.resolve(); toolNames = createOpenClawCodingTools({ - config: { tools: { allow: ["read"], deny: ["crestodian"], toolSearch: true } }, - runtimeToolAllowlist: ["crestodian"], + config: { tools: { allow: ["read"], deny: ["openclaw"], toolSearch: true } }, + runtimeToolAllowlist: ["openclaw"], toolConstructionPlan: { includeBaseCodingTools: false, includeShellTools: false, @@ -541,16 +541,16 @@ describe("runAgentHarnessAttempt", () => { }); const params = createAttemptParams( providerRuntimeConfig("codex", "openclaw"), - ) as EmbeddedRunAttemptParams & { crestodianTool?: CrestodianToolOptions }; - params.toolsAllow = ["crestodian"]; - params.crestodianTool = { surface: "gateway", proposalRef: {}, directiveRef: {} }; + ) as EmbeddedRunAttemptParams & { systemAgentTool?: SystemAgentToolOptions }; + params.toolsAllow = ["openclaw"]; + params.systemAgentTool = { surface: "gateway", proposalRef: {}, directiveRef: {} }; const result = await runAgentHarnessAttempt(params); expect(result.sessionIdUsed).toBe("openclaw"); - expect(toolNames).toEqual(["crestodian"]); - expect(createOpenClawCodingTools().some((tool) => tool.name === "crestodian")).toBe(false); - expect(isHostScopedAgentToolActive("crestodian")).toBe(false); + expect(toolNames).toEqual(["openclaw"]); + expect(createOpenClawCodingTools().some((tool) => tool.name === "openclaw")).toBe(false); + expect(isHostScopedAgentToolActive("openclaw")).toBe(false); }); it("unwraps sentinels only at the plugin harness handoff", async () => { diff --git a/src/agents/harness/selection.ts b/src/agents/harness/selection.ts index 0ecce51e3ed9..cef8523cb5ad 100644 --- a/src/agents/harness/selection.ts +++ b/src/agents/harness/selection.ts @@ -30,7 +30,7 @@ import { } from "../provider-secret-egress.js"; import { resolveSandboxRuntimeStatus } from "../sandbox/runtime-status.js"; import { expandToolGroups, mergeAlsoAllowPolicy, normalizeToolName } from "../tool-policy.js"; -import type { CrestodianToolOptions } from "../tools/crestodian-tool.js"; +import type { SystemAgentToolOptions } from "../tools/system-agent-tool.js"; import { createOpenClawAgentHarness } from "./builtin-openclaw.js"; import { MissingAgentHarnessError } from "./errors.js"; import { runAgentHarnessLifecycleAttempt } from "./lifecycle.js"; @@ -444,7 +444,7 @@ export async function runAgentHarnessAttempt( params: EmbeddedRunAttemptParams, ): Promise { const internalParams = params as EmbeddedRunAttemptParams & { - crestodianTool?: CrestodianToolOptions; + systemAgentTool?: SystemAgentToolOptions; }; const activeTrace = getActiveDiagnosticTraceContext(); const harnessTrace = freezeDiagnosticTraceContext( @@ -467,13 +467,13 @@ export async function runAgentHarnessAttempt( preparedModelProvider: params.runtimePlan?.auth !== undefined, }); const harness = selection.harness; - if (internalParams.crestodianTool && !isCrestodianOnlyAllowlist(internalParams.toolsAllow)) { - throw new Error('Crestodian host authority requires toolsAllow: ["crestodian"]'); + if (internalParams.systemAgentTool && !isSystemAgentOnlyAllowlist(internalParams.toolsAllow)) { + throw new Error('OpenClaw host authority requires toolsAllow: ["openclaw"]'); } - const ringZeroTools = internalParams.crestodianTool + const ringZeroTools = internalParams.systemAgentTool ? [ - (await import("../tools/crestodian-tool.js")).createCrestodianTool( - internalParams.crestodianTool, + (await import("../tools/system-agent-tool.js")).createSystemAgentTool( + internalParams.systemAgentTool, ), ] : []; @@ -509,17 +509,17 @@ export async function runAgentHarnessAttempt( } } -function isCrestodianOnlyAllowlist(toolsAllow: readonly string[] | undefined): boolean { - return toolsAllow?.length === 1 && normalizeToolName(toolsAllow[0] ?? "") === "crestodian"; +function isSystemAgentOnlyAllowlist(toolsAllow: readonly string[] | undefined): boolean { + return toolsAllow?.length === 1 && normalizeToolName(toolsAllow[0] ?? "") === "openclaw"; } function withoutInternalHarnessAuthority( - params: EmbeddedRunAttemptParams & { crestodianTool?: CrestodianToolOptions }, + params: EmbeddedRunAttemptParams & { systemAgentTool?: SystemAgentToolOptions }, ): EmbeddedRunAttemptParams { - if (!Object.hasOwn(params, "crestodianTool")) { + if (!Object.hasOwn(params, "systemAgentTool")) { return params; } - const { crestodianTool: _crestodianTool, ...pluginParams } = params; + const { systemAgentTool: _systemAgentTool, ...pluginParams } = params; return pluginParams; } @@ -543,9 +543,9 @@ function applyPluginHarnessDenyAllToolPolicy( params: EmbeddedRunAttemptParams, ): EmbeddedRunAttemptParams { if ( - isHostScopedAgentToolActive("crestodian") && + isHostScopedAgentToolActive("openclaw") && params.toolsAllow?.length === 1 && - normalizeToolName(params.toolsAllow[0] ?? "") === "crestodian" + normalizeToolName(params.toolsAllow[0] ?? "") === "openclaw" ) { return params; } diff --git a/src/agents/harness/tool-surface-bridge.test.ts b/src/agents/harness/tool-surface-bridge.test.ts index 788895f0c388..47d6c53bb5e4 100644 --- a/src/agents/harness/tool-surface-bridge.test.ts +++ b/src/agents/harness/tool-surface-bridge.test.ts @@ -25,25 +25,25 @@ function createRuntime(config: OpenClawConfig) { describe("createAgentHarnessToolSurfaceRuntime", () => { it("suppresses catalog controls for a host-scoped ring-zero run", () => { - const crestodian = { - ...createStubTool("crestodian"), + const openclaw = { + ...createStubTool("openclaw"), catalogMode: "direct-only" as const, }; - runWithAgentRingZeroTools([crestodian], () => { + runWithAgentRingZeroTools([openclaw], () => { const runtime = createAgentHarnessToolSurfaceRuntime({ config: { tools: { toolSearch: true } }, executeTool: async () => ({ content: [], details: {} }), modelToolsEnabled: true, - runtimeToolAllowlist: ["crestodian"], - toolsAllow: ["crestodian"], + runtimeToolAllowlist: ["openclaw"], + toolsAllow: ["openclaw"], }); expect(runtime.codeModeControlsEnabled).toBe(false); expect(runtime.toolSearchControlsEnabled).toBe(false); expect(runtime.includeToolSearchControls).toBe(false); - expect(runtime.runtimeToolAllowlist).toEqual(["crestodian"]); - expect(runtime.compactTools([crestodian]).tools).toEqual([crestodian]); + expect(runtime.runtimeToolAllowlist).toEqual(["openclaw"]); + expect(runtime.compactTools([openclaw]).tools).toEqual([openclaw]); runtime.cleanup(); }); }); diff --git a/src/agents/tool-display-config.ts b/src/agents/tool-display-config.ts index fd6c108777e7..dbc4ea1dbe72 100644 --- a/src/agents/tool-display-config.ts +++ b/src/agents/tool-display-config.ts @@ -305,9 +305,9 @@ export const TOOL_DISPLAY_CONFIG: ToolDisplayConfig = { title: "Skill Workshop", detailKeys: ["action", "name", "proposal_id"], }, - crestodian: { + openclaw: { emoji: "🦀", - title: "Crestodian", + title: "OpenClaw", detailKeys: ["action", "path", "model"], }, gateway: { diff --git a/src/agents/tools/crestodian-tool.test.ts b/src/agents/tools/system-agent-tool.test.ts similarity index 74% rename from src/agents/tools/crestodian-tool.test.ts rename to src/agents/tools/system-agent-tool.test.ts index 17a864fe62ee..d3a2edce7e81 100644 --- a/src/agents/tools/crestodian-tool.test.ts +++ b/src/agents/tools/system-agent-tool.test.ts @@ -1,18 +1,20 @@ -// Crestodian ring-zero tool tests: approval gating, action mapping, verification. +// OpenClaw ring-zero tool tests: approval gating, action mapping, verification. import { afterEach, describe, expect, it, vi } from "vitest"; import { - createCrestodianTool, - hashCrestodianOperation, - resolveCrestodianDirectiveTransition, - resolveCrestodianProposalTransition, - type CrestodianToolDirective, -} from "./crestodian-tool.js"; + createSystemAgentTool, + hashSystemAgentOperation, + resolveSystemAgentDirectiveTransition, + resolveSystemAgentProposalTransition, + type SystemAgentToolDirective, +} from "./system-agent-tool.js"; const mocks = vi.hoisted(() => ({ - executeCrestodianOperation: vi.fn(async (_op: unknown, runtime: { log: (m: string) => void }) => { - runtime.log("op-output"); - return { applied: false }; - }), + executeSystemAgentOperation: vi.fn( + async (_op: unknown, runtime: { log: (m: string) => void }) => { + runtime.log("op-output"); + return { applied: false }; + }, + ), readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -24,9 +26,9 @@ const mocks = vi.hoisted(() => ({ })), })); -vi.mock("../../crestodian/operations.js", async (importOriginal) => ({ - ...(await importOriginal()), - executeCrestodianOperation: mocks.executeCrestodianOperation, +vi.mock("../../system-agent/operations.js", async (importOriginal) => ({ + ...(await importOriginal()), + executeSystemAgentOperation: mocks.executeSystemAgentOperation, })); vi.mock("../../config/config.js", async (importOriginal) => ({ @@ -46,27 +48,25 @@ function toolText(result: unknown): string { .join("\n"); } -describe("crestodian tool", () => { +describe("openclaw tool", () => { it("stays directly callable instead of entering tool catalogs", () => { - const tool = createCrestodianTool({ surface: "cli" }); + const tool = createSystemAgentTool({ surface: "cli" }); expect(tool.catalogMode).toBe("direct-only"); - expect(tool.description).toContain( - "approved=true only after user clearly agrees to exact change in this conversation", - ); + expect(tool.description).toContain("Exact user approval required; then approved=true."); }); it("runs read actions immediately", async () => { - const tool = createCrestodianTool({ surface: "cli" }); + const tool = createSystemAgentTool({ surface: "cli" }); const result = await tool.execute("t1", { action: "status" }); expect(toolText(result)).toContain("op-output"); - expect(mocks.executeCrestodianOperation).toHaveBeenCalledWith( + expect(mocks.executeSystemAgentOperation).toHaveBeenCalledWith( { kind: "status" }, expect.anything(), expect.objectContaining({ approved: false }), ); await tool.execute("t1b", { action: "channel_info", channel: "Slack" }); - expect(mocks.executeCrestodianOperation).toHaveBeenCalledWith( + expect(mocks.executeSystemAgentOperation).toHaveBeenCalledWith( { kind: "channel-info", channel: "slack" }, expect.anything(), expect.objectContaining({ approved: false }), @@ -75,7 +75,7 @@ describe("crestodian tool", () => { it("refuses mutating actions without the approved assertion", async () => { const proposalRef: { current?: string } = {}; - const tool = createCrestodianTool({ surface: "cli", approvalArmed: true, proposalRef }); + const tool = createSystemAgentTool({ surface: "cli", approvalArmed: true, proposalRef }); const result = await tool.execute("t2", { action: "config_set", path: "gateway.port", @@ -84,13 +84,13 @@ describe("crestodian tool", () => { // An armed turn can never mint its own proposal. expect(toolText(result)).toContain("approval-mismatch"); expect(proposalRef.current).toBeUndefined(); - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); }); it("refuses model-asserted approval without host-verified consent", async () => { // approved=true from the model alone must never mutate: the host arms // approval only when the user's actual message was an explicit yes. - const tool = createCrestodianTool({ surface: "cli" }); + const tool = createSystemAgentTool({ surface: "cli" }); const result = await tool.execute("t2b", { action: "config_set", path: "gateway.port", @@ -98,7 +98,7 @@ describe("crestodian tool", () => { approved: true, }); expect(toolText(result)).toContain("needs-approval"); - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); }); it("rejects arbitrary plugin installs before creating an approval proposal", async () => { @@ -119,7 +119,7 @@ describe("crestodian tool", () => { it("defers an approved mutation to the host after the full proposal handshake", async () => { const proposalRef: { current?: string } = {}; // Phase 1: unarmed proposal is denied and records the exact operation. - const proposingTool = createCrestodianTool({ surface: "gateway", proposalRef }); + const proposingTool = createSystemAgentTool({ surface: "gateway", proposalRef }); const denied = await proposingTool.execute("t3a", { action: "set_default_model", model: "openai/gpt-5.5", @@ -127,12 +127,12 @@ describe("crestodian tool", () => { }); expect(toolText(denied)).toContain("needs-approval"); expect(proposalRef.current).toBeDefined(); - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); // Phase 2: the user's yes arms the turn; the identical call becomes one // host-owned directive so the inference binding can be checked again. - const directiveRef: { current?: CrestodianToolDirective } = {}; - const armedTool = createCrestodianTool({ + const directiveRef: { current?: SystemAgentToolDirective } = {}; + const armedTool = createSystemAgentTool({ surface: "gateway", approvalArmed: true, proposalRef, @@ -148,7 +148,7 @@ describe("crestodian tool", () => { kind: "approved-operation", operation: { kind: "set-default-model", model: "openai/gpt-5.5" }, }); - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); await armedTool.execute("t3c", { action: "connect_channel", channel: "telegram" }); expect(directiveRef.current).toEqual({ kind: "approved-operation", @@ -165,21 +165,21 @@ describe("crestodian tool", () => { workspace: "/tmp/work", model: "openai/gpt-5.5", }; - const result = await createCrestodianTool({ surface: "gateway", proposalRef }).execute( + const result = await createSystemAgentTool({ surface: "gateway", proposalRef }).execute( "setup-proposal", args, ); expect(toolText(result)).toContain("needs-approval"); expect(proposalRef.current).toBe( - hashCrestodianOperation({ + hashSystemAgentOperation({ kind: "setup", workspace: "/tmp/work", model: "openai/gpt-5.5", }), ); expect( - resolveCrestodianProposalTransition({ + resolveSystemAgentProposalTransition({ args: { action: "setup", workspace: "/tmp/work" }, resultText: toolText(result), }), @@ -188,12 +188,12 @@ describe("crestodian tool", () => { it("voids setup approval when the requested model changes", async () => { const proposalRef = { - current: hashCrestodianOperation({ + current: hashSystemAgentOperation({ kind: "setup", model: "openai/gpt-5.5", }), }; - const tool = createCrestodianTool({ + const tool = createSystemAgentTool({ surface: "gateway", approvalArmed: true, proposalRef, @@ -207,18 +207,18 @@ describe("crestodian tool", () => { expect(toolText(result)).toContain("approval-mismatch"); expect(proposalRef.current).toBeUndefined(); - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); }); it("refuses an armed call that differs from the proposed operation", async () => { const proposalRef: { current?: string } = {}; - const proposingTool = createCrestodianTool({ surface: "cli", proposalRef }); + const proposingTool = createSystemAgentTool({ surface: "cli", proposalRef }); await proposingTool.execute("t3c", { action: "set_default_model", model: "openai/gpt-5.5", approved: true, }); - const armedTool = createCrestodianTool({ surface: "cli", approvalArmed: true, proposalRef }); + const armedTool = createSystemAgentTool({ surface: "cli", approvalArmed: true, proposalRef }); const result = await armedTool.execute("t3d", { action: "config_set", path: "gateway.port", @@ -236,19 +236,19 @@ describe("crestodian tool", () => { approved: true, }); expect(toolText(retry)).toContain("approval-mismatch"); - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); }); it("never performs an approved write inside the model tool process", async () => { const proposalRef: { current?: string } = {}; - await createCrestodianTool({ surface: "cli", proposalRef }).execute("t4a", { + await createSystemAgentTool({ surface: "cli", proposalRef }).execute("t4a", { action: "config_set", path: "gateway.port", value: "banana", approved: true, }); - const directiveRef: { current?: CrestodianToolDirective } = {}; - const tool = createCrestodianTool({ + const directiveRef: { current?: SystemAgentToolDirective } = {}; + const tool = createSystemAgentTool({ surface: "cli", approvalArmed: true, proposalRef, @@ -265,20 +265,20 @@ describe("crestodian tool", () => { kind: "approved-operation", operation: { kind: "config-set", path: "gateway.port", value: "banana" }, }); - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); expect(mocks.readConfigFileSnapshot).not.toHaveBeenCalled(); }); it("maps create_agent with optional workspace and model", async () => { const proposalRef: { current?: string } = {}; - await createCrestodianTool({ surface: "cli", proposalRef }).execute("t6a", { + await createSystemAgentTool({ surface: "cli", proposalRef }).execute("t6a", { action: "create_agent", agentId: "work", workspace: "/tmp/work", approved: true, }); - const directiveRef: { current?: CrestodianToolDirective } = {}; - const tool = createCrestodianTool({ + const directiveRef: { current?: SystemAgentToolDirective } = {}; + const tool = createSystemAgentTool({ surface: "cli", approvalArmed: true, proposalRef, @@ -294,17 +294,17 @@ describe("crestodian tool", () => { kind: "approved-operation", operation: { kind: "create-agent", agentId: "work", workspace: "/tmp/work" }, }); - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); }); it("rejects unknown or underspecified actions as input errors", async () => { - const tool = createCrestodianTool({ surface: "cli" }); + const tool = createSystemAgentTool({ surface: "cli" }); await expect(tool.execute("t5", { action: "config_get" })).rejects.toThrow(/path/); }); it("records interactive directives for the host without executing operations", async () => { - const directiveRef: { current?: CrestodianToolDirective } = {}; - const tool = createCrestodianTool({ surface: "cli", directiveRef }); + const directiveRef: { current?: SystemAgentToolDirective } = {}; + const tool = createSystemAgentTool({ surface: "cli", directiveRef }); const connect = await tool.execute("t5", { action: "connect_channel", channel: "Telegram" }); expect(toolText(connect)).toContain("directive:"); @@ -316,7 +316,7 @@ describe("crestodian tool", () => { }); expect(toolText(configureModel)).toContain("directive:"); expect(toolText(configureModel)).toContain( - "active inference route cannot be changed inside Crestodian", + "active inference route cannot be changed inside OpenClaw", ); expect(toolText(configureModel)).toContain("openclaw onboard"); expect(directiveRef.current).toEqual({ kind: "model-setup", workspace: "/tmp/work" }); @@ -341,17 +341,17 @@ describe("crestodian tool", () => { action: "open_setup", target: "guided", }); - expect(toolText(guidedSetup)).toContain("cannot run inside Crestodian"); + expect(toolText(guidedSetup)).toContain("cannot run inside OpenClaw"); expect(toolText(guidedSetup)).toContain("openclaw onboard"); expect(directiveRef.current).toEqual({ kind: "open-setup", target: "guided" }); // Directives are host handoffs, never operation executions. - expect(mocks.executeCrestodianOperation).not.toHaveBeenCalled(); + expect(mocks.executeSystemAgentOperation).not.toHaveBeenCalled(); }); it("mirrors directive transitions for out-of-process (CLI MCP) hosts", () => { expect( - resolveCrestodianDirectiveTransition({ + resolveSystemAgentDirectiveTransition({ args: { action: "config_set", path: "gateway.port", @@ -365,36 +365,36 @@ describe("crestodian tool", () => { operation: { kind: "config-set", path: "gateway.port", value: "19001" }, }); expect( - resolveCrestodianDirectiveTransition({ + resolveSystemAgentDirectiveTransition({ args: { action: "connect_channel", channel: "telegram" }, resultText: "directive: the host chat now starts the guided telegram setup.", }), ).toEqual({ kind: "channel-setup", channel: "telegram" }); expect( - resolveCrestodianDirectiveTransition({ + resolveSystemAgentDirectiveTransition({ args: { action: "open_agent" }, resultText: "directive: the host now hands the user over.", }), ).toEqual({ kind: "open-tui" }); expect( - resolveCrestodianDirectiveTransition({ + resolveSystemAgentDirectiveTransition({ args: { action: "configure_model_provider", workspace: "/tmp/work" }, resultText: - "directive: the active inference route cannot be changed inside Crestodian; run openclaw onboard.", + "directive: the active inference route cannot be changed inside OpenClaw; run openclaw onboard.", }), ).toEqual({ kind: "model-setup", workspace: "/tmp/work" }); expect( - resolveCrestodianDirectiveTransition({ + resolveSystemAgentDirectiveTransition({ args: { action: "open_setup", target: "classic" }, - resultText: "directive: classic setup cannot run inside Crestodian; run openclaw onboard.", + resultText: "directive: classic setup cannot run inside OpenClaw; run openclaw onboard.", }), ).toEqual({ kind: "open-setup", target: "classic" }); // Non-directive results and other actions never mirror. expect( - resolveCrestodianDirectiveTransition({ args: { action: "status" }, resultText: "ok" }), + resolveSystemAgentDirectiveTransition({ args: { action: "status" }, resultText: "ok" }), ).toBeNull(); expect( - resolveCrestodianDirectiveTransition({ + resolveSystemAgentDirectiveTransition({ args: { action: "connect_channel", channel: "telegram" }, resultText: "error: boom", }), @@ -403,38 +403,38 @@ describe("crestodian tool", () => { it("mirrors proposal transitions for out-of-process (CLI MCP) hosts", () => { const args = { action: "set_default_model", model: "openai/gpt-5.5" }; - const hash = hashCrestodianOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }); + const hash = hashSystemAgentOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }); // Denial registers the exact-operation hash on the host. expect( - resolveCrestodianProposalTransition({ + resolveSystemAgentProposalTransition({ args, resultText: "needs-approval: this action changes state.", }), ).toEqual({ proposal: hash }); expect( - resolveCrestodianProposalTransition({ + resolveSystemAgentProposalTransition({ args, resultText: `needs-approval:${hash}\nThis action changes state.`, }), ).toEqual({ proposal: hash }); // A voided approval clears it. expect( - resolveCrestodianProposalTransition({ + resolveSystemAgentProposalTransition({ args, resultText: "approval-mismatch: this call is not the operation the user approved.", }), ).toEqual({ proposal: undefined }); // An executed mutation consumes it. expect( - resolveCrestodianProposalTransition({ args, resultText: "Default model updated." }), + resolveSystemAgentProposalTransition({ args, resultText: "Default model updated." }), ).toEqual({ proposal: undefined }); // Read actions and unparsable calls never touch the proposal. expect( - resolveCrestodianProposalTransition({ args: { action: "status" }, resultText: "ok" }), + resolveSystemAgentProposalTransition({ args: { action: "status" }, resultText: "ok" }), ).toBeNull(); expect( - resolveCrestodianProposalTransition({ args: { action: "bogus" }, resultText: "ok" }), + resolveSystemAgentProposalTransition({ args: { action: "bogus" }, resultText: "ok" }), ).toBeNull(); }); }); diff --git a/src/agents/tools/crestodian-tool.ts b/src/agents/tools/system-agent-tool.ts similarity index 69% rename from src/agents/tools/crestodian-tool.ts rename to src/agents/tools/system-agent-tool.ts index 083eb0a4d836..8e9cd52fa23b 100644 --- a/src/agents/tools/crestodian-tool.ts +++ b/src/agents/tools/system-agent-tool.ts @@ -1,23 +1,23 @@ /** - * crestodian built-in tool: ring-zero setup/repair actions for the Crestodian + * openclaw built-in tool: ring-zero setup/repair actions for the OpenClaw * agent. Never exposed to normal agents — construction is bound to a host-owned - * per-run scope, and every action funnels through Crestodian's typed operation + * per-run scope, and every action funnels through OpenClaw's typed operation * union with approval assertions and the audit log. */ import { createHash } from "node:crypto"; import { Type } from "typebox"; -import { - executeCrestodianOperation, - isPersistentCrestodianOperation, - type CrestodianOperation, -} from "../../crestodian/operations.js"; -import { validateCrestodianPluginInstallSpec } from "../../crestodian/plugin-install.js"; import type { RuntimeEnv } from "../../runtime.js"; +import { + executeSystemAgentOperation, + isPersistentSystemAgentOperation, + type SystemAgentOperation, +} from "../../system-agent/operations.js"; +import { validateSystemAgentPluginInstallSpec } from "../../system-agent/plugin-install.js"; import { stringEnum } from "../schema/typebox.js"; import { stableStringify } from "../stable-stringify.js"; import { textResult, ToolInputError, readStringParam, type AnyAgentTool } from "./common.js"; -export type CrestodianToolOptions = { +export type SystemAgentToolOptions = { /** Where setup side effects run; the gateway surface never manages its own daemon. */ surface: "cli" | "gateway"; /** @@ -39,50 +39,50 @@ export type CrestodianToolOptions = { * agent TUI). The engine reads it after the turn; CLI MCP hosts mirror it * from tool events. */ - directiveRef?: { current?: CrestodianToolDirective }; + directiveRef?: { current?: SystemAgentToolDirective }; }; /** Host directives the hosting chat engine handles after the turn. */ -export type CrestodianToolDirective = +export type SystemAgentToolDirective = | { kind: "channel-setup"; channel: string } | { kind: "model-setup"; workspace?: string } | { kind: "open-tui"; agentId?: string; workspace?: string } - | Extract - | { kind: "approved-operation"; operation: CrestodianOperation }; + | Extract + | { kind: "approved-operation"; operation: SystemAgentOperation }; -type CrestodianHostNavigationDirective = Exclude< - CrestodianToolDirective, +type SystemAgentHostNavigationDirective = Exclude< + SystemAgentToolDirective, { kind: "approved-operation" } >; /** Canonical operation fingerprint used to bind "yes" to one exact mutation. */ -export function hashCrestodianOperation(operation: CrestodianOperation): string { +export function hashSystemAgentOperation(operation: SystemAgentOperation): string { return createHash("sha256").update(stableStringify(operation)).digest("hex"); } /** Result markers shared with out-of-process hosts (CLI MCP runs). */ -const CRESTODIAN_NEEDS_APPROVAL_PREFIX = "needs-approval:"; -const CRESTODIAN_APPROVAL_MISMATCH_PREFIX = "approval-mismatch:"; -const CRESTODIAN_DIRECTIVE_PREFIX = "directive:"; -const CRESTODIAN_APPROVED_OPERATION_PREFIX = `${CRESTODIAN_DIRECTIVE_PREFIX}approved-operation:`; +const SYSTEM_AGENT_NEEDS_APPROVAL_PREFIX = "needs-approval:"; +const SYSTEM_AGENT_APPROVAL_MISMATCH_PREFIX = "approval-mismatch:"; +const SYSTEM_AGENT_DIRECTIVE_PREFIX = "directive:"; +const SYSTEM_AGENT_APPROVED_OPERATION_PREFIX = `${SYSTEM_AGENT_DIRECTIVE_PREFIX}approved-operation:`; /** * Reconstruct a host directive from an out-of-process tool result. Directive * actions run inside the MCP subprocess on CLI-harness runs, so the host * replays them from harness tool events the same way proposals are mirrored. */ -export function resolveCrestodianDirectiveTransition(params: { +export function resolveSystemAgentDirectiveTransition(params: { args: Record; resultText: string; -}): CrestodianToolDirective | null { - if (!params.resultText.startsWith(CRESTODIAN_DIRECTIVE_PREFIX)) { +}): SystemAgentToolDirective | null { + if (!params.resultText.startsWith(SYSTEM_AGENT_DIRECTIVE_PREFIX)) { return null; } try { const operation = operationForAction(params.args); if ( - params.resultText.startsWith(CRESTODIAN_APPROVED_OPERATION_PREFIX) && - isPersistentCrestodianOperation(operation) + params.resultText.startsWith(SYSTEM_AGENT_APPROVED_OPERATION_PREFIX) && + isPersistentSystemAgentOperation(operation) ) { return { kind: "approved-operation", operation }; } @@ -93,8 +93,8 @@ export function resolveCrestodianDirectiveTransition(params: { } function directiveForOperation( - operation: CrestodianOperation, -): CrestodianHostNavigationDirective | null { + operation: SystemAgentOperation, +): SystemAgentHostNavigationDirective | null { if (operation.kind === "channel-setup") { return { kind: "channel-setup", channel: operation.channel }; } @@ -123,36 +123,36 @@ function directiveForOperation( * run; the host replays the same lifecycle from harness tool events: denial * registers the exact-operation hash, mismatch voids it, execution consumes it. */ -export function resolveCrestodianProposalTransition(params: { +export function resolveSystemAgentProposalTransition(params: { args: Record; resultText: string; }): { proposal: string | undefined } | null { - let operation: CrestodianOperation; + let operation: SystemAgentOperation; try { operation = operationForAction(params.args); } catch { return null; } - if (!isPersistentCrestodianOperation(operation)) { + if (!isPersistentSystemAgentOperation(operation)) { return null; } - if (params.resultText.startsWith(CRESTODIAN_APPROVAL_MISMATCH_PREFIX)) { + if (params.resultText.startsWith(SYSTEM_AGENT_APPROVAL_MISMATCH_PREFIX)) { return { proposal: undefined }; } - if (params.resultText.startsWith(CRESTODIAN_NEEDS_APPROVAL_PREFIX)) { + if (params.resultText.startsWith(SYSTEM_AGENT_NEEDS_APPROVAL_PREFIX)) { const markerLine = params.resultText.split("\n", 1)[0] ?? ""; - const carriedHash = markerLine.slice(CRESTODIAN_NEEDS_APPROVAL_PREFIX.length).trim(); + const carriedHash = markerLine.slice(SYSTEM_AGENT_NEEDS_APPROVAL_PREFIX.length).trim(); return { proposal: /^[a-f0-9]{64}$/.test(carriedHash) ? carriedHash - : hashCrestodianOperation(operation), + : hashSystemAgentOperation(operation), }; } // Executed or errored mutation: an armed approval is single-use either way. return { proposal: undefined }; } -const CRESTODIAN_TOOL_ACTIONS = [ +const SYSTEM_AGENT_TOOL_ACTIONS = [ "status", "models", "agents", @@ -183,8 +183,8 @@ const CRESTODIAN_TOOL_ACTIONS = [ "plugin_uninstall", ] as const; -const CrestodianToolSchema = Type.Object({ - action: stringEnum([...CRESTODIAN_TOOL_ACTIONS]), +const SystemAgentToolSchema = Type.Object({ + action: stringEnum([...SYSTEM_AGENT_TOOL_ACTIONS]), path: Type.Optional(Type.String({ description: "Config path for config_* actions" })), value: Type.Optional(Type.String({ description: "Value for config_set (JSON5 or string)" })), envVar: Type.Optional(Type.String({ description: "Env var name for config_set_ref" })), @@ -199,7 +199,7 @@ const CrestodianToolSchema = Type.Object({ target: Type.Optional( stringEnum(["guided", "classic", "channels"], { description: - "Setup target for open_setup. channels runs in this chat; guided/classic require exiting Crestodian and running openclaw onboard.", + "Setup target for open_setup. channels runs in this chat; guided/classic require exiting OpenClaw and running openclaw onboard.", }), ), query: Type.Optional(Type.String({ description: "Search query for plugin_search" })), @@ -219,7 +219,7 @@ function createCaptureRuntime(): RuntimeEnv & { read: () => string } { log: (...args) => lines.push(args.join(" ")), error: (...args) => lines.push(args.join(" ")), exit: (code) => { - throw new Error(`crestodian operation exited with code ${String(code)}`); + throw new Error(`openclaw operation exited with code ${String(code)}`); }, read: () => lines.join("\n").trim(), }; @@ -228,7 +228,7 @@ function createCaptureRuntime(): RuntimeEnv & { read: () => string } { function requireParam(params: Record, name: string): string { const value = readStringParam(params, name); if (!value?.trim()) { - throw new ToolInputError(`crestodian: "${name}" is required for this action`); + throw new ToolInputError(`openclaw: "${name}" is required for this action`); } return value.trim(); } @@ -238,10 +238,10 @@ function readSetupTarget(params: Record): "guided" | "classic" if (target === "guided" || target === "classic" || target === "channels") { return target; } - throw new ToolInputError(`crestodian: unknown setup target "${target}"`); + throw new ToolInputError(`openclaw: unknown setup target "${target}"`); } -function operationForAction(params: Record): CrestodianOperation { +function operationForAction(params: Record): SystemAgentOperation { const action = readStringParam(params, "action", { required: true }); switch (action) { case "status": @@ -302,9 +302,9 @@ function operationForAction(params: Record): CrestodianOperatio return { kind: "plugin-search", query: requireParam(params, "query") }; case "plugin_install": { const spec = requireParam(params, "spec"); - const validationError = validateCrestodianPluginInstallSpec(spec); + const validationError = validateSystemAgentPluginInstallSpec(spec); if (validationError) { - throw new ToolInputError(`crestodian: ${validationError}`); + throw new ToolInputError(`openclaw: ${validationError}`); } return { kind: "plugin-install", spec }; } @@ -345,28 +345,28 @@ function operationForAction(params: Record): CrestodianOperatio id: requireParam(params, "envVar"), }; default: - throw new ToolInputError(`crestodian: unknown action "${action}"`); + throw new ToolInputError(`openclaw: unknown action "${action}"`); } } -export function createCrestodianTool(options: CrestodianToolOptions): AnyAgentTool { +export function createSystemAgentTool(options: SystemAgentToolOptions): AnyAgentTool { return { - name: "crestodian", - label: "Crestodian", + name: "openclaw", + label: "OpenClaw", // Setup authority is never discoverable through tool catalogs: the host // scopes it to this run and the model must receive it directly. catalogMode: "direct-only", description: [ - "Ring-zero setup/repair. Reads (status/models/agents/channels/channel_info/config_get/config_schema/gateway_status/plugin_search/validate_config/doctor/audit) run now.", - "connect_channel/open_setup(target=channels) starts guided chat setup; open_agent hands off normal agent.", - "Cannot change active inference route here. configure_model_provider/open_setup guided|classic, provider/credential/default-model change: exit and `openclaw onboard`; never ask credentials here.", - "Writes (setup/set_default_model/config_set/config_set_ref/create_agent/gateway_*/plugin_install) need approved=true only after user clearly agrees to exact change in this conversation. Host applies after turn and rechecks live inference owner.", - "plugin_install accepts only ClawHub, bundled, or official-catalog plugins; direct arbitrary-source installs belong in a trusted shell.", - "Unknown config path: config_schema first; schema is truth. Secret: config_set_ref env, never plaintext. Raw auth/models/env/secrets/plugins/tools/agent-route/$include writes refused; typed workflow only.", - "Plugin uninstall refused: exit, CLI. Doctor repair refused: exit, `openclaw doctor --fix`.", - "Every write validated/audited. CONFIG INVALID => fix immediately.", + "System agent. Setup, config, channels, plugins, agents, repair.", + "Read now: status, models, agents, channels, channel_info, config_get, config_schema, gateway_status, plugin_search, validate_config, doctor, audit.", + "Handoff: connect_channel; open_setup target=channels; open_agent.", + "Inference, provider, auth, credentials: exit; run `openclaw onboard`. Never request credentials.", + "Write: setup, set_default_model, config_set, config_set_ref, create_agent, gateway_*, plugin_install. Exact user approval required; then approved=true. Host applies after turn; rechecks inference owner.", + "plugin_install: ClawHub/bundled/official only. Arbitrary source: exit, trusted shell.", + "Unknown config: config_schema first. Secrets: config_set_ref env. No plaintext. No raw auth/models/env/secrets/plugins/tools/agent-route/$include; typed workflows.", + "No plugin uninstall. No doctor repair. Writes validated, audited. Invalid config: fix now.", ].join(" "), - parameters: CrestodianToolSchema, + parameters: SystemAgentToolSchema, execute: async (_toolCallId, args) => { const params = (args ?? {}) as Record; const operation = operationForAction(params); @@ -379,20 +379,20 @@ export function createCrestodianTool(options: CrestodianToolOptions): AnyAgentTo } return textResult( directive.kind === "channel-setup" - ? `${CRESTODIAN_DIRECTIVE_PREFIX} the host chat now starts the guided ${directive.channel} setup with the user. Tell the user the setup questions come next; do not describe steps yourself.` + ? `${SYSTEM_AGENT_DIRECTIVE_PREFIX} the host chat now starts the guided ${directive.channel} setup with the user. Tell the user the setup questions come next; do not describe steps yourself.` : directive.kind === "model-setup" - ? `${CRESTODIAN_DIRECTIVE_PREFIX} the active inference route cannot be changed inside Crestodian. Tell the user to exit Crestodian and run \`openclaw onboard\`; do not ask for provider credentials here.` + ? `${SYSTEM_AGENT_DIRECTIVE_PREFIX} the active inference route cannot be changed inside OpenClaw. Tell the user to exit OpenClaw and run \`openclaw onboard\`; do not ask for provider credentials here.` : directive.kind === "open-tui" - ? `${CRESTODIAN_DIRECTIVE_PREFIX} the host now hands the user over to their normal agent. Say goodbye briefly.` + ? `${SYSTEM_AGENT_DIRECTIVE_PREFIX} the host now hands the user over to their normal agent. Say goodbye briefly.` : directive.target === "channels" - ? `${CRESTODIAN_DIRECTIVE_PREFIX} the host now opens channel setup${directive.channel ? ` for ${directive.channel}` : ""}. Tell the user the channel setup questions come next.` - : `${CRESTODIAN_DIRECTIVE_PREFIX} ${directive.target} setup cannot run inside Crestodian because it may change the active inference route. Tell the user to exit Crestodian and run \`openclaw onboard\`.`, + ? `${SYSTEM_AGENT_DIRECTIVE_PREFIX} the host now opens channel setup${directive.channel ? ` for ${directive.channel}` : ""}. Tell the user the channel setup questions come next.` + : `${SYSTEM_AGENT_DIRECTIVE_PREFIX} ${directive.target} setup cannot run inside OpenClaw because it may change the active inference route. Tell the user to exit OpenClaw and run \`openclaw onboard\`.`, {}, ); } - const persistent = isPersistentCrestodianOperation(operation); + const persistent = isPersistentSystemAgentOperation(operation); if (persistent) { - const operationHash = hashCrestodianOperation(operation); + const operationHash = hashSystemAgentOperation(operation); const armedForThisOperation = params.approved === true && options.approvalArmed === true && @@ -409,7 +409,7 @@ export function createCrestodianTool(options: CrestodianToolOptions): AnyAgentTo options.proposalRef.current = undefined; } return textResult( - `${CRESTODIAN_APPROVAL_MISMATCH_PREFIX} this call is not the operation the user approved. The approval is void; describe the new change and get a fresh yes before retrying.`, + `${SYSTEM_AGENT_APPROVAL_MISMATCH_PREFIX} this call is not the operation the user approved. The approval is void; describe the new change and get a fresh yes before retrying.`, { needsApproval: true }, ); } @@ -417,7 +417,7 @@ export function createCrestodianTool(options: CrestodianToolOptions): AnyAgentTo options.proposalRef.current = operationHash; } return textResult( - `${CRESTODIAN_NEEDS_APPROVAL_PREFIX}${operationHash}\nThis action changes state. The proposal is registered; describe this exact change and ask the user to reply yes (their approval unlocks THIS action only — then retry the exact registered operation with approved=true).`, + `${SYSTEM_AGENT_NEEDS_APPROVAL_PREFIX}${operationHash}\nThis action changes state. The proposal is registered; describe this exact change and ask the user to reply yes (their approval unlocks THIS action only — then retry the exact registered operation with approved=true).`, { needsApproval: true }, ); } @@ -425,7 +425,7 @@ export function createCrestodianTool(options: CrestodianToolOptions): AnyAgentTo // One approval, one mutation: re-proposals need a fresh yes. options.proposalRef.current = undefined; } - const approvedDirective: CrestodianToolDirective = { + const approvedDirective: SystemAgentToolDirective = { kind: "approved-operation", operation, }; @@ -436,13 +436,13 @@ export function createCrestodianTool(options: CrestodianToolOptions): AnyAgentTo // its out-of-process MCP server. The host rechecks the verified // inference binding immediately before applying this exact operation. return textResult( - `${CRESTODIAN_APPROVED_OPERATION_PREFIX} the host accepted this exact approved action and will apply it after this turn. Do not call it again.`, + `${SYSTEM_AGENT_APPROVED_OPERATION_PREFIX} the host accepted this exact approved action and will apply it after this turn. Do not call it again.`, {}, ); } const capture = createCaptureRuntime(); try { - await executeCrestodianOperation(operation, capture, { + await executeSystemAgentOperation(operation, capture, { approved: false, deps: { setupSurface: options.surface }, }); diff --git a/src/auto-reply/commands-registry.shared.ts b/src/auto-reply/commands-registry.shared.ts index f353e292dcdb..a1f772e84755 100644 --- a/src/auto-reply/commands-registry.shared.ts +++ b/src/auto-reply/commands-registry.shared.ts @@ -317,9 +317,9 @@ export function buildBuiltinChatCommands( ], }), defineChatCommand({ - key: "crestodian", - description: "Run the Crestodian setup and repair helper.", - textAlias: "/crestodian", + key: "openclaw", + description: "Run the OpenClaw setup and repair helper.", + textAlias: "/openclaw", acceptsArgs: true, scope: "text", category: "management", diff --git a/src/auto-reply/reply/commands-crestodian.ts b/src/auto-reply/reply/commands-crestodian.ts deleted file mode 100644 index 882bcfee009e..000000000000 --- a/src/auto-reply/reply/commands-crestodian.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Implements maintenance commands for Crestodian-backed session cleanup. -import { logVerbose } from "../../globals.js"; -import type { CommandHandler } from "./commands-types.js"; - -export const handleCrestodianCommand: CommandHandler = async (params, allowTextCommands) => { - if (!allowTextCommands) { - return null; - } - const { extractCrestodianRescueMessage, runCrestodianRescueMessage } = - await import("../../crestodian/rescue-message.js"); - if (extractCrestodianRescueMessage(params.command.commandBodyNormalized) === null) { - return null; - } - if (!params.command.isAuthorizedSender) { - logVerbose( - `Ignoring /crestodian from unauthorized sender: ${params.command.senderId || ""}`, - ); - return { shouldContinue: false }; - } - return { - shouldContinue: false, - reply: { - text: - (await runCrestodianRescueMessage({ - cfg: params.cfg, - command: params.command, - commandBody: params.command.commandBodyNormalized, - agentId: params.agentId, - isGroup: params.isGroup, - })) ?? "Crestodian did not find a rescue request.", - }, - }; -}; diff --git a/src/auto-reply/reply/commands-handlers.runtime.ts b/src/auto-reply/reply/commands-handlers.runtime.ts index 33d5b87e4b8b..1328d62c5fb5 100644 --- a/src/auto-reply/reply/commands-handlers.runtime.ts +++ b/src/auto-reply/reply/commands-handlers.runtime.ts @@ -7,7 +7,6 @@ import { handleBtwCommand } from "./commands-btw.js"; import { handleCompactCommand } from "./commands-compact.js"; import { handleConfigCommand, handleDebugCommand } from "./commands-config.js"; import { handleContextCommand } from "./commands-context-command.js"; -import { handleCrestodianCommand } from "./commands-crestodian.js"; import { handleDiagnosticsCommand } from "./commands-diagnostics.js"; import { handleDockCommand } from "./commands-dock.js"; import { handleGoalCommand } from "./commands-goal.js"; @@ -39,6 +38,7 @@ import { } from "./commands-session.js"; import { handleSteerCommand } from "./commands-steer.js"; import { handleSubagentsCommand } from "./commands-subagents.js"; +import { handleSystemAgentCommand } from "./commands-system-agent.js"; import { handleTasksCommand } from "./commands-tasks.js"; import { handleTtsCommands } from "./commands-tts.js"; import type { CommandHandler } from "./commands-types.js"; @@ -77,7 +77,7 @@ export function loadCommandHandlers(): CommandHandler[] { handleExportSessionCommand, handleExportTrajectoryCommand, handleWhoamiCommand, - handleCrestodianCommand, + handleSystemAgentCommand, handleSubagentsCommand, handleAcpCommand, handleMcpCommand, diff --git a/src/auto-reply/reply/commands-system-agent.ts b/src/auto-reply/reply/commands-system-agent.ts new file mode 100644 index 000000000000..b695b934f8ee --- /dev/null +++ b/src/auto-reply/reply/commands-system-agent.ts @@ -0,0 +1,33 @@ +// Implements maintenance commands for OpenClaw-backed session cleanup. +import { logVerbose } from "../../globals.js"; +import type { CommandHandler } from "./commands-types.js"; + +export const handleSystemAgentCommand: CommandHandler = async (params, allowTextCommands) => { + if (!allowTextCommands) { + return null; + } + const { extractSystemAgentRescueMessage, runSystemAgentRescueMessage } = + await import("../../system-agent/rescue-message.js"); + if (extractSystemAgentRescueMessage(params.command.commandBodyNormalized) === null) { + return null; + } + if (!params.command.isAuthorizedSender) { + logVerbose( + `Ignoring /openclaw from unauthorized sender: ${params.command.senderId || ""}`, + ); + return { shouldContinue: false }; + } + return { + shouldContinue: false, + reply: { + text: + (await runSystemAgentRescueMessage({ + cfg: params.cfg, + command: params.command, + commandBody: params.command.commandBodyNormalized, + agentId: params.agentId, + isGroup: params.isGroup, + })) ?? "OpenClaw did not find a rescue request.", + }, + }; +}; diff --git a/src/channels/plugins/setup-wizard-helpers.ts b/src/channels/plugins/setup-wizard-helpers.ts index 9512d2c5094d..39f3f8e6db0f 100644 --- a/src/channels/plugins/setup-wizard-helpers.ts +++ b/src/channels/plugins/setup-wizard-helpers.ts @@ -952,7 +952,7 @@ async function promptSingleChannelToken(params: { ( await params.prompter.text({ message: params.inputPrompt, - // Credential input: masked in terminal prompts, and the Crestodian + // Credential input: masked in terminal prompts, and the OpenClaw // chat bridge relies on this flag to refuse plain-text secret entry. sensitive: true, validate: (value) => (value?.trim() ? undefined : "Required"), diff --git a/src/cli/command-catalog.ts b/src/cli/command-catalog.ts index 9238dfb0b693..91f962a002ca 100644 --- a/src/cli/command-catalog.ts +++ b/src/cli/command-catalog.ts @@ -67,7 +67,11 @@ function hasCliOption(argv: readonly string[], name: string): boolean { /** Command path registry used before Commander registration has loaded all plugins. */ export const cliCommandCatalog: readonly CliCommandCatalogEntry[] = [ { - commandPath: ["crestodian"], + commandPath: ["setup"], + policy: { bypassConfigGuard: true, loadPlugins: "never", ensureCliPath: false }, + }, + { + commandPath: ["crestodian"], // hidden alias policy: { bypassConfigGuard: true, loadPlugins: "never", ensureCliPath: false }, }, { diff --git a/src/cli/program.smoke.test.ts b/src/cli/program.smoke.test.ts index c33d38144647..6c59fb1471c5 100644 --- a/src/cli/program.smoke.test.ts +++ b/src/cli/program.smoke.test.ts @@ -4,7 +4,7 @@ import { buildProgram } from "./program.js"; import { configureCommand, ensureConfigReady, - runCrestodianWithInference, + runSystemAgentWithInference, runTui, runtime, setupCommand, @@ -44,7 +44,7 @@ describe("cli program (smoke)", () => { program = createProgram(); vi.clearAllMocks(); runTui.mockResolvedValue(undefined); - runCrestodianWithInference.mockResolvedValue(undefined); + runSystemAgentWithInference.mockResolvedValue(undefined); ensureConfigReady.mockResolvedValue(undefined); }); @@ -64,9 +64,9 @@ describe("cli program (smoke)", () => { expect(options?.forceProcessExitOnReturn).toBe(true); }); - it("runs crestodian one-shot requests", async () => { - await runProgram(["crestodian", "--message", "status"]); - const options = firstMockArg(runCrestodianWithInference) as { + it("runs setup one-shot requests", async () => { + await runProgram(["setup", "--message", "status"]); + const options = firstMockArg(runSystemAgentWithInference) as { message?: string; yes?: boolean; json?: boolean; @@ -74,7 +74,7 @@ describe("cli program (smoke)", () => { expect(options?.message).toBe("status"); expect(options?.yes).toBe(false); expect(options?.json).toBe(false); - expect(runCrestodianWithInference).toHaveBeenCalledWith(options, runtime); + expect(runSystemAgentWithInference).toHaveBeenCalledWith(options, runtime); }); it("warns and ignores invalid tui timeout override", async () => { diff --git a/src/cli/program.test-mocks.ts b/src/cli/program.test-mocks.ts index 947ddc5df265..fe0ca41e6f75 100644 --- a/src/cli/program.test-mocks.ts +++ b/src/cli/program.test-mocks.ts @@ -30,7 +30,7 @@ const programMocks = vi.hoisted(() => { runChannelLogin: vi.fn(), runChannelLogout: vi.fn(), runTui: vi.fn(), - runCrestodianWithInference: vi.fn(), + runSystemAgentWithInference: vi.fn(), loadAndMaybeMigrateDoctorConfig: vi.fn(), ensureConfigReady: vi.fn(), ensurePluginRegistryLoaded: vi.fn(), @@ -43,7 +43,7 @@ export const setupCommand = programMocks.setupCommand as AnyMock; export const setupWizardCommand = programMocks.setupWizardCommand as AnyMock; export const callGateway = programMocks.callGateway as AnyMock; export const runTui = programMocks.runTui as AnyMock; -export const runCrestodianWithInference = programMocks.runCrestodianWithInference as AnyMock; +export const runSystemAgentWithInference = programMocks.runSystemAgentWithInference as AnyMock; export const ensureConfigReady = programMocks.ensureConfigReady as AnyMock; export const runtime = programMocks.runtime as { @@ -89,8 +89,8 @@ vi.mock("./channel-auth.js", () => ({ runChannelLogout: programMocks.runChannelLogout, })); vi.mock("../tui/tui.js", () => ({ runTui: programMocks.runTui })); -vi.mock("../commands/crestodian-with-inference.js", () => ({ - runCrestodianWithInference: programMocks.runCrestodianWithInference, +vi.mock("../commands/system-agent-with-inference.js", () => ({ + runSystemAgentWithInference: programMocks.runSystemAgentWithInference, })); vi.mock("../gateway/call.js", () => ({ callGateway: programMocks.callGateway, diff --git a/src/cli/program/command-descriptor-utils.test.ts b/src/cli/program/command-descriptor-utils.test.ts index 7594b8578a6c..fb7430985cfd 100644 --- a/src/cli/program/command-descriptor-utils.test.ts +++ b/src/cli/program/command-descriptor-utils.test.ts @@ -77,6 +77,18 @@ describe("command-descriptor-utils", () => { expect(program.commands[0]?.description()).toBe("Open link now"); }); + it("keeps hidden descriptors out of help", () => { + const program = new Command(); + addCommandDescriptorsToProgram(program, [ + { name: "visible", description: "Visible" }, + { name: "retired", description: "Retired", hidden: true }, + ]); + + expect(program.commands.map((command) => command.name())).toContain("retired"); + expect(program.helpInformation()).toContain("visible"); + expect(program.helpInformation()).not.toContain("retired"); + }); + it("rejects unsafe descriptor command names before rendering", () => { const program = new Command(); diff --git a/src/cli/program/command-descriptor-utils.ts b/src/cli/program/command-descriptor-utils.ts index 5d1cf6b37dab..1c09bfbe73a0 100644 --- a/src/cli/program/command-descriptor-utils.ts +++ b/src/cli/program/command-descriptor-utils.ts @@ -4,7 +4,7 @@ import { sanitizeForLog } from "../../../packages/terminal-core/src/ansi.js"; import type { NamedCommandDescriptor } from "./command-group-descriptors.js"; /** Minimal descriptor shape used before a command is fully registered. */ -type CommandDescriptorLike = Pick; +type CommandDescriptorLike = Pick; const SAFE_COMMAND_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/; @@ -97,7 +97,9 @@ export function addCommandDescriptorsToProgram( if (existingCommands.has(name)) { continue; } - program.command(name).description(sanitizeCommandDescriptorDescription(descriptor.description)); + program + .command(name, { hidden: descriptor.hidden }) + .description(sanitizeCommandDescriptorDescription(descriptor.description)); existingCommands.add(name); } return existingCommands; diff --git a/src/cli/program/command-group-descriptors.ts b/src/cli/program/command-group-descriptors.ts index 461d6f10da35..78be605a0534 100644 --- a/src/cli/program/command-group-descriptors.ts +++ b/src/cli/program/command-group-descriptors.ts @@ -6,6 +6,7 @@ export type NamedCommandDescriptor = { name: string; description: string; hasSubcommands: boolean; + hidden?: boolean; parentDefaultHelp?: boolean; }; diff --git a/src/cli/program/command-registry-core.ts b/src/cli/program/command-registry-core.ts index 7c502d63d3e5..d0564f20169f 100644 --- a/src/cli/program/command-registry-core.ts +++ b/src/cli/program/command-registry-core.ts @@ -45,12 +45,7 @@ const coreEntrySpecs: readonly CommandGroupDescriptorSpec< ...withProgramOnlySpecs( defineImportedProgramCommandGroupSpecs([ { - commandNames: ["crestodian"], - loadModule: () => import("./register.crestodian.js"), - exportName: "registerCrestodianCommand", - }, - { - commandNames: ["setup"], + commandNames: ["setup", "crestodian"], // hidden alias loadModule: () => import("./register.setup.js"), exportName: "registerSetupCommand", }, diff --git a/src/cli/program/command-registry.test.ts b/src/cli/program/command-registry.test.ts index eb91c8495735..c4f36a4d1d62 100644 --- a/src/cli/program/command-registry.test.ts +++ b/src/cli/program/command-registry.test.ts @@ -44,9 +44,10 @@ vi.mock("./register.status-health-sessions.js", () => ({ }, })); -vi.mock("./register.crestodian.js", () => ({ - registerCrestodianCommand: (program: Command) => { - program.command("crestodian"); +vi.mock("./register.setup.js", () => ({ + registerSetupCommand: (program: Command) => { + program.command("setup"); + program.command("crestodian", { hidden: true }); // hidden alias }, })); @@ -80,7 +81,8 @@ describe("command-registry", () => { it("includes both agent and agents in core CLI command names", () => { const names = getCoreCliCommandNames(); - expect(names).toContain("crestodian"); + expect(names).toContain("setup"); + expect(names).toContain("crestodian"); // hidden alias expect(names).toContain("mcp"); expect(names).toContain("agent"); expect(names).toContain("agents"); @@ -96,7 +98,7 @@ describe("command-registry", () => { expect(names).toContain("commitments"); expect(names).toContain("tasks"); expect(names).not.toContain("agent"); - expect(names).not.toContain("crestodian"); + expect(names).not.toContain("setup"); expect(names).not.toContain("status"); expect(names).not.toContain("doctor"); }); diff --git a/src/cli/program/core-command-descriptors.ts b/src/cli/program/core-command-descriptors.ts index 8622c1dd7569..42817fea5b8b 100644 --- a/src/cli/program/core-command-descriptors.ts +++ b/src/cli/program/core-command-descriptors.ts @@ -7,14 +7,15 @@ type CoreCliCommandDescriptor = NamedCommandDescriptor; const coreCliCommandCatalog = defineCommandDescriptorCatalog([ { - name: "crestodian", - description: "Open the ring-zero setup and repair helper", + name: "setup", + description: "Chat with OpenClaw; onboard when setup is incomplete", hasSubcommands: false, }, { - name: "setup", - description: "Alias for openclaw onboard", + name: "crestodian", // hidden alias + description: "Deprecated: use openclaw setup", hasSubcommands: false, + hidden: true, }, { name: "onboard", diff --git a/src/cli/program/register.crestodian.ts b/src/cli/program/register.crestodian.ts deleted file mode 100644 index 399a020073a9..000000000000 --- a/src/cli/program/register.crestodian.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Crestodian command registration: setup/repair assistant entrypoint exposed from the root CLI. -import type { Command } from "commander"; -import { theme } from "../../../packages/terminal-core/src/theme.js"; -import { runCrestodianWithInference } from "../../commands/crestodian-with-inference.js"; -import { defaultRuntime } from "../../runtime.js"; -import { runCommandWithRuntime } from "../cli-utils.js"; -import { formatHelpExamples } from "../help-format.js"; - -/** Register the Crestodian helper command and its one-shot request flags. */ -export function registerCrestodianCommand(program: Command) { - program - .command("crestodian") - .description("Open the ring-zero setup and repair helper") - .option("-m, --message ", "Run one Crestodian request") - .option("--yes", "Approve persistent config writes for one --message request", false) - .option("--json", "Output startup overview as JSON", false) - .addHelpText( - "after", - () => - `\n${theme.heading("Examples:")}\n${formatHelpExamples([ - ["openclaw crestodian", "Start Crestodian after a live inference check."], - ['openclaw crestodian -m "status"', "Run one status request."], - [ - 'openclaw crestodian -m "set default model openai/gpt-5.2" --yes', - "Apply a typed config write.", - ], - ])}`, - ) - .action(async (opts) => { - await runCommandWithRuntime(defaultRuntime, async () => { - await runCrestodianWithInference( - { - message: opts.message as string | undefined, - yes: Boolean(opts.yes), - json: Boolean(opts.json), - }, - defaultRuntime, - ); - }); - }); -} diff --git a/src/cli/program/register.onboard.test.ts b/src/cli/program/register.onboard.test.ts index e41dc8d5c892..2d423b7edb88 100644 --- a/src/cli/program/register.onboard.test.ts +++ b/src/cli/program/register.onboard.test.ts @@ -4,7 +4,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { registerOnboardCommand } from "./register.onboard.js"; const mocks = vi.hoisted(() => ({ - runCrestodianWithInference: vi.fn(), + runSystemAgentWithInference: vi.fn(), setupWizardCommandMock: vi.fn(), runtime: { log: vi.fn(), @@ -49,8 +49,8 @@ vi.mock("../../commands/onboard.js", () => ({ setupWizardCommand: mocks.setupWizardCommandMock, })); -vi.mock("../../commands/crestodian-with-inference.js", () => ({ - runCrestodianWithInference: mocks.runCrestodianWithInference, +vi.mock("../../commands/system-agent-with-inference.js", () => ({ + runSystemAgentWithInference: mocks.runSystemAgentWithInference, })); vi.mock("../../runtime.js", () => ({ @@ -75,7 +75,7 @@ describe("registerOnboardCommand", () => { beforeEach(() => { vi.clearAllMocks(); - mocks.runCrestodianWithInference.mockResolvedValue(undefined); + mocks.runSystemAgentWithInference.mockResolvedValue(undefined); setupWizardCommandMock.mockResolvedValue(undefined); }); @@ -164,10 +164,10 @@ describe("registerOnboardCommand", () => { expect(runtime.exit).toHaveBeenCalledWith(1); }); - it("routes --modern through the inference-gated Crestodian entrypoint", async () => { + it("routes --modern through the inference-gated OpenClaw entrypoint", async () => { await runCli(["onboard", "--modern", "--json"]); - expect(mocks.runCrestodianWithInference).toHaveBeenCalledWith( + expect(mocks.runSystemAgentWithInference).toHaveBeenCalledWith( { yes: false, json: true, @@ -183,7 +183,7 @@ describe("registerOnboardCommand", () => { it("uses the single-output noninteractive overview behind the inference gate", async () => { await runCli(["onboard", "--modern", "--non-interactive", "--accept-risk"]); - expect(mocks.runCrestodianWithInference).toHaveBeenCalledWith( + expect(mocks.runSystemAgentWithInference).toHaveBeenCalledWith( { yes: false, json: false, @@ -199,7 +199,7 @@ describe("registerOnboardCommand", () => { it("preserves guided fallback context for --modern", async () => { await runCli(["onboard", "--modern", "--workspace", "/tmp/work", "--accept-risk"]); - expect(mocks.runCrestodianWithInference).toHaveBeenCalledWith( + expect(mocks.runSystemAgentWithInference).toHaveBeenCalledWith( expect.objectContaining({ welcomeVariant: "onboarding", setupWorkspace: "/tmp/work", @@ -218,7 +218,7 @@ describe("registerOnboardCommand", () => { expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("--accept-risk")); expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("onboard --modern")); expect(runtime.exit).toHaveBeenCalledWith(1); - expect(mocks.runCrestodianWithInference).not.toHaveBeenCalled(); + expect(mocks.runSystemAgentWithInference).not.toHaveBeenCalled(); expect(setupWizardCommandMock).not.toHaveBeenCalled(); }); @@ -236,14 +236,14 @@ describe("registerOnboardCommand", () => { expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining(args[0]!)); expect(runtime.exit).toHaveBeenCalledWith(1); - expect(mocks.runCrestodianWithInference).not.toHaveBeenCalled(); + expect(mocks.runSystemAgentWithInference).not.toHaveBeenCalled(); expect(setupWizardCommandMock).not.toHaveBeenCalled(); }); it("keeps noninteractive JSON modern onboarding to one overview request", async () => { await runCli(["onboard", "--modern", "--non-interactive", "--accept-risk", "--json"]); - expect(mocks.runCrestodianWithInference).toHaveBeenCalledWith( + expect(mocks.runSystemAgentWithInference).toHaveBeenCalledWith( expect.objectContaining({ json: true, interactive: false, @@ -252,6 +252,6 @@ describe("registerOnboardCommand", () => { runtime, { acceptRisk: true }, ); - expect(mocks.runCrestodianWithInference.mock.calls[0]?.[0]).not.toHaveProperty("message"); + expect(mocks.runSystemAgentWithInference.mock.calls[0]?.[0]).not.toHaveProperty("message"); }); }); diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index 4dcd98086159..3f0e90f1a34f 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -191,7 +191,7 @@ export function registerOnboardCommand(program: Command): void { ) .option("--reset-scope ", "Reset scope: config|config+creds+sessions|full") .option("--non-interactive", "Run without prompts", false) - .option("--modern", "Open inference-gated Crestodian (kept for compatibility)", false) + .option("--modern", "Open inference-gated OpenClaw (kept for compatibility)", false) .option("--classic", "Use the classic multi-step setup wizard", false) .option( "--accept-risk", @@ -244,7 +244,7 @@ export function registerOnboardCommand(program: Command): void { defaultRuntime.error( [ `--modern cannot be combined with: ${unsupportedOptions.join(", ")}.`, - "Run those setup options without --modern, or remove them to open Crestodian.", + "Run those setup options without --modern, or remove them to open OpenClaw.", ].join("\n"), ); defaultRuntime.exit(1); @@ -261,9 +261,9 @@ export function registerOnboardCommand(program: Command): void { defaultRuntime.exit(1); return; } - const { runCrestodianWithInference } = - await import("../../commands/crestodian-with-inference.js"); - await runCrestodianWithInference( + const { runSystemAgentWithInference } = + await import("../../commands/system-agent-with-inference.js"); + await runSystemAgentWithInference( { yes: false, json: Boolean(opts.json), diff --git a/src/cli/program/register.setup.test.ts b/src/cli/program/register.setup.test.ts index 5b8f186f9c76..cf36b0422e74 100644 --- a/src/cli/program/register.setup.test.ts +++ b/src/cli/program/register.setup.test.ts @@ -1,11 +1,13 @@ // Register setup tests cover setup command registration and option wiring. import { Command } from "commander"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { registerSetupCommand } from "./register.setup.js"; +import { registerSetupCommand, resolveSetupCommandRoute } from "./register.setup.js"; const mocks = vi.hoisted(() => ({ setupCommandMock: vi.fn(), setupWizardCommandMock: vi.fn(), + runSystemAgentMock: vi.fn(), + readConfigFileSnapshotMock: vi.fn(), runtime: { log: vi.fn(), error: vi.fn(), @@ -15,6 +17,8 @@ const mocks = vi.hoisted(() => ({ const setupCommandMock = mocks.setupCommandMock; const setupWizardCommandMock = mocks.setupWizardCommandMock; +const runSystemAgentMock = mocks.runSystemAgentMock; +const readConfigFileSnapshotMock = mocks.readConfigFileSnapshotMock; const runtime = mocks.runtime; function lastSetupOptions(): Record | undefined { @@ -35,6 +39,14 @@ vi.mock("../../commands/onboard.js", () => ({ setupWizardCommand: mocks.setupWizardCommandMock, })); +vi.mock("../../commands/system-agent-with-inference.js", () => ({ + runSystemAgentWithInference: mocks.runSystemAgentMock, +})); + +vi.mock("../../config/config.js", () => ({ + readConfigFileSnapshot: mocks.readConfigFileSnapshotMock, +})); + vi.mock("../../runtime.js", () => ({ defaultRuntime: mocks.runtime, })); @@ -50,6 +62,98 @@ describe("registerSetupCommand", () => { vi.clearAllMocks(); setupCommandMock.mockResolvedValue(undefined); setupWizardCommandMock.mockResolvedValue(undefined); + runSystemAgentMock.mockResolvedValue(undefined); + readConfigFileSnapshotMock.mockResolvedValue({ + exists: false, + valid: true, + sourceConfig: {}, + }); + }); + + it("keeps routing precedence explicit", () => { + expect( + resolveSetupCommandRoute({ + hasOnboardingFlag: true, + hasSystemAgentRequest: true, + configured: true, + interactive: true, + json: true, + }), + ).toBe("onboarding"); + expect( + resolveSetupCommandRoute({ + hasOnboardingFlag: false, + hasSystemAgentRequest: true, + configured: false, + interactive: false, + json: false, + }), + ).toBe("system-agent"); + expect( + resolveSetupCommandRoute({ + hasOnboardingFlag: false, + hasSystemAgentRequest: false, + configured: true, + interactive: true, + json: false, + }), + ).toBe("system-agent"); + expect( + resolveSetupCommandRoute({ + hasOnboardingFlag: false, + hasSystemAgentRequest: false, + configured: false, + interactive: true, + json: true, + }), + ).toBe("onboarding"); + }); + + it("runs one-shot system-agent requests without probing config", async () => { + await runCli(["setup", "-m", "status", "--yes"]); + + expect(runSystemAgentMock).toHaveBeenCalledWith( + { message: "status", yes: true, json: false }, + runtime, + ); + expect(readConfigFileSnapshotMock).not.toHaveBeenCalled(); + expect(setupWizardCommandMock).not.toHaveBeenCalled(); + }); + + it("uses system overview JSON on configured systems", async () => { + readConfigFileSnapshotMock.mockResolvedValue({ + exists: true, + valid: true, + sourceConfig: { gateway: {} }, + }); + + await runCli(["setup", "--json"]); + + expect(runSystemAgentMock).toHaveBeenCalledWith( + { message: undefined, yes: false, json: true }, + runtime, + ); + expect(setupWizardCommandMock).not.toHaveBeenCalled(); + }); + + it("keeps onboarding JSON for unconfigured systems", async () => { + await runCli(["setup", "--json"]); + + expect(setupWizardCommandMock).toHaveBeenCalledWith(lastWizardOptions(), runtime); + expect(lastWizardOptions()?.json).toBe(true); + expect(runSystemAgentMock).not.toHaveBeenCalled(); + }); + + it("registers a hidden retired-name alias", async () => { + const program = new Command(); + registerSetupCommand(program); + + expect(program.helpInformation()).not.toContain("crestodian"); // hidden alias + await program.parseAsync(["crestodian", "--message", "status"], { from: "user" }); // hidden alias + expect(runSystemAgentMock).toHaveBeenCalledWith( + { message: "status", yes: false, json: false }, + runtime, + ); }); it("runs setup wizard command by default", async () => { diff --git a/src/cli/program/register.setup.ts b/src/cli/program/register.setup.ts index 7eb5f3479500..18170d4efeb4 100644 --- a/src/cli/program/register.setup.ts +++ b/src/cli/program/register.setup.ts @@ -1,4 +1,4 @@ -// Setup command registration: baseline setup by default, onboarding wizard when wizard flags appear. +// Setup command registration: system-agent chat for configured systems, onboarding otherwise. import type { Command } from "commander"; import { formatDocsLink } from "../../../packages/terminal-core/src/links.js"; import { theme } from "../../../packages/terminal-core/src/theme.js"; @@ -10,7 +10,10 @@ import type { ResetScope, TailscaleMode, } from "../../commands/onboard-types.js"; +import type { RuntimeEnv } from "../../runtime.js"; import { runCommandWithRuntime } from "../cli-utils.js"; +import { hasExplicitOptions } from "../command-options.js"; +import { isUnconfiguredConfigSource } from "../fresh-install-config.js"; import { parsePort } from "../shared/parse-port.js"; import { pickOnboardAuthOptionValues, @@ -18,17 +21,140 @@ import { resolveInstallDaemonFlag, } from "./register.onboard.js"; -/** Register the `setup` command as an onboarding alias. */ +const SYSTEM_AGENT_OPTION_NAMES = new Set(["message", "yes", "json"]); + +type SetupRoute = "onboarding" | "system-agent"; + +export function resolveSetupCommandRoute(input: { + hasOnboardingFlag: boolean; + hasSystemAgentRequest: boolean; + configured: boolean; + interactive: boolean; + json: boolean; +}): SetupRoute { + if (input.hasOnboardingFlag) { + return "onboarding"; + } + if (input.hasSystemAgentRequest) { + return "system-agent"; + } + if (input.configured && (input.interactive || input.json)) { + return "system-agent"; + } + return "onboarding"; +} + +function hasExplicitOnboardingOption(command: Command): boolean { + return command.options.some((option) => { + const name = option.attributeName(); + return !SYSTEM_AGENT_OPTION_NAMES.has(name) && command.getOptionValueSource(name) === "cli"; + }); +} + +async function isConfiguredInstance(): Promise { + const { readConfigFileSnapshot } = await import("../../config/config.js"); + const snapshot = await readConfigFileSnapshot(); + if (!snapshot.exists) { + return false; + } + if (!snapshot.valid) { + return true; + } + return !isUnconfiguredConfigSource(snapshot.sourceConfig); +} + +async function runSystemAgentEntry( + opts: Record, + runtime: RuntimeEnv, +): Promise { + const { runSystemAgentWithInference } = + await import("../../commands/system-agent-with-inference.js"); + await runSystemAgentWithInference( + { + message: opts.message as string | undefined, + yes: Boolean(opts.yes), + json: Boolean(opts.json), + }, + runtime, + ); +} + +async function runOnboardingEntry( + opts: Record, + commandRuntime: Command, + runtime: RuntimeEnv, +): Promise { + if (opts.baseline) { + const { setupCommand } = await import("../../commands/setup.js"); + await setupCommand({ workspace: opts.workspace as string | undefined }, runtime); + return; + } + const installDaemon = resolveInstallDaemonFlag(commandRuntime); + const gatewayPort = parsePort(opts.gatewayPort); + const { setupWizardCommand } = await import("../../commands/onboard.js"); + await setupWizardCommand( + { + workspace: opts.workspace as string | undefined, + nonInteractive: Boolean(opts.nonInteractive), + acceptRisk: Boolean(opts.acceptRisk), + classic: Boolean(opts.classic), + flow: opts.flow as "quickstart" | "advanced" | "manual" | "import" | undefined, + mode: opts.mode as "local" | "remote" | undefined, + ...pickOnboardAuthOptionValues(opts), + reset: Boolean(opts.reset), + resetScope: opts.resetScope as ResetScope | undefined, + gatewayPort: gatewayPort ?? undefined, + gatewayBind: opts.gatewayBind as GatewayBind | undefined, + gatewayAuth: opts.gatewayAuth as GatewayAuthChoice | undefined, + gatewayToken: opts.gatewayToken as string | undefined, + gatewayTokenRefEnv: opts.gatewayTokenRefEnv as string | undefined, + gatewayPassword: opts.gatewayPassword as string | undefined, + tailscale: opts.tailscale as TailscaleMode | undefined, + tailscaleResetOnExit: Boolean(opts.tailscaleResetOnExit), + installDaemon, + daemonRuntime: opts.daemonRuntime as GatewayDaemonRuntime | undefined, + skipChannels: Boolean(opts.skipChannels), + skipSkills: Boolean(opts.skipSkills), + skipBootstrap: Boolean(opts.skipBootstrap), + skipSearch: Boolean(opts.skipSearch), + skipHealth: Boolean(opts.skipHealth), + skipUi: Boolean(opts.skipUi), + suppressGatewayTokenOutput: Boolean(opts.suppressGatewayTokenOutput), + skipHooks: Boolean(opts.skipHooks), + nodeManager: opts.nodeManager as NodeManagerChoice | undefined, + importFrom: opts.importFrom as string | undefined, + importSource: opts.importSource as string | undefined, + importSecrets: Boolean(opts.importSecrets), + remoteUrl: opts.remoteUrl as string | undefined, + remoteToken: opts.remoteToken as string | undefined, + json: Boolean(opts.json), + }, + runtime, + ); +} + +function addSystemAgentOptions(command: Command): Command { + return command + .option("-m, --message ", "Run one OpenClaw request") + .option("--yes", "Approve persistent config writes for one --message request", false) + .option("--json", "Output system overview or onboarding summary as JSON", false); +} + +/** Register the canonical `setup` command and its hidden retired-name alias. */ export function registerSetupCommand(program: Command): void { const command = program .command("setup") - .description("Alias for openclaw onboard") + .description("Chat with OpenClaw; onboard when setup is incomplete") .addHelpText( "after", () => `\n${theme.heading("Examples:")}\n` + ` ${theme.command("openclaw setup")}\n` + - ` ${theme.muted("Run full onboarding for auth, models, Gateway, and channels.")}\n\n` + + ` ${theme.muted("Chat with OpenClaw, or onboard when setup is incomplete.")}\n` + + ` ${theme.command('openclaw setup -m "status"')}\n` + + ` ${theme.muted("Run one system-agent request.")}\n` + + ` ${theme.command("openclaw setup --wizard")}\n` + + ` ${theme.muted("Run full onboarding.")}\n\n` + `${theme.muted("Docs:")} ${formatDocsLink("/cli/setup", "docs.openclaw.ai/cli/setup")}\n`, ) .option( @@ -87,58 +213,39 @@ export function registerSetupCommand(program: Command): void { .option("--import-source ", "Source agent home for --import-from") .option("--import-secrets", "Import supported secrets during onboarding migration", false) .option("--remote-url ", "Remote Gateway WebSocket URL") - .option("--remote-token ", "Remote Gateway token (optional)") - .option("--json", "Output JSON summary", false) - .action(async (opts, commandRuntime: Command) => { - const { defaultRuntime } = await import("../../runtime.js"); - await runCommandWithRuntime(defaultRuntime, async () => { - if (opts.baseline) { - const { setupCommand } = await import("../../commands/setup.js"); - await setupCommand({ workspace: opts.workspace as string | undefined }, defaultRuntime); - return; - } - const installDaemon = resolveInstallDaemonFlag(commandRuntime); - const gatewayPort = parsePort(opts.gatewayPort); - const { setupWizardCommand } = await import("../../commands/onboard.js"); - await setupWizardCommand( - { - workspace: opts.workspace as string | undefined, - nonInteractive: Boolean(opts.nonInteractive), - acceptRisk: Boolean(opts.acceptRisk), - classic: Boolean(opts.classic), - flow: opts.flow as "quickstart" | "advanced" | "manual" | "import" | undefined, - mode: opts.mode as "local" | "remote" | undefined, - ...pickOnboardAuthOptionValues(opts as Record), - reset: Boolean(opts.reset), - resetScope: opts.resetScope as ResetScope | undefined, - gatewayPort: gatewayPort ?? undefined, - gatewayBind: opts.gatewayBind as GatewayBind | undefined, - gatewayAuth: opts.gatewayAuth as GatewayAuthChoice | undefined, - gatewayToken: opts.gatewayToken as string | undefined, - gatewayTokenRefEnv: opts.gatewayTokenRefEnv as string | undefined, - gatewayPassword: opts.gatewayPassword as string | undefined, - tailscale: opts.tailscale as TailscaleMode | undefined, - tailscaleResetOnExit: Boolean(opts.tailscaleResetOnExit), - installDaemon, - daemonRuntime: opts.daemonRuntime as GatewayDaemonRuntime | undefined, - skipChannels: Boolean(opts.skipChannels), - skipSkills: Boolean(opts.skipSkills), - skipBootstrap: Boolean(opts.skipBootstrap), - skipSearch: Boolean(opts.skipSearch), - skipHealth: Boolean(opts.skipHealth), - skipUi: Boolean(opts.skipUi), - suppressGatewayTokenOutput: Boolean(opts.suppressGatewayTokenOutput), - skipHooks: Boolean(opts.skipHooks), - nodeManager: opts.nodeManager as NodeManagerChoice | undefined, - importFrom: opts.importFrom as string | undefined, - importSource: opts.importSource as string | undefined, - importSecrets: Boolean(opts.importSecrets), - remoteUrl: opts.remoteUrl as string | undefined, - remoteToken: opts.remoteToken as string | undefined, - json: Boolean(opts.json), - }, - defaultRuntime, - ); + .option("--remote-token ", "Remote Gateway token (optional)"); + + addSystemAgentOptions(command).action(async (opts, commandRuntime: Command) => { + const { defaultRuntime } = await import("../../runtime.js"); + await runCommandWithRuntime(defaultRuntime, async () => { + const options = opts as Record; + const hasOnboardingFlag = hasExplicitOnboardingOption(commandRuntime); + const hasSystemAgentRequest = hasExplicitOptions(commandRuntime, ["message", "yes"]); + const configured = + hasOnboardingFlag || hasSystemAgentRequest ? false : await isConfiguredInstance(); + const route = resolveSetupCommandRoute({ + hasOnboardingFlag, + hasSystemAgentRequest, + configured, + interactive: process.stdin.isTTY === true && process.stdout.isTTY === true, + json: Boolean(options.json), }); + if (route === "system-agent") { + await runSystemAgentEntry(options, defaultRuntime); + return; + } + await runOnboardingEntry(options, commandRuntime, defaultRuntime); }); + }); + + addSystemAgentOptions( + program + .command("crestodian", { hidden: true }) // hidden alias + .description("Deprecated: use openclaw setup"), + ).action(async (opts) => { + const { defaultRuntime } = await import("../../runtime.js"); + await runCommandWithRuntime(defaultRuntime, async () => { + await runSystemAgentEntry(opts as Record, defaultRuntime); + }); + }); } diff --git a/src/commands/agents.add.test.ts b/src/commands/agents.add.test.ts index 1fa7e4c1bd47..cef3f56ed470 100644 --- a/src/commands/agents.add.test.ts +++ b/src/commands/agents.add.test.ts @@ -115,6 +115,7 @@ import { WizardCancelledError } from "../wizard/prompts.js"; import { agentsAddCommand, testing } from "./agents.commands.add.js"; const runtime = createTestRuntime(); +const RESERVED_SYSTEM_AGENT_IDS_FOR_TEST = ["openclaw", "crestodian"] as const; // reserved ids describe("agents add command", () => { const suiteTempDirs = createSuiteTempRootTracker({ prefix: "openclaw-agents-add-" }); @@ -181,6 +182,42 @@ describe("agents add command", () => { expect(writeConfigFileMock).not.toHaveBeenCalled(); }); + it.each(RESERVED_SYSTEM_AGENT_IDS_FOR_TEST)( + "rejects reserved system-agent id %s", + async (name) => { + readConfigFileSnapshotMock.mockResolvedValue({ ...baseConfigSnapshot }); + + await agentsAddCommand({ name, workspace: "/tmp/reserved" }, runtime, { hasFlags: true }); + + expect(runtime.error).toHaveBeenCalledWith( + `"${name}" is reserved. Choose another name, or run ${formatCliCommand("openclaw agents list")} to inspect configured agents.`, + ); + expect(runtime.exit).toHaveBeenCalledWith(1); + expect(writeConfigFileMock).not.toHaveBeenCalled(); + }, + ); + + it.each(RESERVED_SYSTEM_AGENT_IDS_FOR_TEST)( + "rejects reserved system-agent id %s from an interactive positional argument", + async (name) => { + readConfigFileSnapshotMock.mockResolvedValue({ ...baseConfigSnapshot }); + const prompter = { + intro: vi.fn(), + text: vi.fn(), + confirm: vi.fn(), + note: vi.fn(), + outro: vi.fn(), + }; + wizardMocks.createClackPrompter.mockReturnValue(prompter); + + await agentsAddCommand({ name }, runtime); + + expect(prompter.outro).toHaveBeenCalledWith(`"${name}" is reserved. Choose another name.`); + expect(prompter.text).not.toHaveBeenCalled(); + expect(writeConfigFileMock).not.toHaveBeenCalled(); + }, + ); + it("exits with code 1 when the interactive wizard is cancelled", async () => { readConfigFileSnapshotMock.mockResolvedValue({ ...baseConfigSnapshot }); wizardMocks.createClackPrompter.mockReturnValue({ diff --git a/src/commands/agents.commands.add.ts b/src/commands/agents.commands.add.ts index 287d673829fe..3ba1348159c0 100644 --- a/src/commands/agents.commands.add.ts +++ b/src/commands/agents.commands.add.ts @@ -26,6 +26,7 @@ import { import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js"; import { type RuntimeEnv, writeRuntimeJson } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; +import { isReservedSystemAgentId } from "../system-agent/agent-id.js"; import { resolveUserPath, shortenHomePath } from "../utils.js"; import { createClackPrompter } from "../wizard/clack-prompter.js"; import { WizardCancelledError } from "../wizard/prompts.js"; @@ -145,9 +146,9 @@ export async function agentsAddCommand( return; } const agentId = normalizeAgentId(nameInput); - if (agentId === DEFAULT_AGENT_ID) { + if (agentId === DEFAULT_AGENT_ID || isReservedSystemAgentId(agentId)) { runtime.error( - `"${DEFAULT_AGENT_ID}" is reserved. Choose another name, or run ${formatCliCommand("openclaw agents list")} to inspect the default agent.`, + `"${agentId}" is reserved. Choose another name, or run ${formatCliCommand("openclaw agents list")} to inspect configured agents.`, ); runtime.exit(1); return; @@ -276,8 +277,8 @@ export async function agentsAddCommand( return "Required"; } const normalized = normalizeAgentId(value); - if (normalized === DEFAULT_AGENT_ID) { - return `"${DEFAULT_AGENT_ID}" is reserved. Choose another name.`; + if (normalized === DEFAULT_AGENT_ID || isReservedSystemAgentId(normalized)) { + return `"${normalized}" is reserved. Choose another name.`; } return undefined; }, @@ -285,6 +286,10 @@ export async function agentsAddCommand( const agentName = normalizeOptionalString(name) ?? ""; const agentId = normalizeAgentId(agentName); + if (agentId === DEFAULT_AGENT_ID || isReservedSystemAgentId(agentId)) { + await prompter.outro(`"${agentId}" is reserved. Choose another name.`); + return; + } if (agentName !== agentId) { await prompter.note(`Normalized id to "${agentId}".`, "Agent id"); } diff --git a/src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.test.ts b/src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.test.ts new file mode 100644 index 000000000000..db7a7ee57a32 --- /dev/null +++ b/src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.test.ts @@ -0,0 +1,38 @@ +// System-agent legacy config migration tests. +import { describe, expect, it } from "vitest"; +import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_SYSTEM_AGENT } from "./legacy-config-migrations.runtime.system-agent.js"; + +const migration = LEGACY_CONFIG_MIGRATIONS_RUNTIME_SYSTEM_AGENT[0]; + +describe("system-agent config migration", () => { + it("moves the retired config block", () => { + const raw: Record = { + crestodian: { rescue: { enabled: true, pendingTtlMinutes: 10 } }, + }; + const changes: string[] = []; + + migration?.apply(raw, changes); + + expect(raw).toEqual({ + systemAgent: { rescue: { enabled: true, pendingTtlMinutes: 10 } }, + }); + expect(changes).toEqual(["Moved legacy crestodian config to systemAgent."]); + }); + + it("keeps explicit canonical values while filling missing values", () => { + const raw: Record = { + crestodian: { rescue: { enabled: true, ownerDmOnly: false } }, + systemAgent: { rescue: { enabled: false } }, + }; + const changes: string[] = []; + + migration?.apply(raw, changes); + + expect(raw).toEqual({ + systemAgent: { rescue: { enabled: false, ownerDmOnly: false } }, + }); + expect(changes).toEqual([ + "Merged legacy crestodian config into systemAgent; kept explicit systemAgent values.", + ]); + }); +}); diff --git a/src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.ts b/src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.ts new file mode 100644 index 000000000000..61d77df6d40c --- /dev/null +++ b/src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.ts @@ -0,0 +1,43 @@ +// System-agent config migration from the retired user-facing name. +import { + defineLegacyConfigMigration, + getRecord, + mergeMissing, + type LegacyConfigMigrationSpec, + type LegacyConfigRule, +} from "../../../config/legacy.shared.js"; + +const LEGACY_SYSTEM_AGENT_CONFIG_RULE: LegacyConfigRule = { + path: ["crestodian"], + message: 'crestodian config moved to systemAgent. Run "openclaw doctor --fix" to migrate it.', +}; + +export const LEGACY_CONFIG_MIGRATIONS_RUNTIME_SYSTEM_AGENT: LegacyConfigMigrationSpec[] = [ + defineLegacyConfigMigration({ + id: "crestodian->systemAgent", + describe: "Move retired system-agent config to systemAgent", + legacyRules: [LEGACY_SYSTEM_AGENT_CONFIG_RULE], + apply: (raw, changes) => { + if (!Object.hasOwn(raw, "crestodian")) { + return; + } + const retired = getRecord(raw.crestodian); + const canonical = getRecord(raw.systemAgent); + if (retired) { + if (canonical) { + mergeMissing(canonical, retired); + raw.systemAgent = canonical; + changes.push( + "Merged legacy crestodian config into systemAgent; kept explicit systemAgent values.", + ); + } else { + raw.systemAgent = retired; + changes.push("Moved legacy crestodian config to systemAgent."); + } + } else { + changes.push("Removed invalid legacy crestodian config."); + } + delete raw.crestodian; + }, + }), +]; diff --git a/src/commands/doctor/shared/legacy-config-migrations.runtime.ts b/src/commands/doctor/shared/legacy-config-migrations.runtime.ts index 18c2b40d5c78..1ed3108d2816 100644 --- a/src/commands/doctor/shared/legacy-config-migrations.runtime.ts +++ b/src/commands/doctor/shared/legacy-config-migrations.runtime.ts @@ -8,6 +8,7 @@ import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_MCP } from "./legacy-config-migrations import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_MODELS } from "./legacy-config-migrations.runtime.models.js"; import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_PROVIDERS } from "./legacy-config-migrations.runtime.providers.js"; import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_SESSION } from "./legacy-config-migrations.runtime.session.js"; +import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_SYSTEM_AGENT } from "./legacy-config-migrations.runtime.system-agent.js"; import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_TTS } from "./legacy-config-migrations.runtime.tts.js"; /** Ordered runtime legacy config migrations applied by doctor. */ @@ -20,5 +21,6 @@ export const LEGACY_CONFIG_MIGRATIONS_RUNTIME: LegacyConfigMigrationSpec[] = [ ...LEGACY_CONFIG_MIGRATIONS_RUNTIME_MODELS, ...LEGACY_CONFIG_MIGRATIONS_RUNTIME_PROVIDERS, ...LEGACY_CONFIG_MIGRATIONS_RUNTIME_SESSION, + ...LEGACY_CONFIG_MIGRATIONS_RUNTIME_SYSTEM_AGENT, ...LEGACY_CONFIG_MIGRATIONS_RUNTIME_TTS, ]; diff --git a/src/commands/onboard-guided.test.ts b/src/commands/onboard-guided.test.ts index 1f2be4ccef48..1149d9228459 100644 --- a/src/commands/onboard-guided.test.ts +++ b/src/commands/onboard-guided.test.ts @@ -102,11 +102,11 @@ function setupDeps(params: { prompter: WizardPrompter; detect?: GuidedOnboardingDeps["detect"]; activate?: GuidedOnboardingDeps["activate"]; - runCrestodianChat?: GuidedOnboardingDeps["runCrestodianChat"]; + runSystemAgentChat?: GuidedOnboardingDeps["runSystemAgentChat"]; persistRiskAcknowledgement?: GuidedOnboardingDeps["persistRiskAcknowledgement"]; }) { - const runCrestodianChat = vi.fn>( - params.runCrestodianChat ?? (async () => {}), + const runSystemAgentChat = vi.fn>( + params.runSystemAgentChat ?? (async () => {}), ); return { createPrompter: () => params.prompter, @@ -120,7 +120,7 @@ function setupDeps(params: { lines: ["Workspace: /tmp/work", "Gateway: running"], })), persistRiskAcknowledgement: params.persistRiskAcknowledgement ?? vi.fn(async () => undefined), - runCrestodianChat, + runSystemAgentChat, } satisfies GuidedOnboardingDeps; } @@ -174,9 +174,9 @@ describe("runGuidedOnboarding", () => { ); expect(text).not.toHaveBeenCalled(); expect(select).not.toHaveBeenCalled(); - expect(deps.runCrestodianChat).toHaveBeenCalledWith("/tmp/work", expect.anything(), true); + expect(deps.runSystemAgentChat).toHaveBeenCalledWith("/tmp/work", expect.anything(), true); expect(restoreTerminalState.mock.invocationCallOrder[0]).toBeLessThan( - deps.runCrestodianChat.mock.invocationCallOrder[0]!, + deps.runSystemAgentChat.mock.invocationCallOrder[0]!, ); }); @@ -196,7 +196,7 @@ describe("runGuidedOnboarding", () => { ); }); - it("uses the configured workspace only as inference and Crestodian context", async () => { + it("uses the configured workspace only as inference and OpenClaw context", async () => { readConfigFileSnapshot.mockResolvedValueOnce({ exists: true, valid: true, @@ -215,7 +215,7 @@ describe("runGuidedOnboarding", () => { expect(deps.activate).toHaveBeenCalledWith( expect.objectContaining({ workspace: "/tmp/configured" }), ); - expect(deps.runCrestodianChat).toHaveBeenCalledWith("/tmp/configured", runtime, true); + expect(deps.runSystemAgentChat).toHaveBeenCalledWith("/tmp/configured", runtime, true); }); it("uses the default workspace as context when none is configured", async () => { @@ -230,7 +230,7 @@ describe("runGuidedOnboarding", () => { expect(deps.activate).toHaveBeenCalledWith( expect.objectContaining({ workspace: "/tmp/openclaw-workspace" }), ); - expect(deps.runCrestodianChat).toHaveBeenCalledWith("/tmp/openclaw-workspace", runtime, true); + expect(deps.runSystemAgentChat).toHaveBeenCalledWith("/tmp/openclaw-workspace", runtime, true); }); it("live-tests an unverified CLI before automatic setup", async () => { @@ -349,7 +349,7 @@ describe("runGuidedOnboarding", () => { const notes = JSON.stringify((prompter.note as ReturnType).mock.calls); expect(notes).toContain("kept unchanged"); expect(promptAuthChoiceGrouped).toHaveBeenCalledOnce(); - expect(deps.runCrestodianChat).toHaveBeenCalledOnce(); + expect(deps.runSystemAgentChat).toHaveBeenCalledOnce(); }); it("falls through after an auth failure and surfaces both outcomes", async () => { @@ -424,7 +424,7 @@ describe("runGuidedOnboarding", () => { ], }), ); - expect(deps.runCrestodianChat).toHaveBeenCalledWith("/tmp/work", expect.anything(), true); + expect(deps.runSystemAgentChat).toHaveBeenCalledWith("/tmp/work", expect.anything(), true); }); it("accepts and verifies a manual provider key without displaying it", async () => { @@ -549,7 +549,7 @@ describe("runGuidedOnboarding", () => { expect.objectContaining({ includeSkip: true }), ); expect(deps.activate).not.toHaveBeenCalled(); - expect(deps.runCrestodianChat).not.toHaveBeenCalled(); + expect(deps.runSystemAgentChat).not.toHaveBeenCalled(); expect(prompter.note).toHaveBeenCalledWith( expect.stringContaining("Add AI later"), "Next steps", @@ -569,7 +569,7 @@ describe("runGuidedOnboarding", () => { expect(select).not.toHaveBeenCalled(); expect(deps.activate).not.toHaveBeenCalled(); - expect(deps.runCrestodianChat).not.toHaveBeenCalled(); + expect(deps.runSystemAgentChat).not.toHaveBeenCalled(); expect(runtime.exit).toHaveBeenCalledWith(1); expect(prompter.note).toHaveBeenCalledWith( expect.stringContaining("No inference option is available yet"), @@ -577,18 +577,18 @@ describe("runGuidedOnboarding", () => { ); }); - it("keeps Crestodian unavailable until a manual key passes", async () => { + it("keeps OpenClaw unavailable until a manual key passes", async () => { promptAuthChoiceGrouped.mockResolvedValue("openai-api-key"); const text = vi.fn().mockResolvedValueOnce("bad-key").mockResolvedValueOnce("good-key"); const prompter = createWizardPrompter({ text: text as WizardPrompter["text"], confirm: vi.fn(async () => false), }); - const runCrestodianChat = vi.fn(async () => {}); + const runSystemAgentChat = vi.fn(async () => {}); const activate = vi .fn>() .mockImplementationOnce(async () => { - expect(runCrestodianChat).not.toHaveBeenCalled(); + expect(runSystemAgentChat).not.toHaveBeenCalled(); return { ok: false, status: "auth", error: "bad key" }; }) .mockResolvedValueOnce({ @@ -606,7 +606,7 @@ describe("runGuidedOnboarding", () => { }), ), activate, - runCrestodianChat, + runSystemAgentChat, }); const runtime = makeRuntime(); @@ -614,23 +614,23 @@ describe("runGuidedOnboarding", () => { expect(activate.mock.calls.map(([call]) => call.apiKey)).toEqual(["bad-key", "good-key"]); expect(promptAuthChoiceGrouped).toHaveBeenCalledTimes(2); - expect(runCrestodianChat).toHaveBeenCalledOnce(); + expect(runSystemAgentChat).toHaveBeenCalledOnce(); }); - it("opens Crestodian chat with the explicit workspace after activation", async () => { + it("opens OpenClaw chat with the explicit workspace after activation", async () => { const text = vi.fn(async () => "unexpected"); const prompter = createWizardPrompter({ text }); - const runCrestodianChat = vi.fn(async () => {}); + const runSystemAgentChat = vi.fn(async () => {}); const deps = setupDeps({ prompter, - runCrestodianChat, + runSystemAgentChat, }); const runtime = makeRuntime(); await runGuidedOnboarding({ acceptRisk: true, workspace: "/tmp/work" }, runtime, deps); expect(text).not.toHaveBeenCalled(); - expect(runCrestodianChat).toHaveBeenCalledWith("/tmp/work", runtime, true); + expect(runSystemAgentChat).toHaveBeenCalledWith("/tmp/work", runtime, true); }); it("cancels before detection or activation when risk is declined", async () => { @@ -667,12 +667,12 @@ describe("runGuidedOnboarding", () => { expect.stringContaining("openclaw config validate"), ); expect(runtime.exit).toHaveBeenCalledWith(1); - expect(deps.runCrestodianChat).not.toHaveBeenCalled(); + expect(deps.runSystemAgentChat).not.toHaveBeenCalled(); expect(deps.detect).not.toHaveBeenCalled(); expect(deps.activate).not.toHaveBeenCalled(); }); - it("converges remote inference before remote Crestodian without mutating local config", async () => { + it("converges remote inference before remote OpenClaw without mutating local config", async () => { const localConfig = { wizard: { securityAcknowledgedAt: "2026-07-11T00:00:00.000Z" }, agents: { @@ -704,7 +704,7 @@ describe("runGuidedOnboarding", () => { expect(options.ignoreEnvUrlOverride).toBe(true); expect(options.config?.gateway?.remote?.url).toBe("wss://selected.example/ws"); order.push(options.method); - if (options.method === "crestodian.setup.detect") { + if (options.method === "openclaw.setup.detect") { return { candidates: [ { @@ -729,7 +729,7 @@ describe("runGuidedOnboarding", () => { setupComplete: false, }; } - if (options.method === "crestodian.setup.activate") { + if (options.method === "openclaw.setup.activate") { expect(options.params).toEqual({ kind: "claude-cli", modelRef: "claude-cli/opus", @@ -743,11 +743,11 @@ describe("runGuidedOnboarding", () => { lines: ["Default model: claude-cli/opus"], }; } - if (options.method === "crestodian.setup.verify") { + if (options.method === "openclaw.setup.verify") { expect(remoteConfig.modelRef).toBe("claude-cli/opus"); return { ok: true, modelRef: remoteConfig.modelRef, latencyMs: 100 }; } - if (options.method === "crestodian.chat") { + if (options.method === "openclaw.chat") { expect(remoteConfig.modelRef).toBe("claude-cli/opus"); expect(options.params).toEqual({ sessionId: expect.any(String), @@ -800,10 +800,10 @@ describe("runGuidedOnboarding", () => { ); expect(order).toEqual([ - "crestodian.setup.detect", - "crestodian.setup.activate", - "crestodian.setup.verify", - "crestodian.chat", + "openclaw.setup.detect", + "openclaw.setup.activate", + "openclaw.setup.verify", + "openclaw.chat", "tui", ]); expect(remoteConfig.modelRef).toBe("claude-cli/opus"); diff --git a/src/commands/onboard-guided.ts b/src/commands/onboard-guided.ts index 9504197ee289..e337a5ed371a 100644 --- a/src/commands/onboard-guided.ts +++ b/src/commands/onboard-guided.ts @@ -1,15 +1,15 @@ import { formatCliCommand } from "../cli/command-format.js"; import { formatConfigIssueLines } from "../config/issue-format.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; +import { withConsoleSubsystemsSuppressed } from "../logging/console.js"; +import type { RuntimeEnv } from "../runtime.js"; // Guided onboarding: detect AI access, live-test it, then persist only a working route. import type { ActivateSetupInferenceResult, SetupInferenceCandidate, SetupInferenceDetection, SetupInferenceFailureStatus, -} from "../crestodian/setup-inference.js"; -import { withConsoleSubsystemsSuppressed } from "../logging/console.js"; -import type { RuntimeEnv } from "../runtime.js"; +} from "../system-agent/setup-inference.js"; import { resolveUserPath, shortenHomePath } from "../utils.js"; import { t } from "../wizard/i18n/index.js"; import { WizardCancelledError, type WizardPrompter } from "../wizard/prompts.js"; @@ -22,13 +22,14 @@ import { import type { OnboardOptions } from "./onboard-types.js"; type ActivateSetupInference = - typeof import("../crestodian/setup-inference.js").activateSetupInference; -type DetectSetupInference = typeof import("../crestodian/setup-inference.js").detectSetupInference; + typeof import("../system-agent/setup-inference.js").activateSetupInference; +type DetectSetupInference = + typeof import("../system-agent/setup-inference.js").detectSetupInference; export type GuidedOnboardingDeps = { detect?: DetectSetupInference; activate?: ActivateSetupInference; - runCrestodianChat?: ( + runSystemAgentChat?: ( workspace: string, runtime: RuntimeEnv, acceptRisk: boolean, @@ -43,14 +44,14 @@ type CandidateAttempt = | { kind: "success"; result: Extract } | { kind: "failure" }; -async function openCrestodianChat( +async function openSystemAgentChat( deps: GuidedOnboardingDeps, workspace: string, runtime: RuntimeEnv, acceptRisk: boolean, ): Promise { const runChat = - deps.runCrestodianChat ?? + deps.runSystemAgentChat ?? (async (setupWorkspace: string, chatRuntime: RuntimeEnv, riskAccepted: boolean) => { const { runConversationalOnboarding } = await import("./onboard-interactive.js"); await runConversationalOnboarding( @@ -333,8 +334,8 @@ async function runGuidedOnboardingFlow( await (deps.persistRiskAcknowledgement ?? persistRiskAcknowledgement)(acknowledgedConfig); } - // Inference is the only prerequisite for Crestodian. Use the caller's or - // current default workspace as isolated probe context; Crestodian owns any + // Inference is the only prerequisite for OpenClaw. Use the caller's or + // current default workspace as isolated probe context; OpenClaw owns any // workspace choice and persistence after the live completion succeeds. const workspace = resolveUserPath( opts.workspace?.trim() || @@ -343,7 +344,7 @@ async function runGuidedOnboardingFlow( ); const detect = - deps.detect ?? (await import("../crestodian/setup-inference.js")).detectSetupInference; + deps.detect ?? (await import("../system-agent/setup-inference.js")).detectSetupInference; const detectionProgress = prompter.progress(t("wizard.guided.detecting")); const detection = await detect(); detectionProgress.stop(t("wizard.guided.detected")); @@ -360,7 +361,7 @@ async function runGuidedOnboardingFlow( } const activate = - deps.activate ?? (await import("../crestodian/setup-inference.js")).activateSetupInference; + deps.activate ?? (await import("../system-agent/setup-inference.js")).activateSetupInference; const autoAttemptedKinds = new Set(); let resultLines: string[] | undefined; // Logged-out CLIs stay visible as manual choices, but auto-testing them would @@ -418,7 +419,7 @@ export async function runGuidedOnboarding( const handoff = state.handoff; if (handoff) { // The live completion makes conversational setup safe. Start only after - // the wizard lifecycle restores stdin so Crestodian receives a clean TTY. - await openCrestodianChat(deps, handoff.workspace, runtime, true); + // the wizard lifecycle restores stdin so OpenClaw receives a clean TTY. + await openSystemAgentChat(deps, handoff.workspace, runtime, true); } } diff --git a/src/commands/onboard-inference.test.ts b/src/commands/onboard-inference.test.ts index 9e48e2bdefdd..30000233e417 100644 --- a/src/commands/onboard-inference.test.ts +++ b/src/commands/onboard-inference.test.ts @@ -1,6 +1,6 @@ // Inference backend detection tests cover the documented ladder and login-awareness. import { describe, expect, it } from "vitest"; -import type { LocalCommandProbe } from "../crestodian/probes.js"; +import type { LocalCommandProbe } from "../system-agent/probes.js"; import { ANTHROPIC_API_DEFAULT_MODEL_REF, CLAUDE_CLI_DEFAULT_MODEL_REF, diff --git a/src/commands/onboard-inference.ts b/src/commands/onboard-inference.ts index f719f1a4df86..34430663061e 100644 --- a/src/commands/onboard-inference.ts +++ b/src/commands/onboard-inference.ts @@ -1,5 +1,5 @@ import { randomInt } from "node:crypto"; -// Inference backend detection shared by onboarding bootstrap and Crestodian setup. +// Inference backend detection shared by onboarding bootstrap and OpenClaw setup. import os from "node:os"; import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; @@ -11,13 +11,13 @@ import { import { resolveDefaultModelForAgent } from "../agents/model-selection.js"; import { resolveAgentModelPrimaryValue } from "../config/model-input.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { probeLocalCommand, type LocalCommandProbe } from "../crestodian/probes.js"; +import { probeLocalCommand, type LocalCommandProbe } from "../system-agent/probes.js"; /** * Onboarding treats inference as the one required step: reuse whatever the * machine already has (env API keys, Claude Code login, Codex login) before * asking the user anything. The ladder order is a documented contract - * (docs/cli/crestodian.md "Setup bootstrap") — change docs when changing it. + * (docs/cli/setup.md "Setup bootstrap") — change docs when changing it. */ export const OPENAI_API_DEFAULT_MODEL_REF = "openai/gpt-5.6"; export const ANTHROPIC_API_DEFAULT_MODEL_REF = "anthropic/claude-opus-4-8"; diff --git a/src/commands/onboard-interactive.ts b/src/commands/onboard-interactive.ts index e341038cf558..8cad5b941157 100644 --- a/src/commands/onboard-interactive.ts +++ b/src/commands/onboard-interactive.ts @@ -27,7 +27,7 @@ export async function runInteractiveSetup( } /** - * Opens the Crestodian onboarding conversation used by the guided escape hatch. + * Opens the OpenClaw onboarding conversation used by the guided escape hatch. * The first-run greeting proposes a setup plan and keeps subsequent setup and * agent handoff in the same conversation. */ @@ -42,15 +42,15 @@ export async function runConversationalOnboarding( runtime.exit(1); return; } - const { verifySetupInference } = await import("../crestodian/setup-inference.js"); + const { verifySetupInference } = await import("../system-agent/setup-inference.js"); const inference = await verifySetupInference({ runtime, bindSession: true }); if (!inference.ok) { - runtime.error(`Crestodian requires working inference: ${inference.error}`); + runtime.error(`OpenClaw requires working inference: ${inference.error}`); runtime.exit(1); return; } - const { runCrestodian } = await import("../crestodian/crestodian.js"); - await runCrestodian( + const { runSystemAgent } = await import("../system-agent/system-agent.js"); + await runSystemAgent( { welcomeVariant: "onboarding", ...(opts.workspace ? { setupWorkspace: opts.workspace } : {}), diff --git a/src/commands/onboard-remote-gateway.test.ts b/src/commands/onboard-remote-gateway.test.ts index 92d79314eb50..eafde5b96fe7 100644 --- a/src/commands/onboard-remote-gateway.test.ts +++ b/src/commands/onboard-remote-gateway.test.ts @@ -80,7 +80,7 @@ function detectResult() { function exerciseGuidedAdapters(): RunGuidedOnboarding { const run: RunGuidedOnboarding = async (_opts, runtime, deps) => { const guidedDeps: GuidedOnboardingDeps = deps ?? {}; - if (!guidedDeps.detect || !guidedDeps.activate || !guidedDeps.runCrestodianChat) { + if (!guidedDeps.detect || !guidedDeps.activate || !guidedDeps.runSystemAgentChat) { throw new Error("remote guided adapters missing"); } const detection = await guidedDeps.detect(); @@ -97,7 +97,7 @@ function exerciseGuidedAdapters(): RunGuidedOnboarding { runtime, }); if (activation.ok) { - await guidedDeps.runCrestodianChat("/client/workspace", runtime, true); + await guidedDeps.runSystemAgentChat("/client/workspace", runtime, true); } }; return vi.fn(run); @@ -116,7 +116,7 @@ describe("runRemoteGatewayInferenceOnboarding", () => { secret: "selected-password", }, ])( - "pins $label across detect, activate, verify, Crestodian, and in-process TUI", + "pins $label across detect, activate, verify, OpenClaw, and in-process TUI", async ({ auth, secret }) => { const localConfig = makeLocalConfig(); const localConfigBefore = structuredClone(localConfig); @@ -131,11 +131,11 @@ describe("runRemoteGatewayInferenceOnboarding", () => { expect(options.config?.gateway?.remote?.url).toBe("wss://selected.example/ws"); order.push(options.method); - if (options.method === "crestodian.setup.detect") { + if (options.method === "openclaw.setup.detect") { expect(options.timeoutMs).toBe(20_000); return detectResult(); } - if (options.method === "crestodian.setup.activate") { + if (options.method === "openclaw.setup.activate") { expect(options.timeoutMs).toBe(150_000); expect(options.params).toEqual({ kind: "claude-cli", @@ -150,12 +150,12 @@ describe("runRemoteGatewayInferenceOnboarding", () => { lines: ["Default model: claude-cli/opus"], }; } - if (options.method === "crestodian.setup.verify") { + if (options.method === "openclaw.setup.verify") { expect(options.timeoutMs).toBe(30_000); expect(remoteConfig.modelRef).toBe("claude-cli/opus"); return { ok: true, modelRef: remoteConfig.modelRef, latencyMs: 100 }; } - if (options.method === "crestodian.chat") { + if (options.method === "openclaw.chat") { expect(options.timeoutMs).toBe(190_000); expect(remoteConfig.modelRef).toBe("claude-cli/opus"); expect(options.params).toEqual({ @@ -199,10 +199,10 @@ describe("runRemoteGatewayInferenceOnboarding", () => { }); expect(order).toEqual([ - "crestodian.setup.detect", - "crestodian.setup.activate", - "crestodian.setup.verify", - "crestodian.chat", + "openclaw.setup.detect", + "openclaw.setup.activate", + "openclaw.setup.verify", + "openclaw.chat", "tui", ]); expect(remoteConfig.modelRef).toBe("claude-cli/opus"); @@ -217,10 +217,10 @@ describe("runRemoteGatewayInferenceOnboarding", () => { it("hands an auth-free Gateway to the TUI as the exact bound route", async () => { const callGatewayMock = vi.fn(async (options: CallGatewayCliOptions): Promise => { - if (options.method === "crestodian.setup.detect") { + if (options.method === "openclaw.setup.detect") { return detectResult(); } - if (options.method === "crestodian.setup.activate") { + if (options.method === "openclaw.setup.activate") { return { ok: true, modelRef: "claude-cli/opus", @@ -228,10 +228,10 @@ describe("runRemoteGatewayInferenceOnboarding", () => { lines: ["Default model: claude-cli/opus"], }; } - if (options.method === "crestodian.setup.verify") { + if (options.method === "openclaw.setup.verify") { return { ok: true, modelRef: "claude-cli/opus", latencyMs: 100 }; } - if (options.method === "crestodian.chat") { + if (options.method === "openclaw.chat") { return { sessionId: (options.params as { sessionId: string }).sessionId, reply: "Ready.", @@ -274,16 +274,16 @@ describe("runRemoteGatewayInferenceOnboarding", () => { verification: { ok: true, modelRef: "openai/other", latencyMs: 100 }, error: "Gateway verified openai/other, not the activated claude-cli/opus", }, - ])("fails closed on $label before Crestodian", async ({ verification, error }) => { + ])("fails closed on $label before OpenClaw", async ({ verification, error }) => { const localConfig = makeLocalConfig(); const localConfigBefore = structuredClone(localConfig); const methods: string[] = []; const callGatewayMock = vi.fn(async (options: CallGatewayCliOptions): Promise => { methods.push(options.method); - if (options.method === "crestodian.setup.detect") { + if (options.method === "openclaw.setup.detect") { return detectResult(); } - if (options.method === "crestodian.setup.activate") { + if (options.method === "openclaw.setup.activate") { return { ok: true, modelRef: "claude-cli/opus", @@ -291,7 +291,7 @@ describe("runRemoteGatewayInferenceOnboarding", () => { lines: ["Default model: claude-cli/opus"], }; } - if (options.method === "crestodian.setup.verify") { + if (options.method === "openclaw.setup.verify") { return verification; } throw new Error(`unexpected Gateway method ${options.method}`); @@ -312,9 +312,9 @@ describe("runRemoteGatewayInferenceOnboarding", () => { ).rejects.toThrow(error); expect(methods).toEqual([ - "crestodian.setup.detect", - "crestodian.setup.activate", - "crestodian.setup.verify", + "openclaw.setup.detect", + "openclaw.setup.activate", + "openclaw.setup.verify", ]); expect(runTui).not.toHaveBeenCalled(); expect(localConfig).toEqual(localConfigBefore); @@ -324,10 +324,10 @@ describe("runRemoteGatewayInferenceOnboarding", () => { const methods: string[] = []; const callGatewayMock = vi.fn(async (options: CallGatewayCliOptions): Promise => { methods.push(options.method); - if (options.method === "crestodian.setup.detect") { + if (options.method === "openclaw.setup.detect") { return detectResult(); } - if (options.method === "crestodian.setup.activate") { + if (options.method === "openclaw.setup.activate") { throw new Error("gateway connection closed after request"); } throw new Error(`unexpected Gateway method ${options.method}`); @@ -347,18 +347,18 @@ describe("runRemoteGatewayInferenceOnboarding", () => { ), ).rejects.toThrow("gateway connection closed after request"); - expect(methods).toEqual(["crestodian.setup.detect", "crestodian.setup.activate"]); + expect(methods).toEqual(["openclaw.setup.detect", "openclaw.setup.activate"]); expect(runTui).not.toHaveBeenCalled(); }); - it("treats a cancelled remote Crestodian conversation as a pause without opening the agent", async () => { + it("treats a cancelled remote OpenClaw conversation as a pause without opening the agent", async () => { const methods: string[] = []; const callGatewayMock = vi.fn(async (options: CallGatewayCliOptions): Promise => { methods.push(options.method); - if (options.method === "crestodian.setup.detect") { + if (options.method === "openclaw.setup.detect") { return detectResult(); } - if (options.method === "crestodian.setup.activate") { + if (options.method === "openclaw.setup.activate") { return { ok: true, modelRef: "claude-cli/opus", @@ -366,10 +366,10 @@ describe("runRemoteGatewayInferenceOnboarding", () => { lines: ["Default model: claude-cli/opus"], }; } - if (options.method === "crestodian.setup.verify") { + if (options.method === "openclaw.setup.verify") { return { ok: true, modelRef: "claude-cli/opus", latencyMs: 100 }; } - if (options.method === "crestodian.chat") { + if (options.method === "openclaw.chat") { return { sessionId: (options.params as { sessionId: string }).sessionId, reply: "Which channel should I configure?", @@ -397,12 +397,12 @@ describe("runRemoteGatewayInferenceOnboarding", () => { ); expect(methods).toEqual([ - "crestodian.setup.detect", - "crestodian.setup.activate", - "crestodian.setup.verify", - "crestodian.chat", + "openclaw.setup.detect", + "openclaw.setup.activate", + "openclaw.setup.verify", + "openclaw.chat", ]); - expect(prompter.outro).toHaveBeenCalledWith("Crestodian setup paused."); + expect(prompter.outro).toHaveBeenCalledWith("OpenClaw setup paused."); expect(runTui).not.toHaveBeenCalled(); }); }); diff --git a/src/commands/onboard-remote-gateway.ts b/src/commands/onboard-remote-gateway.ts index 6b03425ed0d3..843d3623dc6e 100644 --- a/src/commands/onboard-remote-gateway.ts +++ b/src/commands/onboard-remote-gateway.ts @@ -1,20 +1,20 @@ // Remote-Gateway onboarding adapters keep inference detection and activation on the Gateway host. import { randomUUID } from "node:crypto"; import type { - CrestodianChatResult, - CrestodianSetupActivateResult, - CrestodianSetupDetectResult, - CrestodianSetupVerifyResult, + SystemAgentChatResult, + SystemAgentSetupActivateResult, + SystemAgentSetupDetectResult, + SystemAgentSetupVerifyResult, } from "../../packages/gateway-protocol/src/index.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { CallGatewayCliOptions } from "../gateway/call.js"; +import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; import type { ActivateSetupInferenceParams, ActivateSetupInferenceResult, SetupInferenceDetection, SetupInferenceFailureStatus, -} from "../crestodian/setup-inference.js"; -import type { CallGatewayCliOptions } from "../gateway/call.js"; -import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; +} from "../system-agent/setup-inference.js"; import { WizardCancelledError } from "../wizard/prompts.js"; import type { GuidedOnboardingDeps } from "./onboard-guided.js"; @@ -22,7 +22,7 @@ const GATEWAY_SETUP_DETECT_TIMEOUT_MS = 20_000; const GATEWAY_SETUP_ACTIVATE_TIMEOUT_MS = 150_000; const GATEWAY_CODEX_SETUP_ACTIVATE_TIMEOUT_MS = 480_000; const GATEWAY_SETUP_VERIFY_TIMEOUT_MS = 30_000; -const GATEWAY_CRESTODIAN_CHAT_TIMEOUT_MS = 190_000; +const GATEWAY_SYSTEM_AGENT_CHAT_TIMEOUT_MS = 190_000; type CallGateway = (options: CallGatewayCliOptions) => Promise; @@ -41,7 +41,7 @@ type RemoteGatewayInferenceOnboardingDeps = { runGuidedOnboarding?: typeof import("./onboard-guided.js").runGuidedOnboarding; }; -function toSetupInferenceDetection(result: CrestodianSetupDetectResult): SetupInferenceDetection { +function toSetupInferenceDetection(result: SystemAgentSetupDetectResult): SetupInferenceDetection { return { candidates: result.candidates.map((candidate) => ({ kind: candidate.kind, @@ -89,7 +89,7 @@ function isSetupInferenceFailureStatus(value: unknown): value is SetupInferenceF } function toSetupInferenceActivationResult( - result: CrestodianSetupActivateResult, + result: SystemAgentSetupActivateResult, ): ActivateSetupInferenceResult { if (result.ok) { if ( @@ -135,7 +135,7 @@ function bindGatewayConfig(target: RemoteGatewayInferenceTarget): OpenClawConfig function assertVerifiedActivation(params: { activation: Extract; requestedModelRef?: string; - verification: CrestodianSetupVerifyResult; + verification: SystemAgentSetupVerifyResult; }): void { if ( params.requestedModelRef && @@ -157,7 +157,7 @@ function assertVerifiedActivation(params: { /** * Configure missing inference on the selected remote Gateway, then let that - * Gateway's Crestodian finish setup before handing off to its normal TUI. + * Gateway's OpenClaw finish setup before handing off to its normal TUI. * The local config is routing input only; every setup mutation runs through * Gateway RPC. */ @@ -194,8 +194,8 @@ export async function runRemoteGatewayInferenceOnboarding( }); const detect = async (): Promise => { - const result = await request({ - method: "crestodian.setup.detect", + const result = await request({ + method: "openclaw.setup.detect", payload: {}, timeoutMs: GATEWAY_SETUP_DETECT_TIMEOUT_MS, }); @@ -207,8 +207,8 @@ export async function runRemoteGatewayInferenceOnboarding( const activate = async ( params: ActivateSetupInferenceParams, ): Promise => { - const result = await request({ - method: "crestodian.setup.activate", + const result = await request({ + method: "openclaw.setup.activate", payload: { kind: params.kind, ...(params.modelRef !== undefined ? { modelRef: params.modelRef } : {}), @@ -222,8 +222,8 @@ export async function runRemoteGatewayInferenceOnboarding( if (!activation.ok) { return activation; } - const verification = await request({ - method: "crestodian.setup.verify", + const verification = await request({ + method: "openclaw.setup.verify", payload: {}, timeoutMs: GATEWAY_SETUP_VERIFY_TIMEOUT_MS, }); @@ -239,24 +239,24 @@ export async function runRemoteGatewayInferenceOnboarding( detect, activate, ...(deps.createPrompter ? { createPrompter: deps.createPrompter } : {}), - runCrestodianChat: async () => { + runSystemAgentChat: async () => { const prompter = await (deps.createPrompter?.() ?? import("../wizard/clack-prompter.js").then(({ createClackPrompter }) => createClackPrompter(), )); - await prompter.intro("Crestodian"); + await prompter.intro("OpenClaw"); const sessionId = randomUUID(); - let reply = await request({ - method: "crestodian.chat", + let reply = await request({ + method: "openclaw.chat", payload: { sessionId, welcomeVariant: "onboarding" }, - timeoutMs: GATEWAY_CRESTODIAN_CHAT_TIMEOUT_MS, + timeoutMs: GATEWAY_SYSTEM_AGENT_CHAT_TIMEOUT_MS, }); try { for (;;) { - await prompter.note(reply.reply, "Crestodian"); + await prompter.note(reply.reply, "OpenClaw"); if (reply.action === "exit") { - await prompter.outro("Crestodian setup finished."); + await prompter.outro("OpenClaw setup finished."); return; } if (reply.action === "open-agent") { @@ -264,19 +264,19 @@ export async function runRemoteGatewayInferenceOnboarding( break; } const message = await prompter.text({ - message: "Reply to Crestodian", + message: "Reply to OpenClaw", ...(reply.sensitive ? { sensitive: true } : {}), validate: (value) => (value.trim() ? undefined : "Required"), }); - reply = await request({ - method: "crestodian.chat", + reply = await request({ + method: "openclaw.chat", payload: { sessionId, message }, - timeoutMs: GATEWAY_CRESTODIAN_CHAT_TIMEOUT_MS, + timeoutMs: GATEWAY_SYSTEM_AGENT_CHAT_TIMEOUT_MS, }); } } catch (error) { if (error instanceof WizardCancelledError) { - await prompter.outro("Crestodian setup paused."); + await prompter.outro("OpenClaw setup paused."); return; } throw error; diff --git a/src/commands/onboard.ts b/src/commands/onboard.ts index 4b5f0271ab74..e6d7435fc221 100644 --- a/src/commands/onboard.ts +++ b/src/commands/onboard.ts @@ -31,7 +31,7 @@ const VALID_RESET_SCOPES = new Set(["config", "config+creds+sessions * Boolean false and undefined mean "not passed" (Commander coerces unset * booleans to false); explicit `--no-install-daemon` arrives as `false` via * resolveInstallDaemonFlag and is special-cased. `--modern` never reaches this - * dispatch; the command layer routes it through the inference-gated Crestodian. + * dispatch; the command layer routes it through the inference-gated OpenClaw. */ const GUIDED_SAFE_ONBOARD_KEYS = new Set([ "workspace", diff --git a/src/commands/crestodian-with-inference.test.ts b/src/commands/system-agent-with-inference.test.ts similarity index 83% rename from src/commands/crestodian-with-inference.test.ts rename to src/commands/system-agent-with-inference.test.ts index 73895f329b2e..a1b563294ba4 100644 --- a/src/commands/crestodian-with-inference.test.ts +++ b/src/commands/system-agent-with-inference.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { RuntimeEnv } from "../runtime.js"; -import { runCrestodianWithInference } from "./crestodian-with-inference.js"; +import { runSystemAgentWithInference } from "./system-agent-with-inference.js"; const exitMocks = vi.hoisted(() => ({ requestExitAfterOneShotOutput: vi.fn(), @@ -31,24 +31,24 @@ function workingInference() { }; } -describe("runCrestodianWithInference", () => { +describe("runSystemAgentWithInference", () => { beforeEach(() => { vi.clearAllMocks(); exitMocks.requestExitAfterOneShotOutput.mockReturnValue(false); }); - it("starts Crestodian only after live inference succeeds", async () => { - const runCrestodian = vi.fn(async () => {}); + it("starts OpenClaw only after live inference succeeds", async () => { + const runSystemAgent = vi.fn(async () => {}); const verifyInference = vi.fn(async () => workingInference()); const currentRuntime = runtime(); - await runCrestodianWithInference( + await runSystemAgentWithInference( { input: tty, output: tty }, currentRuntime, {}, { verifyInference, - runCrestodian, + runSystemAgent, }, ); @@ -56,12 +56,12 @@ describe("runCrestodianWithInference", () => { runtime: currentRuntime, bindSession: true, }); - expect(runCrestodian).toHaveBeenCalledWith( + expect(runSystemAgent).toHaveBeenCalledWith( expect.objectContaining({ verifiedInference }), currentRuntime, ); expect(verifyInference.mock.invocationCallOrder[0]).toBeLessThan( - runCrestodian.mock.invocationCallOrder[0]!, + runSystemAgent.mock.invocationCallOrder[0]!, ); expect(exitMocks.requestExitAfterOneShotOutput).not.toHaveBeenCalled(); }); @@ -71,38 +71,38 @@ describe("runCrestodianWithInference", () => { { label: "JSON", options: { json: true } }, { label: "noninteractive", options: { interactive: false } }, ])("requests a clean process exit after successful $label output", async ({ options }) => { - const runCrestodian = vi.fn(async () => {}); + const runSystemAgent = vi.fn(async () => {}); const currentRuntime = runtime(); - await runCrestodianWithInference( + await runSystemAgentWithInference( options, currentRuntime, {}, { verifyInference: vi.fn(async () => workingInference()), - runCrestodian, + runSystemAgent, }, ); expect(exitMocks.requestExitAfterOneShotOutput).toHaveBeenCalledWith(currentRuntime); - expect(runCrestodian.mock.invocationCallOrder[0]).toBeLessThan( + expect(runSystemAgent.mock.invocationCallOrder[0]).toBeLessThan( exitMocks.requestExitAfterOneShotOutput.mock.invocationCallOrder[0]!, ); }); it("reports a one-shot execution error once and requests exit code 1", async () => { const currentRuntime = runtime(); - const runCrestodian = vi.fn(async () => { + const runSystemAgent = vi.fn(async () => { throw new Error("Plugin install spec is invalid."); }); - await runCrestodianWithInference( + await runSystemAgentWithInference( { message: "install plugin https://example.test/plugin.tgz", yes: true }, currentRuntime, {}, { verifyInference: vi.fn(async () => workingInference()), - runCrestodian, + runSystemAgent, }, ); @@ -117,13 +117,13 @@ describe("runCrestodianWithInference", () => { exitMocks.requestExitAfterOneShotOutput.mockReturnValueOnce(true); const currentRuntime = runtime(); - await runCrestodianWithInference( + await runSystemAgentWithInference( { message: "broken request" }, currentRuntime, {}, { verifyInference: vi.fn(async () => workingInference()), - runCrestodian: vi.fn(async () => { + runSystemAgent: vi.fn(async () => { throw new Error("operation failed"); }), }, @@ -136,9 +136,9 @@ describe("runCrestodianWithInference", () => { it("returns one drained JSON error when inference verification throws", async () => { exitMocks.requestExitAfterOneShotOutput.mockReturnValueOnce(true); const currentRuntime = runtime(); - const runCrestodian = vi.fn(async () => {}); + const runSystemAgent = vi.fn(async () => {}); - await runCrestodianWithInference( + await runSystemAgentWithInference( { json: true }, currentRuntime, {}, @@ -146,7 +146,7 @@ describe("runCrestodianWithInference", () => { verifyInference: vi.fn(async () => { throw new Error("verification exploded"); }), - runCrestodian, + runSystemAgent, }, ); @@ -155,23 +155,23 @@ describe("runCrestodianWithInference", () => { expect.stringContaining('"error": "verification exploded"'), ); expect(currentRuntime.error).not.toHaveBeenCalled(); - expect(runCrestodian).not.toHaveBeenCalled(); + expect(runSystemAgent).not.toHaveBeenCalled(); expect(exitMocks.requestExitAfterOneShotOutput).toHaveBeenCalledOnce(); expect(exitMocks.requestExitAfterOneShotOutput).toHaveBeenCalledWith(currentRuntime, 1); expect(currentRuntime.exit).not.toHaveBeenCalled(); }); - it("lets interactive Crestodian execution errors propagate", async () => { + it("lets interactive OpenClaw execution errors propagate", async () => { const currentRuntime = runtime(); await expect( - runCrestodianWithInference( + runSystemAgentWithInference( { input: tty, output: tty }, currentRuntime, {}, { verifyInference: vi.fn(async () => workingInference()), - runCrestodian: vi.fn(async () => { + runSystemAgent: vi.fn(async () => { throw new Error("interactive operation failed"); }), }, @@ -184,10 +184,10 @@ describe("runCrestodianWithInference", () => { it("routes an interactive inference failure into guided setup", async () => { const runGuidedOnboarding = vi.fn(async () => {}); - const runCrestodian = vi.fn(async () => {}); + const runSystemAgent = vi.fn(async () => {}); const currentRuntime = runtime(); - await runCrestodianWithInference( + await runSystemAgentWithInference( { input: tty, output: tty }, currentRuntime, { workspace: "/tmp/work", acceptRisk: true }, @@ -198,7 +198,7 @@ describe("runCrestodianWithInference", () => { error: "login expired", })), runGuidedOnboarding, - runCrestodian, + runSystemAgent, }, ); @@ -206,14 +206,14 @@ describe("runCrestodianWithInference", () => { { workspace: "/tmp/work", acceptRisk: true }, currentRuntime, ); - expect(runCrestodian).not.toHaveBeenCalled(); + expect(runSystemAgent).not.toHaveBeenCalled(); }); it("rejects an impossible interactive request before probing inference", async () => { const currentRuntime = runtime(); const verifyInference = vi.fn(); - await runCrestodianWithInference( + await runSystemAgentWithInference( { input: pipe, output: pipe }, currentRuntime, {}, @@ -221,7 +221,7 @@ describe("runCrestodianWithInference", () => { ); expect(currentRuntime.error).toHaveBeenCalledWith( - "Crestodian needs an interactive TTY. Use --message for one command.", + "OpenClaw needs an interactive TTY. Use --message for one command.", ); expect(currentRuntime.exit).toHaveBeenCalledWith(1); expect(verifyInference).not.toHaveBeenCalled(); @@ -231,7 +231,7 @@ describe("runCrestodianWithInference", () => { const currentRuntime = runtime(); const verifyInference = vi.fn(); - await runCrestodianWithInference( + await runSystemAgentWithInference( { input: tty, output: tty, yes: true }, currentRuntime, {}, @@ -239,7 +239,7 @@ describe("runCrestodianWithInference", () => { ); expect(currentRuntime.error).toHaveBeenCalledWith( - "Crestodian --yes requires --message so approval is limited to one request.", + "OpenClaw --yes requires --message so approval is limited to one request.", ); expect(currentRuntime.exit).toHaveBeenCalledWith(1); expect(verifyInference).not.toHaveBeenCalled(); @@ -248,10 +248,10 @@ describe("runCrestodianWithInference", () => { it("returns one structured error for --json --yes without a message", async () => { const currentRuntime = runtime(); - await runCrestodianWithInference({ json: true, yes: true }, currentRuntime); + await runSystemAgentWithInference({ json: true, yes: true }, currentRuntime); expect(currentRuntime.log).toHaveBeenCalledWith( - expect.stringContaining('"error": "Crestodian --yes requires --message'), + expect.stringContaining('"error": "OpenClaw --yes requires --message'), ); expect(currentRuntime.error).not.toHaveBeenCalled(); expect(exitMocks.requestExitAfterOneShotOutput).toHaveBeenCalledWith(currentRuntime, 1); @@ -267,7 +267,7 @@ describe("runCrestodianWithInference", () => { const currentRuntime = runtime(); const runGuidedOnboarding = vi.fn(async () => {}); - await runCrestodianWithInference( + await runSystemAgentWithInference( options, currentRuntime, {}, @@ -293,7 +293,7 @@ describe("runCrestodianWithInference", () => { it("returns a structured JSON error when inference is unavailable", async () => { const currentRuntime = runtime(); - await runCrestodianWithInference( + await runSystemAgentWithInference( { json: true }, currentRuntime, {}, @@ -318,7 +318,7 @@ describe("runCrestodianWithInference", () => { exitMocks.requestExitAfterOneShotOutput.mockReturnValueOnce(true); const currentRuntime = runtime(); - await runCrestodianWithInference( + await runSystemAgentWithInference( { json: true }, currentRuntime, {}, diff --git a/src/commands/crestodian-with-inference.ts b/src/commands/system-agent-with-inference.ts similarity index 67% rename from src/commands/crestodian-with-inference.ts rename to src/commands/system-agent-with-inference.ts index 839e6136b8ab..08c6d57422d8 100644 --- a/src/commands/crestodian-with-inference.ts +++ b/src/commands/system-agent-with-inference.ts @@ -1,26 +1,26 @@ -// Crestodian command gate: prove inference before starting conversational setup. +// OpenClaw command gate: prove inference before starting conversational setup. import { requestExitAfterOneShotOutput } from "../cli/one-shot-exit.js"; -import type { CrestodianCommandOptions } from "../crestodian/crestodian.js"; -import type { BoundVerifySetupInferenceResult } from "../crestodian/setup-inference.js"; import { withConsoleSubsystemsSuppressed } from "../logging/console.js"; import { defaultRuntime, writeRuntimeJson, type RuntimeEnv } from "../runtime.js"; +import type { BoundVerifySetupInferenceResult } from "../system-agent/setup-inference.js"; +import type { SystemAgentCommandOptions } from "../system-agent/system-agent.js"; import type { OnboardOptions } from "./onboard-types.js"; -type RunCrestodian = typeof import("../crestodian/crestodian.js").runCrestodian; +type RunSystemAgent = typeof import("../system-agent/system-agent.js").runSystemAgent; type VerifySetupInference = (params: { runtime: RuntimeEnv; bindSession: true; }) => Promise; type RunGuidedOnboarding = typeof import("./onboard-guided.js").runGuidedOnboarding; -type CrestodianWithInferenceDeps = { +type SystemAgentWithInferenceDeps = { verifyInference?: VerifySetupInference; runGuidedOnboarding?: RunGuidedOnboarding; - runCrestodian?: RunCrestodian; + runSystemAgent?: RunSystemAgent; }; -function hasInteractiveTty(opts: CrestodianCommandOptions): boolean { +function hasInteractiveTty(opts: SystemAgentCommandOptions): boolean { const input = opts.input ?? process.stdin; const output = opts.output ?? process.stdout; return ( @@ -28,7 +28,7 @@ function hasInteractiveTty(opts: CrestodianCommandOptions): boolean { ); } -function isOneShotRequest(opts: CrestodianCommandOptions): boolean { +function isOneShotRequest(opts: SystemAgentCommandOptions): boolean { return Boolean(opts.json || opts.message?.trim() || opts.interactive === false); } @@ -37,7 +37,7 @@ function formatOneShotExecutionError(error: unknown): string { } function failOneShotExecution( - opts: CrestodianCommandOptions, + opts: SystemAgentCommandOptions, runtime: RuntimeEnv, error: unknown, ): void { @@ -53,27 +53,27 @@ function failOneShotExecution( } /** - * Start Crestodian only after the configured default model completes a real + * Start OpenClaw only after the configured default model completes a real * turn. Interactive failures return to inference onboarding; automation fails * closed with a stable command operators can run to repair the prerequisite. */ -export async function runCrestodianWithInference( - opts: CrestodianCommandOptions = {}, +export async function runSystemAgentWithInference( + opts: SystemAgentCommandOptions = {}, runtime: RuntimeEnv = defaultRuntime, onboardingOptions: Pick = {}, - deps: CrestodianWithInferenceDeps = {}, + deps: SystemAgentWithInferenceDeps = {}, ): Promise { if (opts.yes && !opts.message?.trim()) { failOneShotExecution( opts, runtime, - new Error("Crestodian --yes requires --message so approval is limited to one request."), + new Error("OpenClaw --yes requires --message so approval is limited to one request."), ); return; } const oneShot = isOneShotRequest(opts); if (!oneShot && !hasInteractiveTty(opts)) { - runtime.error("Crestodian needs an interactive TTY. Use --message for one command."); + runtime.error("OpenClaw needs an interactive TTY. Use --message for one command."); runtime.exit(1); return; } @@ -81,7 +81,7 @@ export async function runCrestodianWithInference( try { const verifyInference = deps.verifyInference ?? - (await import("../crestodian/setup-inference.js")).verifySetupInference; + (await import("../system-agent/setup-inference.js")).verifySetupInference; inference = await withConsoleSubsystemsSuppressed(() => verifyInference({ runtime, bindSession: true }), ); @@ -93,10 +93,10 @@ export async function runCrestodianWithInference( return; } if (inference.ok) { - const runCrestodian = - deps.runCrestodian ?? (await import("../crestodian/crestodian.js")).runCrestodian; + const runSystemAgent = + deps.runSystemAgent ?? (await import("../system-agent/system-agent.js")).runSystemAgent; try { - await runCrestodian({ ...opts, verifiedInference: inference.binding }, runtime); + await runSystemAgent({ ...opts, verifiedInference: inference.binding }, runtime); } catch (error) { if (!oneShot) { throw error; @@ -116,12 +116,12 @@ export async function runCrestodianWithInference( writeRuntimeJson(runtime, { ok: false, status: inference.status, - error: `Crestodian requires working inference: ${inference.error}`, + error: `OpenClaw requires working inference: ${inference.error}`, guidance, }); } else { runtime.error( - [`Crestodian requires working inference: ${inference.error}`, guidance].join("\n"), + [`OpenClaw requires working inference: ${inference.error}`, guidance].join("\n"), ); } if (!requestExitAfterOneShotOutput(runtime, 1)) { @@ -130,7 +130,7 @@ export async function runCrestodianWithInference( return; } - runtime.log("Crestodian requires working inference. Starting guided AI setup…"); + runtime.log("OpenClaw requires working inference. Starting guided AI setup…"); const runGuidedOnboarding = deps.runGuidedOnboarding ?? (await import("./onboard-guided.js")).runGuidedOnboarding; await runGuidedOnboarding(onboardingOptions, runtime); diff --git a/src/config/config-misc.test.ts b/src/config/config-misc.test.ts index 40e3477aac36..c9fe8c6e4a83 100644 --- a/src/config/config-misc.test.ts +++ b/src/config/config-misc.test.ts @@ -400,10 +400,10 @@ describe("models.pricing", () => { }); }); -describe("crestodian.rescue", () => { +describe("systemAgent.rescue", () => { it("accepts documented rescue config", () => { const result = OpenClawSchema.safeParse({ - crestodian: { + systemAgent: { rescue: { enabled: "auto", ownerDmOnly: false, @@ -416,7 +416,7 @@ describe("crestodian.rescue", () => { it("accepts boolean rescue enablement", () => { const result = OpenClawSchema.safeParse({ - crestodian: { + systemAgent: { rescue: { enabled: true, ownerDmOnly: true, @@ -428,7 +428,7 @@ describe("crestodian.rescue", () => { it("rejects unknown rescue keys", () => { const result = OpenClawSchema.safeParse({ - crestodian: { + systemAgent: { rescue: { enabled: true, shell: true, diff --git a/src/config/types.openclaw.ts b/src/config/types.openclaw.ts index cfe14a2880b1..4201063bfb6a 100644 --- a/src/config/types.openclaw.ts +++ b/src/config/types.openclaw.ts @@ -18,7 +18,6 @@ import type { ChannelsConfig } from "./types.channels.js"; import type { CliConfig } from "./types.cli.js"; import type { CloudWorkersConfig } from "./types.cloud-workers.js"; import type { CommitmentsConfig } from "./types.commitments.js"; -import type { CrestodianConfig } from "./types.crestodian.js"; import type { CronConfig } from "./types.cron.js"; import type { DiscoveryConfig, GatewayConfig, TalkConfig } from "./types.gateway.js"; import type { HooksConfig } from "./types.hooks.js"; @@ -36,6 +35,7 @@ import type { NodeHostConfig } from "./types.node-host.js"; import type { PluginsConfig } from "./types.plugins.js"; import type { SecretsConfig } from "./types.secrets.js"; import type { SkillsConfig } from "./types.skills.js"; +import type { SystemAgentConfig } from "./types.system-agent.js"; import type { ToolsConfig } from "./types.tools.js"; import type { ProxyConfig } from "./zod-schema.proxy.js"; @@ -157,8 +157,8 @@ export type OpenClawConfig = { security?: SecurityConfig; /** CLI defaults and command-specific settings. */ cli?: CliConfig; - /** Crestodian rescue/maintenance integration settings. */ - crestodian?: CrestodianConfig; + /** System-agent rescue/maintenance integration settings. */ + systemAgent?: SystemAgentConfig; update?: { /** Update channel for git + npm installs ("stable", "extended-stable", "beta", or "dev"). */ channel?: "stable" | "extended-stable" | "beta" | "dev"; diff --git a/src/config/types.crestodian.ts b/src/config/types.system-agent.ts similarity index 67% rename from src/config/types.crestodian.ts rename to src/config/types.system-agent.ts index fb7ade29997d..d5aad2ea19ed 100644 --- a/src/config/types.crestodian.ts +++ b/src/config/types.system-agent.ts @@ -1,9 +1,9 @@ /** - * Crestodian config types for local control-plane and remote rescue behavior. + * System-agent config types for local control-plane and remote rescue behavior. * Rescue config is deliberately narrow because it can approve state-changing maintainer actions. */ /** Remote rescue gate and approval retention policy. */ -type CrestodianRescueConfig = { +type SystemAgentRescueConfig = { /** * Remote message rescue gate. * "auto" enables only for YOLO host posture with sandboxing off. @@ -15,7 +15,7 @@ type CrestodianRescueConfig = { pendingTtlMinutes?: number; }; -/** Top-level Crestodian config block. */ -export type CrestodianConfig = { - rescue?: CrestodianRescueConfig; +/** Top-level system-agent config block. */ +export type SystemAgentConfig = { + rescue?: SystemAgentRescueConfig; }; diff --git a/src/config/zod-schema.ts b/src/config/zod-schema.ts index 76dd762a84fa..0f7eca0ea167 100644 --- a/src/config/zod-schema.ts +++ b/src/config/zod-schema.ts @@ -486,7 +486,7 @@ const NodeHostSchema = z }) .optional(); -const CrestodianSchema = z +const SystemAgentSchema = z .strictObject({ rescue: z .strictObject({ @@ -744,7 +744,7 @@ export const OpenClawSchema = z .optional(), }) .optional(), - crestodian: CrestodianSchema, + systemAgent: SystemAgentSchema, update: z .strictObject({ channel: z diff --git a/src/crestodian/dialogue.ts b/src/crestodian/dialogue.ts deleted file mode 100644 index 0fe91c4800f1..000000000000 --- a/src/crestodian/dialogue.ts +++ /dev/null @@ -1,110 +0,0 @@ -// Crestodian dialogue parses direct commands and optionally asks the assistant planner. -import type { RuntimeEnv } from "../runtime.js"; -import type { CrestodianAssistantPlan, CrestodianAssistantPlanner } from "./assistant.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; -import { - describeCrestodianPersistentOperation, - parseCrestodianOperation, - type CrestodianOperation, -} from "./operations.js"; -import { loadCrestodianOverview, type CrestodianOverview } from "./overview.js"; -import { - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceDeps, -} from "./verified-inference.js"; - -/** - * Dialogue helpers for turning user text into Crestodian operations. - * - * Direct command parsing wins; the assistant planner is only consulted for - * non-empty text that did not parse into a known operation. - */ -type CrestodianDialogueOptions = { - loadOverview?: typeof loadCrestodianOverview; - planWithAssistant?: CrestodianAssistantPlanner; - deps?: CrestodianVerifiedInferenceDeps; - readonly verifiedInference: CrestodianVerifiedInferenceBinding; -}; - -/** Format the interactive approval prompt for a persistent operation. */ -export function approvalQuestion(operation: CrestodianOperation): string { - return `Apply this operation: ${describeCrestodianPersistentOperation(operation)}?`; -} - -/** Resolve user input to a Crestodian operation, optionally using the assistant planner. */ -export async function resolveCrestodianOperation( - input: string, - runtime: RuntimeEnv, - opts: CrestodianDialogueOptions, -): Promise { - if (!opts.verifiedInference) { - throw new CrestodianInferenceUnavailableError("conversation"); - } - const operation = parseCrestodianOperation(input); - if (!shouldAskAssistant(input, operation)) { - return operation; - } - const overview = await (opts.loadOverview ?? loadCrestodianOverview)(); - const planner = opts.planWithAssistant ?? (await import("./assistant.js")).planCrestodianCommand; - let plan: CrestodianAssistantPlan | null; - try { - plan = await planner({ - input, - overview, - verifiedInference: opts.verifiedInference, - }); - if ( - plan && - !(await resolveCrestodianVerifiedInferenceRoute(opts.verifiedInference, opts.deps)) - ) { - throw new CrestodianInferenceUnavailableError("planner"); - } - } catch (error) { - if (error instanceof CrestodianInferenceUnavailableError) { - throw error; - } - throw new CrestodianInferenceUnavailableError("planner", [error]); - } - if (!plan) { - throw new CrestodianInferenceUnavailableError("planner"); - } - if (!plan.command) { - if (!plan.reply?.trim()) { - throw new CrestodianInferenceUnavailableError("planner"); - } - runtime.log(plan.reply); - return { kind: "none", message: "" }; - } - const planned = parseCrestodianOperation(plan.command); - if (planned.kind === "none") { - throw new CrestodianInferenceUnavailableError("planner"); - } - logAssistantPlan(runtime, plan, overview); - return planned; -} - -function shouldAskAssistant(input: string, operation: CrestodianOperation): boolean { - if (operation.kind !== "none") { - return false; - } - const trimmed = input.trim().toLowerCase(); - if (!trimmed || trimmed === "quit" || trimmed === "exit") { - return false; - } - return true; -} - -function logAssistantPlan( - runtime: RuntimeEnv, - plan: CrestodianAssistantPlan, - overview: CrestodianOverview, -): void { - // Assistant plans are echoed before execution so the user can see the interpreted command. - const modelLabel = plan.modelLabel ?? overview.defaultModel ?? "configured model"; - runtime.log(`[crestodian] planner: ${modelLabel}`); - if (plan.reply) { - runtime.log(plan.reply); - } - runtime.log(`[crestodian] interpreted: ${plan.command}`); -} diff --git a/src/crestodian/inference-error.ts b/src/crestodian/inference-error.ts deleted file mode 100644 index d07f3230d374..000000000000 --- a/src/crestodian/inference-error.ts +++ /dev/null @@ -1,25 +0,0 @@ -type CrestodianInferenceStage = "agent-turn" | "planner" | "conversation"; - -/** Safe public error for a Crestodian turn that could not complete with intelligence. */ -export class CrestodianInferenceUnavailableError extends Error { - readonly code = "CRESTODIAN_INFERENCE_UNAVAILABLE"; - - constructor( - readonly stage: CrestodianInferenceStage, - readonly failures: readonly unknown[] = [], - ) { - super( - "Crestodian could not reach working inference. Run `openclaw onboard` to reconnect and live-test AI, then try again.", - ); - this.name = "CrestodianInferenceUnavailableError"; - } -} - -export function isCrestodianInferenceUnavailableError( - error: unknown, -): error is CrestodianInferenceUnavailableError { - return ( - error instanceof CrestodianInferenceUnavailableError || - (error instanceof Error && "code" in error && error.code === "CRESTODIAN_INFERENCE_UNAVAILABLE") - ); -} diff --git a/src/crestodian/plugin-install.ts b/src/crestodian/plugin-install.ts deleted file mode 100644 index 83da3bed11cb..000000000000 --- a/src/crestodian/plugin-install.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { isOpenClawTrustedPluginInstallSpec } from "../plugins/install-provenance.js"; - -export function validateCrestodianPluginInstallSpec(spec: string): string | null { - const trimmed = spec.trim(); - if (!trimmed) { - return "Plugin install spec is required."; - } - if (/\s/.test(trimmed)) { - return "Crestodian plugin install accepts one npm or ClawHub package spec."; - } - if (/^(?:\.{1,2}\/|\/|~\/|file:|git(?:\+ssh|\+https)?:|https?:)/i.test(trimmed)) { - // Crestodian does not install local paths or URLs; those can execute arbitrary package code. - return "Crestodian plugin install accepts npm or ClawHub package specs only."; - } - if (!isOpenClawTrustedPluginInstallSpec(trimmed)) { - return "Crestodian installs only ClawHub, bundled, or official-catalog plugins. Use `openclaw plugins install ` in a trusted shell to review an arbitrary executable source."; - } - return null; -} diff --git a/src/gateway/local-request-context.ts b/src/gateway/local-request-context.ts index dbca73107bab..cf9ef2c60fa7 100644 --- a/src/gateway/local-request-context.ts +++ b/src/gateway/local-request-context.ts @@ -140,7 +140,7 @@ function createLocalGatewayRequestContext( registerToolEventRecipient: () => {}, dedupe: new Map(), wizardSessions: new Map(), - crestodianSessions: new Map(), + systemAgentSessions: new Map(), findRunningWizard: () => null, purgeWizardSession: () => {}, getRuntimeSnapshot: () => ({}) as ChannelRuntimeSnapshot, diff --git a/src/gateway/methods/core-descriptors.ts b/src/gateway/methods/core-descriptors.ts index c2f1b71a4ac3..cfe4bf084c80 100644 --- a/src/gateway/methods/core-descriptors.ts +++ b/src/gateway/methods/core-descriptors.ts @@ -66,12 +66,12 @@ const CORE_GATEWAY_METHOD_SPECS: readonly CoreGatewayMethodSpec[] = [ { name: "plugin.approval.resolve", scope: "operator.approvals" }, { name: "plugins.uiDescriptors", scope: "operator.read" }, { name: "plugins.sessionAction", scope: "dynamic" }, - { name: "crestodian.chat", scope: "operator.admin" }, - { name: "crestodian.setup.detect", scope: "operator.admin" }, + { name: "openclaw.chat", scope: "operator.admin" }, + { name: "openclaw.setup.detect", scope: "operator.admin" }, // Failed activation candidates are non-mutating probes. Keep this admin-only // without the shared three-write budget so the automatic ladder can finish. - { name: "crestodian.setup.activate", scope: "operator.admin" }, - { name: "crestodian.setup.auth.start", scope: "operator.admin" }, + { name: "openclaw.setup.activate", scope: "operator.admin" }, + { name: "openclaw.setup.auth.start", scope: "operator.admin" }, { name: "wizard.start", scope: "operator.admin" }, { name: "wizard.next", scope: "operator.admin" }, { name: "wizard.cancel", scope: "operator.admin" }, @@ -325,7 +325,7 @@ const CORE_GATEWAY_METHOD_SPECS: readonly CoreGatewayMethodSpec[] = [ // sessions.files.* trusted-operator read domain. { name: "sessions.diff", scope: "operator.read" }, // Additive protocol methods append here to preserve existing advertised indices. - { name: "crestodian.setup.verify", scope: "operator.admin" }, + { name: "openclaw.setup.verify", scope: "operator.admin" }, // Cloud-worker mutations depend on the loaded provider registry and owned // reconciler, so advertise them early but gate dispatch until sidecars are ready. { diff --git a/src/gateway/server-methods.control-plane-rate-limit.test.ts b/src/gateway/server-methods.control-plane-rate-limit.test.ts index 770ba65b2b1e..48eb973dd83c 100644 --- a/src/gateway/server-methods.control-plane-rate-limit.test.ts +++ b/src/gateway/server-methods.control-plane-rate-limit.test.ts @@ -121,7 +121,7 @@ describe("gateway control-plane write rate limit", () => { expect(logWarn).toHaveBeenCalledTimes(1); }); - it("allows the Crestodian inference ladder to probe more than 3 candidates", async () => { + it("allows the OpenClaw inference ladder to probe more than 3 candidates", async () => { const handlerCalls = vi.fn(); const handler: GatewayRequestHandler = (opts) => { handlerCalls(opts); @@ -134,7 +134,7 @@ describe("gateway control-plane write rate limit", () => { for (let attempt = 0; attempt < 4; attempt += 1) { responses.push( await runRequest({ - method: "crestodian.setup.activate", + method: "openclaw.setup.activate", context, client, handler, diff --git a/src/gateway/server-methods.ts b/src/gateway/server-methods.ts index e14822f140a0..d19c7aab3e47 100644 --- a/src/gateway/server-methods.ts +++ b/src/gateway/server-methods.ts @@ -260,9 +260,9 @@ const loadWebHandlers = lazyHandlerModule( () => import("./server-methods/web.js"), (module) => module.webHandlers, ); -const loadCrestodianHandlers = lazyHandlerModule( - () => import("./server-methods/crestodian.js"), - (module) => module.crestodianHandlers, +const loadSystemAgentHandlers = lazyHandlerModule( + () => import("./server-methods/system-agent.js"), + (module) => module.systemAgentHandlers, ); const loadWizardHandlers = lazyHandlerModule( () => import("./server-methods/wizard.js"), @@ -513,13 +513,13 @@ export const coreGatewayHandlers: GatewayRequestHandlers = { }), ...createLazyCoreHandlers({ methods: [ - "crestodian.chat", - "crestodian.setup.detect", - "crestodian.setup.verify", - "crestodian.setup.activate", - "crestodian.setup.auth.start", + "openclaw.chat", + "openclaw.setup.detect", + "openclaw.setup.verify", + "openclaw.setup.activate", + "openclaw.setup.auth.start", ], - loadHandlers: loadCrestodianHandlers, + loadHandlers: loadSystemAgentHandlers, }), ...createLazyCoreHandlers({ methods: [ diff --git a/src/gateway/server-methods/agents-mutate.test.ts b/src/gateway/server-methods/agents-mutate.test.ts index 353ef2e9651e..88806f8729ef 100644 --- a/src/gateway/server-methods/agents-mutate.test.ts +++ b/src/gateway/server-methods/agents-mutate.test.ts @@ -67,6 +67,8 @@ const mocks = vi.hoisted(() => ({ rootWrite: vi.fn(async (_params?: unknown) => {}), })); +const RESERVED_SYSTEM_AGENT_IDS_FOR_TEST = ["openclaw", "crestodian"] as const; // reserved ids + vi.mock("../../config/config.js", async () => { const actual = await vi.importActual("../../config/config.js"); @@ -569,6 +571,20 @@ describe("agents.create", () => { expectRespondErrorContaining(respond, "reserved"); }); + it.each(RESERVED_SYSTEM_AGENT_IDS_FOR_TEST)( + "rejects creating an agent with reserved system-agent id %s", + async (name) => { + const { respond, promise } = makeCall("agents.create", { + name, + workspace: "/tmp/ws", + }); + await promise; + + expectRespondErrorContaining(respond, `"${name}" is reserved`); + expect(mocks.writeConfigFile).not.toHaveBeenCalled(); + }, + ); + it("rejects creating a duplicate agent", async () => { mocks.findAgentEntryIndex.mockReturnValue(0); diff --git a/src/gateway/server-methods/agents.ts b/src/gateway/server-methods/agents.ts index eed28bd39536..360d5ae28132 100644 --- a/src/gateway/server-methods/agents.ts +++ b/src/gateway/server-methods/agents.ts @@ -47,6 +47,7 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { root, FsSafeError, type ReadResult } from "../../infra/fs-safe.js"; import { movePathToTrash } from "../../plugin-sdk/browser-maintenance.js"; import { DEFAULT_AGENT_ID, normalizeAgentId } from "../../routing/session-key.js"; +import { isReservedSystemAgentId } from "../../system-agent/agent-id.js"; import { resolveUserPath } from "../../utils.js"; import { listAgentsForGateway } from "../session-utils.js"; import { @@ -512,12 +513,8 @@ export const agentsHandlers: GatewayRequestHandlers = { const cfg = context.getRuntimeConfig(); const rawName = params.name.trim(); const agentId = normalizeAgentId(rawName); - if (agentId === DEFAULT_AGENT_ID) { - respond( - false, - undefined, - errorShape(ErrorCodes.INVALID_REQUEST, `"${DEFAULT_AGENT_ID}" is reserved`), - ); + if (agentId === DEFAULT_AGENT_ID || isReservedSystemAgentId(agentId)) { + respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `"${agentId}" is reserved`)); return; } diff --git a/src/gateway/server-methods/shared-types.ts b/src/gateway/server-methods/shared-types.ts index 1a5738f392e6..d652fcbca000 100644 --- a/src/gateway/server-methods/shared-types.ts +++ b/src/gateway/server-methods/shared-types.ts @@ -81,8 +81,8 @@ export type RespondFn = ( meta?: Record, ) => void; -/** Minimal hosted Crestodian contract retained by the gateway request router. */ -type GatewayCrestodianSession = { +/** Minimal hosted OpenClaw contract retained by the gateway request router. */ +type GatewaySystemAgentSession = { engine: { handle: (message: string) => Promise<{ text: string; @@ -188,7 +188,7 @@ export type GatewayRequestContext = { registerToolEventRecipient: (runId: string, connId: string) => void; dedupe: Map; wizardSessions: Map; - crestodianSessions: Map; + systemAgentSessions: Map; findRunningWizard: () => string | null; purgeWizardSession: (id: string) => void; getRuntimeSnapshot: () => ChannelRuntimeSnapshot; diff --git a/src/gateway/server-methods/crestodian.test.ts b/src/gateway/server-methods/system-agent.test.ts similarity index 79% rename from src/gateway/server-methods/crestodian.test.ts rename to src/gateway/server-methods/system-agent.test.ts index 1bcf8caa7477..949648f0aa1b 100644 --- a/src/gateway/server-methods/crestodian.test.ts +++ b/src/gateway/server-methods/system-agent.test.ts @@ -1,25 +1,25 @@ -// Crestodian gateway tests cover activation serialization and chat sessions. +// OpenClaw gateway tests cover activation serialization and chat sessions. import { expectDefined } from "@openclaw/normalization-core"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; -import { CrestodianChatEngine } from "../../crestodian/chat-engine.js"; -import { createCrestodianVerifiedInferenceTestFixture } from "../../crestodian/crestodian.test-helpers.js"; -import { CrestodianInferenceUnavailableError } from "../../crestodian/inference-error.js"; -import type { - CrestodianVerifiedInferenceBinding, - CrestodianVerifiedInferenceDeps, -} from "../../crestodian/verified-inference.js"; import { getCommandLaneSnapshot } from "../../process/command-queue.js"; import { resetCommandQueueStateForTest } from "../../process/command-queue.test-support.js"; import { CommandLane } from "../../process/lanes.js"; import { defaultRuntime } from "../../runtime.js"; +import { SystemAgentChatEngine } from "../../system-agent/chat-engine.js"; +import { SystemAgentInferenceUnavailableError } from "../../system-agent/inference-error.js"; +import { createSystemAgentVerifiedInferenceTestFixture } from "../../system-agent/system-agent.test-helpers.js"; +import type { + SystemAgentVerifiedInferenceBinding, + SystemAgentVerifiedInferenceDeps, +} from "../../system-agent/verified-inference.js"; import { createDeferred } from "../../test-utils/deferred.js"; import { - crestodianHandlers, - runExclusiveCrestodianSetupActivation, - type CrestodianChatSession, -} from "./crestodian.js"; + systemAgentHandlers, + runExclusiveSystemAgentSetupActivation, + type SystemAgentChatSession, +} from "./system-agent.js"; import type { GatewayRequestContext } from "./types.js"; const setupInferenceMocks = vi.hoisted(() => ({ @@ -28,7 +28,7 @@ const setupInferenceMocks = vi.hoisted(() => ({ verifySetupInference: vi.fn(), })); -vi.mock("../../crestodian/setup-inference.js", () => ({ +vi.mock("../../system-agent/setup-inference.js", () => ({ activateSetupInference: setupInferenceMocks.activateSetupInference, detectSetupInference: setupInferenceMocks.detectSetupInference, verifySetupInference: setupInferenceMocks.verifySetupInference, @@ -48,8 +48,8 @@ function makeRespond() { return { calls, respond }; } -function makeContext(sessions: Map): GatewayRequestContext { - return { crestodianSessions: sessions } as unknown as GatewayRequestContext; +function makeContext(sessions: Map): GatewayRequestContext { + return { systemAgentSessions: sessions } as unknown as GatewayRequestContext; } const verifiedConfig: OpenClawConfig = { @@ -60,17 +60,17 @@ const verifiedConfig: OpenClawConfig = { }, }, }; -let verifiedInference: CrestodianVerifiedInferenceBinding | undefined; -let verifiedInferenceDeps: CrestodianVerifiedInferenceDeps | undefined; +let verifiedInference: SystemAgentVerifiedInferenceBinding | undefined; +let verifiedInferenceDeps: SystemAgentVerifiedInferenceDeps | undefined; -function requireVerifiedInferenceFixture(): CrestodianVerifiedInferenceBinding { +function requireVerifiedInferenceFixture(): SystemAgentVerifiedInferenceBinding { if (!verifiedInference) { throw new Error("verified inference fixture was not initialized"); } return verifiedInference; } -function requireVerifiedInferenceDeps(): CrestodianVerifiedInferenceDeps { +function requireVerifiedInferenceDeps(): SystemAgentVerifiedInferenceDeps { if (!verifiedInferenceDeps) { throw new Error("verified inference dependencies were not initialized"); } @@ -90,15 +90,15 @@ function requireVerifiedInferenceDeps(): CrestodianVerifiedInferenceDeps { }; } -function makeVerifiedEngine(): CrestodianChatEngine { - return new CrestodianChatEngine({ +function makeVerifiedEngine(): SystemAgentChatEngine { + return new SystemAgentChatEngine({ verifiedInference: requireVerifiedInferenceFixture(), deps: requireVerifiedInferenceDeps(), }); } function stubEngineOverview() { - return vi.spyOn(CrestodianChatEngine.prototype, "loadOverview").mockResolvedValue({ + return vi.spyOn(SystemAgentChatEngine.prototype, "loadOverview").mockResolvedValue({ config: { path: "/tmp/openclaw.json", exists: true, valid: true, issues: [], hash: null }, agents: [], defaultAgentId: "main", @@ -117,7 +117,7 @@ function stubEngineOverview() { } as never); } -function seededSession(overrides?: Partial): CrestodianChatSession { +function seededSession(overrides?: Partial): SystemAgentChatSession { return { engine: makeVerifiedEngine(), welcome: "welcome text", @@ -127,7 +127,7 @@ function seededSession(overrides?: Partial): CrestodianCh } beforeEach(async () => { - const fixture = await createCrestodianVerifiedInferenceTestFixture(verifiedConfig); + const fixture = await createSystemAgentVerifiedInferenceTestFixture(verifiedConfig); verifiedInference = fixture.binding; verifiedInferenceDeps = fixture.deps; setupInferenceMocks.verifySetupInference.mockResolvedValue({ @@ -154,8 +154,8 @@ async function callChat( ): Promise { const { calls, respond } = makeRespond(); await expectDefined( - crestodianHandlers["crestodian.chat"], - 'crestodianHandlers["crestodian.chat"] test invariant', + systemAgentHandlers["openclaw.chat"], + 'systemAgentHandlers["openclaw.chat"] test invariant', )({ params, respond, @@ -168,13 +168,13 @@ async function callChat( return call; } -describe("crestodian.setup.activate", () => { +describe("openclaw.setup.activate", () => { it("rejects a concurrent activation instead of queueing stale work", async () => { const firstStarted = createDeferred(); const releaseFirst = createDeferred(); const events: string[] = []; - const first = runExclusiveCrestodianSetupActivation(async () => { + const first = runExclusiveSystemAgentSetupActivation(async () => { events.push("first:start"); firstStarted.resolve(); await releaseFirst.promise; @@ -186,7 +186,7 @@ describe("crestodian.setup.activate", () => { events.push("second:start"); events.push("second:end"); }); - const second = runExclusiveCrestodianSetupActivation(secondTask); + const second = runExclusiveSystemAgentSetupActivation(secondTask); expect(events).toEqual(["first:start"]); await expect(second).rejects.toThrow("setup is already in progress"); @@ -195,7 +195,7 @@ describe("crestodian.setup.activate", () => { await first; expect(events).toEqual(["first:start", "first:end"]); - await runExclusiveCrestodianSetupActivation(async () => { + await runExclusiveSystemAgentSetupActivation(async () => { events.push("third:start"); }); expect(events).toEqual(["first:start", "first:end", "third:start"]); @@ -204,7 +204,7 @@ describe("crestodian.setup.activate", () => { it("returns a retryable busy error while another activation is running", async () => { const firstStarted = createDeferred(); const releaseFirst = createDeferred(); - const first = runExclusiveCrestodianSetupActivation(async () => { + const first = runExclusiveSystemAgentSetupActivation(async () => { firstStarted.resolve(); await releaseFirst.promise; }); @@ -213,8 +213,8 @@ describe("crestodian.setup.activate", () => { try { const { calls, respond } = makeRespond(); await expectDefined( - crestodianHandlers["crestodian.setup.activate"], - 'crestodianHandlers["crestodian.setup.activate"] test invariant', + systemAgentHandlers["openclaw.setup.activate"], + 'systemAgentHandlers["openclaw.setup.activate"] test invariant', )({ params: { kind: "claude-cli" }, respond, @@ -226,7 +226,7 @@ describe("crestodian.setup.activate", () => { payload: undefined, error: { code: "UNAVAILABLE", - message: "Crestodian setup is already in progress; try again when it finishes.", + message: "OpenClaw setup is already in progress; try again when it finishes.", retryable: true, }, }, @@ -239,18 +239,18 @@ describe("crestodian.setup.activate", () => { it("releases the activation slot when the owning task fails", async () => { await expect( - runExclusiveCrestodianSetupActivation(async () => { + runExclusiveSystemAgentSetupActivation(async () => { throw new Error("probe failed"); }), ).rejects.toThrow("probe failed"); const nextTask = vi.fn(async () => "ok"); - await expect(runExclusiveCrestodianSetupActivation(nextTask)).resolves.toBe("ok"); + await expect(runExclusiveSystemAgentSetupActivation(nextTask)).resolves.toBe("ok"); expect(nextTask).toHaveBeenCalledOnce(); }); }); -describe("crestodian.setup.auth.start", () => { +describe("openclaw.setup.auth.start", () => { it("starts provider auth as an interactive wizard session", async () => { const wizardSessions = new Map(); const context = { @@ -265,8 +265,8 @@ describe("crestodian.setup.auth.start", () => { const { calls, respond } = makeRespond(); await expectDefined( - crestodianHandlers["crestodian.setup.auth.start"], - 'crestodianHandlers["crestodian.setup.auth.start"] test invariant', + systemAgentHandlers["openclaw.setup.auth.start"], + 'systemAgentHandlers["openclaw.setup.auth.start"] test invariant', )({ params: { sessionId: "auth-session-1", authChoice: "github-copilot" }, respond, @@ -295,14 +295,14 @@ describe("crestodian.setup.auth.start", () => { }); }); -describe("crestodian.chat", () => { +describe("openclaw.chat", () => { it("refuses to create a session before inference is available", async () => { setupInferenceMocks.verifySetupInference.mockResolvedValueOnce({ ok: false, status: "unavailable", error: "no configured model", }); - const sessions = new Map(); + const sessions = new Map(); const call = await callChat(makeContext(sessions), { sessionId: "s1" }); @@ -310,7 +310,7 @@ describe("crestodian.chat", () => { ok: false, error: { code: "UNAVAILABLE", - message: "Crestodian requires working inference: no configured model", + message: "OpenClaw requires working inference: no configured model", }, }); expect(sessions.size).toBe(0); @@ -330,7 +330,7 @@ describe("crestodian.chat", () => { binding: requireVerifiedInferenceFixture(), }; }); - const sessions = new Map(); + const sessions = new Map(); const context = makeContext(sessions); const first = callChat(context, { sessionId: "shared" }); @@ -364,22 +364,22 @@ describe("crestodian.chat", () => { const activeAtResponse: number[] = []; const pending = expectDefined( - crestodianHandlers["crestodian.setup.detect"], - 'crestodianHandlers["crestodian.setup.detect"] test invariant', + systemAgentHandlers["openclaw.setup.detect"], + 'systemAgentHandlers["openclaw.setup.detect"] test invariant', )({ params: {}, respond: () => { - activeAtResponse.push(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount); + activeAtResponse.push(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount); }, } as never); await started.promise; - expect(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount).toBe(1); + expect(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount).toBe(1); release.resolve(); await pending; expect(activeAtResponse).toEqual([1]); - expect(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount).toBe(0); + expect(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount).toBe(0); }); it.each([ @@ -400,8 +400,8 @@ describe("crestodian.chat", () => { const { calls, respond } = makeRespond(); await expectDefined( - crestodianHandlers["crestodian.setup.verify"], - 'crestodianHandlers["crestodian.setup.verify"] test invariant', + systemAgentHandlers["openclaw.setup.verify"], + 'systemAgentHandlers["openclaw.setup.verify"] test invariant', )({ params: {}, respond } as never); expect(setupInferenceMocks.verifySetupInference).toHaveBeenCalledWith({ @@ -414,8 +414,8 @@ describe("crestodian.chat", () => { const { calls, respond } = makeRespond(); await expectDefined( - crestodianHandlers["crestodian.setup.verify"], - 'crestodianHandlers["crestodian.setup.verify"] test invariant', + systemAgentHandlers["openclaw.setup.verify"], + 'systemAgentHandlers["openclaw.setup.verify"] test invariant', )({ params: { modelRef: "openai/gpt-5.5" }, respond, @@ -443,8 +443,8 @@ describe("crestodian.chat", () => { const activeAtResponse: number[] = []; const pending = expectDefined( - crestodianHandlers["crestodian.setup.activate"], - 'crestodianHandlers["crestodian.setup.activate"] test invariant', + systemAgentHandlers["openclaw.setup.activate"], + 'systemAgentHandlers["openclaw.setup.activate"] test invariant', )({ params: { kind: "api-key", @@ -454,13 +454,13 @@ describe("crestodian.chat", () => { workspace: "/tmp/work", }, respond: (ok: boolean, payload?: unknown, error?: unknown) => { - activeAtResponse.push(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount); + activeAtResponse.push(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount); respond(ok, payload, error); }, } as never); await started.promise; - expect(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount).toBe(1); + expect(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount).toBe(1); release.resolve(); await pending; @@ -475,7 +475,7 @@ describe("crestodian.chat", () => { }); expect(calls).toEqual([{ ok: true, payload: activationResult, error: undefined }]); expect(activeAtResponse).toEqual([1]); - expect(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount).toBe(0); + expect(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount).toBe(0); }); it("rejects invalid params", async () => { @@ -484,7 +484,7 @@ describe("crestodian.chat", () => { }); it("returns the stored welcome when no message is sent", async () => { - const sessions = new Map([["s1", seededSession()]]); + const sessions = new Map([["s1", seededSession()]]); const call = await callChat(makeContext(sessions), { sessionId: "s1" }); expect(call.ok).toBe(true); expect(call.payload).toMatchObject({ sessionId: "s1", reply: "welcome text", action: "none" }); @@ -495,7 +495,7 @@ describe("crestodian.chat", () => { const handle = vi .spyOn(engine, "handle") .mockResolvedValue({ text: "did the thing", action: "none" }); - const sessions = new Map([["s1", seededSession({ engine })]]); + const sessions = new Map([["s1", seededSession({ engine })]]); const call = await callChat(makeContext(sessions), { sessionId: "s1", message: "status" }); @@ -507,10 +507,10 @@ describe("crestodian.chat", () => { stubEngineOverview(); const engine = makeVerifiedEngine(); vi.spyOn(engine, "handle").mockRejectedValue( - new CrestodianInferenceUnavailableError("conversation"), + new SystemAgentInferenceUnavailableError("conversation"), ); const dispose = vi.spyOn(engine, "dispose").mockResolvedValue(); - const sessions = new Map([["s1", seededSession({ engine })]]); + const sessions = new Map([["s1", seededSession({ engine })]]); const context = makeContext(sessions); const failed = await callChat(context, { sessionId: "s1", message: "status please" }); @@ -536,7 +536,7 @@ describe("crestodian.chat", () => { it("does not relabel unrelated session failures as inference errors", async () => { const engine = makeVerifiedEngine(); vi.spyOn(engine, "handle").mockRejectedValue(new Error("wizard bug")); - const sessions = new Map([["s1", seededSession({ engine })]]); + const sessions = new Map([["s1", seededSession({ engine })]]); await expect( callChat(makeContext(sessions), { sessionId: "s1", message: "status please" }), @@ -561,30 +561,30 @@ describe("crestodian.chat", () => { await releaseSecond.promise; return { text: "second setup complete", action: "none" }; }); - const sessions = new Map([ + const sessions = new Map([ ["s1", seededSession({ engine: firstEngine })], ["s2", seededSession({ engine: secondEngine })], ]); const activeAtResponse: number[] = []; const first = expectDefined( - crestodianHandlers["crestodian.chat"], - 'crestodianHandlers["crestodian.chat"] test invariant', + systemAgentHandlers["openclaw.chat"], + 'systemAgentHandlers["openclaw.chat"] test invariant', )({ params: { sessionId: "s1", message: "yes" }, context: makeContext(sessions), respond: () => { - activeAtResponse.push(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount); + activeAtResponse.push(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount); }, } as never); const second = expectDefined( - crestodianHandlers["crestodian.chat"], - 'crestodianHandlers["crestodian.chat"] test invariant', + systemAgentHandlers["openclaw.chat"], + 'systemAgentHandlers["openclaw.chat"] test invariant', )({ params: { sessionId: "s2", message: "yes" }, context: makeContext(sessions), respond: () => { - activeAtResponse.push(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount); + activeAtResponse.push(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount); }, } as never); @@ -592,7 +592,7 @@ describe("crestodian.chat", () => { await new Promise((resolve) => { setTimeout(resolve, 0); }); - expect(getCommandLaneSnapshot(CommandLane.Crestodian)).toMatchObject({ + expect(getCommandLaneSnapshot(CommandLane.SystemAgent)).toMatchObject({ activeCount: 2, queuedCount: 0, }); @@ -600,12 +600,12 @@ describe("crestodian.chat", () => { releaseFirst.resolve(); await first; await secondStarted.promise; - expect(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount).toBe(1); + expect(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount).toBe(1); releaseSecond.resolve(); await second; expect(activeAtResponse).toEqual([2, 1]); - expect(getCommandLaneSnapshot(CommandLane.Crestodian).activeCount).toBe(0); + expect(getCommandLaneSnapshot(CommandLane.SystemAgent).activeCount).toBe(0); }); it("keeps the session map bounded during concurrent unique initialization", async () => { @@ -616,7 +616,7 @@ describe("crestodian.chat", () => { evictionStarted.resolve(); await releaseEviction.promise; }); - const sessions = new Map([["oldest", oldest]]); + const sessions = new Map([["oldest", oldest]]); for (let index = 1; index < 8; index += 1) { sessions.set(`existing-${index}`, seededSession({ lastUsedAt: index })); } @@ -645,7 +645,7 @@ describe("crestodian.chat", () => { action: "none", sensitive: true, }); - const sessions = new Map([["s1", seededSession({ engine })]]); + const sessions = new Map([["s1", seededSession({ engine })]]); const call = await callChat(makeContext(sessions), { sessionId: "s1", message: "yes" }); @@ -659,7 +659,7 @@ describe("crestodian.chat", () => { action: "open-tui", handoff: { kind: "open-tui" }, }); - const sessions = new Map([["s1", seededSession({ engine })]]); + const sessions = new Map([["s1", seededSession({ engine })]]); const call = await callChat(makeContext(sessions), { sessionId: "s1", @@ -675,15 +675,15 @@ describe("crestodian.chat", () => { const engine = makeVerifiedEngine(); const handle = vi.spyOn(engine, "handle"); const dispose = vi.spyOn(engine, "dispose").mockResolvedValue(); - const sessions = new Map([["s1", seededSession({ engine })]]); + const sessions = new Map([["s1", seededSession({ engine })]]); // Reset drops the stored session; loading a fresh welcome would hit real // discovery, so stub the overview loader on the replacement engine path by // asserting the old engine is gone instead. const { calls, respond } = makeRespond(); const context = makeContext(sessions); const pending = expectDefined( - crestodianHandlers["crestodian.chat"], - 'crestodianHandlers["crestodian.chat"] test invariant', + systemAgentHandlers["openclaw.chat"], + 'systemAgentHandlers["openclaw.chat"] test invariant', )({ params: { sessionId: "s1", reset: true }, respond, diff --git a/src/gateway/server-methods/crestodian.ts b/src/gateway/server-methods/system-agent.ts similarity index 66% rename from src/gateway/server-methods/crestodian.ts rename to src/gateway/server-methods/system-agent.ts index 31ee22c88ab7..b5a38f3777e5 100644 --- a/src/gateway/server-methods/crestodian.ts +++ b/src/gateway/server-methods/system-agent.ts @@ -1,86 +1,93 @@ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue"; -// Crestodian gateway methods host the setup/repair conversation for clients. +// OpenClaw gateway methods host the setup/repair conversation for clients. import { ErrorCodes, errorShape, - validateCrestodianChatParams, - validateCrestodianSetupActivateParams, - validateCrestodianSetupAuthStartParams, - validateCrestodianSetupDetectParams, - validateCrestodianSetupVerifyParams, + validateSystemAgentChatParams, + validateSystemAgentSetupActivateParams, + validateSystemAgentSetupAuthStartParams, + validateSystemAgentSetupDetectParams, + validateSystemAgentSetupVerifyParams, } from "../../../packages/gateway-protocol/src/index.js"; -import { CrestodianChatEngine } from "../../crestodian/chat-engine.js"; -import { isCrestodianInferenceUnavailableError } from "../../crestodian/inference-error.js"; -import { buildOnboardingWelcome } from "../../crestodian/onboarding-welcome.js"; -import { formatCrestodianStartupMessage } from "../../crestodian/overview.js"; import { enqueueCommandInLane, setCommandLaneConcurrency } from "../../process/command-queue.js"; import { CommandLane } from "../../process/lanes.js"; import { defaultRuntime } from "../../runtime.js"; +import { SystemAgentChatEngine } from "../../system-agent/chat-engine.js"; +import { isSystemAgentInferenceUnavailableError } from "../../system-agent/inference-error.js"; +import { buildOnboardingWelcome } from "../../system-agent/onboarding-welcome.js"; +import { formatSystemAgentStartupMessage } from "../../system-agent/overview.js"; import { WizardSession } from "../../wizard/session.js"; import type { GatewayRequestContext, GatewayRequestHandlers } from "./types.js"; import { assertValidParams } from "./validation.js"; /** - * `crestodian.chat` lets clients (macOS app onboarding, future UIs) run the - * same conversational setup as `openclaw crestodian`. Structured setup owns + * `openclaw.chat` lets clients (macOS app onboarding, future UIs) run the + * same conversational setup as `openclaw setup`. Structured setup owns * the pre-inference phase; a new chat session starts only after a live model * turn succeeds. * - * Sessions are process-local by design — Crestodian state is an in-flight + * Sessions are process-local by design — OpenClaw state is an in-flight * conversation, not persisted data. The map is bounded; the oldest session is * evicted first, and `reset: true` starts a session over explicitly. */ -export type CrestodianChatSession = - GatewayRequestContext["crestodianSessions"] extends Map ? Session : never; +export type SystemAgentChatSession = + GatewayRequestContext["systemAgentSessions"] extends Map ? Session : never; -const MAX_CRESTODIAN_SESSIONS = 8; +const MAX_SYSTEM_AGENT_SESSIONS = 8; const PROVIDER_AUTH_SESSION_TIMEOUT_MS = 25 * 60 * 1000; -const CRESTODIAN_GATEWAY_EXECUTION_KEY = "gateway"; -const crestodianGatewayExecutionQueue = new KeyedAsyncQueue(); -const crestodianSessionQueues = new WeakMap, KeyedAsyncQueue>(); +const SYSTEM_AGENT_GATEWAY_EXECUTION_KEY = "gateway"; +const systemAgentGatewayExecutionQueue = new KeyedAsyncQueue(); +const systemAgentSessionQueues = new WeakMap< + Map, + KeyedAsyncQueue +>(); -function getCrestodianSessionQueue(sessions: Map): KeyedAsyncQueue { - let queue = crestodianSessionQueues.get(sessions); +function getSystemAgentSessionQueue( + sessions: Map, +): KeyedAsyncQueue { + let queue = systemAgentSessionQueues.get(sessions); if (!queue) { queue = new KeyedAsyncQueue(); - crestodianSessionQueues.set(sessions, queue); + systemAgentSessionQueues.set(sessions, queue); } return queue; } -async function runCrestodianGatewayTask(task: () => Promise): Promise { +async function runSystemAgentGatewayTask(task: () => Promise): Promise { // Track every accepted RPC as active, never queued: restart draining snapshots - // active ids, so a queued Crestodian request could otherwise outlive its socket. - setCommandLaneConcurrency(CommandLane.Crestodian, Number.MAX_SAFE_INTEGER); - return await enqueueCommandInLane(CommandLane.Crestodian, () => + // active ids, so a queued OpenClaw request could otherwise outlive its socket. + setCommandLaneConcurrency(CommandLane.SystemAgent, Number.MAX_SAFE_INTEGER); + return await enqueueCommandInLane(CommandLane.SystemAgent, () => // Bound expensive detection, activation, and agent turns without hiding // accepted work from restart draining. This also makes session eviction and - // setup writes atomic with respect to other Crestodian gateway requests. - crestodianGatewayExecutionQueue.enqueue(CRESTODIAN_GATEWAY_EXECUTION_KEY, task), + // setup writes atomic with respect to other OpenClaw gateway requests. + systemAgentGatewayExecutionQueue.enqueue(SYSTEM_AGENT_GATEWAY_EXECUTION_KEY, task), ); } -let crestodianSetupActivationInProgress = false; +let systemAgentSetupActivationInProgress = false; -class CrestodianSetupActivationBusyError extends Error {} +class SystemAgentSetupActivationBusyError extends Error {} /** Admit one setup mutation without queueing work past a caller timeout. */ -export async function runExclusiveCrestodianSetupActivation(task: () => Promise): Promise { - if (crestodianSetupActivationInProgress) { - throw new CrestodianSetupActivationBusyError( - "Crestodian setup is already in progress; try again when it finishes.", +export async function runExclusiveSystemAgentSetupActivation( + task: () => Promise, +): Promise { + if (systemAgentSetupActivationInProgress) { + throw new SystemAgentSetupActivationBusyError( + "OpenClaw setup is already in progress; try again when it finishes.", ); } - crestodianSetupActivationInProgress = true; + systemAgentSetupActivationInProgress = true; try { return await task(); } finally { - crestodianSetupActivationInProgress = false; + systemAgentSetupActivationInProgress = false; } } -async function evictOldestSession(sessions: Map): Promise { - if (sessions.size < MAX_CRESTODIAN_SESSIONS) { +async function evictOldestSession(sessions: Map): Promise { + if (sessions.size < MAX_SYSTEM_AGENT_SESSIONS) { return; } let oldestKey: string | undefined; @@ -97,48 +104,48 @@ async function evictOldestSession(sessions: Map): } } -export const crestodianHandlers: GatewayRequestHandlers = { +export const systemAgentHandlers: GatewayRequestHandlers = { /** Structured onboarding: list reusable AI access on this host. */ - "crestodian.setup.detect": async ({ params, respond }) => { + "openclaw.setup.detect": async ({ params, respond }) => { if ( !assertValidParams( params, - validateCrestodianSetupDetectParams, - "crestodian.setup.detect", + validateSystemAgentSetupDetectParams, + "openclaw.setup.detect", respond, ) ) { return; } - await runCrestodianGatewayTask(async () => { - const { detectSetupInference } = await import("../../crestodian/setup-inference.js"); + await runSystemAgentGatewayTask(async () => { + const { detectSetupInference } = await import("../../system-agent/setup-inference.js"); respond(true, await detectSetupInference(), undefined); }); }, /** Re-run the exact current default-agent inference route without mutating setup. */ - "crestodian.setup.verify": async ({ params, respond }) => { + "openclaw.setup.verify": async ({ params, respond }) => { if ( !assertValidParams( params, - validateCrestodianSetupVerifyParams, - "crestodian.setup.verify", + validateSystemAgentSetupVerifyParams, + "openclaw.setup.verify", respond, ) ) { return; } - await runCrestodianGatewayTask(async () => { - const { verifySetupInference } = await import("../../crestodian/setup-inference.js"); + await runSystemAgentGatewayTask(async () => { + const { verifySetupInference } = await import("../../system-agent/setup-inference.js"); respond(true, await verifySetupInference({ runtime: defaultRuntime }), undefined); }); }, /** Start one provider-owned OAuth/device-code login over the shared wizard transport. */ - "crestodian.setup.auth.start": async ({ params, respond, context }) => { + "openclaw.setup.auth.start": async ({ params, respond, context }) => { if ( !assertValidParams( params, - validateCrestodianSetupAuthStartParams, - "crestodian.setup.auth.start", + validateSystemAgentSetupAuthStartParams, + "openclaw.setup.auth.start", respond, ) ) { @@ -154,9 +161,10 @@ export const crestodianHandlers: GatewayRequestHandlers = { // Match setup.activate's lock order: setup admission before the Gateway // queue. Both stay held for the session, so a relaunched client cannot // start competing setup work while this server-owned flow can commit. - const result = await runExclusiveCrestodianSetupActivation(async () => - runCrestodianGatewayTask(async () => { - const { activateSetupInference } = await import("../../crestodian/setup-inference.js"); + const result = await runExclusiveSystemAgentSetupActivation(async () => + runSystemAgentGatewayTask(async () => { + const { activateSetupInference } = + await import("../../system-agent/setup-inference.js"); return await activateSetupInference({ kind: "provider-auth", authChoice: params.authChoice, @@ -192,21 +200,21 @@ export const crestodianHandlers: GatewayRequestHandlers = { * queueing work that could outlive their RPC timeout. A failed attempt never * commits a broken model, managed plugin install, or setup state. */ - "crestodian.setup.activate": async ({ params, respond }) => { + "openclaw.setup.activate": async ({ params, respond }) => { if ( !assertValidParams( params, - validateCrestodianSetupActivateParams, - "crestodian.setup.activate", + validateSystemAgentSetupActivateParams, + "openclaw.setup.activate", respond, ) ) { return; } try { - await runExclusiveCrestodianSetupActivation(async () => { - await runCrestodianGatewayTask(async () => { - const { activateSetupInference } = await import("../../crestodian/setup-inference.js"); + await runExclusiveSystemAgentSetupActivation(async () => { + await runSystemAgentGatewayTask(async () => { + const { activateSetupInference } = await import("../../system-agent/setup-inference.js"); const runtime = { ...defaultRuntime, // Setup runs inside the gateway process; a failing sub-step must reject @@ -228,7 +236,7 @@ export const crestodianHandlers: GatewayRequestHandlers = { }); }); } catch (error) { - if (!(error instanceof CrestodianSetupActivationBusyError)) { + if (!(error instanceof SystemAgentSetupActivationBusyError)) { throw error; } respond( @@ -238,17 +246,17 @@ export const crestodianHandlers: GatewayRequestHandlers = { ); } }, - "crestodian.chat": async ({ params, respond, context }) => { - if (!assertValidParams(params, validateCrestodianChatParams, "crestodian.chat", respond)) { + "openclaw.chat": async ({ params, respond, context }) => { + if (!assertValidParams(params, validateSystemAgentChatParams, "openclaw.chat", respond)) { return; } - await runCrestodianGatewayTask(async () => { - const sessions = context.crestodianSessions; + await runSystemAgentGatewayTask(async () => { + const sessions = context.systemAgentSessions; const sessionId = params.sessionId; // Initialization, resets, and turns share one per-session queue. Without // it, concurrent first messages can create competing engines and lose // conversation state when the later initializer replaces the first. - await getCrestodianSessionQueue(sessions).enqueue(sessionId, async () => { + await getSystemAgentSessionQueue(sessions).enqueue(sessionId, async () => { if (params.reset) { const existing = sessions.get(sessionId); sessions.delete(sessionId); @@ -256,7 +264,7 @@ export const crestodianHandlers: GatewayRequestHandlers = { } let session = sessions.get(sessionId); if (!session) { - const { verifySetupInference } = await import("../../crestodian/setup-inference.js"); + const { verifySetupInference } = await import("../../system-agent/setup-inference.js"); const inference = await verifySetupInference({ runtime: defaultRuntime, bindSession: true, @@ -267,14 +275,14 @@ export const crestodianHandlers: GatewayRequestHandlers = { undefined, errorShape( ErrorCodes.UNAVAILABLE, - `Crestodian requires working inference: ${inference.error}`, + `OpenClaw requires working inference: ${inference.error}`, ), ); return; } // The gateway surface must never install/restart its own daemon; the // engine's setup path honors this via surface: "gateway". - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "gateway", verifiedInference: inference.binding, }); @@ -283,12 +291,12 @@ export const crestodianHandlers: GatewayRequestHandlers = { if (params.welcomeVariant === "onboarding") { welcome = await buildOnboardingWelcome({ engine }); } else { - welcome = formatCrestodianStartupMessage(await engine.loadOverview()); + welcome = formatSystemAgentStartupMessage(await engine.loadOverview()); engine.noteAssistantMessage(welcome); } } catch (error) { await engine.dispose().catch(() => undefined); - if (!isCrestodianInferenceUnavailableError(error)) { + if (!isSystemAgentInferenceUnavailableError(error)) { throw error; } respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, error.message)); @@ -307,11 +315,11 @@ export const crestodianHandlers: GatewayRequestHandlers = { respond(true, { sessionId, reply: session.welcome, action: "none" }, undefined); return; } - let reply: Awaited>; + let reply: Awaited>; try { reply = await session.engine.handle(params.message); } catch (error) { - if (!isCrestodianInferenceUnavailableError(error)) { + if (!isSystemAgentInferenceUnavailableError(error)) { throw error; } // A failed inference turn invalidates this conversation. Remove the diff --git a/src/gateway/server-request-context.test.ts b/src/gateway/server-request-context.test.ts index 081317bb1984..b280a3c3baa9 100644 --- a/src/gateway/server-request-context.test.ts +++ b/src/gateway/server-request-context.test.ts @@ -71,7 +71,7 @@ function makeContextParams( registerToolEventRecipient: vi.fn(), dedupe: new Map(), wizardSessions: new Map(), - crestodianSessions: new Map(), + systemAgentSessions: new Map(), findRunningWizard: vi.fn(() => null), purgeWizardSession: vi.fn(), getRuntimeSnapshot: vi.fn(() => ({}) as never), diff --git a/src/gateway/server-request-context.ts b/src/gateway/server-request-context.ts index 0eba4eb89c80..2a27c52f1336 100644 --- a/src/gateway/server-request-context.ts +++ b/src/gateway/server-request-context.ts @@ -73,7 +73,7 @@ type GatewayRequestContextParams = { registerToolEventRecipient: GatewayRequestContext["registerToolEventRecipient"]; dedupe: GatewayRequestContext["dedupe"]; wizardSessions: GatewayRequestContext["wizardSessions"]; - crestodianSessions: GatewayRequestContext["crestodianSessions"]; + systemAgentSessions: GatewayRequestContext["systemAgentSessions"]; findRunningWizard: GatewayRequestContext["findRunningWizard"]; purgeWizardSession: GatewayRequestContext["purgeWizardSession"]; getRuntimeSnapshot: GatewayRequestContext["getRuntimeSnapshot"]; @@ -245,7 +245,7 @@ export function createGatewayRequestContext( registerToolEventRecipient: params.registerToolEventRecipient, dedupe: params.dedupe, wizardSessions: params.wizardSessions, - crestodianSessions: params.crestodianSessions, + systemAgentSessions: params.systemAgentSessions, findRunningWizard: params.findRunningWizard, purgeWizardSession: params.purgeWizardSession, getRuntimeSnapshot: params.getRuntimeSnapshot, diff --git a/src/gateway/server.auth.default-token.suite.ts b/src/gateway/server.auth.default-token.suite.ts index d4f39507372b..0a20141f91c5 100644 --- a/src/gateway/server.auth.default-token.suite.ts +++ b/src/gateway/server.auth.default-token.suite.ts @@ -168,7 +168,7 @@ export function registerDefaultAuthTokenSuite(): void { GATEWAY_SERVER_CAPS.CHAT_SEND_ROUTING_CONTRACT, ); expect(payload?.features?.capabilities).toContain( - GATEWAY_SERVER_CAPS.CRESTODIAN_SETUP_MODEL_REF, + GATEWAY_SERVER_CAPS.SYSTEM_AGENT_SETUP_MODEL_REF, ); expect(payload?.snapshot?.configPath).toBe(createConfigIO().configPath); expect(payload?.snapshot?.stateDir).toBe(STATE_DIR); diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index 5eb97217d00f..3c2744f3e8d7 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -1065,7 +1065,7 @@ export async function startGatewayServer( const wizardRunner = opts.wizardRunner ?? runDefaultSetupWizard; const channelWizardRunner = opts.channelWizardRunner ?? runDefaultChannelSetupWizard; const { wizardSessions, findRunningWizard, purgeWizardSession } = createWizardSessionTracker(); - const crestodianSessions: GatewayRequestContext["crestodianSessions"] = new Map(); + const systemAgentSessions: GatewayRequestContext["systemAgentSessions"] = new Map(); const deps = createDefaultDeps(); let runtimeState: GatewayServerLiveState | null = null; @@ -1903,7 +1903,7 @@ export async function startGatewayServer( registerToolEventRecipient: toolEventRecipients.add, dedupe, wizardSessions, - crestodianSessions, + systemAgentSessions, findRunningWizard, purgeWizardSession, getRuntimeSnapshot, diff --git a/src/gateway/server/ws-connection/connect-hello.ts b/src/gateway/server/ws-connection/connect-hello.ts index 92dab42155c6..c98fab271fa6 100644 --- a/src/gateway/server/ws-connection/connect-hello.ts +++ b/src/gateway/server/ws-connection/connect-hello.ts @@ -84,7 +84,7 @@ export async function sendGatewayHello( events, capabilities: [ GATEWAY_SERVER_CAPS.CHAT_SEND_ROUTING_CONTRACT, - GATEWAY_SERVER_CAPS.CRESTODIAN_SETUP_MODEL_REF, + GATEWAY_SERVER_CAPS.SYSTEM_AGENT_SETUP_MODEL_REF, ], }, snapshot, diff --git a/src/mcp/openclaw-tools-serve-config.ts b/src/mcp/openclaw-tools-serve-config.ts index 97a825d1f222..9d2f1a7ffb17 100644 --- a/src/mcp/openclaw-tools-serve-config.ts +++ b/src/mcp/openclaw-tools-serve-config.ts @@ -7,17 +7,19 @@ import fs from "node:fs"; import { createRequire } from "node:module"; import path from "node:path"; -import type { CrestodianToolOptions } from "../agents/tools/crestodian-tool.js"; +import type { SystemAgentToolOptions } from "../agents/tools/system-agent-tool.js"; import { resolveOpenClawPackageRootSync } from "../infra/openclaw-root.js"; import type { BundleMcpConfig } from "../plugins/bundle-mcp.js"; export const OPENCLAW_TOOLS_MCP_TOOLS_ENV = "OPENCLAW_TOOLS_MCP_TOOLS"; -export const OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV = "OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE"; -export const OPENCLAW_TOOLS_MCP_CRESTODIAN_APPROVAL_ARMED_ENV = - "OPENCLAW_TOOLS_MCP_CRESTODIAN_APPROVAL_ARMED"; -export const OPENCLAW_TOOLS_MCP_CRESTODIAN_PROPOSAL_ENV = "OPENCLAW_TOOLS_MCP_CRESTODIAN_PROPOSAL"; +export const OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV = + "OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE"; +export const OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV = + "OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED"; +export const OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV = + "OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL"; -const OPENCLAW_TOOLS_MCP_TOOL_IDS = ["cron", "crestodian"] as const; +const OPENCLAW_TOOLS_MCP_TOOL_IDS = ["cron", "openclaw"] as const; export type OpenClawToolsMcpToolId = (typeof OPENCLAW_TOOLS_MCP_TOOL_IDS)[number]; function isOpenClawToolsMcpToolId(value: string): value is OpenClawToolsMcpToolId { @@ -45,33 +47,33 @@ export function resolveOpenClawToolsMcpToolSelection( return selection; } -/** Parse the Crestodian surface for served crestodian tools; defaults to cli. */ -export function resolveOpenClawToolsMcpCrestodianSurface( +/** Parse the OpenClaw surface for served openclaw tools; defaults to cli. */ +export function resolveOpenClawToolsMcpSystemAgentSurface( env: NodeJS.ProcessEnv = process.env, -): CrestodianToolOptions["surface"] { - const raw = env[OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV]?.trim(); +): SystemAgentToolOptions["surface"] { + const raw = env[OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV]?.trim(); if (!raw || raw === "cli") { return "cli"; } if (raw === "gateway") { return "gateway"; } - throw new Error(`${OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV} must be "cli" or "gateway"`); + throw new Error(`${OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV} must be "cli" or "gateway"`); } /** - * Reconstruct per-turn approval state for the served crestodian tool. The + * Reconstruct per-turn approval state for the served openclaw tool. The * stdio server runs out of process, so the host passes the armed bit and the * pending proposal hash through env; the host mirrors transitions back from - * tool events (see mirrorCrestodianProposalFromToolEvents in agent-turn.ts). + * tool events (see mirrorSystemAgentProposalFromToolEvents in agent-turn.ts). */ -export function resolveOpenClawToolsMcpCrestodianApproval(env: NodeJS.ProcessEnv = process.env): { +export function resolveOpenClawToolsMcpSystemAgentApproval(env: NodeJS.ProcessEnv = process.env): { approvalArmed: boolean; proposalRef: { current?: string }; } { - const pendingProposal = env[OPENCLAW_TOOLS_MCP_CRESTODIAN_PROPOSAL_ENV]?.trim(); + const pendingProposal = env[OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV]?.trim(); return { - approvalArmed: env[OPENCLAW_TOOLS_MCP_CRESTODIAN_APPROVAL_ARMED_ENV]?.trim() === "1", + approvalArmed: env[OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV]?.trim() === "1", proposalRef: pendingProposal ? { current: pendingProposal } : {}, }; } @@ -112,13 +114,13 @@ function resolveOpenClawToolsServeCommand(): { command: string; args: string[] } } /** - * Crestodian CLI-harness runs get exactly one MCP server: this stdio entry - * serving the ring-zero crestodian tool. The server keeps the "openclaw" name + * OpenClaw CLI-harness runs get exactly one MCP server: this stdio entry + * serving the ring-zero openclaw tool. The server keeps the "openclaw" name * so backend tool pre-approvals (e.g. Claude's --allowedTools mcp__openclaw__*) * apply without per-backend argument surgery. */ -export function buildCrestodianToolsMcpServerConfig( - options: CrestodianToolOptions, +export function buildSystemAgentToolsMcpServerConfig( + options: SystemAgentToolOptions, ): BundleMcpConfig { const entry = resolveOpenClawToolsServeCommand(); const pendingProposal = options.proposalRef?.current; @@ -128,15 +130,15 @@ export function buildCrestodianToolsMcpServerConfig( command: entry.command, args: entry.args, env: { - [OPENCLAW_TOOLS_MCP_TOOLS_ENV]: "crestodian" satisfies OpenClawToolsMcpToolId, - [OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV]: options.surface, + [OPENCLAW_TOOLS_MCP_TOOLS_ENV]: "openclaw" satisfies OpenClawToolsMcpToolId, + [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV]: options.surface, // Per-turn approval state travels with the per-run MCP config; the // host mirrors proposal transitions back from tool events. ...(options.approvalArmed === true - ? { [OPENCLAW_TOOLS_MCP_CRESTODIAN_APPROVAL_ARMED_ENV]: "1" } + ? { [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV]: "1" } : {}), ...(pendingProposal - ? { [OPENCLAW_TOOLS_MCP_CRESTODIAN_PROPOSAL_ENV]: pendingProposal } + ? { [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV]: pendingProposal } : {}), }, }, diff --git a/src/mcp/openclaw-tools-serve.test.ts b/src/mcp/openclaw-tools-serve.test.ts index 32000478c26d..1f45c0018445 100644 --- a/src/mcp/openclaw-tools-serve.test.ts +++ b/src/mcp/openclaw-tools-serve.test.ts @@ -1,13 +1,13 @@ // OpenClaw MCP tools tests cover core tool server startup and registration. import { afterEach, describe, expect, it, vi } from "vitest"; -import { hashCrestodianOperation } from "../agents/tools/crestodian-tool.js"; +import { hashSystemAgentOperation } from "../agents/tools/system-agent-tool.js"; import { - buildCrestodianToolsMcpServerConfig, - OPENCLAW_TOOLS_MCP_CRESTODIAN_APPROVAL_ARMED_ENV, - OPENCLAW_TOOLS_MCP_CRESTODIAN_PROPOSAL_ENV, - OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV, + buildSystemAgentToolsMcpServerConfig, + OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV, + OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV, + OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV, OPENCLAW_TOOLS_MCP_TOOLS_ENV, - resolveOpenClawToolsMcpCrestodianSurface, + resolveOpenClawToolsMcpSystemAgentSurface, resolveOpenClawToolsMcpToolSelection, } from "./openclaw-tools-serve-config.js"; import { @@ -45,25 +45,25 @@ describe("OpenClaw tools MCP server", () => { ).toBe("agent:worker:main"); }); - it("serves the ring-zero crestodian tool without an agent session key", async () => { + it("serves the ring-zero openclaw tool without an agent session key", async () => { const handlers = createPluginToolsMcpHandlers( - resolveOpenClawToolsForMcp({ tools: ["crestodian"], crestodianSurface: "cli" }), + resolveOpenClawToolsForMcp({ tools: ["openclaw"], systemAgentSurface: "cli" }), ); const listed = await handlers.listTools(); - expect(listed.tools.map((tool) => tool.name)).toEqual(["crestodian"]); + expect(listed.tools.map((tool) => tool.name)).toEqual(["openclaw"]); }); it("returns approved CLI MCP mutations to the host instead of applying them", async () => { const operation = { kind: "config-set", path: "gateway.port", value: "19001" } as const; - vi.stubEnv(OPENCLAW_TOOLS_MCP_CRESTODIAN_APPROVAL_ARMED_ENV, "1"); - vi.stubEnv(OPENCLAW_TOOLS_MCP_CRESTODIAN_PROPOSAL_ENV, hashCrestodianOperation(operation)); + vi.stubEnv(OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV, "1"); + vi.stubEnv(OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV, hashSystemAgentOperation(operation)); const handlers = createPluginToolsMcpHandlers( - resolveOpenClawToolsForMcp({ tools: ["crestodian"], crestodianSurface: "cli" }), + resolveOpenClawToolsForMcp({ tools: ["openclaw"], systemAgentSurface: "cli" }), ); const result = await handlers.callTool({ - name: "crestodian", + name: "openclaw", arguments: { action: "config_set", path: "gateway.port", @@ -79,30 +79,30 @@ describe("OpenClaw tools MCP server", () => { expect(resolveOpenClawToolsMcpToolSelection({})).toEqual(["cron"]); expect( resolveOpenClawToolsMcpToolSelection({ - [OPENCLAW_TOOLS_MCP_TOOLS_ENV]: " crestodian , cron ", + [OPENCLAW_TOOLS_MCP_TOOLS_ENV]: " openclaw , cron ", }), - ).toEqual(["crestodian", "cron"]); + ).toEqual(["openclaw", "cron"]); expect(() => resolveOpenClawToolsMcpToolSelection({ [OPENCLAW_TOOLS_MCP_TOOLS_ENV]: "exec" }), ).toThrow(OPENCLAW_TOOLS_MCP_TOOLS_ENV); }); - it("parses the crestodian surface from env and defaults to cli", () => { - expect(resolveOpenClawToolsMcpCrestodianSurface({})).toBe("cli"); + it("parses the openclaw surface from env and defaults to cli", () => { + expect(resolveOpenClawToolsMcpSystemAgentSurface({})).toBe("cli"); expect( - resolveOpenClawToolsMcpCrestodianSurface({ - [OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV]: "gateway", + resolveOpenClawToolsMcpSystemAgentSurface({ + [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV]: "gateway", }), ).toBe("gateway"); expect(() => - resolveOpenClawToolsMcpCrestodianSurface({ - [OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV]: "remote", + resolveOpenClawToolsMcpSystemAgentSurface({ + [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV]: "remote", }), - ).toThrow(OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV); + ).toThrow(OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV); }); - it("builds a crestodian-only stdio server config under the openclaw name", () => { - const config = buildCrestodianToolsMcpServerConfig({ surface: "gateway" }); + it("builds a openclaw-only stdio server config under the openclaw name", () => { + const config = buildSystemAgentToolsMcpServerConfig({ surface: "gateway" }); expect(Object.keys(config.mcpServers)).toEqual(["openclaw"]); const server = config.mcpServers.openclaw as { @@ -113,8 +113,8 @@ describe("OpenClaw tools MCP server", () => { expect(server.command).toBe(process.execPath); expect(server.args?.at(-1)).toMatch(/openclaw-tools-serve\.(js|ts)$/); expect(server.env).toEqual({ - [OPENCLAW_TOOLS_MCP_TOOLS_ENV]: "crestodian", - [OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV]: "gateway", + [OPENCLAW_TOOLS_MCP_TOOLS_ENV]: "openclaw", + [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV]: "gateway", }); }); }); diff --git a/src/mcp/openclaw-tools-serve.ts b/src/mcp/openclaw-tools-serve.ts index ad72a43e5ebf..d8179722c5bf 100644 --- a/src/mcp/openclaw-tools-serve.ts +++ b/src/mcp/openclaw-tools-serve.ts @@ -7,20 +7,20 @@ import { pathToFileURL } from "node:url"; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import type { AnyAgentTool } from "../agents/tools/common.js"; -import { createCrestodianTool } from "../agents/tools/crestodian-tool.js"; -import type { CrestodianToolOptions } from "../agents/tools/crestodian-tool.js"; import { createCronTool } from "../agents/tools/cron-tool.js"; +import { createSystemAgentTool } from "../agents/tools/system-agent-tool.js"; +import type { SystemAgentToolOptions } from "../agents/tools/system-agent-tool.js"; import { formatErrorMessage } from "../infra/errors.js"; import { - resolveOpenClawToolsMcpCrestodianApproval, - resolveOpenClawToolsMcpCrestodianSurface, + resolveOpenClawToolsMcpSystemAgentApproval, + resolveOpenClawToolsMcpSystemAgentSurface, resolveOpenClawToolsMcpToolSelection, type OpenClawToolsMcpToolId, } from "./openclaw-tools-serve-config.js"; import { connectToolsMcpServerToStdio, createToolsMcpServer } from "./tools-stdio-server.js"; export { - OPENCLAW_TOOLS_MCP_CRESTODIAN_SURFACE_ENV, + OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV, OPENCLAW_TOOLS_MCP_TOOLS_ENV, } from "./openclaw-tools-serve-config.js"; @@ -36,15 +36,15 @@ export function resolveOpenClawToolsForMcp( params: { agentSessionKey?: string; tools?: OpenClawToolsMcpToolId[]; - crestodianSurface?: CrestodianToolOptions["surface"]; + systemAgentSurface?: SystemAgentToolOptions["surface"]; } = {}, ): AnyAgentTool[] { const selection = params.tools ?? resolveOpenClawToolsMcpToolSelection(); return selection.map((tool) => { - if (tool === "crestodian") { - return createCrestodianTool({ - surface: params.crestodianSurface ?? resolveOpenClawToolsMcpCrestodianSurface(), - ...resolveOpenClawToolsMcpCrestodianApproval(), + if (tool === "openclaw") { + return createSystemAgentTool({ + surface: params.systemAgentSurface ?? resolveOpenClawToolsMcpSystemAgentSurface(), + ...resolveOpenClawToolsMcpSystemAgentApproval(), }); } const agentSessionKey = ( diff --git a/src/plugins/plugin-registry-snapshot.ts b/src/plugins/plugin-registry-snapshot.ts index 8bc92f2e80d7..a311f158af06 100644 --- a/src/plugins/plugin-registry-snapshot.ts +++ b/src/plugins/plugin-registry-snapshot.ts @@ -404,7 +404,7 @@ function isAllowedPersistedBundledPluginRoot( } if (sourcePluginOptsOutOfBundledDist(path.join(legacyRoot, relativePluginRoot))) { // Older index records lack packageBuild. Re-derive once so runtime loading - // and Crestodian fingerprint the same source-only artifact. + // and OpenClaw fingerprint the same source-only artifact. return false; } // Discovery prefers a built plugin whenever the same child exists in the diff --git a/src/process/lanes.ts b/src/process/lanes.ts index 730cfb5041b8..8ff40807c3de 100644 --- a/src/process/lanes.ts +++ b/src/process/lanes.ts @@ -1,7 +1,7 @@ /** Named queue lanes for work that must not interleave with the main command stream. */ export const enum CommandLane { Main = "main", - Crestodian = "crestodian", + SystemAgent = "system-agent", Cron = "cron", CronNested = "cron-nested", SkillWorkshopReview = "skill-workshop-review", diff --git a/src/system-agent/agent-id.ts b/src/system-agent/agent-id.ts new file mode 100644 index 000000000000..e6f26be80ea5 --- /dev/null +++ b/src/system-agent/agent-id.ts @@ -0,0 +1,12 @@ +import { normalizeAgentId } from "../routing/session-key.js"; + +export const SYSTEM_AGENT_ID = "openclaw"; + +const RESERVED_SYSTEM_AGENT_IDS = new Set([ + normalizeAgentId(SYSTEM_AGENT_ID), + normalizeAgentId("crestodian"), // reserved retired id +]); + +export function isReservedSystemAgentId(agentId: string): boolean { + return RESERVED_SYSTEM_AGENT_IDS.has(normalizeAgentId(agentId)); +} diff --git a/src/crestodian/agent-turn.test.ts b/src/system-agent/agent-turn.test.ts similarity index 87% rename from src/crestodian/agent-turn.test.ts rename to src/system-agent/agent-turn.test.ts index bc508c9bdc9e..8600fbcdb643 100644 --- a/src/crestodian/agent-turn.test.ts +++ b/src/system-agent/agent-turn.test.ts @@ -6,16 +6,16 @@ import { testing as cliBackendsTesting } from "../agents/cli-backends.js"; import { fingerprintResolvedProviderAuth } from "../agents/execution-auth-binding.js"; import type { CliBackendConfig, OpenClawConfig } from "../config/types.js"; import { - cleanupCrestodianAgentSession, - createCrestodianAgentSession, - runCrestodianAgentTurnWithDeps, - type CrestodianAgentSession, - type CrestodianAgentTurnDeps, + cleanupSystemAgentSession, + createSystemAgentSession, + runSystemAgentTurnWithDeps, + type SystemAgentSession, + type SystemAgentTurnDeps, } from "./agent-turn.js"; -import { createCrestodianVerifiedInferenceTestFixture } from "./crestodian.test-helpers.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; -import { resolveCrestodianConfiguredRouteFromConfig } from "./inference-route.js"; -import { createCrestodianVerifiedInferenceBinding } from "./verified-inference.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; +import { resolveSystemAgentConfiguredRouteFromConfig } from "./inference-route.js"; +import { createSystemAgentVerifiedInferenceTestFixture } from "./system-agent.test-helpers.js"; +import { createSystemAgentVerifiedInferenceBinding } from "./verified-inference.js"; vi.mock("../plugins/providers.js", async (importOriginal) => ({ ...(await importOriginal()), @@ -30,10 +30,8 @@ vi.mock("../agents/harness/runtime-plugin.js", async (importOriginal) => ({ ), })); -type RunCliAgentParams = Parameters>[0]; -type RunEmbeddedAgentParams = Parameters< - NonNullable ->[0]; +type RunCliAgentParams = Parameters>[0]; +type RunEmbeddedAgentParams = Parameters>[0]; const mocks = vi.hoisted(() => ({ runEmbeddedAgent: vi.fn(async (_params: RunEmbeddedAgentParams) => ({ @@ -62,7 +60,7 @@ vi.mock("../config/config.js", async (importOriginal) => ({ const tempDirs: string[] = []; function useTempStateDir(): string { - const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "crestodian-turn-")); + const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-turn-")); tempDirs.push(stateDir); vi.stubEnv("OPENCLAW_STATE_DIR", stateDir); return stateDir; @@ -89,10 +87,10 @@ function requireValue(value: T | undefined, message: string): T { } async function createVerifiedSession(config: OpenClawConfig) { - const fixture = await createCrestodianVerifiedInferenceTestFixture(config); + const fixture = await createSystemAgentVerifiedInferenceTestFixture(config); return { ...fixture, - session: createCrestodianAgentSession(fixture.binding), + session: createSystemAgentSession(fixture.binding), }; } @@ -158,7 +156,7 @@ afterEach(() => { } }); -describe("runCrestodianAgentTurn", () => { +describe("runSystemAgentTurn", () => { it("keeps every turn on the verified profile and clears continuity on route drift", async () => { useTempStateDir(); const verifiedConfig = { @@ -174,7 +172,7 @@ describe("runCrestodianAgentTurn", () => { profiles: { "openai:p2": { provider: "openai", mode: "api_key" } }, }, } satisfies OpenClawConfig; - const configuredRoute = await resolveCrestodianConfiguredRouteFromConfig(verifiedConfig); + const configuredRoute = await resolveSystemAgentConfiguredRouteFromConfig(verifiedConfig); if (!configuredRoute) { throw new Error("missing test route"); } @@ -198,7 +196,7 @@ describe("runCrestodianAgentTurn", () => { if (!authFingerprint) { throw new Error("missing test auth fingerprint"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute, executionRoute, auth: { @@ -208,13 +206,13 @@ describe("runCrestodianAgentTurn", () => { }, deps: authDeps, }); - const session = createCrestodianAgentSession(binding); + const session = createSystemAgentSession(binding); let currentConfig: OpenClawConfig = verifiedConfig; const runEmbeddedAgent = vi.fn(async () => ({ meta: { finalAssistantVisibleText: "ready" }, })); const turn = async () => - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "continue setup", overview: { defaultModel: "openai/gpt-5.5" } as never, @@ -241,7 +239,7 @@ describe("runCrestodianAgentTurn", () => { currentConfig = { agents: { defaults: { model: "anthropic/claude-opus-4-8" } }, }; - await expect(turn()).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + await expect(turn()).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(runEmbeddedAgent).toHaveBeenCalledOnce(); expect(session.verifiedInference).toBe(binding); expect(session.cliSession).toBeUndefined(); @@ -253,15 +251,15 @@ describe("runCrestodianAgentTurn", () => { agents: { defaults: { model: { primary: "openai/gpt-5.5" } } }, } satisfies OpenClawConfig; const overview = { defaultModel: "openai/gpt-5.5" } as never; - const fixture = await createCrestodianVerifiedInferenceTestFixture(config); - const first = createCrestodianAgentSession(fixture.binding); - const second = createCrestodianAgentSession(fixture.binding); + const fixture = await createSystemAgentVerifiedInferenceTestFixture(config); + const first = createSystemAgentSession(fixture.binding); + const second = createSystemAgentSession(fixture.binding); const deps = { ...fixture.deps, readConfigFileSnapshot: vi.fn(async () => configSnapshot(config)) as never, }; - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview, @@ -271,7 +269,7 @@ describe("runCrestodianAgentTurn", () => { }, deps, ); - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview, @@ -295,11 +293,11 @@ describe("runCrestodianAgentTurn", () => { expect(firstPath).not.toBe(secondPath); await fs.promises.writeFile(firstPath, "transcript"); - await cleanupCrestodianAgentSession(first); + await cleanupSystemAgentSession(first); await expect(fs.promises.access(firstPath)).rejects.toThrow(); }); - it("uses the default agent CLI route while keeping Crestodian session identity", async () => { + it("uses the default agent CLI route while keeping OpenClaw session identity", async () => { const stateDir = useTempStateDir(); const agentDir = path.join(stateDir, "ops-agent"); const config = { @@ -326,7 +324,7 @@ describe("runCrestodianAgentTurn", () => { })); const { session, deps } = await createVerifiedSession(config); - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "claude-cli/claude-opus-4-8" } as never, @@ -350,27 +348,27 @@ describe("runCrestodianAgentTurn", () => { model: "claude-opus-4-8", agentDir, authProfileId: "claude-cli:ops", - agentId: "crestodian", - sessionKey: "agent:crestodian:main", + agentId: "openclaw", + sessionKey: "agent:openclaw:main", sessionId: session.sessionId, - workspaceDir: path.join(stateDir, "crestodian", "workspace"), - sessionFile: path.join(stateDir, "crestodian", "sessions", `${session.sessionId}.jsonl`), - messageChannel: "crestodian", - messageProvider: "crestodian", + workspaceDir: path.join(stateDir, "openclaw", "workspace"), + sessionFile: path.join(stateDir, "openclaw", "sessions", `${session.sessionId}.jsonl`), + messageChannel: "openclaw", + messageProvider: "openclaw", }); expect(call.disableCliLiveSession).toBe(true); expect(call.cleanupCliLiveSessionOnRunEnd).toBe(true); expect(call.cliToolAvailability).toEqual({ native: [], - mcp: ["mcp__openclaw__crestodian"], + mcp: ["mcp__openclaw__openclaw"], }); expect(call.toolsAllow).toBeUndefined(); - expect(requireValue(call.crestodianTool, "missing CLI Crestodian tool").proposalRef).toBe( + expect(requireValue(call.systemAgentTool, "missing CLI OpenClaw tool").proposalRef).toBe( session.proposalRef, ); }); - it("rejects an always-on CLI backend before launching Crestodian", async () => { + it("rejects an always-on CLI backend before launching OpenClaw", async () => { useTempStateDir(); const config = { agents: { @@ -386,7 +384,7 @@ describe("runCrestodianAgentTurn", () => { let failure: unknown; try { - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "set up my workspace", overview: { defaultModel: "google-gemini-cli/gemini-3.1-pro-preview" } as never, @@ -405,11 +403,11 @@ describe("runCrestodianAgentTurn", () => { failure = error; } - expect(failure).toBeInstanceOf(CrestodianInferenceUnavailableError); - expect((failure as CrestodianInferenceUnavailableError).failures).toEqual([ + expect(failure).toBeInstanceOf(SystemAgentInferenceUnavailableError); + expect((failure as SystemAgentInferenceUnavailableError).failures).toEqual([ expect.objectContaining({ message: expect.stringContaining( - "CLI backend google-gemini-cli cannot enforce Crestodian's exact tool availability", + "CLI backend google-gemini-cli cannot enforce OpenClaw's exact tool availability", ), }), ]); @@ -438,7 +436,7 @@ describe("runCrestodianAgentTurn", () => { })); const { session, deps } = await createVerifiedSession(config); const turn = async (input: string) => - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input, overview: { defaultModel: "claude-cli/claude-opus-4-8" } as never, @@ -468,10 +466,10 @@ describe("runCrestodianAgentTurn", () => { disableCliLiveSession: true, cleanupCliLiveSessionOnRunEnd: true, }); - const transcript = path.join(stateDir, "crestodian", "sessions", `${session.sessionId}.jsonl`); + const transcript = path.join(stateDir, "openclaw", "sessions", `${session.sessionId}.jsonl`); await fs.promises.writeFile(transcript, "transcript"); - await cleanupCrestodianAgentSession(session); + await cleanupSystemAgentSession(session); expect(session.cliSession).toBeUndefined(); await expect(fs.promises.access(transcript)).rejects.toThrow(); @@ -501,7 +499,7 @@ describe("runCrestodianAgentTurn", () => { })); const { session, deps } = await createVerifiedSession(config); - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "anthropic/claude-opus-4-8" } as never, @@ -551,7 +549,7 @@ describe("runCrestodianAgentTurn", () => { authEpoch: "auth-epoch", authEpochVersion: 1, cwdHash: "cwd-hash", - mcpResumeHash: "crestodian-mcp-resume", + mcpResumeHash: "openclaw-mcp-resume", }; const runCliAgent = vi.fn(async (_params: RunCliAgentParams) => ({ payloads: [{ text: "ready" }], @@ -560,7 +558,7 @@ describe("runCrestodianAgentTurn", () => { const { session, deps } = await createVerifiedSession(config); const readConfigFileSnapshot = vi.fn(async () => configSnapshot(config)) as never; - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "set the default model", overview: { defaultModel: "claude-cli/claude-opus-4-8" } as never, @@ -572,7 +570,7 @@ describe("runCrestodianAgentTurn", () => { ); // Mirrors the denied tool result that arms the exact-operation hash. session.proposalRef.current = "proposal-sha256"; - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "yes", overview: { defaultModel: "claude-cli/claude-opus-4-8" } as never, @@ -591,7 +589,7 @@ describe("runCrestodianAgentTurn", () => { cliSessionBinding: binding, disableCliLiveSession: true, cleanupCliLiveSessionOnRunEnd: true, - crestodianTool: { + systemAgentTool: { approvalArmed: true, proposalRef: { current: "proposal-sha256" }, }, @@ -621,7 +619,7 @@ describe("runCrestodianAgentTurn", () => { .mockResolvedValueOnce(configSnapshot(configForProfile("claude-cli:other"))); const { session, deps } = await createVerifiedSession(configForProfile("claude-cli:ops")); const turn = async () => - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "claude-cli/claude-opus-4-8" } as never, @@ -637,7 +635,7 @@ describe("runCrestodianAgentTurn", () => { ); await turn(); - await expect(turn()).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + await expect(turn()).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(runCliAgent).toHaveBeenCalledOnce(); expect(session.cliSession).toBeUndefined(); @@ -675,7 +673,7 @@ describe("runCrestodianAgentTurn", () => { .mockResolvedValueOnce(configSnapshot(configForBackend(second))); const { session, deps } = await createVerifiedSession(configForBackend(first)); const turn = async () => - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "claude-cli/current" } as never, @@ -691,7 +689,7 @@ describe("runCrestodianAgentTurn", () => { ); await turn(); - await expect(turn()).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + await expect(turn()).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(runCliAgent).toHaveBeenCalledOnce(); const firstCall = requireValue(runCliAgent.mock.calls[0]?.[0], "missing first CLI call"); @@ -731,7 +729,7 @@ describe("runCrestodianAgentTurn", () => { .mockResolvedValueOnce(configSnapshot(configForModel("stable"))); const { session, deps } = await createVerifiedSession(configForModel("current")); const turn = async () => - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "claude-cli/claude-opus-4-8" } as never, @@ -747,7 +745,7 @@ describe("runCrestodianAgentTurn", () => { ); await turn(); - await expect(turn()).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + await expect(turn()).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(runCliAgent).toHaveBeenCalledOnce(); expect(session.cliSession).toBeUndefined(); @@ -767,7 +765,7 @@ describe("runCrestodianAgentTurn", () => { { id: "ops", default: true, - // Keep the model owner's policy stable. Crestodian executes with + // Keep the model owner's policy stable. OpenClaw executes with // its own identity and therefore follows the changing global policy. tools: { exec: { security: "allowlist", ask: "on-miss" } }, }, @@ -790,7 +788,7 @@ describe("runCrestodianAgentTurn", () => { .mockResolvedValueOnce(configSnapshot(configForGlobalPolicy("deny", "always"))); const { session, deps } = await createVerifiedSession(configForGlobalPolicy("full", "off")); const turn = async () => - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "claude-cli/claude-opus-4-8" } as never, @@ -806,7 +804,7 @@ describe("runCrestodianAgentTurn", () => { ); await turn(); - await expect(turn()).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + await expect(turn()).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(runCliAgent).toHaveBeenCalledOnce(); expect(session.cliSession).toBeUndefined(); @@ -856,7 +854,7 @@ describe("runCrestodianAgentTurn", () => { .mockResolvedValueOnce(configSnapshot(embeddedConfig)); const { session, deps } = await createVerifiedSession(cliConfig); const turn = async (input: string) => - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input, overview: { defaultModel: "configured" } as never, @@ -874,7 +872,9 @@ describe("runCrestodianAgentTurn", () => { await turn("first CLI turn"); expect(session.cliSession?.binding.sessionId).toBe(binding.sessionId); - await expect(turn("embedded turn")).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + await expect(turn("embedded turn")).rejects.toBeInstanceOf( + SystemAgentInferenceUnavailableError, + ); expect(session.cliSession).toBeUndefined(); expect(runCliAgent).toHaveBeenCalledOnce(); @@ -905,7 +905,7 @@ describe("runCrestodianAgentTurn", () => { }, }, { - id: "crestodian", + id: "openclaw", params: { temperature: 1.7 }, tools: { allow: ["exec"] }, }, @@ -918,7 +918,7 @@ describe("runCrestodianAgentTurn", () => { })); const { session, deps } = await createVerifiedSession(config); - await runCrestodianAgentTurnWithDeps( + await runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "openai/gpt-5.4" } as never, @@ -944,23 +944,23 @@ describe("runCrestodianAgentTurn", () => { authProfileId: "openai:ops", authProfileIdSource: "user", agentHarnessRuntimeOverride: "codex", - agentId: "crestodian", - sessionKey: "agent:crestodian:main", + agentId: "openclaw", + sessionKey: "agent:openclaw:main", sessionId: session.sessionId, - workspaceDir: path.join(stateDir, "crestodian", "workspace"), - sessionFile: path.join(stateDir, "crestodian", "sessions", `${session.sessionId}.jsonl`), - messageChannel: "crestodian", - messageProvider: "crestodian", - toolsAllow: ["crestodian"], + workspaceDir: path.join(stateDir, "openclaw", "workspace"), + sessionFile: path.join(stateDir, "openclaw", "sessions", `${session.sessionId}.jsonl`), + messageChannel: "openclaw", + messageProvider: "openclaw", + toolsAllow: ["openclaw"], disableMessageTool: true, }); expect(call.agentHarnessId).toBeUndefined(); - expect(call.config?.agents?.list?.find((agent) => agent.id === "crestodian")).toEqual({ - id: "crestodian", + expect(call.config?.agents?.list?.find((agent) => agent.id === "openclaw")).toEqual({ + id: "openclaw", params: { temperature: 0.2 }, tools: { allow: ["read"], deny: ["exec"] }, }); - expect(requireValue(call.crestodianTool, "missing embedded Crestodian tool").proposalRef).toBe( + expect(requireValue(call.systemAgentTool, "missing embedded OpenClaw tool").proposalRef).toBe( session.proposalRef, ); }); @@ -973,12 +973,12 @@ describe("runCrestodianAgentTurn", () => { configSnapshot({ agents: { defaults: { model: "openai/gpt-5.5" } } }), ); const unverifiedSession = { - sessionId: "crestodian-unverified", + sessionId: "openclaw-unverified", proposalRef: {}, - } as unknown as CrestodianAgentSession; + } as unknown as SystemAgentSession; await expect( - runCrestodianAgentTurnWithDeps( + runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "openai/stale-overview-model" } as never, @@ -992,7 +992,7 @@ describe("runCrestodianAgentTurn", () => { readConfigFileSnapshot: readConfigFileSnapshot as never, }, ), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(readConfigFileSnapshot).not.toHaveBeenCalled(); expect(runCliAgent).not.toHaveBeenCalled(); expect(runEmbeddedAgent).not.toHaveBeenCalled(); @@ -1011,7 +1011,7 @@ describe("runCrestodianAgentTurn", () => { }; await expect( - runCrestodianAgentTurnWithDeps( + runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "openai/gpt-5.5" } as never, @@ -1026,7 +1026,7 @@ describe("runCrestodianAgentTurn", () => { }) as never, }, ), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(session.proposalRef.current).toBeUndefined(); expect(session.cliSession).toBeUndefined(); }); @@ -1055,7 +1055,7 @@ describe("runCrestodianAgentTurn", () => { }; await expect( - runCrestodianAgentTurnWithDeps( + runSystemAgentTurnWithDeps( { input: "hello", overview: { defaultModel: "openai/gpt-5.5" } as never, @@ -1069,7 +1069,7 @@ describe("runCrestodianAgentTurn", () => { readConfigFileSnapshot: vi.fn(async () => configSnapshot(config)) as never, }, ), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(session.proposalRef.current).toBeUndefined(); expect(session.cliSession).toBeUndefined(); }); diff --git a/src/crestodian/agent-turn.ts b/src/system-agent/agent-turn.ts similarity index 62% rename from src/crestodian/agent-turn.ts rename to src/system-agent/agent-turn.ts index 59b2e4d4a89c..184758ffb8cc 100644 --- a/src/crestodian/agent-turn.ts +++ b/src/system-agent/agent-turn.ts @@ -1,4 +1,4 @@ -// Crestodian agent turns run the real embedded agent loop with the ring-zero tool. +// OpenClaw agent turns run the real embedded agent loop with the ring-zero tool. import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; @@ -7,55 +7,54 @@ import { normalizeCliModel } from "../agents/cli-runner/helpers.js"; import { resolveStateDir } from "../config/paths.js"; import type { CliSessionBinding } from "../config/sessions.js"; import { buildAgentMainSessionKey } from "../routing/session-key.js"; -import { CRESTODIAN_AGENT_SYSTEM_PROMPT } from "./assistant-prompts.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; -import type { CrestodianConfiguredRoute } from "./inference-route.js"; -import type { CrestodianOverview } from "./overview.js"; +import { SYSTEM_AGENT_ID } from "./agent-id.js"; +import { SYSTEM_AGENT_SYSTEM_PROMPT } from "./assistant-prompts.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; +import type { SystemAgentConfiguredRoute } from "./inference-route.js"; +import type { SystemAgentOverview } from "./overview.js"; import { - resolveCrestodianExpectedAgentHarnessRuntimeArtifact, - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceDeps, + resolveSystemAgentExpectedAgentHarnessRuntimeArtifact, + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceDeps, } from "./verified-inference.js"; /** - * Crestodian is a real agent: same loop, session transcript, and tool pipeline - * as regular agents — restricted to the single ring-zero `crestodian` tool. + * OpenClaw is a real agent: same loop, session transcript, and tool pipeline + * as regular agents — restricted to the single ring-zero `openclaw` tool. * Embedded runtimes enforce that restriction with toolsAllow. CLI harnesses * must explicitly support per-run native-tool selection, then receive the tool * over a dedicated stdio MCP server that replaces the normal bundle surface. * Turns share one persistent session so the conversation has genuine * multi-turn memory. Inference setup must succeed before this runner is entered. */ -const CRESTODIAN_AGENT_ID = "crestodian"; - const AGENT_TURN_TIMEOUT_MS = 120_000; -const CRESTODIAN_MCP_TOOL_NAME = "mcp__openclaw__crestodian"; +const SYSTEM_AGENT_MCP_TOOL_NAME = "mcp__openclaw__openclaw"; -export type CrestodianAgentTurnDirective = - import("../agents/tools/crestodian-tool.js").CrestodianToolDirective; +export type SystemAgentTurnDirective = + import("../agents/tools/system-agent-tool.js").SystemAgentToolDirective; -type CrestodianAgentTurnReply = { +type SystemAgentTurnReply = { text: string; modelLabel?: string; /** Interactive handoff the tool requested; the host chat executes it. */ - directive?: CrestodianAgentTurnDirective; + directive?: SystemAgentTurnDirective; }; -export type CrestodianAgentTurnRunner = (params: { +export type SystemAgentTurnRunner = (params: { input: string; - overview: CrestodianOverview; + overview: SystemAgentOverview; surface: "cli" | "gateway"; /** Host-verified: the user's current message is an explicit approval. */ approvalArmed: boolean; - session: CrestodianAgentSession; -}) => Promise; + session: SystemAgentSession; +}) => Promise; -export type CrestodianAgentSession = { +export type SystemAgentSession = { sessionId: string; /** Exact live-tested inference owner for this ephemeral conversation. */ - readonly verifiedInference: CrestodianVerifiedInferenceBinding; - /** Host-owned pending-proposal fingerprint; see crestodian-tool.ts. */ + readonly verifiedInference: SystemAgentVerifiedInferenceBinding; + /** Host-owned pending-proposal fingerprint; see system-agent-tool.ts. */ proposalRef: { current?: string }; /** Native CLI continuity, bound to the exact configured model/auth owner route. */ cliSession?: { @@ -64,34 +63,34 @@ export type CrestodianAgentSession = { }; }; -export function createCrestodianAgentSession( - verifiedInference: CrestodianVerifiedInferenceBinding, -): CrestodianAgentSession { +export function createSystemAgentSession( + verifiedInference: SystemAgentVerifiedInferenceBinding, +): SystemAgentSession { if (!verifiedInference) { - throw new CrestodianInferenceUnavailableError("agent-turn"); + throw new SystemAgentInferenceUnavailableError("agent-turn"); } return { - sessionId: `crestodian-${randomUUID()}`, + sessionId: `openclaw-${randomUUID()}`, verifiedInference, proposalRef: {}, }; } -type CrestodianRunEmbeddedAgent = ( +type SystemAgentRunEmbeddedAgent = ( params: Parameters[0] & { - crestodianTool?: import("../agents/tools/crestodian-tool.js").CrestodianToolOptions; + systemAgentTool?: import("../agents/tools/system-agent-tool.js").SystemAgentToolOptions; }, ) => ReturnType; -type CrestodianRunCliAgent = ( +type SystemAgentRunCliAgent = ( params: Parameters[0] & { - crestodianTool?: import("../agents/tools/crestodian-tool.js").CrestodianToolOptions; + systemAgentTool?: import("../agents/tools/system-agent-tool.js").SystemAgentToolOptions; }, ) => ReturnType; -export type CrestodianAgentTurnDeps = CrestodianVerifiedInferenceDeps & { - runEmbeddedAgent?: CrestodianRunEmbeddedAgent; - runCliAgent?: CrestodianRunCliAgent; +export type SystemAgentTurnDeps = SystemAgentVerifiedInferenceDeps & { + runEmbeddedAgent?: SystemAgentRunEmbeddedAgent; + runCliAgent?: SystemAgentRunCliAgent; readConfigFileSnapshot?: typeof import("../config/config.js").readConfigFileSnapshot; }; @@ -118,22 +117,20 @@ function extractRunText(result: EmbeddedRunResult): string | undefined { ); } -async function ensureCrestodianDirs( +async function ensureSystemAgentDirs( sessionId: string, ): Promise<{ workspaceDir: string; sessionFile: string }> { - const base = path.join(resolveStateDir(), "crestodian"); + const base = path.join(resolveStateDir(), "openclaw"); const workspaceDir = path.join(base, "workspace"); await fs.mkdir(workspaceDir, { recursive: true }); await fs.mkdir(path.join(base, "sessions"), { recursive: true }); return { workspaceDir, sessionFile: path.join(base, "sessions", `${sessionId}.jsonl`) }; } -export async function cleanupCrestodianAgentSession( - session: CrestodianAgentSession, -): Promise { +export async function cleanupSystemAgentSession(session: SystemAgentSession): Promise { const sessionFile = path.join( resolveStateDir(), - "crestodian", + "openclaw", "sessions", `${session.sessionId}.jsonl`, ); @@ -141,26 +138,29 @@ export async function cleanupCrestodianAgentSession( await fs.rm(sessionFile, { force: true }); } -type CrestodianAgentTurnParams = Parameters[0]; +type SystemAgentTurnParams = Parameters[0]; -function clearCrestodianCliSession(session: CrestodianAgentSession): void { +function clearSystemAgentCliSession(session: SystemAgentSession): void { delete session.cliSession; } -function clearFailedCrestodianSessionState(session: CrestodianAgentSession): void { +function clearFailedSystemAgentSessionState(session: SystemAgentSession): void { session.proposalRef.current = undefined; - clearCrestodianCliSession(session); + clearSystemAgentCliSession(session); } -function throwCrestodianInferenceUnavailable(params: { - session: CrestodianAgentSession; +function throwSystemAgentInferenceUnavailable(params: { + session: SystemAgentSession; failures?: unknown[]; }): never { - clearFailedCrestodianSessionState(params.session); - throw new CrestodianInferenceUnavailableError("agent-turn", params.failures); + clearFailedSystemAgentSessionState(params.session); + throw new SystemAgentInferenceUnavailableError("agent-turn", params.failures); } -function cliRouteKey(route: CrestodianConfiguredRoute, backend: ResolvedCliBackend | null): string { +function cliRouteKey( + route: SystemAgentConfiguredRoute, + backend: ResolvedCliBackend | null, +): string { return JSON.stringify({ provider: route.provider, backendId: backend?.id ?? route.provider, @@ -187,13 +187,15 @@ function cliRouteKey(route: CrestodianConfiguredRoute, backend: ResolvedCliBacke }); } -function resolveCrestodianCliBackend(route: CrestodianConfiguredRoute): ResolvedCliBackend | null { +function resolveSystemAgentCliBackend( + route: SystemAgentConfiguredRoute, +): ResolvedCliBackend | null { // The helper owns the executable/session identity even though its model and - // auth come from the configured default agent. Crestodian also forces a + // auth come from the configured default agent. OpenClaw also forces a // process per turn so each approval gets fresh MCP authority; fingerprint // that effective execution identity rather than the configured live mode. const backend = resolveCliBackendConfig(route.provider, route.runConfig, { - agentId: CRESTODIAN_AGENT_ID, + agentId: SYSTEM_AGENT_ID, }); if (!backend) { return null; @@ -202,48 +204,48 @@ function resolveCrestodianCliBackend(route: CrestodianConfiguredRoute): Resolved return { ...backend, config }; } -function resolveCrestodianCliToolAvailability( +function resolveSystemAgentCliToolAvailability( backend: ResolvedCliBackend | null, ): { native: []; mcp: string[] } | undefined { if (backend?.nativeToolMode === "none") { return undefined; } if (backend?.nativeToolMode === "selectable" && backend.resolveExecutionArgs) { - return { native: [], mcp: [CRESTODIAN_MCP_TOOL_NAME] }; + return { native: [], mcp: [SYSTEM_AGENT_MCP_TOOL_NAME] }; } const backendId = backend?.id ?? "unknown"; - throw new Error(`CLI backend ${backendId} cannot enforce Crestodian's exact tool availability`); + throw new Error(`CLI backend ${backendId} cannot enforce OpenClaw's exact tool availability`); } /** - * CLI harnesses run the crestodian tool in a stdio MCP subprocess, so the + * CLI harnesses run the openclaw tool in a stdio MCP subprocess, so the * in-process proposalRef/directiveRef cannot be shared with the host. Mirror * the tool's transitions from the harness tool events instead: a denial * registers the exact-operation hash, a mismatch voids it, an executed * mutation consumes it, and directive actions replay the interactive handoff — - * same lifecycle as crestodian-tool.ts enforces. + * same lifecycle as system-agent-tool.ts enforces. */ -async function mirrorCrestodianToolStateFromEvents(params: { +async function mirrorSystemAgentToolStateFromEvents(params: { runId: string; proposalRef: { current?: string }; - directiveRef: { current?: CrestodianAgentTurnDirective }; + directiveRef: { current?: SystemAgentTurnDirective }; }): Promise<() => void> { const [ { onAgentEvent }, { extractToolResultText }, - { resolveCrestodianProposalTransition, resolveCrestodianDirectiveTransition }, + { resolveSystemAgentProposalTransition, resolveSystemAgentDirectiveTransition }, ] = await Promise.all([ import("../infra/agent-events.js"), import("../agents/embedded-agent-subscribe.tools.js"), - import("../agents/tools/crestodian-tool.js"), + import("../agents/tools/system-agent-tool.js"), ]); return onAgentEvent((evt) => { if (evt.runId !== params.runId || evt.stream !== "tool" || evt.data.phase !== "result") { return; } const name = typeof evt.data.name === "string" ? evt.data.name : ""; - // CLI harnesses report MCP tools with transport prefixes (mcp__openclaw__crestodian). - if (name !== "crestodian" && !name.endsWith("__crestodian")) { + // CLI harnesses report MCP tools with transport prefixes (mcp__openclaw__openclaw). + if (name !== "openclaw" && !name.endsWith("__openclaw")) { return; } const args = @@ -251,11 +253,11 @@ async function mirrorCrestodianToolStateFromEvents(params: { ? (evt.data.args as Record) : {}; const resultText = extractToolResultText(evt.data.result) ?? ""; - const transition = resolveCrestodianProposalTransition({ args, resultText }); + const transition = resolveSystemAgentProposalTransition({ args, resultText }); if (transition) { params.proposalRef.current = transition.proposal; } - const directive = resolveCrestodianDirectiveTransition({ args, resultText }); + const directive = resolveSystemAgentDirectiveTransition({ args, resultText }); if (directive && params.directiveRef.current?.kind !== "approved-operation") { params.directiveRef.current = directive; } @@ -263,55 +265,55 @@ async function mirrorCrestodianToolStateFromEvents(params: { } /** - * Run one Crestodian turn through the embedded agent loop. Route, runner, and + * Run one OpenClaw turn through the embedded agent loop. Route, runner, and * output failures are typed so callers may try another inference path without * mistaking the failure for deterministic setup authority. */ -export async function runCrestodianAgentTurnWithDeps( - params: CrestodianAgentTurnParams, - deps: CrestodianAgentTurnDeps = {}, -): Promise { +export async function runSystemAgentTurnWithDeps( + params: SystemAgentTurnParams, + deps: SystemAgentTurnDeps = {}, +): Promise { const binding = params.session.verifiedInference; if (!binding) { - return throwCrestodianInferenceUnavailable({ session: params.session }); + return throwSystemAgentInferenceUnavailable({ session: params.session }); } - let plan: CrestodianConfiguredRoute | null; + let plan: SystemAgentConfiguredRoute | null; try { - plan = await resolveCrestodianVerifiedInferenceRoute(binding, deps); + plan = await resolveSystemAgentVerifiedInferenceRoute(binding, deps); } catch (error) { - return throwCrestodianInferenceUnavailable({ + return throwSystemAgentInferenceUnavailable({ session: params.session, failures: [error], }); } if (!plan) { - return throwCrestodianInferenceUnavailable({ session: params.session }); + return throwSystemAgentInferenceUnavailable({ session: params.session }); } let expectedAgentHarnessRuntimeArtifact: ReturnType< - typeof resolveCrestodianExpectedAgentHarnessRuntimeArtifact + typeof resolveSystemAgentExpectedAgentHarnessRuntimeArtifact >; try { expectedAgentHarnessRuntimeArtifact = - resolveCrestodianExpectedAgentHarnessRuntimeArtifact(binding); + resolveSystemAgentExpectedAgentHarnessRuntimeArtifact(binding); } catch (error) { - return throwCrestodianInferenceUnavailable({ session: params.session, failures: [error] }); + return throwSystemAgentInferenceUnavailable({ session: params.session, failures: [error] }); } let workspaceDir: string; let sessionFile: string; try { - ({ workspaceDir, sessionFile } = await ensureCrestodianDirs(params.session.sessionId)); + ({ workspaceDir, sessionFile } = await ensureSystemAgentDirs(params.session.sessionId)); } catch (error) { - return throwCrestodianInferenceUnavailable({ + return throwSystemAgentInferenceUnavailable({ session: params.session, failures: [error], }); } - const runId = `crestodian-turn-${randomUUID()}`; + const runId = `openclaw-turn-${randomUUID()}`; const shared = { sessionId: params.session.sessionId, - sessionKey: buildAgentMainSessionKey({ agentId: CRESTODIAN_AGENT_ID }), - agentId: CRESTODIAN_AGENT_ID, + sessionKey: buildAgentMainSessionKey({ agentId: SYSTEM_AGENT_ID }), + agentId: SYSTEM_AGENT_ID, trigger: "manual" as const, sessionFile, workspaceDir, @@ -319,13 +321,13 @@ export async function runCrestodianAgentTurnWithDeps( prompt: params.input, timeoutMs: AGENT_TURN_TIMEOUT_MS, runId, - messageChannel: "crestodian", - messageProvider: "crestodian", + messageChannel: "openclaw", + messageProvider: "openclaw", }; // Directives are per-turn: the tool records at most one interactive handoff // and the engine executes it after the reply. - const directiveRef: { current?: CrestodianAgentTurnDirective } = {}; - const crestodianTool = { + const directiveRef: { current?: SystemAgentTurnDirective } = {}; + const systemAgentTool = { surface: params.surface, approvalArmed: params.approvalArmed, proposalRef: params.session.proposalRef, @@ -334,18 +336,18 @@ export async function runCrestodianAgentTurnWithDeps( try { let result: EmbeddedRunResult; if (plan.runner === "cli") { - const backend = resolveCrestodianCliBackend(plan); - const cliToolAvailability = resolveCrestodianCliToolAvailability(backend); + const backend = resolveSystemAgentCliBackend(plan); + const cliToolAvailability = resolveSystemAgentCliToolAvailability(backend); const routeKey = cliRouteKey(plan, backend); const previousBinding = params.session.cliSession?.routeKey === routeKey ? params.session.cliSession.binding : undefined; if (!previousBinding) { - clearCrestodianCliSession(params.session); + clearSystemAgentCliSession(params.session); } const runCli = deps.runCliAgent ?? (await import("../agents/cli-runner.js")).runCliAgent; - const stopToolStateMirror = await mirrorCrestodianToolStateFromEvents({ + const stopToolStateMirror = await mirrorSystemAgentToolStateFromEvents({ runId, proposalRef: params.session.proposalRef, directiveRef, @@ -357,9 +359,9 @@ export async function runCrestodianAgentTurnWithDeps( model: plan.model, agentDir: plan.agentDir, ...(plan.authProfileId ? { authProfileId: plan.authProfileId } : {}), - extraSystemPrompt: CRESTODIAN_AGENT_SYSTEM_PROMPT, - extraSystemPromptStatic: CRESTODIAN_AGENT_SYSTEM_PROMPT, - crestodianTool, + extraSystemPrompt: SYSTEM_AGENT_SYSTEM_PROMPT, + extraSystemPromptStatic: SYSTEM_AGENT_SYSTEM_PROMPT, + systemAgentTool, ...(cliToolAvailability ? { cliToolAvailability } : {}), ...(previousBinding ? { cliSessionBinding: previousBinding } : {}), disableCliLiveSession: true, @@ -372,7 +374,7 @@ export async function runCrestodianAgentTurnWithDeps( // native CLI transcript instead of reseeding from scratch. const agentMeta = result.meta?.agentMeta; if (agentMeta?.clearCliSessionBinding || !agentMeta?.cliSessionBinding?.sessionId) { - clearCrestodianCliSession(params.session); + clearSystemAgentCliSession(params.session); } else if (agentMeta?.cliSessionBinding?.sessionId) { params.session.cliSession = { routeKey, @@ -382,14 +384,14 @@ export async function runCrestodianAgentTurnWithDeps( } else { // An intervening embedded turn cannot be represented in the CLI's native // transcript. A later CLI route must reseed instead of reviving stale context. - clearCrestodianCliSession(params.session); + clearSystemAgentCliSession(params.session); const runEmbedded = deps.runEmbeddedAgent ?? (await import("../agents/embedded-agent.js")).runEmbeddedAgent; result = (await runEmbedded({ ...shared, - extraSystemPrompt: CRESTODIAN_AGENT_SYSTEM_PROMPT, - toolsAllow: ["crestodian"], - crestodianTool, + extraSystemPrompt: SYSTEM_AGENT_SYSTEM_PROMPT, + toolsAllow: ["openclaw"], + systemAgentTool, disableMessageTool: true, provider: plan.provider, model: plan.model, @@ -402,17 +404,17 @@ export async function runCrestodianAgentTurnWithDeps( })) as EmbeddedRunResult; } if (params.session.verifiedInference !== binding) { - throw new CrestodianInferenceUnavailableError("agent-turn"); + throw new SystemAgentInferenceUnavailableError("agent-turn"); } // A completed model turn is still untrusted until the exact route owner is // revalidated. This also rejects directives produced while config changed. - const currentRoute = await resolveCrestodianVerifiedInferenceRoute(binding, deps); + const currentRoute = await resolveSystemAgentVerifiedInferenceRoute(binding, deps); if (!currentRoute) { - throw new CrestodianInferenceUnavailableError("agent-turn"); + throw new SystemAgentInferenceUnavailableError("agent-turn"); } const text = extractRunText(result)?.trim(); if (!text) { - throw new CrestodianInferenceUnavailableError("agent-turn"); + throw new SystemAgentInferenceUnavailableError("agent-turn"); } return { text, @@ -423,10 +425,10 @@ export async function runCrestodianAgentTurnWithDeps( // A failed run may have registered a proposal or returned a CLI session id // before rejecting. Neither is safe to arm or resume on a later attempt. const failures = - error instanceof CrestodianInferenceUnavailableError ? [...error.failures] : [error]; - return throwCrestodianInferenceUnavailable({ session: params.session, failures }); + error instanceof SystemAgentInferenceUnavailableError ? [...error.failures] : [error]; + return throwSystemAgentInferenceUnavailable({ session: params.session, failures }); } } -export const runCrestodianAgentTurn: CrestodianAgentTurnRunner = (params) => - runCrestodianAgentTurnWithDeps(params); +export const runSystemAgentTurn: SystemAgentTurnRunner = (params) => + runSystemAgentTurnWithDeps(params); diff --git a/src/crestodian/approval-intent.test.ts b/src/system-agent/approval-intent.test.ts similarity index 81% rename from src/crestodian/approval-intent.test.ts rename to src/system-agent/approval-intent.test.ts index 43d7a10e258f..a19ddb145a59 100644 --- a/src/crestodian/approval-intent.test.ts +++ b/src/system-agent/approval-intent.test.ts @@ -1,16 +1,16 @@ // Approval-intent tests: closed-list fast path plus model-judged classification. import { describe, expect, it, vi } from "vitest"; import { - classifyCrestodianApprovalIntent, - classifyCrestodianApprovalText, - type CrestodianApprovalIntentDeps, + classifySystemAgentApprovalIntent, + classifySystemAgentApprovalText, + type SystemAgentApprovalIntentDeps, } from "./approval-intent.js"; -import { createCrestodianVerifiedInferenceTestFixture } from "./crestodian.test-helpers.js"; -import type { CrestodianVerifiedInferenceBinding } from "./verified-inference.js"; +import { createSystemAgentVerifiedInferenceTestFixture } from "./system-agent.test-helpers.js"; +import type { SystemAgentVerifiedInferenceBinding } from "./verified-inference.js"; async function verifiedInference( model = "openai/gpt-5.5@openai:p2", -): Promise { +): Promise { const openClawRuntime = model.startsWith("openai/") ? { models: { @@ -19,7 +19,7 @@ async function verifiedInference( } : {}; return ( - await createCrestodianVerifiedInferenceTestFixture({ + await createSystemAgentVerifiedInferenceTestFixture({ agents: { defaults: { model, ...openClawRuntime } }, auth: { order: { openai: ["openai:p1", "openai:p2"] }, @@ -32,14 +32,14 @@ async function verifiedInference( ).binding; } -function completionDeps(replyText: string, binding: CrestodianVerifiedInferenceBinding) { +function completionDeps(replyText: string, binding: SystemAgentVerifiedInferenceBinding) { const route = binding.execution; return { resolveVerifiedInferenceRoute: vi.fn< - NonNullable + NonNullable >(async () => route), prepareSimpleCompletionModelForAgent: vi.fn< - NonNullable + NonNullable >( async () => ({ @@ -55,7 +55,7 @@ function completionDeps(replyText: string, binding: CrestodianVerifiedInferenceB }) as never, ), completeWithPreparedSimpleCompletionModel: vi.fn< - NonNullable + NonNullable >( async () => ({ @@ -65,32 +65,32 @@ function completionDeps(replyText: string, binding: CrestodianVerifiedInferenceB }; } -describe("classifyCrestodianApprovalText", () => { +describe("classifySystemAgentApprovalText", () => { it("accepts natural affirmatives regardless of case and punctuation", () => { for (const text of ["yes", "Yes.", "sure", "ok!", "Okay,", "go ahead", "yes please", "do it"]) { - expect(classifyCrestodianApprovalText(text)).toBe("approve"); + expect(classifySystemAgentApprovalText(text)).toBe("approve"); } }); it("treats clear rejections as declines", () => { for (const text of ["no", "no thanks", "not now", "cancel", "don't", "nah, later"]) { - expect(classifyCrestodianApprovalText(text)).toBe("decline"); + expect(classifySystemAgentApprovalText(text)).toBe("decline"); } }); it("keeps everything ambiguous as other", () => { for (const text of ["maybe", "what does that change?", "yes but use gpt instead", ""]) { - expect(classifyCrestodianApprovalText(text)).toBe("other"); + expect(classifySystemAgentApprovalText(text)).toBe("other"); } }); }); -describe("classifyCrestodianApprovalIntent", () => { +describe("classifySystemAgentApprovalIntent", () => { it("short-circuits closed-list answers without a model call", async () => { const binding = await verifiedInference(); const deps = completionDeps("approve", binding); await expect( - classifyCrestodianApprovalIntent({ message: "yes", verifiedInference: binding }, deps), + classifySystemAgentApprovalIntent({ message: "yes", verifiedInference: binding }, deps), ).resolves.toBe("approve"); expect(deps.resolveVerifiedInferenceRoute).not.toHaveBeenCalled(); expect(deps.completeWithPreparedSimpleCompletionModel).not.toHaveBeenCalled(); @@ -100,7 +100,7 @@ describe("classifyCrestodianApprovalIntent", () => { const binding = await verifiedInference(); const deps = completionDeps("approve", binding); await expect( - classifyCrestodianApprovalIntent( + classifySystemAgentApprovalIntent( { message: "alright, ship that change", proposal: "set config gateway.port to 19001", @@ -123,7 +123,7 @@ describe("classifyCrestodianApprovalIntent", () => { const binding = await verifiedInference(); const deps = completionDeps("I think the user probably agrees", binding); await expect( - classifyCrestodianApprovalIntent( + classifySystemAgentApprovalIntent( { message: "hmm alright I guess?", verifiedInference: binding }, deps, ), @@ -137,7 +137,7 @@ describe("classifyCrestodianApprovalIntent", () => { prepareSimpleCompletionModelForAgent: vi.fn(async () => ({ error: "no model" })) as never, }; await expect( - classifyCrestodianApprovalIntent( + classifySystemAgentApprovalIntent( { message: "alright then", verifiedInference: binding }, deps, ), @@ -159,7 +159,7 @@ describe("classifyCrestodianApprovalIntent", () => { } as never); await expect( - classifyCrestodianApprovalIntent( + classifySystemAgentApprovalIntent( { message: "alright then", verifiedInference: binding }, deps, ), @@ -183,7 +183,7 @@ describe("classifyCrestodianApprovalIntent", () => { } as never); await expect( - classifyCrestodianApprovalIntent( + classifySystemAgentApprovalIntent( { message: "alright then", verifiedInference: binding }, deps, ), @@ -196,7 +196,7 @@ describe("classifyCrestodianApprovalIntent", () => { const deps = completionDeps("approve", binding); await expect( - classifyCrestodianApprovalIntent( + classifySystemAgentApprovalIntent( { message: "alright then", verifiedInference: binding }, deps, ), @@ -212,7 +212,7 @@ describe("classifyCrestodianApprovalIntent", () => { .mockResolvedValueOnce(null); await expect( - classifyCrestodianApprovalIntent( + classifySystemAgentApprovalIntent( { message: "alright then", verifiedInference: binding }, deps, ), diff --git a/src/crestodian/approval-intent.ts b/src/system-agent/approval-intent.ts similarity index 84% rename from src/crestodian/approval-intent.ts rename to src/system-agent/approval-intent.ts index 4d532b443579..bc86b37aa784 100644 --- a/src/crestodian/approval-intent.ts +++ b/src/system-agent/approval-intent.ts @@ -1,33 +1,33 @@ -// Classifies whether a user's chat message approves a pending Crestodian proposal. +// Classifies whether a user's chat message approves a pending OpenClaw proposal. import { extractAssistantText } from "../agents/embedded-agent-utils.js"; import { completeWithPreparedSimpleCompletionModel, prepareSimpleCompletionModelForAgent, } from "../agents/simple-completion-runtime.js"; import { - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceBinding, + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceBinding, } from "./verified-inference.js"; /** * Approval detection for pending mutations. The host — not the conversation * model — decides whether a turn is armed, so the agent loop can never - * self-approve (see crestodian-tool.ts). Users approve in their own words: + * self-approve (see system-agent-tool.ts). Users approve in their own words: * unambiguous replies resolve instantly from a closed list, everything else is * judged by a separate single-shot model call that sees only the user's * message and the proposal description, never tool output. When no completion * model is usable the closed list is the whole decision — "other" (the safe * default) keeps the proposal pending and the conversation re-asks. */ -export type CrestodianApprovalIntent = "approve" | "decline" | "other"; +export type SystemAgentApprovalIntent = "approve" | "decline" | "other"; -export type CrestodianApprovalClassifier = (params: { +export type SystemAgentApprovalClassifier = (params: { message: string; /** Human-readable proposal description when the host knows it. */ proposal?: string; - /** Exact execution owner that completed the live Crestodian inference gate. */ - verifiedInference: CrestodianVerifiedInferenceBinding; -}) => Promise; + /** Exact execution owner that completed the live OpenClaw inference gate. */ + verifiedInference: SystemAgentVerifiedInferenceBinding; +}) => Promise; const APPROVAL_INTENT_TIMEOUT_MS = 10_000; const APPROVAL_INTENT_MAX_TOKENS = 8; @@ -46,7 +46,7 @@ function normalizeApprovalText(message: string): string { } /** Closed-list classification: exact affirmatives, prefix declines. */ -export function classifyCrestodianApprovalText(message: string): CrestodianApprovalIntent { +export function classifySystemAgentApprovalText(message: string): SystemAgentApprovalIntent { const normalized = normalizeApprovalText(message); if (!normalized) { return "other"; @@ -69,8 +69,8 @@ const APPROVAL_INTENT_SYSTEM_PROMPT = [ "Only classify consent for the pending change itself. A message asking to change the proposal is not approval.", ].join("\n"); -export type CrestodianApprovalIntentDeps = { - resolveVerifiedInferenceRoute?: typeof resolveCrestodianVerifiedInferenceRoute; +export type SystemAgentApprovalIntentDeps = { + resolveVerifiedInferenceRoute?: typeof resolveSystemAgentVerifiedInferenceRoute; prepareSimpleCompletionModelForAgent?: typeof prepareSimpleCompletionModelForAgent; completeWithPreparedSimpleCompletionModel?: typeof completeWithPreparedSimpleCompletionModel; }; @@ -82,21 +82,21 @@ export type CrestodianApprovalIntentDeps = { * CLI-harness routes do not spawn a second harness for that check, so their * ambiguous replies stay "other" and the conversation asks for a clear yes. */ -export async function classifyCrestodianApprovalIntent( +export async function classifySystemAgentApprovalIntent( params: { message: string; proposal?: string; - verifiedInference: CrestodianVerifiedInferenceBinding; + verifiedInference: SystemAgentVerifiedInferenceBinding; }, - deps: CrestodianApprovalIntentDeps = {}, -): Promise { - const textIntent = classifyCrestodianApprovalText(params.message); + deps: SystemAgentApprovalIntentDeps = {}, +): Promise { + const textIntent = classifySystemAgentApprovalText(params.message); if (textIntent !== "other") { return textIntent; } try { const resolveVerifiedRoute = - deps.resolveVerifiedInferenceRoute ?? resolveCrestodianVerifiedInferenceRoute; + deps.resolveVerifiedInferenceRoute ?? resolveSystemAgentVerifiedInferenceRoute; const route = await resolveVerifiedRoute(params.verifiedInference); // A second direct completion would bypass CLI and plugin-harness execution // ownership. Those routes require an exact closed-list approval instead. diff --git a/src/crestodian/assistant-prompts.ts b/src/system-agent/assistant-prompts.ts similarity index 81% rename from src/crestodian/assistant-prompts.ts rename to src/system-agent/assistant-prompts.ts index c90d8fbaba9f..d972891383f4 100644 --- a/src/crestodian/assistant-prompts.ts +++ b/src/system-agent/assistant-prompts.ts @@ -1,20 +1,20 @@ -// Crestodian assistant prompts drive the conversational custodian with typed-command output. +// System-agent prompts drive the OpenClaw conversation with typed-command output. import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; -import type { CrestodianOverview } from "./overview.js"; +import type { SystemAgentOverview } from "./overview.js"; /** - * Prompt construction and response parsing for Crestodian's AI turns. + * Prompt construction and response parsing for OpenClaw's AI turns. * * The assistant carries the conversation (personality included) but can only - * touch the system through Crestodian's typed command vocabulary; parsing + * touch the system through OpenClaw's typed command vocabulary; parsing * stays deliberately narrow so free-form model text never executes directly. */ /** Timeout for one assistant turn (local CLI backends cold-start slowly). */ -export const CRESTODIAN_ASSISTANT_TIMEOUT_MS = 30_000; +export const SYSTEM_AGENT_ASSISTANT_TIMEOUT_MS = 30_000; /** System prompt: persona plus the closed command vocabulary. */ -export const CRESTODIAN_ASSISTANT_SYSTEM_PROMPT = [ - "You are Crestodian, OpenClaw's setup custodian: a small, tidy hermit crab that lives in the config shell.", +export const SYSTEM_AGENT_ASSISTANT_SYSTEM_PROMPT = [ + "You are OpenClaw, the system agent: a small, tidy hermit crab that lives in the config shell.", "Personality: warm, competent, concise. Dry humor in small doses. Never corporate. You configure things so the user does not have to.", "You are talking to someone setting up or repairing OpenClaw. A real inference turn has already passed before this session can start. Your goals, in order: a workspace, a running gateway, then channels (Discord, Slack, Telegram, WhatsApp, ...) and handing off to their agent (`talk to agent`).", 'Return only compact JSON: {"reply": string, "command"?: string}.', @@ -27,8 +27,8 @@ export const CRESTODIAN_ASSISTANT_SYSTEM_PROMPT = [ "", "Config knowledge — the file is ~/.openclaw/openclaw.json (JSON5). You change it ONLY through `config set` / `config set-ref` / `setup` / `set default model` / `connect `.", "Top-level areas available for ordinary writes: gateway (port, bind, auth.mode/token) and channels. (enabled plus per-channel credentials, e.g. channels.telegram.botToken).", - "Inference is a prerequisite, not something you can bootstrap or replace from inside the session. Never change inference-provider credentials, top-level auth (`auth.*`), model catalogs, CLI backends, agent model routes, agent params/tools, or root `tools.*` with `config set` or `config set-ref`. Raw writes under `env.*`, `secrets.*`, `plugins.*`, and `$include` are also refused because they can replace credential resolution or provider activation. Use typed channel/plugin workflows instead. If the user asks to configure or repair provider/auth access, tell them to exit Crestodian and run `openclaw onboard`, which live-tests a candidate before saving it. Doctor repairs can also change the active inference route; tell the user to exit Crestodian and run `openclaw doctor --fix`.", - "A new agent cannot select its own model during creation. Use `create agent workspace `; it inherits the live-verified default route. The id `crestodian` is reserved for the privileged virtual custodian and cannot be created as a normal agent.", + "Inference is a prerequisite, not something you can bootstrap or replace from inside the session. Never change inference-provider credentials, top-level auth (`auth.*`), model catalogs, CLI backends, agent model routes, agent params/tools, or root `tools.*` with `config set` or `config set-ref`. Raw writes under `env.*`, `secrets.*`, `plugins.*`, and `$include` are also refused because they can replace credential resolution or provider activation. Use typed channel/plugin workflows instead. If the user asks to configure or repair provider/auth access, tell them to exit OpenClaw and run `openclaw onboard`, which live-tests a candidate before saving it. Doctor repairs can also change the active inference route; tell the user to exit OpenClaw and run `openclaw doctor --fix`.", + "A new agent cannot select its own model during creation. Use `create agent workspace `; it inherits the live-verified default route. The ids `openclaw` and `crestodian` are reserved for the system agent and cannot be created as normal agents.", "Before writing a path you are not certain about, FIRST send `config schema ` (or `config get `) and use the result in your next turn; the schema is the source of truth, not memory.", "Secrets (tokens, API keys, passwords) must not be written as plaintext when the user prefers env storage: use `config set-ref env `. Never echo secret values back.", "Values for `config set` are parsed as JSON5 when they look like objects/arrays/booleans/numbers, otherwise as strings. One write per turn; after risky writes suggest `validate config`.", @@ -69,31 +69,31 @@ export const CRESTODIAN_ASSISTANT_SYSTEM_PROMPT = [ /** * System prompt for the real agent loop (embedded runtime with the ring-zero - * `crestodian` tool). Unlike the planner contract, replies are natural text + * `openclaw` tool). Unlike the planner contract, replies are natural text * and actions happen through tool calls. */ -export const CRESTODIAN_AGENT_SYSTEM_PROMPT = [ - "You are Crestodian, OpenClaw's setup custodian: a small, tidy hermit crab that lives in the config shell.", +export const SYSTEM_AGENT_SYSTEM_PROMPT = [ + "You are OpenClaw, the system agent: a small, tidy hermit crab that lives in the config shell.", "Personality: warm, competent, concise. Dry humor in small doses. Never corporate. You configure things so the user does not have to.", "You are talking to someone setting up or repairing OpenClaw. A real inference turn has already passed before this session can start. Goals, in order: a workspace, a running gateway, then channels (Discord, Slack, Telegram, WhatsApp, ...) and handing off to their agent.", - "You act ONLY through the `crestodian` tool. Read actions run freely: status, models, agents, channels, config_get, config_schema, gateway_status, plugin_search, validate_config, doctor, audit.", + "You act ONLY through the `openclaw` tool. Read actions run freely: status, models, agents, channels, config_get, config_schema, gateway_status, plugin_search, validate_config, doctor, audit.", "Mutating actions (setup, set_default_model, config_set, config_set_ref, create_agent, gateway_start/stop/restart, plugin_install) change the user's machine. Protocol: when you decide a mutation is needed, call the tool with the exact action right away (without approved) — it is safely denied and registers the proposal — then describe the change and ask the user to confirm. Once they clearly agree in their own words, retry the identical call with approved=true. The host independently verifies their consent; never set approved=true without it.", - "The config file is ~/.openclaw/openclaw.json (JSON5). Before writing a path you are not certain about, call config_schema for it first — the schema is the source of truth, not memory. Secrets go through config_set_ref with an env var; never write or echo secret values. Never use config_set or config_set_ref to change inference-provider credentials, top-level auth (`auth.*`), model catalogs, CLI backends, agent model routes or params/tools, root `tools.*`, `env.*`, `secrets.*`, `plugins.*`, or `$include`; use typed channel/plugin workflows. Plugin uninstall is unavailable inside Crestodian because it could remove the active inference provider; tell the user to exit and run `openclaw plugins uninstall `.", + "The config file is ~/.openclaw/openclaw.json (JSON5). Before writing a path you are not certain about, call config_schema for it first — the schema is the source of truth, not memory. Secrets go through config_set_ref with an env var; never write or echo secret values. Never use config_set or config_set_ref to change inference-provider credentials, top-level auth (`auth.*`), model catalogs, CLI backends, agent model routes or params/tools, root `tools.*`, `env.*`, `secrets.*`, `plugins.*`, or `$include`; use typed channel/plugin workflows. Plugin uninstall is unavailable inside OpenClaw because it could remove the active inference provider; tell the user to exit and run `openclaw plugins uninstall `.", "If a tool result reports CONFIG INVALID, fix it immediately before anything else.", - "Inference is a prerequisite. Never call configure_model_provider: tell the user to exit Crestodian and run `openclaw onboard`, which live-tests a candidate before saving it. Never run doctor repairs inside Crestodian; tell the user to exit and run `openclaw doctor --fix` because repairs can change the active inference route. To connect a chat channel, call connect_channel with the channel id (for example telegram) — the guided setup then runs right here in the chat. To hand the user off to their normal agent, call open_agent.", - "Never include a model in create_agent; a new agent inherits the live-verified default route. Never create agent id `crestodian`; it is the reserved privileged virtual custodian. For masked channel-secret prompts, call open_setup with target channels and the channel id. Never request the guided or classic target.", + "Inference is a prerequisite. Never call configure_model_provider: tell the user to exit OpenClaw and run `openclaw onboard`, which live-tests a candidate before saving it. Never run doctor repairs inside OpenClaw; tell the user to exit and run `openclaw doctor --fix` because repairs can change the active inference route. To connect a chat channel, call connect_channel with the channel id (for example telegram) — the guided setup then runs right here in the chat. To hand the user off to their normal agent, call open_agent.", + "Never include a model in create_agent; a new agent inherits the live-verified default route. Never create agent ids `openclaw` or `crestodian`; they are reserved for the system agent. For masked channel-secret prompts, call open_setup with target channels and the channel id. Never request the guided or classic target.", "Channel guidance: when the user asks ABOUT a channel or its prerequisites (bot tokens, app creation, e.g. Slack or Telegram), call channel_info and use its docs link; never guess credentials or steps. When they ask to CONNECT a channel, call connect_channel right away — do not detour through channel_info.", "Keep replies under 120 words. Ask one question at a time. Never claim something was done unless the tool result confirms it.", ].join("\n"); /** One prior conversation turn supplied to the assistant. */ -export type CrestodianAssistantTurn = { +export type SystemAgentAssistantTurn = { role: "user" | "assistant"; text: string; }; /** Parsed assistant plan before its command is re-validated as an operation. */ -export type CrestodianAssistantPlan = { +export type SystemAgentAssistantPlan = { command?: string; reply?: string; modelLabel?: string; @@ -102,7 +102,7 @@ export type CrestodianAssistantPlan = { const HISTORY_TURN_LIMIT = 12; const HISTORY_TURN_MAX_CHARS = 500; -function formatHistory(history: CrestodianAssistantTurn[] | undefined): string[] { +function formatHistory(history: SystemAgentAssistantTurn[] | undefined): string[] { if (!history || history.length === 0) { return []; } @@ -114,17 +114,17 @@ function formatHistory(history: CrestodianAssistantTurn[] | undefined): string[] turn.text.length > HISTORY_TURN_MAX_CHARS ? `${truncateUtf16Safe(turn.text, HISTORY_TURN_MAX_CHARS)}…` : turn.text; - return `${turn.role === "user" ? "User" : "Crestodian"}: ${text}`; + return `${turn.role === "user" ? "User" : "OpenClaw"}: ${text}`; }), "", ]; } /** Build the overview-grounded user prompt supplied to assistant planners. */ -export function buildCrestodianAssistantUserPrompt(params: { +export function buildSystemAgentAssistantUserPrompt(params: { input: string; - overview: CrestodianOverview; - history?: CrestodianAssistantTurn[]; + overview: SystemAgentOverview; + history?: SystemAgentAssistantTurn[]; pendingOperation?: string; }): string { const agents = params.overview.agents @@ -169,9 +169,9 @@ export function buildCrestodianAssistantUserPrompt(params: { } /** Parse compact assistant JSON while ignoring surrounding explanatory text. */ -export function parseCrestodianAssistantPlanText( +export function parseSystemAgentAssistantPlanText( rawText: string | undefined, -): CrestodianAssistantPlan | null { +): SystemAgentAssistantPlan | null { const text = rawText?.trim(); if (!text) { return null; diff --git a/src/crestodian/assistant.configured.test.ts b/src/system-agent/assistant.configured.test.ts similarity index 82% rename from src/crestodian/assistant.configured.test.ts rename to src/system-agent/assistant.configured.test.ts index af66a60884a0..1a619ee59c42 100644 --- a/src/crestodian/assistant.configured.test.ts +++ b/src/system-agent/assistant.configured.test.ts @@ -1,14 +1,14 @@ -// Configured Crestodian assistant tests cover route-owned, tool-free planning. +// Configured OpenClaw assistant tests cover route-owned, tool-free planning. import { describe, expect, it, vi } from "vitest"; import type { RunCliAgentParams } from "../agents/cli-runner/types.js"; import { fingerprintResolvedProviderAuth } from "../agents/execution-auth-binding.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { planCrestodianCommandWithConfiguredModel } from "./assistant.js"; -import { createCrestodianVerifiedInferenceTestFixture } from "./crestodian.test-helpers.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; -import { resolveCrestodianConfiguredRouteFromConfig } from "./inference-route.js"; -import type { CrestodianOverview } from "./overview.js"; -import { createCrestodianVerifiedInferenceBinding } from "./verified-inference.js"; +import { planSystemAgentCommandWithConfiguredModel } from "./assistant.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; +import { resolveSystemAgentConfiguredRouteFromConfig } from "./inference-route.js"; +import type { SystemAgentOverview } from "./overview.js"; +import { createSystemAgentVerifiedInferenceTestFixture } from "./system-agent.test-helpers.js"; +import { createSystemAgentVerifiedInferenceBinding } from "./verified-inference.js"; vi.mock("../plugins/providers.js", async (importOriginal) => ({ ...(await importOriginal()), @@ -23,7 +23,7 @@ vi.mock("../agents/harness/runtime-plugin.js", async (importOriginal) => ({ ), })); -function overview(defaultModel?: string): CrestodianOverview { +function overview(defaultModel?: string): SystemAgentOverview { return { config: { path: "/tmp/openclaw.json", @@ -62,20 +62,20 @@ function snapshot(config: OpenClawConfig) { }; } -describe("Crestodian configured-model planner", () => { +describe("OpenClaw configured-model planner", () => { it("rejects a low-level missing binding before config lookup or model execution", async () => { const readConfigFileSnapshot = vi.fn(); const runCliAgent = vi.fn(); const runEmbeddedAgent = vi.fn(); await expect( - planCrestodianCommandWithConfiguredModel({ + planSystemAgentCommandWithConfiguredModel({ input: "please finish setup", overview: overview(), verifiedInference: undefined as never, deps: { readConfigFileSnapshot, runCliAgent, runEmbeddedAgent }, }), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(readConfigFileSnapshot).not.toHaveBeenCalled(); expect(runCliAgent).not.toHaveBeenCalled(); @@ -94,7 +94,7 @@ describe("Crestodian configured-model planner", () => { profiles: { "openai:p2": { provider: "openai", mode: "api_key" } }, }, } satisfies OpenClawConfig; - const configuredRoute = await resolveCrestodianConfiguredRouteFromConfig(config); + const configuredRoute = await resolveSystemAgentConfiguredRouteFromConfig(config); if (!configuredRoute) { throw new Error("missing test route"); } @@ -121,7 +121,7 @@ describe("Crestodian configured-model planner", () => { if (!authFingerprint) { throw new Error("missing test auth fingerprint"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute, executionRoute: { ...configuredRoute, authProfileId: "openai:p2" }, auth: { authProfileId: "openai:p2", authFingerprint }, @@ -131,7 +131,7 @@ describe("Crestodian configured-model planner", () => { payloads: [{ text: '{"reply":"Ready.","command":"gateway status"}' }], })); - const result = await planCrestodianCommandWithConfiguredModel({ + const result = await planSystemAgentCommandWithConfiguredModel({ input: "check the gateway", overview: overview("openai/gpt-5.5"), verifiedInference: binding, @@ -139,7 +139,7 @@ describe("Crestodian configured-model planner", () => { ...authDeps, readConfigFileSnapshot: vi.fn(async () => snapshot(config)) as never, runEmbeddedAgent: runEmbeddedAgent as never, - createTempDir: async () => "/tmp/crestodian-planner", + createTempDir: async () => "/tmp/openclaw-planner", removeTempDir: async () => {}, }, }); @@ -158,7 +158,7 @@ describe("Crestodian configured-model planner", () => { const config = { agents: { defaults: { model: "openai/gpt-5.5" } }, } satisfies OpenClawConfig; - const { binding, deps } = await createCrestodianVerifiedInferenceTestFixture(config); + const { binding, deps } = await createSystemAgentVerifiedInferenceTestFixture(config); const runCliAgent = vi.fn(); const runEmbeddedAgent = vi.fn(); const readConfigFileSnapshot = vi.fn(async () => ({ @@ -167,7 +167,7 @@ describe("Crestodian configured-model planner", () => { })); await expect( - planCrestodianCommandWithConfiguredModel({ + planSystemAgentCommandWithConfiguredModel({ input: "please set up my model", overview: overview(), verifiedInference: binding, @@ -178,7 +178,7 @@ describe("Crestodian configured-model planner", () => { runEmbeddedAgent, }, }), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(readConfigFileSnapshot).toHaveBeenCalledOnce(); expect(runCliAgent).not.toHaveBeenCalled(); @@ -192,14 +192,14 @@ describe("Crestodian configured-model planner", () => { const changedConfig = { agents: { defaults: { model: "anthropic/claude-opus-4-8" } }, } satisfies OpenClawConfig; - const { binding, deps } = await createCrestodianVerifiedInferenceTestFixture(config); + const { binding, deps } = await createSystemAgentVerifiedInferenceTestFixture(config); let currentConfig: OpenClawConfig = config; const runEmbeddedAgent = vi.fn(async () => ({ payloads: [{ text: '{"reply":"Ready."}' }], })); await expect( - planCrestodianCommandWithConfiguredModel({ + planSystemAgentCommandWithConfiguredModel({ input: "is the gateway healthy", overview: overview("openai/gpt-5.5"), verifiedInference: binding, @@ -207,13 +207,13 @@ describe("Crestodian configured-model planner", () => { ...deps, readConfigFileSnapshot: vi.fn(async () => snapshot(currentConfig)) as never, runEmbeddedAgent: runEmbeddedAgent as never, - createTempDir: async () => "/tmp/crestodian-planner", + createTempDir: async () => "/tmp/openclaw-planner", removeTempDir: async () => { currentConfig = changedConfig; }, }, }), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(runEmbeddedAgent).toHaveBeenCalledOnce(); }); @@ -238,9 +238,9 @@ describe("Crestodian configured-model planner", () => { }, })); const removeTempDir = vi.fn(async () => {}); - const { binding, deps } = await createCrestodianVerifiedInferenceTestFixture(config); + const { binding, deps } = await createSystemAgentVerifiedInferenceTestFixture(config); - const result = await planCrestodianCommandWithConfiguredModel({ + const result = await planSystemAgentCommandWithConfiguredModel({ input: "please finish setup", overview: overview("claude-cli/claude-opus-4-8"), verifiedInference: binding, @@ -249,7 +249,7 @@ describe("Crestodian configured-model planner", () => { readConfigFileSnapshot: vi.fn(async () => snapshot(config)) as never, runCliAgent: runCliAgent as never, runEmbeddedAgent: vi.fn() as never, - createTempDir: async () => "/tmp/crestodian-planner", + createTempDir: async () => "/tmp/openclaw-planner", removeTempDir, }, }); @@ -267,13 +267,13 @@ describe("Crestodian configured-model planner", () => { authProfileId: "claude-cli:ops", executionMode: "side-question", disableTools: true, - workspaceDir: "/tmp/crestodian-planner", - cwd: "/tmp/crestodian-planner", + workspaceDir: "/tmp/openclaw-planner", + cwd: "/tmp/openclaw-planner", cleanupCliLiveSessionOnRunEnd: true, }), ); expect(runCliAgent.mock.calls[0]?.[0]?.toolsAllow).toBeUndefined(); - expect(removeTempDir).toHaveBeenCalledWith("/tmp/crestodian-planner"); + expect(removeTempDir).toHaveBeenCalledWith("/tmp/openclaw-planner"); }); it("plans through the configured default agent embedded runtime without tools", async () => { @@ -293,9 +293,9 @@ describe("Crestodian configured-model planner", () => { const runEmbeddedAgent = vi.fn(async () => ({ payloads: [{ text: '{"reply":"Ready.","command":"gateway status"}' }], })); - const { binding, deps } = await createCrestodianVerifiedInferenceTestFixture(config); + const { binding, deps } = await createSystemAgentVerifiedInferenceTestFixture(config); - const result = await planCrestodianCommandWithConfiguredModel({ + const result = await planSystemAgentCommandWithConfiguredModel({ input: "is the gateway healthy", overview: overview("openai/gpt-5.4"), verifiedInference: binding, @@ -304,7 +304,7 @@ describe("Crestodian configured-model planner", () => { readConfigFileSnapshot: vi.fn(async () => snapshot(config)) as never, runCliAgent: vi.fn() as never, runEmbeddedAgent: runEmbeddedAgent as never, - createTempDir: async () => "/tmp/crestodian-planner", + createTempDir: async () => "/tmp/openclaw-planner", removeTempDir: async () => {}, }, }); @@ -346,12 +346,12 @@ describe("Crestodian configured-model planner", () => { ], }, } satisfies OpenClawConfig; - const { binding, deps } = await createCrestodianVerifiedInferenceTestFixture(config); + const { binding, deps } = await createSystemAgentVerifiedInferenceTestFixture(config); const runEmbeddedAgent = vi.fn(async () => ({ payloads: [{ text: '{"reply":"Ready.","command":"gateway status"}' }], })); - await planCrestodianCommandWithConfiguredModel({ + await planSystemAgentCommandWithConfiguredModel({ input: "is the gateway healthy", overview: overview("openai/gpt-5.5"), verifiedInference: binding, @@ -359,7 +359,7 @@ describe("Crestodian configured-model planner", () => { ...deps, readConfigFileSnapshot: vi.fn(async () => snapshot(config)) as never, runEmbeddedAgent: runEmbeddedAgent as never, - createTempDir: async () => "/tmp/crestodian-planner", + createTempDir: async () => "/tmp/openclaw-planner", removeTempDir: async () => {}, }, }); diff --git a/src/crestodian/assistant.test.ts b/src/system-agent/assistant.test.ts similarity index 79% rename from src/crestodian/assistant.test.ts rename to src/system-agent/assistant.test.ts index b5f0fead8e93..ca6ee694297c 100644 --- a/src/crestodian/assistant.test.ts +++ b/src/system-agent/assistant.test.ts @@ -1,12 +1,12 @@ -// Crestodian assistant tests cover plan parsing and inference prompt construction. +// OpenClaw assistant tests cover plan parsing and inference prompt construction. import { describe, expect, it } from "vitest"; import { - buildCrestodianAssistantUserPrompt, - parseCrestodianAssistantPlanText, + buildSystemAgentAssistantUserPrompt, + parseSystemAgentAssistantPlanText, } from "./assistant.js"; -import type { CrestodianOverview } from "./overview.js"; +import type { SystemAgentOverview } from "./overview.js"; -function overview(overrides: Partial = {}): CrestodianOverview { +function overview(overrides: Partial = {}): SystemAgentOverview { return { config: { path: "/tmp/openclaw.json", @@ -36,10 +36,10 @@ function overview(overrides: Partial = {}): Crestod }; } -describe("Crestodian assistant", () => { +describe("OpenClaw assistant", () => { it("parses the first compact JSON command", () => { expect( - parseCrestodianAssistantPlanText( + parseSystemAgentAssistantPlanText( 'thinking... {"reply":"Aye aye.","command":"restart gateway"}', ), ).toEqual({ @@ -49,15 +49,15 @@ describe("Crestodian assistant", () => { }); it("rejects non-JSON and empty plans but accepts chat-only replies", () => { - expect(parseCrestodianAssistantPlanText("I would edit config directly.")).toBeNull(); - expect(parseCrestodianAssistantPlanText("{}")).toBeNull(); - expect(parseCrestodianAssistantPlanText('{"reply":"just chatting"}')).toEqual({ + expect(parseSystemAgentAssistantPlanText("I would edit config directly.")).toBeNull(); + expect(parseSystemAgentAssistantPlanText("{}")).toBeNull(); + expect(parseSystemAgentAssistantPlanText('{"reply":"just chatting"}')).toEqual({ reply: "just chatting", }); }); it("includes only operational summary context in planner prompts", () => { - const prompt = buildCrestodianAssistantUserPrompt({ + const prompt = buildSystemAgentAssistantUserPrompt({ input: "fix my setup", overview: { ...overview({ @@ -101,7 +101,7 @@ describe("Crestodian assistant", () => { it("keeps truncated conversation history valid at a UTF-16 boundary", () => { const prefix = "a".repeat(499); - const prompt = buildCrestodianAssistantUserPrompt({ + const prompt = buildSystemAgentAssistantUserPrompt({ input: "continue", overview: overview(), history: [{ role: "user", text: `${prefix}🎉tail` }], diff --git a/src/crestodian/assistant.ts b/src/system-agent/assistant.ts similarity index 56% rename from src/crestodian/assistant.ts rename to src/system-agent/assistant.ts index 6a704c6a36c0..d3bfaf0bd4a3 100644 --- a/src/crestodian/assistant.ts +++ b/src/system-agent/assistant.ts @@ -1,98 +1,98 @@ -// Crestodian assistant planning converts fuzzy user text into one safe command. +// OpenClaw assistant planning converts fuzzy user text into one safe command. import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { - CRESTODIAN_ASSISTANT_SYSTEM_PROMPT, - CRESTODIAN_ASSISTANT_TIMEOUT_MS, - buildCrestodianAssistantUserPrompt, - parseCrestodianAssistantPlanText, - type CrestodianAssistantPlan, - type CrestodianAssistantTurn, + SYSTEM_AGENT_ASSISTANT_SYSTEM_PROMPT, + SYSTEM_AGENT_ASSISTANT_TIMEOUT_MS, + buildSystemAgentAssistantUserPrompt, + parseSystemAgentAssistantPlanText, + type SystemAgentAssistantPlan, + type SystemAgentAssistantTurn, } from "./assistant-prompts.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; -import type { CrestodianOverview } from "./overview.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; +import type { SystemAgentOverview } from "./overview.js"; import { - resolveCrestodianExpectedAgentHarnessRuntimeArtifact, - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceDeps, + resolveSystemAgentExpectedAgentHarnessRuntimeArtifact, + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceDeps, } from "./verified-inference.js"; export { - buildCrestodianAssistantUserPrompt, - parseCrestodianAssistantPlanText, - type CrestodianAssistantPlan, - type CrestodianAssistantTurn, + buildSystemAgentAssistantUserPrompt, + parseSystemAgentAssistantPlanText, + type SystemAgentAssistantPlan, + type SystemAgentAssistantTurn, } from "./assistant-prompts.js"; -export type CrestodianAssistantPlanner = (params: { +export type SystemAgentAssistantPlanner = (params: { input: string; - overview: CrestodianOverview; - history?: CrestodianAssistantTurn[]; + overview: SystemAgentOverview; + history?: SystemAgentAssistantTurn[]; pendingOperation?: string; - readonly verifiedInference: CrestodianVerifiedInferenceBinding; -}) => Promise; + readonly verifiedInference: SystemAgentVerifiedInferenceBinding; +}) => Promise; type RunCliAgentFn = typeof import("../agents/cli-runner.js").runCliAgent; type RunEmbeddedAgentFn = typeof import("../agents/embedded-agent.js").runEmbeddedAgent; -export type CrestodianConfiguredModelPlannerDeps = CrestodianVerifiedInferenceDeps & { +export type SystemAgentConfiguredModelPlannerDeps = SystemAgentVerifiedInferenceDeps & { runCliAgent?: RunCliAgentFn; runEmbeddedAgent?: RunEmbeddedAgentFn; createTempDir?: () => Promise; removeTempDir?: (dir: string) => Promise; }; -export async function planCrestodianCommand(params: { +export async function planSystemAgentCommand(params: { input: string; - overview: CrestodianOverview; - history?: CrestodianAssistantTurn[]; + overview: SystemAgentOverview; + history?: SystemAgentAssistantTurn[]; pendingOperation?: string; - readonly verifiedInference: CrestodianVerifiedInferenceBinding; - deps?: CrestodianConfiguredModelPlannerDeps; -}): Promise { - return await planCrestodianCommandWithConfiguredModel(params); + readonly verifiedInference: SystemAgentVerifiedInferenceBinding; + deps?: SystemAgentConfiguredModelPlannerDeps; +}): Promise { + return await planSystemAgentCommandWithConfiguredModel(params); } /** Plan only through the configured default agent's verified route. */ -export async function planCrestodianCommandWithConfiguredModel(params: { +export async function planSystemAgentCommandWithConfiguredModel(params: { input: string; - overview: CrestodianOverview; - history?: CrestodianAssistantTurn[]; + overview: SystemAgentOverview; + history?: SystemAgentAssistantTurn[]; pendingOperation?: string; - readonly verifiedInference: CrestodianVerifiedInferenceBinding; - deps?: CrestodianConfiguredModelPlannerDeps; -}): Promise { + readonly verifiedInference: SystemAgentVerifiedInferenceBinding; + deps?: SystemAgentConfiguredModelPlannerDeps; +}): Promise { const route = await requireVerifiedPlannerRoute(params.verifiedInference, params.deps); const input = params.input.trim(); if (!input) { return null; } let expectedAgentHarnessRuntimeArtifact: ReturnType< - typeof resolveCrestodianExpectedAgentHarnessRuntimeArtifact + typeof resolveSystemAgentExpectedAgentHarnessRuntimeArtifact >; try { - expectedAgentHarnessRuntimeArtifact = resolveCrestodianExpectedAgentHarnessRuntimeArtifact( + expectedAgentHarnessRuntimeArtifact = resolveSystemAgentExpectedAgentHarnessRuntimeArtifact( params.verifiedInference, ); } catch (error) { - throw new CrestodianInferenceUnavailableError("planner", [error]); + throw new SystemAgentInferenceUnavailableError("planner", [error]); } - const prompt = buildCrestodianAssistantUserPrompt({ + const prompt = buildSystemAgentAssistantUserPrompt({ input, overview: params.overview, ...(params.history ? { history: params.history } : {}), ...(params.pendingOperation ? { pendingOperation: params.pendingOperation } : {}), }); const tempDir = await (params.deps?.createTempDir ?? createTempPlannerDir)(); - let plan: CrestodianAssistantPlan | null; + let plan: SystemAgentAssistantPlan | null; try { - const runId = `crestodian-planner-${randomUUID()}`; + const runId = `openclaw-planner-${randomUUID()}`; const shared = { sessionId: `${runId}-session`, - agentId: "crestodian", + agentId: "openclaw", trigger: "manual" as const, sessionFile: path.join(tempDir, "session.jsonl"), workspaceDir: tempDir, @@ -102,12 +102,12 @@ export async function planCrestodianCommandWithConfiguredModel(params: { prompt, provider: route.provider, model: route.model, - timeoutMs: CRESTODIAN_ASSISTANT_TIMEOUT_MS, + timeoutMs: SYSTEM_AGENT_ASSISTANT_TIMEOUT_MS, runId, - extraSystemPrompt: CRESTODIAN_ASSISTANT_SYSTEM_PROMPT, - extraSystemPromptStatic: CRESTODIAN_ASSISTANT_SYSTEM_PROMPT, - messageChannel: "crestodian", - messageProvider: "crestodian", + extraSystemPrompt: SYSTEM_AGENT_ASSISTANT_SYSTEM_PROMPT, + extraSystemPromptStatic: SYSTEM_AGENT_ASSISTANT_SYSTEM_PROMPT, + messageChannel: "openclaw", + messageProvider: "openclaw", disableTools: true, disableTrajectory: true, ...(route.authProfileId ? { authProfileId: route.authProfileId } : {}), @@ -132,10 +132,10 @@ export async function planCrestodianCommandWithConfiguredModel(params: { cleanupBundleMcpOnRunEnd: true, ...(route.authProfileId ? { authProfileIdSource: "user" as const } : {}), }); - const parsed = parseCrestodianAssistantPlanText(extractPlannerResultText(result)); + const parsed = parseSystemAgentAssistantPlanText(extractPlannerResultText(result)); plan = parsed ? { ...parsed, modelLabel: route.modelLabel } : null; } catch (error) { - if (error instanceof CrestodianInferenceUnavailableError) { + if (error instanceof SystemAgentInferenceUnavailableError) { throw error; } plan = null; @@ -151,25 +151,25 @@ export async function planCrestodianCommandWithConfiguredModel(params: { } async function requireVerifiedPlannerRoute( - binding: CrestodianVerifiedInferenceBinding | undefined, - deps: CrestodianConfiguredModelPlannerDeps | undefined, + binding: SystemAgentVerifiedInferenceBinding | undefined, + deps: SystemAgentConfiguredModelPlannerDeps | undefined, ) { if (!binding) { - throw new CrestodianInferenceUnavailableError("planner"); + throw new SystemAgentInferenceUnavailableError("planner"); } try { - const route = await resolveCrestodianVerifiedInferenceRoute(binding, deps); + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, deps); if (route) { return route; } } catch (error) { - throw new CrestodianInferenceUnavailableError("planner", [error]); + throw new SystemAgentInferenceUnavailableError("planner", [error]); } - throw new CrestodianInferenceUnavailableError("planner"); + throw new SystemAgentInferenceUnavailableError("planner"); } async function createTempPlannerDir(): Promise { - return await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-crestodian-planner-")); + return await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-openclaw-planner-")); } async function removeTempPlannerDir(dir: string): Promise { diff --git a/src/crestodian/audit.test.ts b/src/system-agent/audit.test.ts similarity index 74% rename from src/crestodian/audit.test.ts rename to src/system-agent/audit.test.ts index 48c071e7adfc..6a14181393a8 100644 --- a/src/crestodian/audit.test.ts +++ b/src/system-agent/audit.test.ts @@ -1,10 +1,10 @@ -// Crestodian audit tests cover filesystem-backed rescue audit scenarios. +// OpenClaw audit tests cover filesystem-backed rescue audit scenarios. import fs from "node:fs/promises"; import { afterEach, describe, expect, it, vi } from "vitest"; import { withTempDir } from "../test-helpers/temp-dir.js"; -import { appendCrestodianAuditEntry, resolveCrestodianAuditPath } from "./audit.js"; +import { appendSystemAgentAuditEntry, resolveSystemAgentAuditPath } from "./audit.js"; -describe("Crestodian audit log", () => { +describe("OpenClaw audit log", () => { const previousStateDir = process.env.OPENCLAW_STATE_DIR; afterEach(() => { @@ -16,17 +16,17 @@ describe("Crestodian audit log", () => { }); it("writes jsonl records under the OpenClaw audit dir", async () => { - await withTempDir({ prefix: "crestodian-audit-" }, async (tempDir) => { + await withTempDir({ prefix: "openclaw-audit-" }, async (tempDir) => { vi.stubEnv("OPENCLAW_STATE_DIR", tempDir); - const auditPath = await appendCrestodianAuditEntry({ + const auditPath = await appendSystemAgentAuditEntry({ operation: "config.setDefaultModel", summary: "Set default model to openai/gpt-5.2", configHashBefore: "before", configHashAfter: "after", }); - expect(auditPath).toBe(resolveCrestodianAuditPath()); + expect(auditPath).toBe(resolveSystemAgentAuditPath()); const lines = (await fs.readFile(auditPath, "utf8")).trim().split("\n"); expect(lines).toHaveLength(1); const entry = JSON.parse(lines[0] ?? "{}") as Record; diff --git a/src/crestodian/audit.ts b/src/system-agent/audit.ts similarity index 63% rename from src/crestodian/audit.ts rename to src/system-agent/audit.ts index 1f38492ac4a0..effa5d6c474f 100644 --- a/src/crestodian/audit.ts +++ b/src/system-agent/audit.ts @@ -1,16 +1,16 @@ -// Crestodian audit helpers append JSONL records for approved local-state changes. +// OpenClaw audit helpers append JSONL records for approved local-state changes. import fs from "node:fs/promises"; import path from "node:path"; import { resolveStateDir } from "../config/paths.js"; import { appendRegularFile } from "../infra/fs-safe.js"; /** - * Append-only audit log helpers for Crestodian writes. + * Append-only audit log helpers for OpenClaw writes. * * Discovery and read-only commands stay quiet; persistent operations append a * JSONL entry under the state directory with config hashes and redacted details. */ -type CrestodianAuditEntry = { +type SystemAgentAuditEntry = { timestamp: string; operation: string; summary: string; @@ -20,25 +20,25 @@ type CrestodianAuditEntry = { details?: Record; }; -/** Resolve the JSONL audit path for Crestodian persistent operations. */ -export function resolveCrestodianAuditPath( +/** Resolve the JSONL audit path for OpenClaw persistent operations. */ +export function resolveSystemAgentAuditPath( env: NodeJS.ProcessEnv = process.env, stateDir = resolveStateDir(env), ): string { - return path.join(stateDir, "audit", "crestodian.jsonl"); + return path.join(stateDir, "audit", "system-agent.jsonl"); } -/** Append one Crestodian audit entry and return the file path written. */ -export async function appendCrestodianAuditEntry( - entry: Omit, +/** Append one OpenClaw audit entry and return the file path written. */ +export async function appendSystemAgentAuditEntry( + entry: Omit, opts: { env?: NodeJS.ProcessEnv; auditPath?: string } = {}, ): Promise { - const auditPath = opts.auditPath ?? resolveCrestodianAuditPath(opts.env); + const auditPath = opts.auditPath ?? resolveSystemAgentAuditPath(opts.env); await fs.mkdir(path.dirname(auditPath), { recursive: true }); const line = JSON.stringify({ timestamp: new Date().toISOString(), ...entry, - } satisfies CrestodianAuditEntry); + } satisfies SystemAgentAuditEntry); // Audit writes reject symlinked parents so approval records cannot be redirected silently. await appendRegularFile({ filePath: auditPath, diff --git a/src/crestodian/chat-engine.channel-hooks.test.ts b/src/system-agent/chat-engine.channel-hooks.test.ts similarity index 89% rename from src/crestodian/chat-engine.channel-hooks.test.ts rename to src/system-agent/chat-engine.channel-hooks.test.ts index a9feaa43f147..75ab0d472508 100644 --- a/src/crestodian/chat-engine.channel-hooks.test.ts +++ b/src/system-agent/chat-engine.channel-hooks.test.ts @@ -1,10 +1,10 @@ import { beforeAll, describe, expect, it, vi } from "vitest"; import type { ConfigFileSnapshot, OpenClawConfig } from "../config/types.openclaw.js"; -import { CrestodianChatEngine } from "./chat-engine.js"; -import { createCrestodianVerifiedInferenceTestFixture } from "./crestodian.test-helpers.js"; +import { SystemAgentChatEngine } from "./chat-engine.js"; +import { createSystemAgentVerifiedInferenceTestFixture } from "./system-agent.test-helpers.js"; import type { - CrestodianVerifiedInferenceBinding, - CrestodianVerifiedInferenceDeps, + SystemAgentVerifiedInferenceBinding, + SystemAgentVerifiedInferenceDeps, } from "./verified-inference.js"; const verifiedInferenceConfig = { @@ -21,8 +21,8 @@ const verifiedInferenceConfig = { }, } satisfies OpenClawConfig; -let verifiedInference: CrestodianVerifiedInferenceBinding; -let verifiedInferenceDeps: CrestodianVerifiedInferenceDeps; +let verifiedInference: SystemAgentVerifiedInferenceBinding; +let verifiedInferenceDeps: SystemAgentVerifiedInferenceDeps; function verifiedConfigSnapshot(): ConfigFileSnapshot { const config = structuredClone(verifiedInferenceConfig); @@ -95,14 +95,14 @@ vi.mock("../config/config.js", async (importOriginal) => ({ })); beforeAll(async () => { - const fixture = await createCrestodianVerifiedInferenceTestFixture(verifiedInferenceConfig); + const fixture = await createSystemAgentVerifiedInferenceTestFixture(verifiedInferenceConfig); verifiedInference = fixture.binding; verifiedInferenceDeps = fixture.deps; }); -describe("Crestodian chat channel setup", () => { +describe("OpenClaw chat channel setup", () => { it("runs collected channel hooks after writing config", async () => { - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ verifiedInference, runAgentTurn: async () => null, planWithAssistant: async () => null, diff --git a/src/crestodian/chat-engine.test.ts b/src/system-agent/chat-engine.test.ts similarity index 91% rename from src/crestodian/chat-engine.test.ts rename to src/system-agent/chat-engine.test.ts index 9f64d93072fb..43694a9abba3 100644 --- a/src/crestodian/chat-engine.test.ts +++ b/src/system-agent/chat-engine.test.ts @@ -11,22 +11,22 @@ import { } from "../agents/execution-auth-binding.js"; import type { ConfigFileSnapshot, OpenClawConfig } from "../config/types.openclaw.js"; import type { WizardPrompter } from "../wizard/prompts.js"; -import { runCrestodianAgentTurnWithDeps } from "./agent-turn.js"; -import { classifyCrestodianApprovalText } from "./approval-intent.js"; +import { runSystemAgentTurnWithDeps } from "./agent-turn.js"; +import { classifySystemAgentApprovalText } from "./approval-intent.js"; import { - CrestodianChatEngine as RuntimeCrestodianChatEngine, - type CrestodianChatEngineOptions, + SystemAgentChatEngine as RuntimeSystemAgentChatEngine, + type SystemAgentChatEngineOptions, } from "./chat-engine.js"; -import { createCrestodianVerifiedInferenceTestFixture } from "./crestodian.test-helpers.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; import { - resolveCrestodianConfiguredRouteFromConfig, - type CrestodianConfiguredRoute, + resolveSystemAgentConfiguredRouteFromConfig, + type SystemAgentConfiguredRoute, } from "./inference-route.js"; +import { createSystemAgentVerifiedInferenceTestFixture } from "./system-agent.test-helpers.js"; import { - createCrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceDeps, + createSystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceDeps, } from "./verified-inference.js"; const mocks = vi.hoisted(() => ({ @@ -76,7 +76,7 @@ const sharedVerifiedInferenceConfig = { { id: "main", default: true, - agentDir: "/tmp/openclaw-crestodian-chat-engine-agent", + agentDir: "/tmp/openclaw-openclaw-chat-engine-agent", model: "openai/gpt-5.5", }, ], @@ -93,11 +93,11 @@ const sharedVerifiedInferenceConfig = { }, } satisfies OpenClawConfig; -let sharedVerifiedInference: CrestodianVerifiedInferenceBinding | undefined; -let sharedVerifiedInferenceDeps: CrestodianVerifiedInferenceDeps | undefined; +let sharedVerifiedInference: SystemAgentVerifiedInferenceBinding | undefined; +let sharedVerifiedInferenceDeps: SystemAgentVerifiedInferenceDeps | undefined; function useTempStateDir(): string { - const dir = fs.mkdtempSync(path.join(os.tmpdir(), "crestodian-engine-")); + const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-engine-")); tempDirs.push(dir); vi.stubEnv("OPENCLAW_STATE_DIR", dir); return dir; @@ -121,7 +121,7 @@ function configSnapshot(config: OpenClawConfig): ConfigFileSnapshot { }; } -function testHarnessBinding(route: CrestodianConfiguredRoute) { +function testHarnessBinding(route: SystemAgentConfiguredRoute) { if (route.runner !== "embedded") { return { auth: {}, deps: {} }; } @@ -145,7 +145,7 @@ function testHarnessBinding(route: CrestodianConfiguredRoute) { } async function createAmbientVerifiedBinding(config: OpenClawConfig) { - const route = await resolveCrestodianConfiguredRouteFromConfig(config); + const route = await resolveSystemAgentConfiguredRouteFromConfig(config); if (!route) { throw new Error("missing test route"); } @@ -158,7 +158,7 @@ async function createAmbientVerifiedBinding(config: OpenClawConfig) { throw new Error("missing test ambient auth fingerprint"); } const harnessBinding = testHarnessBinding(route); - return await createCrestodianVerifiedInferenceBinding({ + return await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { authFingerprint, ...harnessBinding.auth }, @@ -170,7 +170,7 @@ async function createOAuthVerifiedBinding( config: OpenClawConfig, credential: Parameters[0]["credential"], ) { - const route = await resolveCrestodianConfiguredRouteFromConfig(config); + const route = await resolveSystemAgentConfiguredRouteFromConfig(config); if (!route) { throw new Error("missing test OAuth route"); } @@ -180,7 +180,7 @@ async function createOAuthVerifiedBinding( throw new Error("missing test OAuth fingerprint"); } const harnessBinding = testHarnessBinding(route); - return await createCrestodianVerifiedInferenceBinding({ + return await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { authProfileId: profileId, authFingerprint, ...harnessBinding.auth }, @@ -195,7 +195,7 @@ async function createOAuthVerifiedBinding( } async function createCliVerifiedBinding(config: OpenClawConfig) { - const route = await resolveCrestodianConfiguredRouteFromConfig(config); + const route = await resolveSystemAgentConfiguredRouteFromConfig(config); if (!route || route.runner !== "cli") { throw new Error("missing test CLI route"); } @@ -211,11 +211,11 @@ async function createCliVerifiedBinding(config: OpenClawConfig) { if (!runtimeOwnerFingerprint) { throw new Error("missing test CLI runtime-owner fingerprint"); } - const deps: CrestodianVerifiedInferenceDeps = { + const deps: SystemAgentVerifiedInferenceDeps = { resolveCliRuntimeArtifactFingerprint: vi.fn(async () => runtimeArtifactFingerprint), resolveCliRuntimeOwnerFingerprint: vi.fn(async () => runtimeOwnerFingerprint), }; - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -230,13 +230,13 @@ async function createCliVerifiedBinding(config: OpenClawConfig) { return { binding, deps }; } -type TestCrestodianChatEngineOptions = Omit & { - verifiedInference?: CrestodianVerifiedInferenceBinding; +type TestSystemAgentChatEngineOptions = Omit & { + verifiedInference?: SystemAgentVerifiedInferenceBinding; }; /** Every ordinary engine test starts from a real, live-gate-shaped authority grant. */ -class CrestodianChatEngine extends RuntimeCrestodianChatEngine { - constructor(opts: TestCrestodianChatEngineOptions = {}) { +class SystemAgentChatEngine extends RuntimeSystemAgentChatEngine { + constructor(opts: TestSystemAgentChatEngineOptions = {}) { const explicitBinding = opts.verifiedInference; const verifiedInference = explicitBinding ?? sharedVerifiedInference; if (!verifiedInference) { @@ -261,7 +261,9 @@ class CrestodianChatEngine extends RuntimeCrestodianChatEngine { } beforeAll(async () => { - const fixture = await createCrestodianVerifiedInferenceTestFixture(sharedVerifiedInferenceConfig); + const fixture = await createSystemAgentVerifiedInferenceTestFixture( + sharedVerifiedInferenceConfig, + ); sharedVerifiedInference = fixture.binding; sharedVerifiedInferenceDeps = fixture.deps; mocks.readConfigFileSnapshot.mockResolvedValue( @@ -284,11 +286,11 @@ afterEach(() => { } }); -describe("CrestodianChatEngine", () => { +describe("SystemAgentChatEngine", () => { it("applies a seeded proposal on a bare yes with verified inference", async () => { useTempStateDir(); const runConfigSet = vi.fn(async () => {}); - const engine = new CrestodianChatEngine({ deps: { runConfigSet } }); + const engine = new SystemAgentChatEngine({ deps: { runConfigSet } }); const plan = engine.propose({ kind: "config-set", path: "gateway.port", value: "19001" }); expect(plan).toContain("gateway.port"); @@ -297,7 +299,7 @@ describe("CrestodianChatEngine", () => { const reply = await engine.handle("yes"); expect(runConfigSet).toHaveBeenCalledOnce(); expect(reply.action).toBe("none"); - expect(reply.text).toContain("[crestodian] done: config.set"); + expect(reply.text).toContain("[openclaw] done: config.set"); expect(engine.hasPendingProposal()).toBe(false); }); @@ -321,7 +323,7 @@ describe("CrestodianChatEngine", () => { const verifiedInference = await createAmbientVerifiedBinding(baseConfig); let currentConfig = baseConfig as OpenClawConfig; const runConfigSet = vi.fn(async () => {}); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ verifiedInference, classifyApproval: async () => { currentConfig = changedConfig; @@ -334,7 +336,7 @@ describe("CrestodianChatEngine", () => { }); engine.propose({ kind: "config-set", path: "gateway.port", value: "19001" }); - await expect(engine.handle("yes")).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + await expect(engine.handle("yes")).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(runConfigSet).not.toHaveBeenCalled(); }); @@ -348,7 +350,7 @@ describe("CrestodianChatEngine", () => { })); expect( () => - new RuntimeCrestodianChatEngine({ + new RuntimeSystemAgentChatEngine({ surface: "cli", runAgentTurn: async () => null, planWithAssistant: async () => null, @@ -356,13 +358,13 @@ describe("CrestodianChatEngine", () => { applySetup, loadOverview: fakeOverviewLoader(), }, - } as unknown as CrestodianChatEngineOptions), - ).toThrow(CrestodianInferenceUnavailableError); + } as unknown as SystemAgentChatEngineOptions), + ).toThrow(SystemAgentInferenceUnavailableError); expect(applySetup).not.toHaveBeenCalled(); }); it("routes model provider changes out of the active inference session", async () => { - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "gateway", runAgentTurn: async () => null, planWithAssistant: async () => null, @@ -375,11 +377,11 @@ describe("CrestodianChatEngine", () => { expect(reply.handoff).toBeUndefined(); expect(reply.sensitive).toBeUndefined(); expect(reply.text).toContain("replace the inference route powering this session"); - expect(reply.text).toContain("Exit Crestodian and run `openclaw onboard`"); + expect(reply.text).toContain("Exit OpenClaw and run `openclaw onboard`"); }); it("keeps the current inference route when model provider setup is declined", async () => { - const engine = new CrestodianChatEngine(); + const engine = new SystemAgentChatEngine(); engine.propose({ kind: "model-setup" }); const reply = await engine.handle("not now"); @@ -390,7 +392,7 @@ describe("CrestodianChatEngine", () => { it("drops the proposal when the user declines", async () => { const runConfigSet = vi.fn(async () => {}); - const engine = new CrestodianChatEngine({ deps: { runConfigSet } }); + const engine = new SystemAgentChatEngine({ deps: { runConfigSet } }); engine.propose({ kind: "config-set", path: "gateway.port", value: "19001" }); const reply = await engine.handle("no thanks"); @@ -411,9 +413,9 @@ describe("CrestodianChatEngine", () => { return { text: "Okay, leaving it as is." }; }, ); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: runAgentTurn as never, - classifyApproval: async ({ message }) => classifyCrestodianApprovalText(message), + classifyApproval: async ({ message }) => classifySystemAgentApprovalText(message), deps: { loadOverview: fakeOverviewLoader() }, }); @@ -430,7 +432,7 @@ describe("CrestodianChatEngine", () => { it("hosts a channel setup wizard as chat turns", async () => { useTempStateDir(); const wizardRuns: string[] = []; - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: async () => null, deps: { loadOverview: fakeOverviewLoader() }, @@ -502,7 +504,7 @@ describe("CrestodianChatEngine", () => { return nextConfig; }, ); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "gateway", runAgentTurn: async () => null, planWithAssistant: async () => null, @@ -578,7 +580,7 @@ describe("CrestodianChatEngine", () => { }, ); mocks.writeWizardConfigFile.mockImplementation(async (config: OpenClawConfig) => config); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "gateway", verifiedInference, runAgentTurn: async () => null, @@ -653,7 +655,7 @@ describe("CrestodianChatEngine", () => { await params?.beforePersistentEffect?.(); }, ); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "gateway", verifiedInference, runAgentTurn: async () => null, @@ -676,7 +678,7 @@ describe("CrestodianChatEngine", () => { it("marks sensitive hosted-wizard replies and auto-advances notes", async () => { useTempStateDir(); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "gateway", runAgentTurn: async () => null, planWithAssistant: async () => null, @@ -696,7 +698,7 @@ describe("CrestodianChatEngine", () => { it("routes sensitive CLI wizard prompts to the masked channel setup flow", async () => { useTempStateDir(); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "cli", runAgentTurn: async () => null, planWithAssistant: async () => null, @@ -739,7 +741,7 @@ describe("CrestodianChatEngine", () => { planWithAssistant: async () => null, deps: { loadOverview: fakeOverviewLoader() }, }; - const cli = new CrestodianChatEngine({ ...common, surface: "cli" }); + const cli = new SystemAgentChatEngine({ ...common, surface: "cli" }); for (const command of ["open setup wizard", "open classic wizard"]) { const cliReply = await cli.handle(command); expect(cliReply.action).toBe("none"); @@ -747,7 +749,7 @@ describe("CrestodianChatEngine", () => { expect(cliReply.text).toContain("run `openclaw onboard`"); } - const gateway = new CrestodianChatEngine({ ...common, surface: "gateway" }); + const gateway = new SystemAgentChatEngine({ ...common, surface: "gateway" }); const gatewayReply = await gateway.handle("open setup wizard"); expect(gatewayReply.action).toBe("none"); expect(gatewayReply.handoff).toBeUndefined(); @@ -773,10 +775,10 @@ describe("CrestodianChatEngine", () => { return { text: "No pending change." }; }, ); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "cli", runAgentTurn: runAgentTurn as never, - classifyApproval: async ({ message }) => classifyCrestodianApprovalText(message), + classifyApproval: async ({ message }) => classifySystemAgentApprovalText(message), deps: { loadOverview: fakeOverviewLoader() }, }); @@ -791,7 +793,7 @@ describe("CrestodianChatEngine", () => { it("keeps hosted-wizard validation errors on the current prompt", async () => { useTempStateDir(); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: async () => null, deps: { loadOverview: fakeOverviewLoader() }, @@ -814,7 +816,7 @@ describe("CrestodianChatEngine", () => { it("cancels a hosted wizard mid-flight", async () => { useTempStateDir(); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: async () => null, deps: { loadOverview: fakeOverviewLoader() }, @@ -835,9 +837,9 @@ describe("CrestodianChatEngine", () => { const runAgentTurn = vi.fn(async (params: { approvalArmed: boolean }) => ({ text: params.approvalArmed ? "unexpected approval" : "No pending change.", })); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: runAgentTurn as never, - classifyApproval: async ({ message }) => classifyCrestodianApprovalText(message), + classifyApproval: async ({ message }) => classifySystemAgentApprovalText(message), runChannelSetupWizard: async (_channel: string, prompter: WizardPrompter) => { await prompter.text({ message: "Bot token" }); }, @@ -871,9 +873,9 @@ describe("CrestodianChatEngine", () => { return { text: "No pending change." }; }, ); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: runAgentTurn as never, - classifyApproval: async ({ message }) => classifyCrestodianApprovalText(message), + classifyApproval: async ({ message }) => classifySystemAgentApprovalText(message), runChannelSetupWizard: async (_channel: string, prompter: WizardPrompter) => { await prompter.text({ message: "Bot token" }); }, @@ -890,7 +892,7 @@ describe("CrestodianChatEngine", () => { }); it("signals the exact agent handoff without an inference turn", async () => { - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: async () => null, deps: { loadOverview: fakeOverviewLoader() }, @@ -902,7 +904,7 @@ describe("CrestodianChatEngine", () => { it("handles the exact agent handoff without consulting a usable model", async () => { const runAgentTurn = vi.fn(async () => ({ text: "model reply without a directive" })); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn, deps: { loadOverview: fakeOverviewLoader() }, }); @@ -915,7 +917,7 @@ describe("CrestodianChatEngine", () => { }); it("executes an open-tui directive from the agent loop", async () => { - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => ({ text: "Handing you over. *waves claw*", directive: { kind: "open-tui" as const, agentId: "work" }, @@ -932,9 +934,9 @@ describe("CrestodianChatEngine", () => { const armed: boolean[] = []; let turn = 0; const classifyApproval = vi.fn(async ({ message }: { message: string }) => - classifyCrestodianApprovalText(message), + classifySystemAgentApprovalText(message), ); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async (params) => { turn += 1; armed.push(params.approvalArmed); @@ -962,7 +964,7 @@ describe("CrestodianChatEngine", () => { it("does not replay a failed host directive through the planner", async () => { const planner = vi.fn(async () => ({ reply: "should not run" })); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => ({ text: "Opening setup.", directive: { kind: "channel-setup" as const, channel: "telegram" }, @@ -981,7 +983,7 @@ describe("CrestodianChatEngine", () => { }); it("routes an inference-setup directive out of the agent loop", async () => { - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ surface: "cli", runAgentTurn: async () => ({ text: "Opening the menu wizard.", @@ -998,7 +1000,7 @@ describe("CrestodianChatEngine", () => { it("starts the channel wizard from an agent-loop directive", async () => { useTempStateDir(); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => ({ text: "Telegram it is — setup questions follow.", directive: { kind: "channel-setup" as const, channel: "telegram" }, @@ -1037,7 +1039,7 @@ describe("CrestodianChatEngine", () => { .mockResolvedValueOnce(configSnapshot(baseConfig)) .mockResolvedValue(configSnapshot(changedConfig)); const runChannelSetupWizard = vi.fn(async () => {}); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ verifiedInference, runAgentTurn: async () => ({ text: "Telegram it is.", @@ -1051,7 +1053,7 @@ describe("CrestodianChatEngine", () => { }); await expect(engine.handle("please connect a messaging channel")).rejects.toBeInstanceOf( - CrestodianInferenceUnavailableError, + SystemAgentInferenceUnavailableError, ); expect(runChannelSetupWizard).not.toHaveBeenCalled(); }); @@ -1071,7 +1073,7 @@ describe("CrestodianChatEngine", () => { const verifiedInference = await createOAuthVerifiedBinding(config, credential); const runConfigSet = vi.fn(async () => {}); let authReads = 0; - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ verifiedInference, runAgentTurn: async () => ({ text: "Applying the approved port change.", @@ -1097,7 +1099,7 @@ describe("CrestodianChatEngine", () => { }); await expect(engine.handle("yes, apply that exact port change")).rejects.toBeInstanceOf( - CrestodianInferenceUnavailableError, + SystemAgentInferenceUnavailableError, ); expect(runConfigSet).not.toHaveBeenCalled(); }); @@ -1118,7 +1120,7 @@ describe("CrestodianChatEngine", () => { }; const verifiedInference = await createOAuthVerifiedBinding(config, credential); const runConfigSet = vi.fn(async () => {}); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ verifiedInference, runAgentTurn: async () => { credential = { ...credential, access: "access-b", refresh: "refresh-b", expires: 2 }; @@ -1144,12 +1146,12 @@ describe("CrestodianChatEngine", () => { const reply = await engine.handle("yes, apply that exact port change"); expect(runConfigSet).toHaveBeenCalledOnce(); - expect(reply.text).toContain("[crestodian] done: config.set"); + expect(reply.text).toContain("[openclaw] done: config.set"); }); it("arms an agent turn when the classifier approves in the user's own words", async () => { const armedFlags: boolean[] = []; - let classifierBinding: CrestodianVerifiedInferenceBinding | undefined; + let classifierBinding: SystemAgentVerifiedInferenceBinding | undefined; const runAgentTurn = vi.fn( async (params: { approvalArmed: boolean; @@ -1160,7 +1162,7 @@ describe("CrestodianChatEngine", () => { return { text: "ok" }; }, ); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: runAgentTurn as never, classifyApproval: async ({ message, verifiedInference }) => { classifierBinding = verifiedInference; @@ -1177,7 +1179,7 @@ describe("CrestodianChatEngine", () => { }); it("clears a stale host proposal once the agent loop owns the conversation", async () => { - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async (params) => { params.session.proposalRef.current = "agent-proposal"; return { text: "loop reply" }; @@ -1195,7 +1197,7 @@ describe("CrestodianChatEngine", () => { it("keeps a host setup proposal when the loop only answers a question", async () => { let observedInput = ""; - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async (params) => { observedInput = params.input; return { text: "A workspace is where your agent keeps its project files." }; @@ -1230,7 +1232,7 @@ describe("CrestodianChatEngine", () => { lines: ["Workspace: /tmp/new-work"], })); let pendingOperation = ""; - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: async (params) => { pendingOperation = params.pendingOperation ?? ""; @@ -1274,7 +1276,7 @@ describe("CrestodianChatEngine", () => { it("tells the agent loop when a preserved host proposal was resolved", async () => { const observedInputs: string[] = []; const runConfigSet = vi.fn(async () => {}); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async (params) => { observedInputs.push(params.input); return { text: "answer" }; @@ -1302,7 +1304,7 @@ describe("CrestodianChatEngine", () => { return observedInputs.length === 1 ? null : { text: "native reply" }; }); const planner = vi.fn(async () => ({ reply: "planner fallback", modelLabel: "planner" })); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: runAgentTurn as never, planWithAssistant: planner, classifyApproval: async ({ message }) => (message === "yes" ? "approve" : "other"), @@ -1324,7 +1326,7 @@ describe("CrestodianChatEngine", () => { it("clears both proposal stores when the agent takes a directive", async () => { const armedFlags: boolean[] = []; - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async (params) => { armedFlags.push(params.approvalArmed); if (armedFlags.length === 1) { @@ -1351,7 +1353,7 @@ describe("CrestodianChatEngine", () => { it("never injects exact sensitive config JSON into a follow-up model turn", async () => { let observedInput = ""; const secret = "123:very-secret"; - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async (params) => { observedInput = params.input; return { text: "That is the Telegram bot credential." }; @@ -1372,7 +1374,7 @@ describe("CrestodianChatEngine", () => { const runAgentTurn = vi.fn(async () => ({ text: "should never run" })); const planner = vi.fn(async () => ({ reply: "should never run" })); const runConfigSet = vi.fn(async () => {}); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: runAgentTurn as never, planWithAssistant: planner as never, deps: { runConfigSet, loadOverview: fakeOverviewLoader() }, @@ -1388,14 +1390,14 @@ describe("CrestodianChatEngine", () => { const applied = await engine.handle("yes"); expect(runConfigSet).toHaveBeenCalledOnce(); - expect(applied.text).toContain("[crestodian] done: config.set"); + expect(applied.text).toContain("[openclaw] done: config.set"); }); it("redacts sensitive config-set values from the AI-visible history", async () => { const planner = vi.fn(async (_params: { history?: Array<{ role: string; text: string }> }) => ({ reply: "noted", })); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: planner as never, classifyApproval: async () => "other", @@ -1424,7 +1426,7 @@ describe("CrestodianChatEngine", () => { }), ); const planner = vi.fn(async () => null); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn, planWithAssistant: planner, surface: "gateway", @@ -1443,7 +1445,7 @@ describe("CrestodianChatEngine", () => { expect(call.surface).toBe("gateway"); // A question is not consent: mutations stay locked for this turn. expect(call.approvalArmed).toBe(false); - expect(call.session.sessionId).toMatch(/^crestodian-/); + expect(call.session.sessionId).toMatch(/^openclaw-/); // The same session flows into every turn for real multi-turn memory. await engine.handle("and the gateway?"); expect(runAgentTurn.mock.calls[1]?.[0]).toMatchObject({ @@ -1451,13 +1453,13 @@ describe("CrestodianChatEngine", () => { }); }); - it("answers fuzzy messages through the AI custodian with conversation history", async () => { + it("answers fuzzy messages through the system agent with conversation history", async () => { const planner = vi.fn( async (_params: { input: string; history?: Array<{ role: string; text: string }> }) => ({ - reply: "I'm your setup custodian. Nothing changes without your yes.", + reply: "I'm your system agent. Nothing changes without your yes.", }), ); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: planner, deps: { loadOverview: fakeOverviewLoader() }, @@ -1466,7 +1468,7 @@ describe("CrestodianChatEngine", () => { const reply = await engine.handle("what are you going to do to my machine?"); - expect(reply.text).toContain("setup custodian"); + expect(reply.text).toContain("system agent"); expect(reply.action).toBe("none"); const call = expectDefined(planner.mock.calls[0], "planner.mock.calls[0] test invariant")[0]; expect(call.input).toContain("machine"); @@ -1496,7 +1498,7 @@ describe("CrestodianChatEngine", () => { currentConfig = changedConfig; return { reply: "stale reply" }; }); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ verifiedInference, runAgentTurn: async () => null, planWithAssistant: planner, @@ -1507,7 +1509,7 @@ describe("CrestodianChatEngine", () => { }); await expect(engine.handle("what should I do next?")).rejects.toBeInstanceOf( - CrestodianInferenceUnavailableError, + SystemAgentInferenceUnavailableError, ); }); @@ -1517,7 +1519,7 @@ describe("CrestodianChatEngine", () => { command: "set default model openai/gpt-5.5", modelLabel: "claude-cli", })); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: planner, deps: { loadOverview: fakeOverviewLoader() }, @@ -1535,7 +1537,7 @@ describe("CrestodianChatEngine", () => { const planner = vi.fn(async (_params: { input: string; pendingOperation?: string }) => ({ reply: "A workspace is where your agent keeps its files.", })); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: planner, classifyApproval: async () => "other", @@ -1576,7 +1578,7 @@ describe("CrestodianChatEngine", () => { issues: [{ path: "gateway.port", message: "Expected number, received string" }], } as never); }); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: planner as never, deps: { runConfigSet: runInvalidConfigSet, loadOverview: fakeOverviewLoader() }, @@ -1607,9 +1609,9 @@ describe("CrestodianChatEngine", () => { issues: [{ path: "gateway.port", message: "Expected number, received string" }], } as never); }); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => { - throw new CrestodianInferenceUnavailableError("agent-turn"); + throw new SystemAgentInferenceUnavailableError("agent-turn"); }, planWithAssistant: async () => null, deps: { runConfigSet: runInvalidConfigSet, loadOverview: fakeOverviewLoader() }, @@ -1637,7 +1639,7 @@ describe("CrestodianChatEngine", () => { issues: [], } as never); }); - const engine = new CrestodianChatEngine({ deps: { runConfigSet } }); + const engine = new SystemAgentChatEngine({ deps: { runConfigSet } }); engine.propose({ kind: "config-set", path: "gateway.port", value: "18789" }); const reply = await engine.handle("yes"); @@ -1665,7 +1667,7 @@ describe("CrestodianChatEngine", () => { .mockResolvedValueOnce(validSnapshot) .mockRejectedValueOnce(new Error("snapshot read failed")); const runConfigSet = vi.fn(async () => {}); - const engine = new CrestodianChatEngine({ deps: { runConfigSet } }); + const engine = new SystemAgentChatEngine({ deps: { runConfigSet } }); engine.propose({ kind: "config-set", path: "gateway.port", value: "18789" }); const reply = await engine.handle("yes"); @@ -1681,7 +1683,7 @@ describe("CrestodianChatEngine", () => { useTempStateDir(); const runConfigSet = vi.fn(async () => {}); const planner = vi.fn(async () => null); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: planner as never, deps: { runConfigSet, loadOverview: fakeOverviewLoader() }, @@ -1696,19 +1698,19 @@ describe("CrestodianChatEngine", () => { it("fails closed when neither inference path is usable", async () => { const planner = vi.fn(async () => null); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ runAgentTurn: async () => null, planWithAssistant: planner, deps: { loadOverview: fakeOverviewLoader() }, }); await expect(engine.handle("please make everything nice")).rejects.toBeInstanceOf( - CrestodianInferenceUnavailableError, + SystemAgentInferenceUnavailableError, ); }); }); -describe("Crestodian agent loop backends", () => { +describe("OpenClaw agent loop backends", () => { it("runs a configured claude-cli model through the CLI loop with the ring-zero MCP tool", async () => { useTempStateDir(); const config = { @@ -1730,10 +1732,10 @@ describe("Crestodian agent loop backends", () => { meta: { agentMeta: { cliSessionBinding: { sessionId: "native-1" } } }, })); const planner = vi.fn(async () => null); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ verifiedInference: inference.binding, runAgentTurn: (params) => - runCrestodianAgentTurnWithDeps(params, { + runSystemAgentTurnWithDeps(params, { ...inferenceDeps, runCliAgent: runCliAgent as never, }), @@ -1754,7 +1756,7 @@ describe("Crestodian agent loop backends", () => { )[0]; expect(call.provider).toBe("claude-cli"); expect(call.model).toBe("claude-opus-4-8"); - expect(call.crestodianTool).toEqual({ + expect(call.systemAgentTool).toEqual({ surface: "cli", approvalArmed: false, proposalRef: {}, @@ -1793,10 +1795,10 @@ describe("Crestodian agent loop backends", () => { throw new Error("claude exploded"); }); const planner = vi.fn(async () => ({ reply: "planner fallback reply" })); - const engine = new CrestodianChatEngine({ + const engine = new SystemAgentChatEngine({ verifiedInference: inference.binding, runAgentTurn: (params) => - runCrestodianAgentTurnWithDeps(params, { + runSystemAgentTurnWithDeps(params, { ...inferenceDeps, runCliAgent: runCliAgent as never, }), diff --git a/src/crestodian/chat-engine.ts b/src/system-agent/chat-engine.ts similarity index 83% rename from src/crestodian/chat-engine.ts rename to src/system-agent/chat-engine.ts index 478f0bd6c7bc..c9ed26b3cbf3 100644 --- a/src/crestodian/chat-engine.ts +++ b/src/system-agent/chat-engine.ts @@ -1,60 +1,60 @@ -// Crestodian chat engine: transport-agnostic conversation over typed operations. +// OpenClaw chat engine: transport-agnostic conversation over typed operations. import { isSensitiveConfigPath } from "../config/sensitive-paths.js"; import type { RuntimeEnv } from "../runtime.js"; import { WizardSession, type WizardStep } from "../wizard/session.js"; import { - cleanupCrestodianAgentSession, - createCrestodianAgentSession, - runCrestodianAgentTurn, - type CrestodianAgentSession, - type CrestodianAgentTurnRunner, + cleanupSystemAgentSession, + createSystemAgentSession, + runSystemAgentTurn, + type SystemAgentSession, + type SystemAgentTurnRunner, } from "./agent-turn.js"; import { - classifyCrestodianApprovalText, - type CrestodianApprovalClassifier, - type CrestodianApprovalIntent, + classifySystemAgentApprovalText, + type SystemAgentApprovalClassifier, + type SystemAgentApprovalIntent, } from "./approval-intent.js"; -import type { CrestodianAssistantPlanner, CrestodianAssistantTurn } from "./assistant.js"; +import type { SystemAgentAssistantPlanner, SystemAgentAssistantTurn } from "./assistant.js"; import { approvalQuestion } from "./dialogue.js"; import { - CrestodianInferenceUnavailableError, - isCrestodianInferenceUnavailableError, + SystemAgentInferenceUnavailableError, + isSystemAgentInferenceUnavailableError, } from "./inference-error.js"; import { - describeCrestodianPersistentOperation, - executeCrestodianOperation, - isPersistentCrestodianOperation, - parseCrestodianOperation, - type CrestodianCommandDeps, - type CrestodianOperation, - type CrestodianOperationResult, + describeSystemAgentPersistentOperation, + executeSystemAgentOperation, + isPersistentSystemAgentOperation, + parseSystemAgentOperation, + type SystemAgentCommandDeps, + type SystemAgentOperation, + type SystemAgentOperationResult, } from "./operations.js"; -import { loadCrestodianOverview, type CrestodianOverview } from "./overview.js"; +import { loadSystemAgentOverview, type SystemAgentOverview } from "./overview.js"; import { - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceBinding, + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceBinding, } from "./verified-inference.js"; /** - * One conversation with Crestodian, independent of transport. The TUI backend - * and the gateway `crestodian.chat` RPC both drive this engine, so onboarding + * One conversation with OpenClaw, independent of transport. The TUI backend + * and the gateway `openclaw.chat` RPC both drive this engine, so onboarding * behaves the same in a terminal and in the macOS app. * * The conversation is AI-backed: free-form messages run through the agent loop * first and the single-turn planner second. Approval of pending mutations is * judged from the user's own words by a host-run classifier — never by the * conversation model itself, which cannot self-approve (see - * crestodian-tool.ts). Hosted wizard replies and host navigation remain + * system-agent-tool.ts). Hosted wizard replies and host navigation remain * deterministic because they are structured UI actions, not conversation. */ -export type CrestodianChatEngineOptions = { +export type SystemAgentChatEngineOptions = { yes?: boolean; - deps?: CrestodianCommandDeps; - planWithAssistant?: CrestodianAssistantPlanner; + deps?: SystemAgentCommandDeps; + planWithAssistant?: SystemAgentAssistantPlanner; /** Test seam for the embedded agent-loop turn runner. */ - runAgentTurn?: CrestodianAgentTurnRunner; + runAgentTurn?: SystemAgentTurnRunner; /** Test seam for the approval-intent classifier. */ - classifyApproval?: CrestodianApprovalClassifier; + classifyApproval?: SystemAgentApprovalClassifier; /** Where side effects run; the gateway surface never manages its own daemon. */ surface?: "cli" | "gateway"; /** Test seam for the channel-setup wizard hosted by the chat bridge. */ @@ -64,18 +64,18 @@ export type CrestodianChatEngineOptions = { beforePersistentApply: (runtime: RuntimeEnv) => Promise, ) => Promise; /** Exact route/credential that passed the host's live inference gate. */ - readonly verifiedInference: CrestodianVerifiedInferenceBinding; + readonly verifiedInference: SystemAgentVerifiedInferenceBinding; }; -type CrestodianChatReplyAction = "none" | "exit" | "open-tui" | "open-setup"; +type SystemAgentChatReplyAction = "none" | "exit" | "open-tui" | "open-setup"; -type CrestodianChatReply = { +type SystemAgentChatReply = { text: string; - action: CrestodianChatReplyAction; + action: SystemAgentChatReplyAction; /** The next hosted-wizard reply contains a secret and must be masked/redacted by hosts. */ sensitive?: boolean; /** Present when the host must leave chat for an interactive handoff. */ - handoff?: CrestodianOperation; + handoff?: SystemAgentOperation; }; type WizardPrompterLike = import("../wizard/prompts.js").WizardPrompter; @@ -107,7 +107,7 @@ function createCaptureRuntime(): CaptureRuntime { log: (...args) => lines.push(args.join(" ")), error: (...args) => lines.push(args.join(" ")), exit: (code) => { - throw new Error(`Crestodian operation exited with code ${String(code)}`); + throw new Error(`OpenClaw operation exited with code ${String(code)}`); }, read: () => lines.join("\n").trim(), }; @@ -211,7 +211,7 @@ function parseWizardAnswer(step: WizardStep, text: string): { value: unknown } | if (step.type === "confirm") { // Wizard confirms are structured form fields, so the closed-list // classifier decides; ambiguous answers re-render the prompt. - const intent = classifyCrestodianApprovalText(trimmed); + const intent = classifySystemAgentApprovalText(trimmed); if (intent === "approve") { return { value: true }; } @@ -273,24 +273,24 @@ function formatOperationError(error: unknown): string { * so the value is masked the same way hosted-wizard secrets are. */ function redactSensitiveCommandText(text: string): string { - const operation = parseCrestodianOperation(text); + const operation = parseSystemAgentOperation(text); if (operation.kind === "config-set" && isSensitiveConfigPath(operation.path)) { return `config set ${operation.path} `; } return text; } -function formatPendingOperationForAssistant(operation: CrestodianOperation): string { - const description = describeCrestodianPersistentOperation(operation); +function formatPendingOperationForAssistant(operation: SystemAgentOperation): string { + const description = describeSystemAgentPersistentOperation(operation); return operation.kind === "setup" - ? `${description}. Exact setup JSON: ${JSON.stringify(operation)}. Keep the verified model unless the user explicitly asks to leave Crestodian and reconfigure inference.` + ? `${description}. Exact setup JSON: ${JSON.stringify(operation)}. Keep the verified model unless the user explicitly asks to leave OpenClaw and reconfigure inference.` : description; } function preservePendingSetupModel( - pending: CrestodianOperation | null, - operation: CrestodianOperation, -): CrestodianOperation { + pending: SystemAgentOperation | null, + operation: SystemAgentOperation, +): SystemAgentOperation { if (pending?.kind !== "setup" || operation.kind !== "setup") { return operation; } @@ -305,25 +305,25 @@ function preservePendingSetupModel( }; } -export class CrestodianChatEngine { - private pending: CrestodianOperation | null = null; +export class SystemAgentChatEngine { + private pending: SystemAgentOperation | null = null; private wizardBridge: ActiveWizardBridge | null = null; private lastSensitiveChannel: string | undefined; private awaitingSetupChannel = false; private hostProposalResolution: "approved" | "declined" | undefined; - private readonly history: CrestodianAssistantTurn[] = []; - private readonly agentSession: CrestodianAgentSession; - private readonly verifiedInference: CrestodianVerifiedInferenceBinding; + private readonly history: SystemAgentAssistantTurn[] = []; + private readonly agentSession: SystemAgentSession; + private readonly verifiedInference: SystemAgentVerifiedInferenceBinding; /** Turns run strictly one at a time; interleaved handles corrupt wizard/pending state. */ private turnQueue: Promise = Promise.resolve(); - constructor(private readonly opts: CrestodianChatEngineOptions) { + constructor(private readonly opts: SystemAgentChatEngineOptions) { const binding = opts?.verifiedInference; if (!binding) { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } this.verifiedInference = binding; - this.agentSession = createCrestodianAgentSession(binding); + this.agentSession = createSystemAgentSession(binding); } /** @@ -331,10 +331,10 @@ export class CrestodianChatEngine { * by first-run onboarding: the welcome message states the plan, the user * just agrees. */ - propose(operation: CrestodianOperation): string { + propose(operation: SystemAgentOperation): string { this.clearPendingProposals(); this.pending = operation; - return describeCrestodianPersistentOperation(operation); + return describeSystemAgentPersistentOperation(operation); } hasPendingProposal(): boolean { @@ -351,17 +351,17 @@ export class CrestodianChatEngine { this.wizardBridge = null; this.lastSensitiveChannel = undefined; this.awaitingSetupChannel = false; - await cleanupCrestodianAgentSession(this.agentSession); + await cleanupSystemAgentSession(this.agentSession); } - async handle(text: string): Promise { + async handle(text: string): Promise { const turn = this.turnQueue.then(() => this.handleSerialized(text)); // The queue must survive a failed turn or every later message would reject. this.turnQueue = turn.catch(() => undefined); return await turn; } - private async handleSerialized(text: string): Promise { + private async handleSerialized(text: string): Promise { await this.requireVerifiedInference(); // Snapshot before resolving: wizard answers to sensitive steps (tokens, // passwords) must never enter the AI-visible history. @@ -380,7 +380,7 @@ export class CrestodianChatEngine { }; } - private async resolveTurn(text: string): Promise { + private async resolveTurn(text: string): Promise { if (this.wizardBridge) { // A hosted wizard consumes every reply until it finishes or is cancelled. return { text: await this.resolveWizardBridgeReply(text), action: "none" }; @@ -394,7 +394,7 @@ export class CrestodianChatEngine { } if (/^(quit|exit)$/i.test(trimmed)) { // Leaving the process is a host action, not a conversation the AI owns. - return { text: "Crestodian retracts into shell. Bye.", action: "exit" }; + return { text: "OpenClaw retracts into shell. Bye.", action: "exit" }; } if (this.awaitingSetupChannel) { if (/^(cancel|abort|stop)$/i.test(trimmed)) { @@ -417,7 +417,7 @@ export class CrestodianChatEngine { // Secret hygiene: an exact `config set` on a sensitive path carries a raw // token and must never reach a model. The host handles its redacted // proposal + approval directly, matching the wizard's masked-input rules. - const typed = parseCrestodianOperation(text); + const typed = parseSystemAgentOperation(text); if (typed.kind === "config-set" && isSensitiveConfigPath(typed.path)) { return await this.runOperation(typed, undefined); } @@ -469,7 +469,7 @@ export class CrestodianChatEngine { return await this.resolveAssistantTurn(text, intent === "approve"); } - private async classifyApprovalIntent(text: string): Promise { + private async classifyApprovalIntent(text: string): Promise { const hasProposal = this.pending !== null || this.agentSession.proposalRef.current !== undefined; if (!hasProposal) { @@ -477,15 +477,15 @@ export class CrestodianChatEngine { } const classify = this.opts.classifyApproval ?? - (await import("./approval-intent.js")).classifyCrestodianApprovalIntent; + (await import("./approval-intent.js")).classifySystemAgentApprovalIntent; return await classify({ message: text, - ...(this.pending ? { proposal: describeCrestodianPersistentOperation(this.pending) } : {}), + ...(this.pending ? { proposal: describeSystemAgentPersistentOperation(this.pending) } : {}), verifiedInference: this.verifiedInference, }); } - private async applyPendingProposal(): Promise { + private async applyPendingProposal(): Promise { const pending = this.pending; this.clearPendingProposals(); this.hostProposalResolution = "approved"; @@ -498,22 +498,22 @@ export class CrestodianChatEngine { if (pending.kind === "model-setup") { return await this.startModelSetup(pending.workspace); } - if (!isPersistentCrestodianOperation(pending)) { + if (!isPersistentSystemAgentOperation(pending)) { return await this.runOperation(pending, undefined); } return await this.applyApprovedPersistentOperation(pending); } private async applyApprovedPersistentOperation( - operation: CrestodianOperation, - ): Promise { - if (!isPersistentCrestodianOperation(operation)) { - throw new Error(`Crestodian host received a non-persistent approved operation.`); + operation: SystemAgentOperation, + ): Promise { + if (!isPersistentSystemAgentOperation(operation)) { + throw new Error(`OpenClaw host received a non-persistent approved operation.`); } const capture = createCaptureRuntime(); - let result: CrestodianOperationResult | undefined; + let result: SystemAgentOperationResult | undefined; try { - result = await executeCrestodianOperation(operation, capture, { + result = await executeSystemAgentOperation(operation, capture, { approved: true, deps: this.commandDeps(), // The model turn, approval classifier, and operation preflight all @@ -523,7 +523,7 @@ export class CrestodianChatEngine { }, }); } catch (error) { - if (isCrestodianInferenceUnavailableError(error)) { + if (isSystemAgentInferenceUnavailableError(error)) { throw error; } capture.error(formatOperationError(error)); @@ -539,38 +539,38 @@ export class CrestodianChatEngine { } /** - * AI turn: the custodian persona answers and acts through the ring-zero + * AI turn: the OpenClaw persona answers and acts through the ring-zero * tool. The single-turn planner is a second inference path; if neither path * answers, the turn fails closed instead of executing model-free guesses. */ private async resolveAssistantTurn( text: string, approvalArmed: boolean, - ): Promise { + ): Promise { const overview = await this.loadOverview(); // Preferred path: the real agent loop (embedded runtime, ring-zero tool, // persistent session). It acts through audited tool calls, so its reply is // final — no engine-side command extraction or approval bookkeeping. - const agentTurn = this.opts.runAgentTurn ?? runCrestodianAgentTurn; + const agentTurn = this.opts.runAgentTurn ?? runSystemAgentTurn; const resolutionMarker = this.hostProposalResolution ? `[host-proposal-resolved] The previously host-seeded proposal was ${this.hostProposalResolution}. Do not present it as pending.\n` : ""; let agentFailure: unknown; - let loopReply: Awaited>; + let loopReply: Awaited>; try { loopReply = await agentTurn({ input: `${resolutionMarker}${ this.pending ? // Hand a host-seeded proposal (onboarding welcome) to the loop so // the conversation can reshape it through the tool handshake. - `[pending-proposal] Awaiting the user's approval: ${formatPendingOperationForAssistant(this.pending)}. It is already host-seeded; if they want it (or a variant), drive it through the crestodian tool yourself.\n${text}` + `[pending-proposal] Awaiting the user's approval: ${formatPendingOperationForAssistant(this.pending)}. It is already host-seeded; if they want it (or a variant), drive it through the openclaw tool yourself.\n${text}` : text }`, overview, surface: this.opts.surface ?? "cli", // Mutations unlock only on host-verified approval of THIS message; - // the model cannot self-approve (see crestodian-tool.ts). + // the model cannot self-approve (see system-agent-tool.ts). approvalArmed, session: this.agentSession, }); @@ -595,9 +595,9 @@ export class CrestodianChatEngine { } const planner = - this.opts.planWithAssistant ?? (await import("./assistant.js")).planCrestodianCommand; + this.opts.planWithAssistant ?? (await import("./assistant.js")).planSystemAgentCommand; let plannerFailure: unknown; - let plan: Awaited>; + let plan: Awaited>; try { plan = await planner({ input: text, @@ -618,7 +618,7 @@ export class CrestodianChatEngine { plan = null; } if (!plan) { - throw new CrestodianInferenceUnavailableError( + throw new SystemAgentInferenceUnavailableError( "conversation", [agentFailure, plannerFailure].filter((failure) => failure !== undefined), ); @@ -627,24 +627,24 @@ export class CrestodianChatEngine { const replyText = plan.reply ?? ""; if (!plan.command) { if (!replyText.trim()) { - throw new CrestodianInferenceUnavailableError("planner", [agentFailure]); + throw new SystemAgentInferenceUnavailableError("planner", [agentFailure]); } return { text: replyText, action: "none" }; } const operation = preservePendingSetupModel( this.pending, - parseCrestodianOperation(plan.command), + parseSystemAgentOperation(plan.command), ); if (operation.kind === "none") { if (!replyText.trim()) { - throw new CrestodianInferenceUnavailableError("planner", [agentFailure]); + throw new SystemAgentInferenceUnavailableError("planner", [agentFailure]); } // A conversational reply is still valid even when its optional command // falls outside the closed operation vocabulary. return { text: replyText, action: "none" }; } // Security contract: surface the interpreted command and model before - // anything runs (docs/cli/crestodian.md, AI conversation). + // anything runs (docs/cli/setup.md, AI conversation). const provenance = `(${plan.modelLabel ?? "model"} → \`${plan.command}\`)`; const executed = await this.runOperation(operation, provenance); return { @@ -655,8 +655,8 @@ export class CrestodianChatEngine { private async applyAgentTurnReply(loopReply: { text: string; - directive?: import("./agent-turn.js").CrestodianAgentTurnDirective; - }): Promise { + directive?: import("./agent-turn.js").SystemAgentTurnDirective; + }): Promise { // Recheck after the model turn: the route may have changed while inference // was running, and its stale directive must never cross that boundary. await this.requireVerifiedInference(); @@ -702,16 +702,16 @@ export class CrestodianChatEngine { } private async runOperation( - operation: CrestodianOperation, + operation: SystemAgentOperation, provenance: string | undefined, - ): Promise { + ): Promise { // Planning and approval classification are asynchronous. Bind every // operation to the same inference owner checked at turn start. await this.requireVerifiedInference(); if (operation.kind === "open-tui") { this.clearPendingProposals(); return { - text: "Opening your normal agent TUI. Use /crestodian there to come back.", + text: "Opening your normal agent TUI. Use /openclaw there to come back.", action: "open-tui", handoff: operation, }; @@ -730,7 +730,7 @@ export class CrestodianChatEngine { } if (operation.target !== "channels") { return { - text: "Setup can replace the inference route powering this session. Exit Crestodian and run `openclaw onboard`; it saves only a route that passes a live test. Then start Crestodian again.", + text: "Setup can replace the inference route powering this session. Exit OpenClaw and run `openclaw onboard`; it saves only a route that passes a live test. Then start OpenClaw again.", action: "none", }; } @@ -767,10 +767,10 @@ export class CrestodianChatEngine { } const capture = createCaptureRuntime(); - if (isPersistentCrestodianOperation(operation) && !this.opts.yes) { + if (isPersistentSystemAgentOperation(operation) && !this.opts.yes) { this.clearPendingProposals(); this.pending = operation; - await executeCrestodianOperation(operation, capture, { + await executeSystemAgentOperation(operation, capture, { approved: false, deps: this.commandDeps(), }); @@ -782,17 +782,17 @@ export class CrestodianChatEngine { }; } - let result: CrestodianOperationResult | undefined; + let result: SystemAgentOperationResult | undefined; try { - result = await executeCrestodianOperation(operation, capture, { - approved: this.opts.yes === true || !isPersistentCrestodianOperation(operation), + result = await executeSystemAgentOperation(operation, capture, { + approved: this.opts.yes === true || !isPersistentSystemAgentOperation(operation), deps: this.commandDeps(), beforePersistentApply: async () => { await this.requirePersistentApplyInference(capture); }, }); } catch (error) { - if (isCrestodianInferenceUnavailableError(error)) { + if (isSystemAgentInferenceUnavailableError(error)) { throw error; } capture.error(formatOperationError(error)); @@ -806,11 +806,11 @@ export class CrestodianChatEngine { return { text: reply, action: "none" }; } - async loadOverview(): Promise { + async loadOverview(): Promise { const verifiedRoute = await this.requireVerifiedInference(); const overview = this.opts.deps?.loadOverview ? await this.opts.deps.loadOverview() - : await loadCrestodianOverview(); + : await loadSystemAgentOverview(); return { ...overview, defaultModel: verifiedRoute.modelLabel }; } @@ -824,7 +824,7 @@ export class CrestodianChatEngine { return this.throwInferenceUnavailable(); } try { - const route = await resolveCrestodianVerifiedInferenceRoute(binding, this.opts.deps); + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, this.opts.deps); if (route) { return route; } @@ -844,8 +844,9 @@ export class CrestodianChatEngine { return this.throwInferenceUnavailable(); } try { - const { resolveCrestodianInferenceForPersistentApply } = await import("./setup-inference.js"); - const route = await resolveCrestodianInferenceForPersistentApply({ + const { resolveSystemAgentInferenceForPersistentApply } = + await import("./setup-inference.js"); + const route = await resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: this.opts.deps, @@ -854,7 +855,7 @@ export class CrestodianChatEngine { return route; } } catch (error) { - if (isCrestodianInferenceUnavailableError(error)) { + if (isSystemAgentInferenceUnavailableError(error)) { return this.throwInferenceUnavailable(error.failures, false); } return this.throwInferenceUnavailable([error], false); @@ -877,7 +878,7 @@ export class CrestodianChatEngine { this.lastSensitiveChannel = undefined; this.awaitingSetupChannel = false; this.history.splice(0); - throw new CrestodianInferenceUnavailableError("conversation", failures); + throw new SystemAgentInferenceUnavailableError("conversation", failures); } /** @@ -906,25 +907,25 @@ export class CrestodianChatEngine { return this.configVerificationUnavailable("openclaw.json could not be read"); } const notice = `⚠ openclaw.json failed validation after that write:\n${issuesText}`; - let recovery: CrestodianChatReply; + let recovery: SystemAgentChatReply; try { recovery = await this.resolveAssistantTurn( `[config-verify] The config file is now invalid:\n${issuesText}\nPropose one corrective command from the allowed list.`, false, ); } catch (error) { - if (!isCrestodianInferenceUnavailableError(error)) { + if (!isSystemAgentInferenceUnavailableError(error)) { throw error; } return `${notice}\nThe write was applied, but inference could not propose a repair. Run \`openclaw doctor --fix\`, then try again.`; } if (!recovery.text) { - return `${notice}\nExit Crestodian and run \`openclaw doctor --fix\`, or use \`config schema \` to check the expected shape before leaving.`; + return `${notice}\nExit OpenClaw and run \`openclaw doctor --fix\`, or use \`config schema \` to check the expected shape before leaving.`; } return `${notice}\n\n${recovery.text}`; } - private commandDeps(): CrestodianCommandDeps | undefined { + private commandDeps(): SystemAgentCommandDeps | undefined { if (!this.opts.deps && !this.opts.surface) { return undefined; } @@ -946,12 +947,12 @@ export class CrestodianChatEngine { ].join("\n"); } - private armFollowUp(operation: CrestodianOperation | undefined): string | null { + private armFollowUp(operation: SystemAgentOperation | undefined): string | null { if (operation?.kind !== "model-setup") { return null; } return [ - "No usable inference route is configured, so Crestodian cannot continue.", + "No usable inference route is configured, so OpenClaw cannot continue.", "Exit and run `openclaw onboard`; it saves only a route that passes a live test.", ].join("\n"); } @@ -978,12 +979,12 @@ export class CrestodianChatEngine { return await this.pumpWizardBridge(); } - private async startModelSetup(_workspace: string | undefined): Promise { + private async startModelSetup(_workspace: string | undefined): Promise { this.clearPendingProposals(); return { text: [ "Changing provider credentials would replace the inference route powering this session.", - "Exit Crestodian and run `openclaw onboard`; it stages credentials, live-tests the new route, and saves only a passing setup. Then start Crestodian again.", + "Exit OpenClaw and run `openclaw onboard`; it stages credentials, live-tests the new route, and saves only a passing setup. Then start OpenClaw again.", ].join("\n"), action: "none", }; @@ -1023,8 +1024,8 @@ export class CrestodianChatEngine { this.wizardBridge = null; const label = bridge.label; if (result.status === "done") { - const { appendCrestodianAuditEntry } = await import("./audit.js"); - await appendCrestodianAuditEntry({ + const { appendSystemAgentAuditEntry } = await import("./audit.js"); + await appendSystemAgentAuditEntry({ operation: "channels.setup", summary: `Configured channel ${label} via chat setup`, details: { channel: label }, @@ -1057,7 +1058,7 @@ export class CrestodianChatEngine { this.wizardBridge = null; this.lastSensitiveChannel = bridge.label; return [ - "Sensitive input is not accepted in the Crestodian chat because terminal input is visible.", + "Sensitive input is not accepted in the OpenClaw chat because terminal input is visible.", `Say \`open channel wizard\` and I'll hand you to the masked terminal wizard for ${bridge.label}, or run \`openclaw channels add --channel ${bridge.label}\` yourself later.`, ].join("\n"); } diff --git a/src/system-agent/dialogue.ts b/src/system-agent/dialogue.ts new file mode 100644 index 000000000000..64fd339d49ef --- /dev/null +++ b/src/system-agent/dialogue.ts @@ -0,0 +1,110 @@ +// OpenClaw dialogue parses direct commands and optionally asks the assistant planner. +import type { RuntimeEnv } from "../runtime.js"; +import type { SystemAgentAssistantPlan, SystemAgentAssistantPlanner } from "./assistant.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; +import { + describeSystemAgentPersistentOperation, + parseSystemAgentOperation, + type SystemAgentOperation, +} from "./operations.js"; +import { loadSystemAgentOverview, type SystemAgentOverview } from "./overview.js"; +import { + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceDeps, +} from "./verified-inference.js"; + +/** + * Dialogue helpers for turning user text into OpenClaw operations. + * + * Direct command parsing wins; the assistant planner is only consulted for + * non-empty text that did not parse into a known operation. + */ +type SystemAgentDialogueOptions = { + loadOverview?: typeof loadSystemAgentOverview; + planWithAssistant?: SystemAgentAssistantPlanner; + deps?: SystemAgentVerifiedInferenceDeps; + readonly verifiedInference: SystemAgentVerifiedInferenceBinding; +}; + +/** Format the interactive approval prompt for a persistent operation. */ +export function approvalQuestion(operation: SystemAgentOperation): string { + return `Apply this operation: ${describeSystemAgentPersistentOperation(operation)}?`; +} + +/** Resolve user input to an OpenClaw operation, optionally using the assistant planner. */ +export async function resolveSystemAgentOperation( + input: string, + runtime: RuntimeEnv, + opts: SystemAgentDialogueOptions, +): Promise { + if (!opts.verifiedInference) { + throw new SystemAgentInferenceUnavailableError("conversation"); + } + const operation = parseSystemAgentOperation(input); + if (!shouldAskAssistant(input, operation)) { + return operation; + } + const overview = await (opts.loadOverview ?? loadSystemAgentOverview)(); + const planner = opts.planWithAssistant ?? (await import("./assistant.js")).planSystemAgentCommand; + let plan: SystemAgentAssistantPlan | null; + try { + plan = await planner({ + input, + overview, + verifiedInference: opts.verifiedInference, + }); + if ( + plan && + !(await resolveSystemAgentVerifiedInferenceRoute(opts.verifiedInference, opts.deps)) + ) { + throw new SystemAgentInferenceUnavailableError("planner"); + } + } catch (error) { + if (error instanceof SystemAgentInferenceUnavailableError) { + throw error; + } + throw new SystemAgentInferenceUnavailableError("planner", [error]); + } + if (!plan) { + throw new SystemAgentInferenceUnavailableError("planner"); + } + if (!plan.command) { + if (!plan.reply?.trim()) { + throw new SystemAgentInferenceUnavailableError("planner"); + } + runtime.log(plan.reply); + return { kind: "none", message: "" }; + } + const planned = parseSystemAgentOperation(plan.command); + if (planned.kind === "none") { + throw new SystemAgentInferenceUnavailableError("planner"); + } + logAssistantPlan(runtime, plan, overview); + return planned; +} + +function shouldAskAssistant(input: string, operation: SystemAgentOperation): boolean { + if (operation.kind !== "none") { + return false; + } + const trimmed = input.trim().toLowerCase(); + if (!trimmed || trimmed === "quit" || trimmed === "exit") { + return false; + } + return true; +} + +function logAssistantPlan( + runtime: RuntimeEnv, + plan: SystemAgentAssistantPlan, + overview: SystemAgentOverview, +): void { + // Assistant plans are echoed before execution so the user can see the interpreted command. + const modelLabel = plan.modelLabel ?? overview.defaultModel ?? "configured model"; + runtime.log(`[openclaw] planner: ${modelLabel}`); + if (plan.reply) { + runtime.log(plan.reply); + } + runtime.log(`[openclaw] interpreted: ${plan.command}`); +} diff --git a/src/system-agent/inference-error.ts b/src/system-agent/inference-error.ts new file mode 100644 index 000000000000..1f92f626a334 --- /dev/null +++ b/src/system-agent/inference-error.ts @@ -0,0 +1,27 @@ +type SystemAgentInferenceStage = "agent-turn" | "planner" | "conversation"; + +/** Safe public error for an OpenClaw turn that could not complete with intelligence. */ +export class SystemAgentInferenceUnavailableError extends Error { + readonly code = "SYSTEM_AGENT_INFERENCE_UNAVAILABLE"; + + constructor( + readonly stage: SystemAgentInferenceStage, + readonly failures: readonly unknown[] = [], + ) { + super( + "OpenClaw could not reach working inference. Run `openclaw onboard` to reconnect and live-test AI, then try again.", + ); + this.name = "SystemAgentInferenceUnavailableError"; + } +} + +export function isSystemAgentInferenceUnavailableError( + error: unknown, +): error is SystemAgentInferenceUnavailableError { + return ( + error instanceof SystemAgentInferenceUnavailableError || + (error instanceof Error && + "code" in error && + error.code === "SYSTEM_AGENT_INFERENCE_UNAVAILABLE") + ); +} diff --git a/src/crestodian/inference-route.ts b/src/system-agent/inference-route.ts similarity index 91% rename from src/crestodian/inference-route.ts rename to src/system-agent/inference-route.ts index 1249cbc6d2ea..01ffa41f87f9 100644 --- a/src/crestodian/inference-route.ts +++ b/src/system-agent/inference-route.ts @@ -1,4 +1,4 @@ -// Resolves the configured default agent route shared by Crestodian inference calls. +// Resolves the configured default agent route shared by OpenClaw inference calls. import { isDeepStrictEqual } from "node:util"; import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id"; import { @@ -8,7 +8,7 @@ import { import type { OpenClawConfig } from "../config/types.openclaw.js"; import { normalizeAgentId } from "../routing/session-key.js"; -export type CrestodianConfiguredRoute = { +export type SystemAgentConfiguredRoute = { runConfig: OpenClawConfig; modelLabel: string; provider: string; @@ -24,14 +24,14 @@ export type CrestodianConfiguredRoute = { } ); -export type CrestodianConfiguredRouteDeps = { +export type SystemAgentConfiguredRouteDeps = { readConfigFileSnapshot?: typeof import("../config/config.js").readConfigFileSnapshot; }; type DistributiveOmit = T extends unknown ? Omit : never; export type DefaultInferenceRouteProjection = { - route: DistributiveOmit | null; + route: DistributiveOmit | null; defaultSelection: { explicitIds: string[]; fallbackId?: string }; auth: unknown; models: unknown; @@ -44,9 +44,9 @@ export type DefaultInferenceRouteProjection = { tools: OpenClawConfig["tools"]; }; -const CRESTODIAN_EXECUTION_AGENT_ID = "crestodian"; +const SYSTEM_AGENT_EXECUTION_AGENT_ID = "openclaw"; -function projectCrestodianExecutionConfig( +function projectSystemAgentExecutionConfig( config: OpenClawConfig, routeAgentId: string, ): OpenClawConfig { @@ -55,11 +55,11 @@ function projectCrestodianExecutionConfig( return config; } const routeAgent = - routeAgentId === CRESTODIAN_EXECUTION_AGENT_ID + routeAgentId === SYSTEM_AGENT_EXECUTION_AGENT_ID ? undefined : agents.find((agent) => normalizeAgentId(agent.id) === routeAgentId); const retainedAgents = agents.filter( - (agent) => normalizeAgentId(agent.id) !== CRESTODIAN_EXECUTION_AGENT_ID, + (agent) => normalizeAgentId(agent.id) !== SYSTEM_AGENT_EXECUTION_AGENT_ID, ); const hasProjectedSettings = routeAgent?.params !== undefined || routeAgent?.tools !== undefined; if (retainedAgents.length === agents.length && !hasProjectedSettings) { @@ -74,7 +74,7 @@ function projectCrestodianExecutionConfig( ...(hasProjectedSettings ? [ { - id: CRESTODIAN_EXECUTION_AGENT_ID, + id: SYSTEM_AGENT_EXECUTION_AGENT_ID, ...(routeAgent?.params !== undefined ? { params: structuredClone(routeAgent.params) } : {}), @@ -89,9 +89,9 @@ function projectCrestodianExecutionConfig( }; } -export async function resolveCrestodianConfiguredRouteFromConfig( +export async function resolveSystemAgentConfiguredRouteFromConfig( runConfig: OpenClawConfig, -): Promise { +): Promise { const [agentScope, modelSelection, modelRuntimeAliases, simpleCompletion, harnessPolicy] = await Promise.all([ import("../agents/agent-scope.js"), @@ -144,7 +144,7 @@ export async function resolveCrestodianConfiguredRouteFromConfig( }) : undefined; const authProfileId = allowCliAuthProfileForwarding ? cliAuthProfileId : selection.profileId; - const executionConfig = projectCrestodianExecutionConfig(runConfig, modelOwnerAgentId); + const executionConfig = projectSystemAgentExecutionConfig(runConfig, modelOwnerAgentId); const base = { runConfig: executionConfig, modelLabel: `${selection.provider}/${selection.modelId}`, @@ -199,11 +199,11 @@ export async function projectDefaultInferenceRoute( import("../agents/provider-auth-aliases.js"), ]); const defaultAgentId = resolveDefaultAgentId(config); - const route = await resolveCrestodianConfiguredRouteFromConfig(config); + const route = await resolveSystemAgentConfiguredRouteFromConfig(config); const list = config.agents?.list ?? []; const agent = list.find((entry) => normalizeAgentId(entry.id) === defaultAgentId); const executionAgent = route?.runConfig.agents?.list?.find( - (entry) => normalizeAgentId(entry.id) === CRESTODIAN_EXECUTION_AGENT_ID, + (entry) => normalizeAgentId(entry.id) === SYSTEM_AGENT_EXECUTION_AGENT_ID, ); const defaults = config.agents?.defaults; const logicalProvider = normalizeProviderId(route?.modelLabel.split("/", 1)[0] ?? ""); @@ -298,7 +298,7 @@ export async function projectDefaultInferenceRoute( ...(executionAgent ? { executionAgent: { - id: CRESTODIAN_EXECUTION_AGENT_ID, + id: SYSTEM_AGENT_EXECUTION_AGENT_ID, params: structuredClone(executionAgent.params), tools: structuredClone(executionAgent.tools), }, diff --git a/src/crestodian/onboarding-welcome.test.ts b/src/system-agent/onboarding-welcome.test.ts similarity index 100% rename from src/crestodian/onboarding-welcome.test.ts rename to src/system-agent/onboarding-welcome.test.ts diff --git a/src/crestodian/onboarding-welcome.ts b/src/system-agent/onboarding-welcome.ts similarity index 90% rename from src/crestodian/onboarding-welcome.ts rename to src/system-agent/onboarding-welcome.ts index e86544077f0e..2213c5f8b89d 100644 --- a/src/crestodian/onboarding-welcome.ts +++ b/src/system-agent/onboarding-welcome.ts @@ -1,8 +1,8 @@ // First-run onboarding welcome: state findings, propose setup, wait for "yes". import { isSecretRef, normalizeSecretInputString } from "../config/types.secrets.js"; import { resolveUserPath, shortenHomePath } from "../utils.js"; -import type { CrestodianChatEngine } from "./chat-engine.js"; -import { formatCrestodianOnboardingWelcome } from "./overview.js"; +import type { SystemAgentChatEngine } from "./chat-engine.js"; +import { formatSystemAgentOnboardingWelcome } from "./overview.js"; /** * The basic bootstrap is conversational: the welcome message carries the plan @@ -48,7 +48,7 @@ export async function loadAuthoredSetupConfig(params: { } export async function buildOnboardingWelcome(params: { - engine: CrestodianChatEngine; + engine: SystemAgentChatEngine; workspace?: string; }): Promise { const overview = await params.engine.loadOverview(); @@ -68,13 +68,13 @@ export async function buildOnboardingWelcome(params: { defaultModel && (!requestedWorkspace || requestedWorkspace === authoredWorkspace) ) { - const welcome = formatCrestodianOnboardingWelcome(overview); + const welcome = formatSystemAgentOnboardingWelcome(overview); params.engine.noteAssistantMessage(welcome); return welcome; } if (!defaultModel) { throw new Error( - "Crestodian onboarding requires working inference first. Run `openclaw onboard` to configure and verify a default model.", + "OpenClaw onboarding requires working inference first. Run `openclaw onboard` to configure and verify a default model.", ); } @@ -83,7 +83,7 @@ export async function buildOnboardingWelcome(params: { params.engine.propose({ kind: "setup", workspace }); const welcome = [ - "## Hi, I'm Crestodian — let's hatch your agent.", + "## Hi, I'm OpenClaw — let's hatch your agent.", "", "No menus here: tell me what you want and I'll do the configuring. I looked around this machine:", "", diff --git a/src/crestodian/operations.test.ts b/src/system-agent/operations.test.ts similarity index 74% rename from src/crestodian/operations.test.ts rename to src/system-agent/operations.test.ts index 187e37b65e49..5b8fa0191021 100644 --- a/src/crestodian/operations.test.ts +++ b/src/system-agent/operations.test.ts @@ -1,18 +1,18 @@ -// Crestodian operation tests cover rescue operation planning and execution. +// OpenClaw operation tests cover rescue operation planning and execution. import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import type { RuntimeEnv } from "../runtime.js"; import { captureEnv, setTestEnvValue } from "../test-utils/env.js"; -import { createCrestodianTestRuntime } from "./crestodian.test-helpers.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; import { - describeCrestodianPersistentOperation, - executeCrestodianOperation, - isPersistentCrestodianOperation, - parseCrestodianOperation, + describeSystemAgentPersistentOperation, + executeSystemAgentOperation, + isPersistentSystemAgentOperation, + parseSystemAgentOperation, } from "./operations.js"; +import { createSystemAgentTestRuntime } from "./system-agent.test-helpers.js"; type TestConfig = Record; @@ -147,8 +147,8 @@ vi.mock("./probes.js", () => ({ })); vi.mock("./overview.js", () => ({ - formatCrestodianOverview: () => "Default model: openai/gpt-5.5", - loadCrestodianOverview: vi.fn(async () => ({ + formatSystemAgentOverview: () => "Default model: openai/gpt-5.5", + loadSystemAgentOverview: vi.fn(async () => ({ defaultAgentId: "main", defaultModel: undefined, agents: [ @@ -181,7 +181,7 @@ vi.mock("../config/config.js", () => ({ })); const opTempDirs = useAutoCleanupTempDirTracker(afterEach); -describe("parseCrestodianOperation", () => { +describe("parseSystemAgentOperation", () => { let stateDirSnapshot: ReturnType | undefined; beforeEach(() => { @@ -196,112 +196,107 @@ describe("parseCrestodianOperation", () => { }); it("parses typed model writes", () => { - expect(parseCrestodianOperation("set default model openai/gpt-5.2")).toEqual({ + expect(parseSystemAgentOperation("set default model openai/gpt-5.2")).toEqual({ kind: "set-default-model", model: "openai/gpt-5.2", }); - expect(parseCrestodianOperation("configure models openai/gpt-5.2")).toEqual({ + expect(parseSystemAgentOperation("configure models openai/gpt-5.2")).toEqual({ kind: "set-default-model", model: "openai/gpt-5.2", }); }); it("parses interactive model provider setup", () => { - expect(parseCrestodianOperation("configure model provider")).toEqual({ + expect(parseSystemAgentOperation("configure model provider")).toEqual({ kind: "model-setup", }); - expect(parseCrestodianOperation("setup model provider")).toEqual({ + expect(parseSystemAgentOperation("setup model provider")).toEqual({ kind: "model-setup", }); - expect(parseCrestodianOperation("model setup workspace /tmp/work")).toEqual({ + expect(parseSystemAgentOperation("model setup workspace /tmp/work")).toEqual({ kind: "model-setup", workspace: "/tmp/work", }); }); it("parses verbal agent switching", () => { - expect(parseCrestodianOperation("talk to work agent")).toEqual({ + expect(parseSystemAgentOperation("talk to work agent")).toEqual({ kind: "open-tui", agentId: "work", }); }); it("routes ambiguous model requests to the AI instead of guessing", () => { - expect(parseCrestodianOperation("models please").kind).toBe("none"); - expect(parseCrestodianOperation("why did my gateway stop").kind).toBe("none"); - expect(parseCrestodianOperation("should I talk to my agent about this?").kind).toBe("none"); - expect(parseCrestodianOperation("set me up with telegram").kind).toBe("none"); - expect(parseCrestodianOperation("can I set the default model gpt-5.5 later?").kind).toBe( + expect(parseSystemAgentOperation("models please").kind).toBe("none"); + expect(parseSystemAgentOperation("why did my gateway stop").kind).toBe("none"); + expect(parseSystemAgentOperation("should I talk to my agent about this?").kind).toBe("none"); + expect(parseSystemAgentOperation("set me up with telegram").kind).toBe("none"); + expect(parseSystemAgentOperation("can I set the default model gpt-5.5 later?").kind).toBe( "none", ); }); it("parses gateway lifecycle operations", () => { - expect(parseCrestodianOperation("gateway status")).toEqual({ kind: "gateway-status" }); - expect(parseCrestodianOperation("restart gateway")).toEqual({ kind: "gateway-restart" }); - expect(parseCrestodianOperation("start gateway")).toEqual({ kind: "gateway-start" }); - expect(parseCrestodianOperation("stop gateway")).toEqual({ kind: "gateway-stop" }); + expect(parseSystemAgentOperation("gateway status")).toEqual({ kind: "gateway-status" }); + expect(parseSystemAgentOperation("restart gateway")).toEqual({ kind: "gateway-restart" }); + expect(parseSystemAgentOperation("start gateway")).toEqual({ kind: "gateway-start" }); + expect(parseSystemAgentOperation("stop gateway")).toEqual({ kind: "gateway-stop" }); }); it("parses config and doctor repair operations", () => { - expect(parseCrestodianOperation("validate config")).toEqual({ kind: "config-validate" }); - expect(parseCrestodianOperation("config set gateway.port 19001")).toEqual({ + expect(parseSystemAgentOperation("validate config")).toEqual({ kind: "config-validate" }); + expect(parseSystemAgentOperation("config set gateway.port 19001")).toEqual({ kind: "config-set", path: "gateway.port", value: "19001", }); - expect(parseCrestodianOperation("config set-ref gateway.auth.token env GATEWAY_TOKEN")).toEqual( - { - kind: "config-set-ref", - path: "gateway.auth.token", - source: "env", - id: "GATEWAY_TOKEN", - }, - ); - expect(parseCrestodianOperation("doctor fix")).toEqual({ kind: "doctor-fix" }); + expect( + parseSystemAgentOperation("config set-ref gateway.auth.token env GATEWAY_TOKEN"), + ).toEqual({ + kind: "config-set-ref", + path: "gateway.auth.token", + source: "env", + id: "GATEWAY_TOKEN", + }); + expect(parseSystemAgentOperation("doctor fix")).toEqual({ kind: "doctor-fix" }); }); it("parses plugin management operations", () => { - expect(parseCrestodianOperation("plugins list")).toEqual({ kind: "plugin-list" }); - expect(parseCrestodianOperation("list plugin")).toEqual({ kind: "plugin-list" }); - expect(parseCrestodianOperation("plugins search calendar sync")).toEqual({ + expect(parseSystemAgentOperation("plugins list")).toEqual({ kind: "plugin-list" }); + expect(parseSystemAgentOperation("list plugin")).toEqual({ kind: "plugin-list" }); + expect(parseSystemAgentOperation("plugins search calendar sync")).toEqual({ kind: "plugin-search", query: "calendar sync", }); - expect(parseCrestodianOperation("install npm plugin @openclaw/discord")).toEqual({ + expect(parseSystemAgentOperation("install npm plugin @openclaw/demo")).toEqual({ kind: "plugin-install", - spec: "npm:@openclaw/discord", + spec: "npm:@openclaw/demo", }); - expect(parseCrestodianOperation("plugin install clawhub:openclaw-demo")).toEqual({ + expect(parseSystemAgentOperation("plugin install clawhub:openclaw-demo")).toEqual({ kind: "plugin-install", spec: "clawhub:openclaw-demo", }); - expect(parseCrestodianOperation("plugin uninstall openclaw-demo")).toEqual({ + expect(parseSystemAgentOperation("plugin uninstall openclaw-demo")).toEqual({ kind: "plugin-uninstall", pluginId: "openclaw-demo", }); - expect(parseCrestodianOperation("plugin install npm:@example/plugin")).toEqual({ - kind: "none", - message: - "Crestodian installs only ClawHub, bundled, or official-catalog plugins. Use `openclaw plugins install ` in a trusted shell to review an arbitrary executable source.", - }); }); it("parses config read and schema lookups", () => { - expect(parseCrestodianOperation("config get gateway.port")).toEqual({ + expect(parseSystemAgentOperation("config get gateway.port")).toEqual({ kind: "config-get", path: "gateway.port", }); - expect(parseCrestodianOperation("config schema channels.telegram")).toEqual({ + expect(parseSystemAgentOperation("config schema channels.telegram")).toEqual({ kind: "config-schema", path: "channels.telegram", }); - expect(parseCrestodianOperation("config schema")).toEqual({ kind: "config-schema" }); + expect(parseSystemAgentOperation("config schema")).toEqual({ kind: "config-schema" }); // Read-only: no approval gate. - expect(isPersistentCrestodianOperation({ kind: "config-get", path: "gateway.port" })).toBe( + expect(isPersistentSystemAgentOperation({ kind: "config-get", path: "gateway.port" })).toBe( false, ); - expect(isPersistentCrestodianOperation({ kind: "config-schema" })).toBe(false); + expect(isPersistentSystemAgentOperation({ kind: "config-schema" })).toBe(false); }); it("redacts sensitive config values using their complete paths", async () => { @@ -316,9 +311,9 @@ describe("parseCrestodianOperation", () => { }, }, }); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); - await executeCrestodianOperation( + await executeSystemAgentOperation( { kind: "config-get", path: "models.providers.local.localService" }, runtime, ); @@ -326,7 +321,7 @@ describe("parseCrestodianOperation", () => { expect(lines.join("\n")).toContain('"HF_HOME": ""'); expect(lines.join("\n")).not.toContain("/private/model-cache"); expect( - describeCrestodianPersistentOperation({ + describeSystemAgentPersistentOperation({ kind: "config-set", path: "models.providers.local.localService.env.HF_HOME", value: "/private/model-cache", @@ -335,25 +330,25 @@ describe("parseCrestodianOperation", () => { }); it("parses channel listing and connect requests", () => { - expect(parseCrestodianOperation("channels")).toEqual({ kind: "channel-list" }); - expect(parseCrestodianOperation("list channels")).toEqual({ kind: "channel-list" }); - expect(parseCrestodianOperation("connect telegram")).toEqual({ + expect(parseSystemAgentOperation("channels")).toEqual({ kind: "channel-list" }); + expect(parseSystemAgentOperation("list channels")).toEqual({ kind: "channel-list" }); + expect(parseSystemAgentOperation("connect telegram")).toEqual({ kind: "channel-setup", channel: "telegram", }); - expect(parseCrestodianOperation("connect to WhatsApp")).toEqual({ + expect(parseSystemAgentOperation("connect to WhatsApp")).toEqual({ kind: "channel-setup", channel: "whatsapp", }); - expect(parseCrestodianOperation("link discord channel")).toEqual({ + expect(parseSystemAgentOperation("link discord channel")).toEqual({ kind: "channel-setup", channel: "discord", }); // Starting the wizard is not a write; the wizard collects explicit answers. - expect(isPersistentCrestodianOperation({ kind: "channel-setup", channel: "telegram" })).toBe( + expect(isPersistentSystemAgentOperation({ kind: "channel-setup", channel: "telegram" })).toBe( false, ); - expect(isPersistentCrestodianOperation({ kind: "channel-list" })).toBe(false); + expect(isPersistentSystemAgentOperation({ kind: "channel-list" })).toBe(false); }); it("parses anchored setup switches and channel info", () => { @@ -364,41 +359,41 @@ describe("parseCrestodianOperation", () => { "use the setup wizard", "use the wizard", ]) { - expect(parseCrestodianOperation(input)).toEqual({ kind: "open-setup", target: "guided" }); + expect(parseSystemAgentOperation(input)).toEqual({ kind: "open-setup", target: "guided" }); } for (const input of ["open classic wizard", "open classic setup wizard", "classic setup"]) { - expect(parseCrestodianOperation(input)).toEqual({ kind: "open-setup", target: "classic" }); + expect(parseSystemAgentOperation(input)).toEqual({ kind: "open-setup", target: "classic" }); } - expect(parseCrestodianOperation("open channel wizard")).toEqual({ + expect(parseSystemAgentOperation("open channel wizard")).toEqual({ kind: "open-setup", target: "channels", }); - expect(parseCrestodianOperation("open channel wizard for Slack")).toEqual({ + expect(parseSystemAgentOperation("open channel wizard for Slack")).toEqual({ kind: "open-setup", target: "channels", channel: "slack", }); - expect(parseCrestodianOperation("channel info Slack")).toEqual({ + expect(parseSystemAgentOperation("channel info Slack")).toEqual({ kind: "channel-info", channel: "slack", }); - expect(parseCrestodianOperation("about Telegram channel")).toEqual({ + expect(parseSystemAgentOperation("about Telegram channel")).toEqual({ kind: "channel-info", channel: "telegram", }); - expect(parseCrestodianOperation("please open the setup wizard soon").kind).toBe("none"); - expect(parseCrestodianOperation("channel info slack please").kind).toBe("none"); + expect(parseSystemAgentOperation("please open the setup wizard soon").kind).toBe("none"); + expect(parseSystemAgentOperation("channel info slack please").kind).toBe("none"); }); it("prints one-shot setup pointers", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); for (const operation of [ { kind: "open-setup", target: "guided" } as const, { kind: "open-setup", target: "classic" } as const, { kind: "open-setup", target: "channels", channel: "slack" } as const, ]) { - const result = await executeCrestodianOperation(operation, runtime); + const result = await executeSystemAgentOperation(operation, runtime); expect(result.applied).toBe(false); } @@ -408,18 +403,18 @@ describe("parseCrestodianOperation", () => { expect(output).toContain("openclaw channels add --channel slack"); }); - it("routes one-shot model setup through the verified Crestodian flow", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + it("routes one-shot model setup through the verified OpenClaw flow", async () => { + const { runtime, lines } = createSystemAgentTestRuntime(); - const result = await executeCrestodianOperation({ kind: "model-setup" }, runtime); + const result = await executeSystemAgentOperation({ kind: "model-setup" }, runtime); expect(result.applied).toBe(false); - expect(lines.join("\n")).toContain("Exit Crestodian and run `openclaw onboard`"); + expect(lines.join("\n")).toContain("Exit OpenClaw and run `openclaw onboard`"); expect(lines.join("\n")).not.toContain("openclaw configure --section model"); }); it("prints discovered channel metadata and sorted unknown-channel choices", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const entries = [ { id: "telegram", @@ -450,7 +445,7 @@ describe("parseCrestodianOperation", () => { isChannelConfigured: (_cfg: unknown, channel: string) => channel === "slack", } as never; - await executeCrestodianOperation({ kind: "channel-info", channel: "slack" }, runtime, { + await executeSystemAgentOperation({ kind: "channel-info", channel: "slack" }, runtime, { deps, }); const knownOutput = lines.join("\n"); @@ -462,7 +457,7 @@ describe("parseCrestodianOperation", () => { expect(knownOutput).toContain("open channel wizard for slack"); lines.length = 0; - await executeCrestodianOperation({ kind: "channel-info", channel: "matrix" }, runtime, { + await executeSystemAgentOperation({ kind: "channel-info", channel: "matrix" }, runtime, { deps, }); expect(lines.join("\n")).toContain("Known channels: slack, telegram"); @@ -470,44 +465,44 @@ describe("parseCrestodianOperation", () => { it("parses agent creation requests", () => { expect( - parseCrestodianOperation("create agent Work workspace /tmp/work model openai/gpt-5.2"), + parseSystemAgentOperation("create agent Work workspace /tmp/work model openai/gpt-5.2"), ).toEqual({ kind: "create-agent", agentId: "work", workspace: "/tmp/work", model: "openai/gpt-5.2", }); - expect(parseCrestodianOperation("add agent ops")).toEqual({ + expect(parseSystemAgentOperation("add agent ops")).toEqual({ kind: "create-agent", agentId: "ops", }); - expect(parseCrestodianOperation("setup workspace /tmp/work model openai/gpt-5.5")).toEqual({ + expect(parseSystemAgentOperation("setup workspace /tmp/work model openai/gpt-5.5")).toEqual({ kind: "setup", workspace: "/tmp/work", model: "openai/gpt-5.5", }); - expect(parseCrestodianOperation("setup agent ops")).toEqual({ + expect(parseSystemAgentOperation("setup agent ops")).toEqual({ kind: "create-agent", agentId: "ops", }); }); it("rejects an explicit new-agent model before any config write or audit", async () => { - const tempDir = opTempDirs.make("crestodian-agent-model-rejected-"); + const tempDir = opTempDirs.make("openclaw-agent-model-rejected-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runAgentsAdd = vi.fn(async () => {}); expect( - isPersistentCrestodianOperation({ + isPersistentSystemAgentOperation({ kind: "create-agent", agentId: "work", model: "openai/gpt-5.5", }), ).toBe(false); - expect(isPersistentCrestodianOperation({ kind: "create-agent", agentId: "work" })).toBe(true); + expect(isPersistentSystemAgentOperation({ kind: "create-agent", agentId: "work" })).toBe(true); await expect( - executeCrestodianOperation( + executeSystemAgentOperation( { kind: "create-agent", agentId: "work", @@ -520,39 +515,58 @@ describe("parseCrestodianOperation", () => { ).rejects.toThrow("Retry without `model`; the new agent will inherit"); expect(runAgentsAdd).not.toHaveBeenCalled(); - expect(lines.join("\n")).not.toContain("[crestodian] running: agents.create"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] running: agents.create"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); - it("reserves the normalized Crestodian agent identity before any write or audit", async () => { - const tempDir = opTempDirs.make("crestodian-agent-id-reserved-"); + it("reserves the normalized OpenClaw agent identity before any write or audit", async () => { + const tempDir = opTempDirs.make("openclaw-agent-id-reserved-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runAgentsAdd = vi.fn(async () => {}); const operation = { kind: "create-agent" as const, - agentId: "Crestodian", + agentId: "OpenClaw", workspace: "/tmp/work", }; - expect(isPersistentCrestodianOperation(operation)).toBe(false); + expect(isPersistentSystemAgentOperation(operation)).toBe(false); await expect( - executeCrestodianOperation(operation, runtime, { + executeSystemAgentOperation(operation, runtime, { approved: true, deps: { runAgentsAdd }, }), - ).rejects.toThrow('Agent id "crestodian" is reserved'); + ).rejects.toThrow('Agent id "openclaw" is reserved'); expect(runAgentsAdd).not.toHaveBeenCalled(); - expect(lines.join("\n")).not.toContain("[crestodian] running: agents.create"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] running: agents.create"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); + }); + + it("keeps the retired agent identity reserved", async () => { + const { runtime } = createSystemAgentTestRuntime(); + const runAgentsAdd = vi.fn(async () => {}); + const operation = { + kind: "create-agent" as const, + agentId: "crestodian", // reserved retired id + workspace: "/tmp/retired", + }; + + expect(isPersistentSystemAgentOperation(operation)).toBe(false); + await expect( + executeSystemAgentOperation(operation, runtime, { + approved: true, + deps: { runAgentsAdd }, + }), + ).rejects.toThrow('Agent id "crestodian" is reserved'); // reserved retired id + expect(runAgentsAdd).not.toHaveBeenCalled(); }); it("requires approval before restarting gateway", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runGatewayRestart = vi.fn(async () => {}); - const result = await executeCrestodianOperation({ kind: "gateway-restart" }, runtime, { + const result = await executeSystemAgentOperation({ kind: "gateway-restart" }, runtime, { deps: { runGatewayRestart }, }); @@ -565,40 +579,40 @@ describe("parseCrestodianOperation", () => { }); it("does not report or audit a gateway restart that returned false", async () => { - const tempDir = opTempDirs.make("crestodian-restart-failed-"); + const tempDir = opTempDirs.make("openclaw-restart-failed-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runGatewayRestart = vi.fn(async () => false); await expect( - executeCrestodianOperation({ kind: "gateway-restart" }, runtime, { + executeSystemAgentOperation({ kind: "gateway-restart" }, runtime, { approved: true, deps: { runGatewayRestart }, }), ).rejects.toThrow("Gateway restart did not complete"); - expect(lines.join("\n")).toContain("[crestodian] running: gateway.restart"); - expect(lines.join("\n")).not.toContain("[crestodian] done: gateway.restart"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).toContain("[openclaw] running: gateway.restart"); + expect(lines.join("\n")).not.toContain("[openclaw] done: gateway.restart"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("validates missing config without exiting the process", async () => { mockConfig.missing("/tmp/openclaw.json"); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); - const result = await executeCrestodianOperation({ kind: "config-validate" }, runtime); + const result = await executeSystemAgentOperation({ kind: "config-validate" }, runtime); expect(result.applied).toBe(false); expect(lines.join("\n")).toContain("Config missing:"); }); it("applies config set through typed deps and writes an audit entry", async () => { - const tempDir = opTempDirs.make("crestodian-config-set-"); + const tempDir = opTempDirs.make("openclaw-config-set-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runConfigSet = vi.fn(async () => {}); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "config-set", path: "gateway.port", value: "19001" }, runtime, { @@ -614,8 +628,8 @@ describe("parseCrestodianOperation", () => { value: "19001", cliOptions: {}, }); - expect(lines.join("\n")).toContain("[crestodian] done: config.set"); - const auditPath = path.join(tempDir, "audit", "crestodian.jsonl"); + expect(lines.join("\n")).toContain("[openclaw] done: config.set"); + const auditPath = path.join(tempDir, "audit", "system-agent.jsonl"); const audit = JSON.parse((await fs.readFile(auditPath, "utf8")).trim()); expectAuditRecord( audit, @@ -629,15 +643,15 @@ describe("parseCrestodianOperation", () => { }); it("reports an audit failure without claiming the committed operation failed", async () => { - const tempDir = opTempDirs.make("crestodian-audit-warning-"); + const tempDir = opTempDirs.make("openclaw-audit-warning-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); const redirectedAuditDir = path.join(tempDir, "redirected-audit"); await fs.mkdir(redirectedAuditDir); await fs.symlink(redirectedAuditDir, path.join(tempDir, "audit"), "dir"); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runConfigSet = vi.fn(async () => {}); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "config-set", path: "gateway.port", value: "19001" }, runtime, { approved: true, deps: { runConfigSet } }, @@ -648,16 +662,16 @@ describe("parseCrestodianOperation", () => { expect(lines.join("\n")).toContain( "Set config gateway.port, but OpenClaw could not record its audit entry:", ); - expect(lines.join("\n")).toContain("[crestodian] done: config.set"); + expect(lines.join("\n")).toContain("[openclaw] done: config.set"); }); it("applies SecretRef config set through typed deps and writes an audit entry", async () => { - const tempDir = opTempDirs.make("crestodian-config-ref-"); + const tempDir = opTempDirs.make("openclaw-config-ref-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runConfigSet = vi.fn(async () => {}); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "config-set-ref", path: "gateway.auth.token", @@ -681,8 +695,8 @@ describe("parseCrestodianOperation", () => { refId: "OPENCLAW_GATEWAY_TOKEN", }, }); - expect(lines.join("\n")).toContain("[crestodian] done: config.setRef"); - const auditPath = path.join(tempDir, "audit", "crestodian.jsonl"); + expect(lines.join("\n")).toContain("[openclaw] done: config.setRef"); + const auditPath = path.join(tempDir, "audit", "system-agent.jsonl"); const audit = JSON.parse((await fs.readFile(auditPath, "utf8")).trim()); expectAuditRecord( audit, @@ -701,10 +715,10 @@ describe("parseCrestodianOperation", () => { }); it("keeps channel SecretRef writes available after inference is verified", async () => { - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const runConfigSet = vi.fn(async () => {}); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "config-set-ref", path: "channels.telegram.botToken", @@ -785,25 +799,25 @@ describe("parseCrestodianOperation", () => { id: "OPENAI_API_KEY", }, ])("rejects unverified inference-route write $path", async (operation) => { - const tempDir = opTempDirs.make("crestodian-route-write-refused-"); + const tempDir = opTempDirs.make("openclaw-route-write-refused-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runConfigSet = vi.fn(async () => {}); await expect( - executeCrestodianOperation(operation, runtime, { + executeSystemAgentOperation(operation, runtime, { approved: true, deps: { runConfigSet }, }), ).rejects.toThrow("openclaw onboard"); expect(runConfigSet).not.toHaveBeenCalled(); - expect(lines.join("\n")).not.toContain("[crestodian] running:"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] running:"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("runs plugin list and search as read-only operations", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runPluginsList = vi.fn(async (pluginRuntime: RuntimeEnv) => { pluginRuntime.log("plugin rows"); }); @@ -811,11 +825,11 @@ describe("parseCrestodianOperation", () => { pluginRuntime.log(`search rows: ${query}`); }); - const listResult = await executeCrestodianOperation({ kind: "plugin-list" }, runtime, { + const listResult = await executeSystemAgentOperation({ kind: "plugin-list" }, runtime, { deps: { runPluginsList, runPluginsSearch }, }); expect(listResult.applied).toBe(false); - const searchResult = await executeCrestodianOperation( + const searchResult = await executeSystemAgentOperation( { kind: "plugin-search", query: "calendar" }, runtime, { @@ -831,14 +845,14 @@ describe("parseCrestodianOperation", () => { }); it("installs plugins only after approval and audits the write", async () => { - const tempDir = opTempDirs.make("crestodian-plugin-install-"); + const tempDir = opTempDirs.make("openclaw-plugin-install-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runPluginInstall = vi.fn(async (spec: string, pluginRuntime: RuntimeEnv) => { pluginRuntime.log(`installed ${spec}`); }); - const plan = await executeCrestodianOperation( + const plan = await executeSystemAgentOperation( { kind: "plugin-install", spec: "clawhub:openclaw-demo" }, runtime, { deps: { runPluginInstall } }, @@ -849,7 +863,7 @@ describe("parseCrestodianOperation", () => { }); expect(runPluginInstall).not.toHaveBeenCalled(); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "plugin-install", spec: "clawhub:openclaw-demo" }, runtime, { @@ -863,8 +877,8 @@ describe("parseCrestodianOperation", () => { const installCall = requireFirstMockCall(runPluginInstall, "runPluginInstall"); expect(installCall[0]).toBe("clawhub:openclaw-demo"); expectRuntimeArg(installCall[1]); - expect(lines.join("\n")).toContain("[crestodian] done: plugin.install"); - const auditPath = path.join(tempDir, "audit", "crestodian.jsonl"); + expect(lines.join("\n")).toContain("[openclaw] done: plugin.install"); + const auditPath = path.join(tempDir, "audit", "system-agent.jsonl"); const audit = JSON.parse((await fs.readFile(auditPath, "utf8")).trim()); expectAuditRecord( audit, @@ -886,7 +900,7 @@ describe("parseCrestodianOperation", () => { }; await expect( - executeCrestodianOperation( + executeSystemAgentOperation( { kind: "plugin-install", spec: "https://example.test/plugin.tgz" }, runtime, { approved: true, deps: { runPluginInstall } }, @@ -899,23 +913,11 @@ describe("parseCrestodianOperation", () => { expect(mockConfig.readConfigFileSnapshot).not.toHaveBeenCalled(); }); - it("rejects arbitrary plugin sources before proposing or installing them", async () => { - const { runtime } = createCrestodianTestRuntime(); - const runPluginInstall = vi.fn(); - - await expect( - executeCrestodianOperation({ kind: "plugin-install", spec: "npm:@example/plugin" }, runtime, { - deps: { runPluginInstall }, - }), - ).rejects.toThrow("trusted shell"); - expect(runPluginInstall).not.toHaveBeenCalled(); - }); - it("refuses plugin uninstall because it cannot prove inference survives", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runPluginUninstall = vi.fn(); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "plugin-uninstall", pluginId: "openclaw-demo" }, runtime, { approved: true, deps: { runPluginUninstall } }, @@ -929,9 +931,9 @@ describe("parseCrestodianOperation", () => { }); it("runs setup bootstrap only after approval and audits it", async () => { - const tempDir = opTempDirs.make("crestodian-setup-"); + const tempDir = opTempDirs.make("openclaw-setup-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); mockConfig.setConfig({ agents: { defaults: { model: { primary: "openai/gpt-5.5" } } } }); const applySetup = vi.fn(async () => ({ configPath: path.join(tempDir, "openclaw.json"), @@ -949,7 +951,7 @@ describe("parseCrestodianOperation", () => { })), }; - const plan = await executeCrestodianOperation( + const plan = await executeSystemAgentOperation( { kind: "setup", workspace: "/tmp/work" }, runtime, { deps }, @@ -960,7 +962,7 @@ describe("parseCrestodianOperation", () => { expect(lines.join("\n")).toContain("Model choice: keep verified default openai/gpt-5.5."); expect(applySetup).not.toHaveBeenCalled(); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "setup", workspace: "/tmp/work" }, runtime, { @@ -971,7 +973,7 @@ describe("parseCrestodianOperation", () => { ); expect(result.applied).toBe(true); - expect(lines.join("\n")).toContain("[crestodian] done: crestodian.setup"); + expect(lines.join("\n")).toContain("[openclaw] done: openclaw.setup"); expect(applySetup).toHaveBeenCalledWith( { workspace: "/tmp/work", @@ -982,12 +984,12 @@ describe("parseCrestodianOperation", () => { { commit: expect.any(Function) }, ); expect(lines.join("\n")).toContain("Default model: openai/gpt-5.5 (verified and kept)"); - const auditPath = path.join(tempDir, "audit", "crestodian.jsonl"); + const auditPath = path.join(tempDir, "audit", "system-agent.jsonl"); const audit = JSON.parse((await fs.readFile(auditPath, "utf8")).trim()); expectAuditRecord( audit, { - operation: "crestodian.setup", + operation: "openclaw.setup", summary: "Bootstrapped setup workspace", }, { @@ -1001,9 +1003,9 @@ describe("parseCrestodianOperation", () => { }); it("rejects setup without a default model before any workspace or Gateway write", async () => { - const tempDir = opTempDirs.make("crestodian-no-inference-setup-"); + const tempDir = opTempDirs.make("openclaw-no-inference-setup-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const applySetup = vi.fn(); const deps = { applySetup, @@ -1012,26 +1014,26 @@ describe("parseCrestodianOperation", () => { }; await expect( - executeCrestodianOperation({ kind: "setup", workspace: "/tmp/work" }, runtime, { + executeSystemAgentOperation({ kind: "setup", workspace: "/tmp/work" }, runtime, { approved: true, deps, }), ).rejects.toThrow("requires working inference first"); expect(applySetup).not.toHaveBeenCalled(); - expect(lines.join("\n")).not.toContain("[crestodian] running: crestodian.setup"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] running: openclaw.setup"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("rejects setup when the current route fails its live inference check", async () => { - const tempDir = opTempDirs.make("crestodian-failed-inference-setup-"); + const tempDir = opTempDirs.make("openclaw-failed-inference-setup-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); mockConfig.setConfig({ agents: { defaults: { model: { primary: "openai/gpt-5.5" } } } }); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const applySetup = vi.fn(); await expect( - executeCrestodianOperation({ kind: "setup", workspace: "/tmp/work" }, runtime, { + executeSystemAgentOperation({ kind: "setup", workspace: "/tmp/work" }, runtime, { approved: true, deps: { applySetup, @@ -1046,8 +1048,8 @@ describe("parseCrestodianOperation", () => { ).rejects.toThrow("failed a live check"); expect(applySetup).not.toHaveBeenCalled(); - expect(lines.join("\n")).not.toContain("[crestodian] running: crestodian.setup"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] running: openclaw.setup"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("rejects route drift during setup verification but preserves the concurrent edit", async () => { @@ -1055,11 +1057,11 @@ describe("parseCrestodianOperation", () => { agents: { defaults: { model: { primary: "openai/gpt-5.5" } } }, auth: { order: { openai: ["openai:old"] } }, }); - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const applySetup = vi.fn(); await expect( - executeCrestodianOperation({ kind: "setup", workspace: "/tmp/work" }, runtime, { + executeSystemAgentOperation({ kind: "setup", workspace: "/tmp/work" }, runtime, { approved: true, deps: { applySetup, @@ -1086,7 +1088,7 @@ describe("parseCrestodianOperation", () => { agents: { defaults: { model: { primary: "openai/gpt-5.5" } } }, gateway: { port: 18789 }, }); - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const applySetup = vi.fn(async () => ({ configPath: "/tmp/openclaw.json", configHashBefore: "mock-hash-0", @@ -1094,7 +1096,7 @@ describe("parseCrestodianOperation", () => { lines: [], })); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "setup", workspace: "/tmp/work" }, runtime, { @@ -1122,13 +1124,13 @@ describe("parseCrestodianOperation", () => { }); it("rejects a setup model switch before writing", async () => { - const tempDir = opTempDirs.make("crestodian-model-switch-setup-"); + const tempDir = opTempDirs.make("openclaw-model-switch-setup-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const applySetup = vi.fn(); await expect( - executeCrestodianOperation( + executeSystemAgentOperation( { kind: "setup", workspace: "/tmp/work", model: "acme/different" }, runtime, { @@ -1139,15 +1141,15 @@ describe("parseCrestodianOperation", () => { }, }, ), - ).rejects.toThrow("Exit Crestodian and run `openclaw onboard`"); + ).rejects.toThrow("Exit OpenClaw and run `openclaw onboard`"); expect(applySetup).not.toHaveBeenCalled(); }); it("allows the same requested model while preserving it without a model write", async () => { - const tempDir = opTempDirs.make("crestodian-same-model-setup-"); + const tempDir = opTempDirs.make("openclaw-same-model-setup-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); mockConfig.setConfig({ agents: { defaults: { model: { primary: "openai/gpt-5.5" } } } }); const applySetup = vi.fn(async () => ({ configPath: path.join(tempDir, "openclaw.json"), @@ -1156,7 +1158,7 @@ describe("parseCrestodianOperation", () => { lines: ["Workspace: /tmp/work"], })); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "setup", workspace: "/tmp/work", model: "openai/gpt-5.5" }, runtime, { @@ -1186,7 +1188,7 @@ describe("parseCrestodianOperation", () => { }); it("live-verifies a staged default model before writing and preserves concurrent edits", async () => { - const tempDir = opTempDirs.make("crestodian-verified-model-"); + const tempDir = opTempDirs.make("openclaw-verified-model-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); mockConfig.setConfig({ agents: { @@ -1199,7 +1201,7 @@ describe("parseCrestodianOperation", () => { models: { providers: { openai: { baseUrl: "https://api.openai.com/v1" } } }, }); mockConfig.mutateConfigFile.mockClear(); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); let verificationCalls = 0; const verifyInferenceConfig = vi.fn(async ({ config }: { config: TestConfig }) => { verificationCalls += 1; @@ -1256,7 +1258,7 @@ describe("parseCrestodianOperation", () => { return { ok: true as const, modelRef: "openai/gpt-5.5", latencyMs: 17 }; }); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { approved: true, deps: { verifyInferenceConfig } }, @@ -1305,7 +1307,7 @@ describe("parseCrestodianOperation", () => { expect(lines.join("\n")).toContain("Default model: openai/gpt-5.5"); const audit = parseLastJsonLine( - await fs.readFile(path.join(tempDir, "audit", "crestodian.jsonl"), "utf8"), + await fs.readFile(path.join(tempDir, "audit", "system-agent.jsonl"), "utf8"), ); expectAuditRecord( audit, @@ -1440,18 +1442,18 @@ describe("parseCrestodianOperation", () => { ])( "aborts when concurrent $field changes invalidate the verified route", async ({ initial, change }) => { - const tempDir = opTempDirs.make("crestodian-route-conflict-"); + const tempDir = opTempDirs.make("openclaw-route-conflict-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); mockConfig.setConfig(initial); mockConfig.mutateConfigFile.mockClear(); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const verifyInferenceConfig = vi.fn(async () => { mockConfig.setConfig(change(mockConfig.currentConfig())); return { ok: true as const, modelRef: "openai/gpt-5.5", latencyMs: 7 }; }); await expect( - executeCrestodianOperation( + executeSystemAgentOperation( { kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { @@ -1462,13 +1464,13 @@ describe("parseCrestodianOperation", () => { ).rejects.toThrow("inference route changed during verification"); expect(mockConfig.mutateConfigFile).toHaveBeenCalledOnce(); - expect(lines.join("\n")).not.toContain("[crestodian] done: config.setDefaultModel"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] done: config.setDefaultModel"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }, ); it("keeps the working model and writes no audit when live inference fails", async () => { - const tempDir = opTempDirs.make("crestodian-rejected-model-"); + const tempDir = opTempDirs.make("openclaw-rejected-model-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); const originalConfig = { agents: { defaults: { model: { primary: "anthropic/claude-sonnet-4-6" } } }, @@ -1476,7 +1478,7 @@ describe("parseCrestodianOperation", () => { }; mockConfig.setConfig(originalConfig); mockConfig.mutateConfigFile.mockClear(); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const verifyInferenceConfig = vi.fn(async () => ({ ok: false as const, status: "auth" as const, @@ -1484,7 +1486,7 @@ describe("parseCrestodianOperation", () => { })); await expect( - executeCrestodianOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { + executeSystemAgentOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { approved: true, deps: { verifyInferenceConfig }, }), @@ -1494,26 +1496,26 @@ describe("parseCrestodianOperation", () => { expect(mockConfig.currentConfig()).toEqual(originalConfig); expect(mockConfig.mutateConfigFile).not.toHaveBeenCalled(); - expect(lines.join("\n")).not.toContain("[crestodian] done: config.setDefaultModel"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] done: config.setDefaultModel"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("writes nothing when the exact latest route fails its locked recheck", async () => { - const tempDir = opTempDirs.make("crestodian-latest-route-rejected-"); + const tempDir = opTempDirs.make("openclaw-latest-route-rejected-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); const originalConfig = { agents: { defaults: { model: { primary: "anthropic/claude-sonnet-4-6" } } }, }; mockConfig.setConfig(originalConfig); mockConfig.mutateConfigFile.mockClear(); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const verifyInferenceConfig = vi .fn() .mockResolvedValueOnce({ ok: true, modelRef: "openai/gpt-5.5", latencyMs: 5 }) .mockResolvedValueOnce({ ok: false, status: "auth", error: "credential changed" }); await expect( - executeCrestodianOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { + executeSystemAgentOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { approved: true, deps: { verifyInferenceConfig }, }), @@ -1521,19 +1523,19 @@ describe("parseCrestodianOperation", () => { expect(verifyInferenceConfig).toHaveBeenCalledTimes(2); expect(mockConfig.currentConfig()).toEqual(originalConfig); - expect(lines.join("\n")).not.toContain("[crestodian] done: config.setDefaultModel"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] done: config.setDefaultModel"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("rejects a live result from a different model before opening the write boundary", async () => { - const tempDir = opTempDirs.make("crestodian-mismatched-model-result-"); + const tempDir = opTempDirs.make("openclaw-mismatched-model-result-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); const originalConfig = { agents: { defaults: { model: { primary: "anthropic/claude-sonnet-4-6" } } }, }; mockConfig.setConfig(originalConfig); mockConfig.mutateConfigFile.mockClear(); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const verifyInferenceConfig = vi.fn(async () => ({ ok: true as const, modelRef: "openai/gpt-5.4", @@ -1541,7 +1543,7 @@ describe("parseCrestodianOperation", () => { })); await expect( - executeCrestodianOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { + executeSystemAgentOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { approved: true, deps: { verifyInferenceConfig }, }), @@ -1550,26 +1552,26 @@ describe("parseCrestodianOperation", () => { expect(verifyInferenceConfig).toHaveBeenCalledOnce(); expect(mockConfig.mutateConfigFile).not.toHaveBeenCalled(); expect(mockConfig.currentConfig()).toEqual(originalConfig); - expect(lines.join("\n")).not.toContain("[crestodian] done: config.setDefaultModel"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] done: config.setDefaultModel"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("rejects a different model result from the final commit-boundary probe", async () => { - const tempDir = opTempDirs.make("crestodian-final-mismatched-model-result-"); + const tempDir = opTempDirs.make("openclaw-final-mismatched-model-result-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); const originalConfig = { agents: { defaults: { model: { primary: "anthropic/claude-sonnet-4-6" } } }, }; mockConfig.setConfig(originalConfig); mockConfig.mutateConfigFile.mockClear(); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const verifyInferenceConfig = vi .fn() .mockResolvedValueOnce({ ok: true, modelRef: "openai/gpt-5.5", latencyMs: 5 }) .mockResolvedValueOnce({ ok: true, modelRef: "openai/gpt-5.4", latencyMs: 5 }); await expect( - executeCrestodianOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { + executeSystemAgentOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { approved: true, deps: { verifyInferenceConfig }, }), @@ -1577,19 +1579,19 @@ describe("parseCrestodianOperation", () => { expect(verifyInferenceConfig).toHaveBeenCalledTimes(2); expect(mockConfig.currentConfig()).toEqual(originalConfig); - expect(lines.join("\n")).not.toContain("[crestodian] done: config.setDefaultModel"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] done: config.setDefaultModel"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("rechecks the existing inference binding inside the locked model transform", async () => { - const tempDir = opTempDirs.make("crestodian-model-binding-rotated-"); + const tempDir = opTempDirs.make("openclaw-model-binding-rotated-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); const originalConfig = { agents: { defaults: { model: { primary: "anthropic/claude-sonnet-4-6" } } }, }; mockConfig.setConfig(originalConfig); mockConfig.mutateConfigFile.mockClear(); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); let bindingOwner = "verified"; const verifyInferenceConfig = vi.fn(async () => { bindingOwner = "rotated"; @@ -1601,34 +1603,34 @@ describe("parseCrestodianOperation", () => { }); const beforePersistentApply = vi.fn(async () => { if (bindingOwner !== "verified") { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } }); await expect( - executeCrestodianOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { + executeSystemAgentOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { approved: true, deps: { verifyInferenceConfig }, beforePersistentApply, }), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(verifyInferenceConfig).toHaveBeenCalledOnce(); expect(beforePersistentApply).toHaveBeenCalledOnce(); expect(mockConfig.currentConfig()).toEqual(originalConfig); - expect(lines.join("\n")).not.toContain("[crestodian] done: config.setDefaultModel"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] done: config.setDefaultModel"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("rechecks the existing inference binding after the candidate's final live probe", async () => { - const tempDir = opTempDirs.make("crestodian-model-binding-final-probe-rotated-"); + const tempDir = opTempDirs.make("openclaw-model-binding-final-probe-rotated-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); const originalConfig = { agents: { defaults: { model: { primary: "anthropic/claude-sonnet-4-6" } } }, }; mockConfig.setConfig(originalConfig); mockConfig.mutateConfigFile.mockClear(); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); let bindingOwner = "verified"; let verificationCalls = 0; const verifyInferenceConfig = vi.fn(async () => { @@ -1644,27 +1646,27 @@ describe("parseCrestodianOperation", () => { }); const beforePersistentApply = vi.fn(async () => { if (bindingOwner !== "verified") { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } }); await expect( - executeCrestodianOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { + executeSystemAgentOperation({ kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { approved: true, deps: { verifyInferenceConfig }, beforePersistentApply, }), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(verifyInferenceConfig).toHaveBeenCalledTimes(2); expect(beforePersistentApply).toHaveBeenCalledTimes(2); expect(mockConfig.currentConfig()).toEqual(originalConfig); - expect(lines.join("\n")).not.toContain("[crestodian] done: config.setDefaultModel"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] done: config.setDefaultModel"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); it("stages and persists model changes at the effective default-agent owner", async () => { - const tempDir = opTempDirs.make("crestodian-default-agent-model-"); + const tempDir = opTempDirs.make("openclaw-default-agent-model-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); mockConfig.setConfig({ agents: { @@ -1678,7 +1680,7 @@ describe("parseCrestodianOperation", () => { ], }, }); - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const verifyInferenceConfig = vi.fn(async ({ config }: { config: TestConfig }) => { const agents = requireRecord(config.agents, "agents"); expect(requireRecord(agents.defaults, "defaults").model).toEqual({ @@ -1691,7 +1693,7 @@ describe("parseCrestodianOperation", () => { return { ok: true as const, modelRef: "openai/gpt-5.5", latencyMs: 9 }; }); - await executeCrestodianOperation( + await executeSystemAgentOperation( { kind: "set-default-model", model: "openai/gpt-5.5" }, runtime, { approved: true, deps: { verifyInferenceConfig } }, @@ -1708,33 +1710,33 @@ describe("parseCrestodianOperation", () => { }); it("refuses doctor repairs before any write or audit", async () => { - const tempDir = opTempDirs.make("crestodian-doctor-fix-refused-"); + const tempDir = opTempDirs.make("openclaw-doctor-fix-refused-"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const runDoctor = vi.fn(async () => {}); - const result = await executeCrestodianOperation({ kind: "doctor-fix" }, runtime, { + const result = await executeSystemAgentOperation({ kind: "doctor-fix" }, runtime, { approved: true, deps: { runDoctor }, auditDetails: { rescue: true }, }); expect(result).toEqual({ applied: false }); - expect(isPersistentCrestodianOperation({ kind: "doctor-fix" })).toBe(false); + expect(isPersistentSystemAgentOperation({ kind: "doctor-fix" })).toBe(false); expect(runDoctor).not.toHaveBeenCalled(); - expect(lines.join("\n")).toContain("Exit Crestodian"); + expect(lines.join("\n")).toContain("Exit OpenClaw"); expect(lines.join("\n")).toContain("openclaw doctor --fix"); - expect(lines.join("\n")).not.toContain("[crestodian] running: doctor.fix"); - await expect(fs.access(path.join(tempDir, "audit", "crestodian.jsonl"))).rejects.toThrow(); + expect(lines.join("\n")).not.toContain("[openclaw] running: doctor.fix"); + await expect(fs.access(path.join(tempDir, "audit", "system-agent.jsonl"))).rejects.toThrow(); }); - it("returns from the agent TUI back to Crestodian", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + it("returns from the agent TUI back to OpenClaw", async () => { + const { runtime, lines } = createSystemAgentTestRuntime(); const runTui = vi.fn(async () => ({ - exitReason: "return-to-crestodian" as const, - crestodianMessage: "restart gateway", + exitReason: "return-to-system-agent" as const, + systemAgentMessage: "restart gateway", })); - const result = await executeCrestodianOperation( + const result = await executeSystemAgentOperation( { kind: "open-tui", agentId: "work" }, runtime, { @@ -1753,8 +1755,7 @@ describe("parseCrestodianOperation", () => { nextInput: "restart gateway", }); expect(lines.join("\n")).toContain( - "[crestodian] returned from agent with request: restart gateway", + "[openclaw] returned from agent with request: restart gateway", ); }); }); -/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */ diff --git a/src/crestodian/operations.ts b/src/system-agent/operations.ts similarity index 88% rename from src/crestodian/operations.ts rename to src/system-agent/operations.ts index 9e15b29bb750..7d6a7cf4a4f7 100644 --- a/src/crestodian/operations.ts +++ b/src/system-agent/operations.ts @@ -1,4 +1,4 @@ -// Crestodian operations parse, approve, execute, and audit setup-helper commands. +// OpenClaw operations parse, approve, execute, and audit setup-helper commands. import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; import type { ConfigSetOptions } from "../cli/config-set-input.js"; import type { DoctorOptions } from "../commands/doctor.types.js"; @@ -8,21 +8,22 @@ import { buildAgentMainSessionKey, normalizeAgentId } from "../routing/session-k import type { RuntimeEnv } from "../runtime.js"; import type { TuiResult } from "../tui/tui-types.js"; import { resolveUserPath, shortenHomePath } from "../utils.js"; -import { appendCrestodianAuditEntry, resolveCrestodianAuditPath } from "./audit.js"; +import { isReservedSystemAgentId } from "./agent-id.js"; +import { appendSystemAgentAuditEntry, resolveSystemAgentAuditPath } from "./audit.js"; import { projectDefaultInferenceRoute, sameDefaultInferenceRoute, type DefaultInferenceRouteProjection, } from "./inference-route.js"; -import type { CrestodianOverview } from "./overview.js"; -import { validateCrestodianPluginInstallSpec } from "./plugin-install.js"; +import type { SystemAgentOverview } from "./overview.js"; +import { validateSystemAgentPluginInstallSpec } from "./plugin-install.js"; /** - * Crestodian command parser and operation executor. + * OpenClaw command parser and operation executor. * * The grammar is a single anchored command language: every pattern must match * the whole input. Natural language never parses into an operation — it flows - * to the AI custodian instead (chat) or to the planner (one-shot). This is a + * to the system agent instead (chat) or to the planner (one-shot). This is a * security property, not a convenience: unanchored keyword matching used to * turn questions like "why did my gateway stop" into mutation proposals. * @@ -31,14 +32,14 @@ import { validateCrestodianPluginInstallSpec } from "./plugin-install.js"; */ type ConfigModule = typeof import("../config/config.js"); type ConfigFileSnapshot = Awaited>; -type CrestodianOverviewLoader = () => Promise; -type CrestodianOverviewFormatter = (overview: CrestodianOverview) => string; +type SystemAgentOverviewLoader = () => Promise; +type SystemAgentOverviewFormatter = (overview: SystemAgentOverview) => string; const loadConfigModule = async () => await import("../config/config.js"); const loadOverviewModule = async () => await import("./overview.js"); -/** Parsed Crestodian operation before approval/execution. */ -export type CrestodianOperation = +/** Parsed OpenClaw operation before approval/execution. */ +export type SystemAgentOperation = | { kind: "none"; message: string } | { kind: "overview" } | { kind: "doctor" } @@ -82,22 +83,22 @@ export type CrestodianOperation = | { kind: "set-default-model"; model: string }; /** Result returned by the operation executor. */ -export type CrestodianOperationResult = { +export type SystemAgentOperationResult = { applied: boolean; exitsInteractive?: boolean; message?: string; nextInput?: string; - followUp?: Extract; + followUp?: Extract; }; /** Injectable command dependencies used by tests and alternate runners. */ -export type CrestodianCommandDeps = { +export type SystemAgentCommandDeps = { readConfigFileSnapshot?: typeof import("../config/config.js").readConfigFileSnapshot; ensureAuthProfileStore?: typeof import("../agents/auth-profiles/store.js").ensureAuthProfileStore; resolveCliAuthBindingFingerprint?: typeof import("../agents/cli-auth-epoch.js").resolveCliAuthBindingFingerprint; resolveApiKeyForProvider?: typeof import("../agents/model-auth.js").resolveApiKeyForProvider; - formatOverview?: CrestodianOverviewFormatter; - loadOverview?: CrestodianOverviewLoader; + formatOverview?: SystemAgentOverviewFormatter; + loadOverview?: SystemAgentOverviewLoader; runAgentsAdd?: ( opts: { name?: string; @@ -130,7 +131,7 @@ export type CrestodianCommandDeps = { }) => Promise; /** Where setup side effects run; the gateway surface never manages its own daemon. */ setupSurface?: "cli" | "gateway"; - applySetup?: typeof import("./setup-apply.js").applyCrestodianSetup; + applySetup?: typeof import("./setup-apply.js").applySystemAgentSetup; verifyInferenceConfig?: typeof import("./setup-inference.js").verifySetupInferenceConfig; listChannelSetupPlugins?: typeof import("../channels/plugins/setup-registry.js").listChannelSetupPlugins; resolveChannelSetupEntries?: typeof import("../commands/channel-setup/discovery.js").resolveChannelSetupEntries; @@ -197,18 +198,12 @@ const OPEN_CHANNEL_SETUP_RE = /^open\s+channel\s+wizard(?:\s+for\s+(?[a const NO_MATCH_MESSAGE = "I can run doctor/status/health, check or restart Gateway, list agents/models, configure a model provider, set default model, connect channels (`connect telegram`), show `channel info `, open the setup wizard, show audit, or switch to your agent TUI."; -const RESERVED_CRESTODIAN_AGENT_ID = normalizeAgentId("crestodian"); - -function isReservedCrestodianAgentId(agentId: string): boolean { - return normalizeAgentId(agentId) === RESERVED_CRESTODIAN_AGENT_ID; -} - /** - * Parse one user command into Crestodian's closed operation union. Anything + * Parse one user command into OpenClaw's closed operation union. Anything * that does not match the anchored grammar exactly returns kind "none" so the - * caller can route it to the AI custodian (or show guidance). + * caller can route it to the system agent (or show guidance). */ -export function parseCrestodianOperation(input: string): CrestodianOperation { +export function parseSystemAgentOperation(input: string): SystemAgentOperation { const trimmed = input.trim(); const lower = trimmed.toLowerCase(); if (!trimmed) { @@ -249,7 +244,7 @@ export function parseCrestodianOperation(input: string): CrestodianOperation { return { kind: "open-tui" }; case "quit": case "exit": - return { kind: "none", message: "Crestodian retracts into shell. Bye." }; + return { kind: "none", message: "OpenClaw retracts into shell. Bye." }; default: break; } @@ -295,14 +290,11 @@ export function parseCrestodianOperation(input: string): CrestodianOperation { pluginInstallMatch.groups.spec.trim(), pluginInstallMatch.groups.source, ); - const validationError = validateCrestodianPluginInstallSpec(spec); + const validationError = validateSystemAgentPluginInstallSpec(spec); if (validationError) { return { kind: "none", message: validationError }; } - return { - kind: "plugin-install", - spec, - }; + return { kind: "plugin-install", spec }; } const pluginUninstallMatch = trimmed.match(PLUGIN_UNINSTALL_RE); if (pluginUninstallMatch?.groups?.pluginId?.trim()) { @@ -425,7 +417,7 @@ function normalizePluginInstallSpec(spec: string, source: string | undefined): s * Guided setup operations are intentionally absent: starting a wizard is not * itself a write; the wizard owns approval and persistence for its answers. */ -export function isPersistentCrestodianOperation(operation: CrestodianOperation): boolean { +export function isPersistentSystemAgentOperation(operation: SystemAgentOperation): boolean { return ( operation.kind === "set-default-model" || operation.kind === "config-set" || @@ -434,7 +426,7 @@ export function isPersistentCrestodianOperation(operation: CrestodianOperation): operation.kind === "plugin-install" || (operation.kind === "create-agent" && !operation.model?.trim() && - !isReservedCrestodianAgentId(operation.agentId)) || + !isReservedSystemAgentId(operation.agentId)) || operation.kind === "gateway-start" || operation.kind === "gateway-stop" || operation.kind === "gateway-restart" @@ -442,7 +434,7 @@ export function isPersistentCrestodianOperation(operation: CrestodianOperation): } /** Format a user-facing description for an operation requiring approval. */ -export function describeCrestodianPersistentOperation(operation: CrestodianOperation): string { +export function describeSystemAgentPersistentOperation(operation: SystemAgentOperation): string { switch (operation.kind) { case "set-default-model": return `set agents.defaults.model.primary to ${operation.model}`; @@ -455,7 +447,7 @@ export function describeCrestodianPersistentOperation(operation: CrestodianOpera case "model-setup": return "configure a model provider and default model"; case "doctor-fix": - return "exit Crestodian and run openclaw doctor --fix"; + return "exit OpenClaw and run openclaw doctor --fix"; case "plugin-install": return `install plugin ${operation.spec}`; case "plugin-uninstall": @@ -474,8 +466,8 @@ export function describeCrestodianPersistentOperation(operation: CrestodianOpera } /** Format the standard approval plan text for a persistent operation. */ -export function formatCrestodianPersistentPlan(operation: CrestodianOperation): string { - return `Plan: ${describeCrestodianPersistentOperation(operation)}. Say yes to apply.`; +export function formatSystemAgentPersistentPlan(operation: SystemAgentOperation): string { + return `Plan: ${describeSystemAgentPersistentOperation(operation)}. Say yes to apply.`; } function formatCreateAgentWorkspace(workspace: string | undefined): string { @@ -534,13 +526,13 @@ function readConfigValueAtPath(config: unknown, path: string): { found: boolean; } function formatSetupPlanDescription( - operation: Extract, + operation: Extract, ): string { const workspace = shortenHomePath(resolveUserPath(operation.workspace ?? process.cwd())); return `bootstrap OpenClaw setup for workspace ${workspace}`; } -function formatGatewayStatusLine(overview: CrestodianOverview): string { +function formatGatewayStatusLine(overview: SystemAgentOverview): string { return [ `Gateway: ${overview.gateway.reachable ? "reachable" : "not reachable"}`, `URL: ${overview.gateway.url}`, @@ -572,16 +564,16 @@ async function readConfigFileSnapshotLazy(): Promise { } async function loadOverviewForOperation( - deps: CrestodianCommandDeps | undefined, -): Promise { + deps: SystemAgentCommandDeps | undefined, +): Promise { if (deps?.loadOverview) { return await deps.loadOverview(); } - const { loadCrestodianOverview } = await loadOverviewModule(); - return await loadCrestodianOverview(); + const { loadSystemAgentOverview } = await loadOverviewModule(); + return await loadSystemAgentOverview(); } -async function resolveChannelSetupState(deps: CrestodianCommandDeps | undefined) { +async function resolveChannelSetupState(deps: SystemAgentCommandDeps | undefined) { const listPlugins = deps?.listChannelSetupPlugins ?? (await import("../channels/plugins/setup-registry.js")).listChannelSetupPlugins; @@ -641,7 +633,7 @@ function createNoExitRuntime(runtime: RuntimeEnv): RuntimeEnv { async function resolveTuiAgentId(params: { requestedAgentId: string | undefined; requestedWorkspace?: string; - deps?: CrestodianCommandDeps; + deps?: SystemAgentCommandDeps; }): Promise { const overview = await loadOverviewForOperation(params.deps); const workspace = params.requestedWorkspace @@ -670,7 +662,7 @@ async function resolveTuiAgentId(params: { type ExecuteOptions = { approved?: boolean; - deps?: CrestodianCommandDeps; + deps?: SystemAgentCommandDeps; auditDetails?: Record; /** * Authority check used by the guarded commit seam for host-approved writes. @@ -683,12 +675,12 @@ type ExecuteOptions = { /** * One persistent operation = one audited apply. The shared wrapper owns the * approval gate, before/after config hashes, the audit record, and the - * `[crestodian] running/done` markers the e2e lanes assert on; each spec only + * `[openclaw] running/done` markers the e2e lanes assert on; each spec only * describes what to run and what to record. */ type PersistentApplyContext = { runtime: RuntimeEnv; - deps?: CrestodianCommandDeps; + deps?: SystemAgentCommandDeps; /** Re-check authority, then enter one persistent side-effect boundary. */ commit(effect: () => Promise | T): Promise; }; @@ -702,18 +694,18 @@ type PersistentApplyOutcome = { async function applyPersistentOperation(params: { auditOperation: string; - operation: CrestodianOperation; + operation: SystemAgentOperation; runtime: RuntimeEnv; opts: ExecuteOptions; run: (ctx: PersistentApplyContext) => Promise; -}): Promise { +}): Promise { const { auditOperation, runtime, opts } = params; if (!opts.approved) { - const message = formatCrestodianPersistentPlan(params.operation); + const message = formatSystemAgentPersistentPlan(params.operation); runtime.log(message); return { applied: false, message }; } - runtime.log(`[crestodian] running: ${auditOperation}`); + runtime.log(`[openclaw] running: ${auditOperation}`); const { readConfigFileSnapshot } = await loadConfigModule(); const before = await readConfigFileSnapshot(); const commit: PersistentApplyContext["commit"] = async (effect) => { @@ -723,7 +715,7 @@ async function applyPersistentOperation(params: { const outcome = await params.run({ runtime, deps: opts.deps, commit }); const after = await readConfigFileSnapshot(); try { - await appendCrestodianAuditEntry({ + await appendSystemAgentAuditEntry({ operation: auditOperation, summary: outcome.summary, configPath: outcome.configPath ?? after.path ?? before.path ?? undefined, @@ -738,12 +730,12 @@ async function applyPersistentOperation(params: { `${outcome.summary}, but OpenClaw could not record its audit entry: ${formatErrorMessage(error)}`, ); } - runtime.log(`[crestodian] done: ${auditOperation}`); + runtime.log(`[openclaw] done: ${auditOperation}`); return { applied: true }; } async function runConfigSetOperation(params: { - operation: Extract; + operation: Extract; ctx: PersistentApplyContext; }): Promise { const { operation, ctx } = params; @@ -812,20 +804,20 @@ function isInferenceRouteConfigPath(path: readonly string[]): boolean { } async function assertConfigWriteDoesNotBypassInferenceVerification( - operation: Extract, + operation: Extract, ): Promise { const { parseConfigSetPath } = await import("../cli/config-cli.js"); if (!isInferenceRouteConfigPath(parseConfigSetPath(operation.path))) { return; } throw new Error( - "Direct config writes cannot change inference routing or include alternate config. Use `set default model ` for an already configured route, or exit Crestodian and run `openclaw onboard` to change provider/auth access.", + "Direct config writes cannot change inference routing or include alternate config. Use `set default model ` for an already configured route, or exit OpenClaw and run `openclaw onboard` to change provider/auth access.", ); } async function verifyCurrentSetupInference( runtime: RuntimeEnv, - deps?: CrestodianCommandDeps, + deps?: SystemAgentCommandDeps, ): Promise<{ modelRef: string; route: DefaultInferenceRouteProjection; @@ -835,14 +827,14 @@ async function verifyCurrentSetupInference( const before = await readConfigFileSnapshot(); if (!before.exists || !before.valid) { throw new Error( - "Crestodian setup requires a valid configured inference route. Exit Crestodian and run `openclaw onboard`, then retry.", + "OpenClaw setup requires a valid configured inference route. Exit OpenClaw and run `openclaw onboard`, then retry.", ); } const beforeConfig = before.runtimeConfig ?? before.config; const beforeRoute = await projectDefaultInferenceRoute(beforeConfig); if (!beforeRoute.route) { throw new Error( - "Crestodian setup requires working inference first. Exit Crestodian and run `openclaw onboard`, then retry.", + "OpenClaw setup requires working inference first. Exit OpenClaw and run `openclaw onboard`, then retry.", ); } const verifyInferenceConfig = @@ -851,7 +843,7 @@ async function verifyCurrentSetupInference( const verification = await verifyInferenceConfig({ config: beforeConfig, runtime }); if (!verification.ok) { throw new Error( - `Crestodian setup requires working inference first. The configured route failed a live check: ${verification.error} Exit Crestodian and run \`openclaw onboard\`, then retry.`, + `OpenClaw setup requires working inference first. The configured route failed a live check: ${verification.error} Exit OpenClaw and run \`openclaw onboard\`, then retry.`, ); } @@ -879,26 +871,26 @@ async function verifyCurrentSetupInference( } async function executeSetup( - operation: Extract, + operation: Extract, runtime: RuntimeEnv, opts: ExecuteOptions, -): Promise { +): Promise { const overview = await loadOverviewForOperation(opts.deps); const defaultModel = overview.defaultModel?.trim(); if (!defaultModel) { throw new Error( - "Crestodian setup requires working inference first. Run `openclaw onboard` to configure and verify a default model, then start Crestodian again.", + "OpenClaw setup requires working inference first. Run `openclaw onboard` to configure and verify a default model, then start OpenClaw again.", ); } const requestedModel = operation.model?.trim(); if (requestedModel && requestedModel !== defaultModel) { throw new Error( - `Crestodian setup will preserve the verified default model ${defaultModel}. Exit Crestodian and run \`openclaw onboard\` to stage, live-test, and save a different inference route.`, + `OpenClaw setup will preserve the verified default model ${defaultModel}. Exit OpenClaw and run \`openclaw onboard\` to stage, live-test, and save a different inference route.`, ); } if (!opts.approved) { const message = [ - formatCrestodianPersistentPlan(operation), + formatSystemAgentPersistentPlan(operation), `Model choice: keep verified default ${defaultModel}.`, ].join("\n"); runtime.log(message); @@ -907,18 +899,18 @@ async function executeSetup( const verified = await verifyCurrentSetupInference(runtime, opts.deps); if (requestedModel && requestedModel !== verified.modelRef) { throw new Error( - `The verified default model is now ${verified.modelRef}, not ${requestedModel}. Review the current route or exit Crestodian and run \`openclaw onboard\` before retrying setup.`, + `The verified default model is now ${verified.modelRef}, not ${requestedModel}. Review the current route or exit OpenClaw and run \`openclaw onboard\` before retrying setup.`, ); } const workspace = resolveUserPath(operation.workspace ?? process.cwd()); return await applyPersistentOperation({ - auditOperation: "crestodian.setup", + auditOperation: "openclaw.setup", operation, runtime, opts, run: async (ctx) => { const applySetup = - ctx.deps?.applySetup ?? (await import("./setup-apply.js")).applyCrestodianSetup; + ctx.deps?.applySetup ?? (await import("./setup-apply.js")).applySystemAgentSetup; const surface = ctx.deps?.setupSurface ?? "cli"; // The outer boundary covers injected implementations. The production // setup helper also uses this same seam for each of its internal writes. @@ -955,10 +947,10 @@ async function executeSetup( } async function executeSetDefaultModel( - operation: Extract, + operation: Extract, runtime: RuntimeEnv, opts: ExecuteOptions, -): Promise { +): Promise { return await applyPersistentOperation({ auditOperation: "config.setDefaultModel", operation, @@ -966,10 +958,10 @@ async function executeSetDefaultModel( opts, run: async (ctx) => { const { mutateConfigFile, readConfigFileSnapshot } = await loadConfigModule(); - const { applyCrestodianModelSelection, createCrestodianModelSelectionUpdater } = + const { applySystemAgentModelSelection, createSystemAgentModelSelectionUpdater } = await import("./setup-apply.js"); const snapshot = await readConfigFileSnapshot(); - const stagedConfig = await applyCrestodianModelSelection({ + const stagedConfig = await applySystemAgentModelSelection({ config: snapshot.sourceConfig, model: operation.model, }); @@ -996,7 +988,7 @@ async function executeSetDefaultModel( } let persistedVerification = initialVerification; let selectedRouteForCommit = verifiedRoute; - const selectModel = await createCrestodianModelSelectionUpdater({ + const selectModel = await createSystemAgentModelSelectionUpdater({ model: operation.model, }); const result = await mutateConfigFile({ @@ -1025,7 +1017,7 @@ async function executeSetDefaultModel( "The final live inference test did not verify the exact model route at the config commit boundary, so the requested model was not saved. Review model aliases and runtime routing, then retry.", ); } - // The live probe can outlive the original Crestodian authority. + // The live probe can outlive the original OpenClaw authority. // Re-check it last, immediately before the writer crosses to disk. await opts.beforePersistentApply?.(); persistedVerification = latestVerification; @@ -1071,13 +1063,15 @@ async function executeSetDefaultModel( } async function executePluginInstall( - operation: Extract, + operation: Extract, runtime: RuntimeEnv, opts: ExecuteOptions, -): Promise { - const validationError = validateCrestodianPluginInstallSpec(operation.spec); - if (validationError) { - throw new Error(validationError); +): Promise { + if (opts.approved) { + const validationError = validateSystemAgentPluginInstallSpec(operation.spec); + if (validationError) { + throw new Error(validationError); + } } const result = await applyPersistentOperation({ auditOperation: "plugin.install", @@ -1103,12 +1097,12 @@ async function executePluginInstall( return result; } -/** Execute a parsed Crestodian operation after applying approval gates and audit logging. */ -export async function executeCrestodianOperation( - operation: CrestodianOperation, +/** Execute a parsed OpenClaw operation after applying approval gates and audit logging. */ +export async function executeSystemAgentOperation( + operation: SystemAgentOperation, runtime: RuntimeEnv, opts: ExecuteOptions = {}, -): Promise { +): Promise { switch (operation.kind) { case "none": runtime.log(operation.message); @@ -1118,8 +1112,8 @@ export async function executeCrestodianOperation( if (opts.deps?.formatOverview) { runtime.log(opts.deps.formatOverview(overview)); } else { - const { formatCrestodianOverview } = await loadOverviewModule(); - runtime.log(formatCrestodianOverview(overview)); + const { formatSystemAgentOverview } = await loadOverviewModule(); + runtime.log(formatSystemAgentOverview(overview)); } return { applied: false }; } @@ -1178,7 +1172,7 @@ export async function executeCrestodianOperation( return { applied: false }; } case "audit": - runtime.log(`Audit log: ${resolveCrestodianAuditPath()}`); + runtime.log(`Audit log: ${resolveSystemAgentAuditPath()}`); runtime.log("Only applied writes/actions are recorded; discovery stays quiet."); return { applied: false }; case "config-validate": { @@ -1309,13 +1303,13 @@ export async function executeCrestodianOperation( return { applied: false }; } case "channel-setup": - // Channel setup is a multi-step wizard; only interactive Crestodian (TUI + // Channel setup is a multi-step wizard; only interactive OpenClaw (TUI // chat bridge or the gateway chat) can host it. One-shot mode points at // the guided paths. runtime.log( [ `Connecting ${operation.channel} needs an interactive session.`, - "Run `openclaw crestodian` and say `connect " + operation.channel + "`,", + "Run `openclaw setup` and say `connect " + operation.channel + "`,", "or run `openclaw channels add` for the terminal wizard.", ].join("\n"), ); @@ -1323,8 +1317,8 @@ export async function executeCrestodianOperation( case "model-setup": runtime.log( [ - "Changing model providers must happen outside the inference session that powers Crestodian.", - "Exit Crestodian and run `openclaw onboard`; it stages credentials, live-tests the candidate route, and saves only a passing setup.", + "Changing model providers must happen outside the inference session that powers OpenClaw.", + "Exit OpenClaw and run `openclaw onboard`; it stages credentials, live-tests the candidate route, and saves only a passing setup.", ].join("\n"), ); return { applied: false }; @@ -1377,21 +1371,21 @@ export async function executeCrestodianOperation( return await executePluginInstall(operation, runtime, opts); case "plugin-uninstall": { const message = [ - "Crestodian cannot prove that uninstalling a plugin will preserve its own active inference route.", - `Exit Crestodian and run \`openclaw plugins uninstall ${operation.pluginId}\` from a terminal.`, + "OpenClaw cannot prove that uninstalling a plugin will preserve its own active inference route.", + `Exit OpenClaw and run \`openclaw plugins uninstall ${operation.pluginId}\` from a terminal.`, ].join("\n"); runtime.log(message); return { applied: false, message }; } case "create-agent": { - if (isReservedCrestodianAgentId(operation.agentId)) { + if (isReservedSystemAgentId(operation.agentId)) { throw new Error( - 'Agent id "crestodian" is reserved for the privileged setup custodian. Choose a different agent id.', + `Agent id "${normalizeAgentId(operation.agentId)}" is reserved for the system agent. Choose a different agent id.`, ); } if (operation.model?.trim()) { throw new Error( - "Crestodian cannot save an explicit per-agent model until that new route can be live-tested. Retry without `model`; the new agent will inherit the already verified default model.", + "OpenClaw cannot save an explicit per-agent model until that new route can be live-tested. Retry without `model`; the new agent will inherit the already verified default model.", ); } const workspace = resolveUserPath(operation.workspace ?? process.cwd()); @@ -1433,7 +1427,7 @@ export async function executeCrestodianOperation( } case "doctor-fix": runtime.log( - "Doctor repairs can change the inference route that powers this session. Exit Crestodian and run `openclaw doctor --fix` in a terminal.", + "Doctor repairs can change the inference route that powers this session. Exit OpenClaw and run `openclaw doctor --fix` in a terminal.", ); return { applied: false }; case "status": { @@ -1500,13 +1494,13 @@ export async function executeCrestodianOperation( const session = agentId ? buildAgentMainSessionKey({ agentId }) : undefined; const runTui = opts.deps?.runTui ?? (await import("../tui/tui.js")).runTui; const result = await runTui({ local: true, session, deliver: false, historyLimit: 200 }); - if (result?.exitReason === "return-to-crestodian") { + if (result?.exitReason === "return-to-system-agent") { runtime.log( - result.crestodianMessage - ? `[crestodian] returned from agent with request: ${result.crestodianMessage}` - : "[crestodian] returned from agent", + result.systemAgentMessage + ? `[openclaw] returned from agent with request: ${result.systemAgentMessage}` + : "[openclaw] returned from agent", ); - return { applied: false, nextInput: result.crestodianMessage }; + return { applied: false, nextInput: result.systemAgentMessage }; } return { applied: false, exitsInteractive: true }; } @@ -1516,4 +1510,3 @@ export async function executeCrestodianOperation( return { applied: false }; } } -/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */ diff --git a/src/crestodian/overview.test.ts b/src/system-agent/overview.test.ts similarity index 83% rename from src/crestodian/overview.test.ts rename to src/system-agent/overview.test.ts index e35b69379d0a..ce225a70ecd2 100644 --- a/src/crestodian/overview.test.ts +++ b/src/system-agent/overview.test.ts @@ -1,15 +1,15 @@ -// Crestodian overview tests cover summary output for rescue diagnostics. +// OpenClaw overview tests cover summary output for rescue diagnostics. import { describe, expect, it } from "vitest"; import type { ConfigFileSnapshot, OpenClawConfig } from "../config/config.js"; import { - formatCrestodianOverview, - formatCrestodianOnboardingWelcome, - formatCrestodianStartupMessage, - loadCrestodianOverview, - type CrestodianOverview, + formatSystemAgentOverview, + formatSystemAgentOnboardingWelcome, + formatSystemAgentStartupMessage, + loadSystemAgentOverview, + type SystemAgentOverview, } from "./overview.js"; -function createOverview(defaultModel?: string): CrestodianOverview { +function createOverview(defaultModel?: string): SystemAgentOverview { return { config: { path: "/tmp/openclaw.json", @@ -39,7 +39,7 @@ function createOverview(defaultModel?: string): CrestodianOverview { }; } -describe("loadCrestodianOverview", () => { +describe("loadSystemAgentOverview", () => { it("summarizes config, agents, model, tools, and gateway", async () => { const runtimeConfig: OpenClawConfig = { agents: { @@ -66,7 +66,7 @@ describe("loadCrestodianOverview", () => { warnings: [], legacyIssues: [], }; - const overview = await loadCrestodianOverview({ + const overview = await loadSystemAgentOverview({ env: { OPENCLAW_TEST_FAST: "1" }, deps: { readConfigFileSnapshot: async () => snapshot, @@ -97,11 +97,11 @@ describe("loadCrestodianOverview", () => { expect(overview.gateway.reachable).toBe(false); expect(overview.references.docsPath).toMatch(/docs$/); expect(overview.references.sourceUrl).toBe("https://github.com/openclaw/openclaw"); - expect(formatCrestodianOverview(overview)).toContain( + expect(formatSystemAgentOverview(overview)).toContain( 'Next: run "gateway status" or "restart gateway"', ); - const startup = formatCrestodianStartupMessage(overview); - expect(startup).toContain("## Hi, I'm Crestodian."); + const startup = formatSystemAgentStartupMessage(overview); + expect(startup).toContain("## Hi, I'm OpenClaw."); expect(startup).toContain("Using: openai/gpt-5.2"); expect(startup).toContain("Gateway: not reachable"); expect(startup).toContain("I can start debugging with `gateway status`"); @@ -113,13 +113,13 @@ describe("loadCrestodianOverview", () => { it("fails closed in startup copy when inference is unavailable", () => { const overview = createOverview(); - const startup = formatCrestodianStartupMessage(overview); - expect(formatCrestodianOverview(overview)).toContain( + const startup = formatSystemAgentStartupMessage(overview); + expect(formatSystemAgentOverview(overview)).toContain( 'Next: run "openclaw onboard" to establish inference', ); expect(startup).toContain("Inference unavailable"); expect(startup).toContain("run `openclaw onboard`"); - expect(startup).toContain("Crestodian needs working inference"); + expect(startup).toContain("OpenClaw needs working inference"); expect(startup).not.toContain("local Claude Code/Codex/Gemini login"); expect(startup).not.toContain("typed commands as last resort"); }); @@ -127,7 +127,7 @@ describe("loadCrestodianOverview", () => { it("describes post-inference onboarding as the start of remaining setup", () => { const overview = createOverview("openai/gpt-5.2"); - const welcome = formatCrestodianOnboardingWelcome(overview); + const welcome = formatSystemAgentOnboardingWelcome(overview); expect(welcome).toContain("## Inference is ready."); expect(welcome).toContain("Verified model: openai/gpt-5.2"); expect(welcome).toContain("finish your workspace, Gateway"); diff --git a/src/crestodian/overview.ts b/src/system-agent/overview.ts similarity index 87% rename from src/crestodian/overview.ts rename to src/system-agent/overview.ts index 08b67a0810da..26ca00da7886 100644 --- a/src/crestodian/overview.ts +++ b/src/system-agent/overview.ts @@ -1,4 +1,4 @@ -// Crestodian overview gathers config, agent, tool, docs, source, and gateway status. +// OpenClaw overview gathers config, agent, tool, docs, source, and gateway status. import { listAgentEntries, resolveAgentEffectiveModelPrimary, @@ -20,7 +20,7 @@ import { resolveAgentModelPrimaryValue } from "../config/model-input.js"; import { normalizeAgentId } from "../routing/session-key.js"; import { probeGatewayUrl, probeLocalCommand, type LocalCommandProbe } from "./probes.js"; -type CrestodianAgentSummary = { +type SystemAgentSummary = { id: string; name?: string; isDefault: boolean; @@ -28,7 +28,7 @@ type CrestodianAgentSummary = { workspace?: string; }; -export type CrestodianOverview = { +export type SystemAgentOverview = { config: { path: string; exists: boolean; @@ -36,7 +36,7 @@ export type CrestodianOverview = { issues: string[]; hash: string | null; }; - agents: CrestodianAgentSummary[]; + agents: SystemAgentSummary[]; defaultAgentId: string; defaultModel?: string; tools: { @@ -70,7 +70,7 @@ type GatewayConnectionDetails = { remoteFallbackNote?: string; }; -type CrestodianOverviewDependencies = { +type SystemAgentOverviewDependencies = { readConfigFileSnapshot?: typeof readConfigFileSnapshot; resolveConfigPath?: typeof resolveConfigPath; resolveGatewayPort?: typeof resolveGatewayPort; @@ -90,7 +90,7 @@ function issueMessages(snapshot: ConfigFileSnapshot): string[] { }); } -function buildAgentSummaries(cfg: OpenClawConfig): CrestodianAgentSummary[] { +function buildAgentSummaries(cfg: OpenClawConfig): SystemAgentSummary[] { const defaultAgentId = resolveDefaultAgentId(cfg); const entries = listAgentEntries(cfg); if (entries.length === 0) { @@ -103,7 +103,7 @@ function buildAgentSummaries(cfg: OpenClawConfig): CrestodianAgentSummary[] { ]; } const seen = new Set(); - const summaries: CrestodianAgentSummary[] = []; + const summaries: SystemAgentSummary[] = []; // Agent ids are normalized and deduped so config aliases do not produce duplicate setup choices. for (const entry of entries) { const id = normalizeAgentId(entry.id); @@ -111,7 +111,7 @@ function buildAgentSummaries(cfg: OpenClawConfig): CrestodianAgentSummary[] { continue; } seen.add(id); - const summary: CrestodianAgentSummary = { + const summary: SystemAgentSummary = { id, isDefault: id === defaultAgentId, }; @@ -141,9 +141,9 @@ function resolveFastTestReferences(env: NodeJS.ProcessEnv): OpenClawReferencePat }; } -export async function loadCrestodianOverview( - opts: { env?: NodeJS.ProcessEnv; deps?: CrestodianOverviewDependencies } = {}, -): Promise { +export async function loadSystemAgentOverview( + opts: { env?: NodeJS.ProcessEnv; deps?: SystemAgentOverviewDependencies } = {}, +): Promise { const env = opts.env ?? process.env; const deps = opts.deps ?? {}; const readSnapshot = deps.readConfigFileSnapshot ?? readConfigFileSnapshot; @@ -228,7 +228,7 @@ function formatCommandProbe(probe: LocalCommandProbe): string { return probe.error ? `found (${probe.error})` : "found"; } -export function formatCrestodianOverview(overview: CrestodianOverview): string { +export function formatSystemAgentOverview(overview: SystemAgentOverview): string { const agentLines = overview.agents.map((agent) => { const bits = [ agent.id, @@ -249,7 +249,7 @@ export function formatCrestodianOverview(overview: CrestodianOverview): string { ? ["Config issues:", ...overview.config.issues.map((issue) => ` - ${issue}`)] : []; return [ - "Crestodian online. Little claws, typed tools.", + "OpenClaw online. Little claws, typed tools.", "", `Config: ${configStatus}`, `Path: ${overview.config.path}`, @@ -266,7 +266,7 @@ export function formatCrestodianOverview(overview: CrestodianOverview): string { `AI: ${ overview.defaultModel ? `conversation runs on ${overview.defaultModel}` - : "inference unavailable; run openclaw onboard before starting Crestodian" + : "inference unavailable; run openclaw onboard before starting OpenClaw" }`, `Docs: ${overview.references.docsPath ?? overview.references.docsUrl}`, overview.references.sourcePath @@ -274,14 +274,14 @@ export function formatCrestodianOverview(overview: CrestodianOverview): string { : `Source: ${overview.references.sourceUrl}`, `Gateway: ${overview.gateway.reachable ? "reachable" : "not reachable"} (${overview.gateway.url}, ${overview.gateway.source})`, overview.gateway.error ? `Gateway note: ${overview.gateway.error}` : undefined, - `Next: ${recommendCrestodianNextStep(overview)}`, + `Next: ${recommendSystemAgentNextStep(overview)}`, ...issueLines, ] .filter((line): line is string => line !== undefined) .join("\n"); } -function recommendCrestodianNextStep(overview: CrestodianOverview): string { +function recommendSystemAgentNextStep(overview: SystemAgentOverview): string { if (!overview.config.exists) { return 'run "openclaw onboard" to establish inference'; } @@ -297,36 +297,36 @@ function recommendCrestodianNextStep(overview: CrestodianOverview): string { return 'run "talk to agent" to enter your default agent'; } -function formatStartupConfigStatus(overview: CrestodianOverview): string { +function formatStartupConfigStatus(overview: SystemAgentOverview): string { if (!overview.config.exists) { return "missing"; } return overview.config.valid ? "valid" : "invalid"; } -function formatStartupUse(overview: CrestodianOverview): string { +function formatStartupUse(overview: SystemAgentOverview): string { if (overview.defaultModel) { return `Using: ${overview.defaultModel} — just tell me what you want.`; } return "Inference unavailable: run `openclaw onboard` and complete a live model check first."; } -function formatStartupGatewayStatus(overview: CrestodianOverview): string { +function formatStartupGatewayStatus(overview: SystemAgentOverview): string { if (overview.gateway.reachable) { return `Gateway: reachable at ${overview.gateway.url}.`; } return `Gateway: not reachable at ${overview.gateway.url}; I already did the first probe.`; } -function formatStartupAction(overview: CrestodianOverview): string { +function formatStartupAction(overview: SystemAgentOverview): string { if (!overview.config.valid) { return "I can start debugging with `validate config` or `doctor`."; } if (!overview.defaultModel) { - return "Crestodian needs working inference before it can help with the rest of setup."; + return "OpenClaw needs working inference before it can help with the rest of setup."; } if (!overview.config.exists) { - return "Run `openclaw onboard` to establish inference before starting Crestodian."; + return "Run `openclaw onboard` to establish inference before starting OpenClaw."; } if (!overview.gateway.reachable) { return "I can start debugging with `gateway status`, or queue `restart gateway` for approval."; @@ -335,10 +335,10 @@ function formatStartupAction(overview: CrestodianOverview): string { } /** - * Welcome shown right after inference activation. Crestodian owns the + * Welcome shown right after inference activation. OpenClaw owns the * remaining workspace, Gateway, channel, and agent setup. */ -export function formatCrestodianOnboardingWelcome(overview: CrestodianOverview): string { +export function formatSystemAgentOnboardingWelcome(overview: SystemAgentOverview): string { return [ "## Inference is ready.", "", @@ -351,13 +351,13 @@ export function formatCrestodianOnboardingWelcome(overview: CrestodianOverview): ].join("\n"); } -export function formatCrestodianStartupMessage(overview: CrestodianOverview): string { +export function formatSystemAgentStartupMessage(overview: SystemAgentOverview): string { const agent = overview.agents.find((entry) => entry.id === overview.defaultAgentId); const agentLabel = agent?.name ? `${overview.defaultAgentId} (${agent.name})` : overview.defaultAgentId; return [ - "## Hi, I'm Crestodian.", + "## Hi, I'm OpenClaw.", "", "- Start me when setup, config, Gateway, model choice, or agent routing feels off.", `- ${formatStartupUse(overview)}`, diff --git a/src/system-agent/plugin-install.ts b/src/system-agent/plugin-install.ts new file mode 100644 index 000000000000..198c03edc069 --- /dev/null +++ b/src/system-agent/plugin-install.ts @@ -0,0 +1,19 @@ +import { isOpenClawTrustedPluginInstallSpec } from "../plugins/install-provenance.js"; + +export function validateSystemAgentPluginInstallSpec(spec: string): string | null { + const trimmed = spec.trim(); + if (!trimmed) { + return "Plugin install spec is required."; + } + if (/\s/.test(trimmed)) { + return "OpenClaw plugin install accepts one npm or ClawHub package spec."; + } + if (/^(?:\.{1,2}\/|\/|~\/|file:|git(?:\+ssh|\+https)?:|https?:)/i.test(trimmed)) { + // OpenClaw does not install local paths or URLs; those can execute arbitrary package code. + return "OpenClaw plugin install accepts npm or ClawHub package specs only."; + } + if (!isOpenClawTrustedPluginInstallSpec(trimmed)) { + return "OpenClaw installs only ClawHub, bundled, or official-catalog plugins. Use `openclaw plugins install ` in a trusted shell to review an arbitrary executable source."; + } + return null; +} diff --git a/src/crestodian/probes.test.ts b/src/system-agent/probes.test.ts similarity index 95% rename from src/crestodian/probes.test.ts rename to src/system-agent/probes.test.ts index d044e5f99bf2..685700954dda 100644 --- a/src/crestodian/probes.test.ts +++ b/src/system-agent/probes.test.ts @@ -1,9 +1,9 @@ -// Crestodian probe tests cover timeout handling and probe result formatting. +// OpenClaw probe tests cover timeout handling and probe result formatting. import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion"; import { afterEach, describe, expect, it, vi } from "vitest"; import { probeGatewayUrl, probeLocalCommand } from "./probes.js"; -describe("crestodian probes", () => { +describe("openclaw probes", () => { afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); diff --git a/src/crestodian/probes.ts b/src/system-agent/probes.ts similarity index 94% rename from src/crestodian/probes.ts rename to src/system-agent/probes.ts index 805148cdb0e4..a46c1342acec 100644 --- a/src/crestodian/probes.ts +++ b/src/system-agent/probes.ts @@ -1,9 +1,9 @@ -// Crestodian probes check local tools and Gateway health with bounded subprocess/network work. +// OpenClaw probes check local tools and Gateway health with bounded subprocess/network work. import { resolveTimerTimeoutMs } from "@openclaw/normalization-core/number-coercion"; import { runCommandWithTimeout } from "../process/exec.js"; /** - * Local environment probes used by Crestodian overview loading. + * Local environment probes used by OpenClaw overview loading. * * Probes are bounded by output and timeout limits so setup/status commands do * not hang or retain unbounded child output. diff --git a/src/crestodian/rescue-channel.live.test.ts b/src/system-agent/rescue-channel.live.test.ts similarity index 72% rename from src/crestodian/rescue-channel.live.test.ts rename to src/system-agent/rescue-channel.live.test.ts index 0f02869bc29f..ab43fd616f20 100644 --- a/src/crestodian/rescue-channel.live.test.ts +++ b/src/system-agent/rescue-channel.live.test.ts @@ -1,4 +1,4 @@ -// Crestodian live rescue channel tests cover live-channel rescue message delivery. +// OpenClaw live rescue channel tests cover live-channel rescue message delivery. import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; @@ -7,7 +7,7 @@ import { clearConfigCache } from "../config/config.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { withTempDir } from "../test-helpers/temp-dir.js"; import { deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js"; -import { runCrestodianRescueMessage } from "./rescue-message.js"; +import { runSystemAgentRescueMessage } from "./rescue-message.js"; const originalStateDir = process.env.OPENCLAW_STATE_DIR; const originalConfigPath = process.env.OPENCLAW_CONFIG_PATH; @@ -18,10 +18,10 @@ function truthy(value: string | undefined): boolean { const runLive = truthy(process.env.OPENCLAW_LIVE_TEST) && - truthy(process.env.OPENCLAW_LIVE_CRESTODIAN_RESCUE_CHANNEL); + truthy(process.env.OPENCLAW_LIVE_SYSTEM_AGENT_RESCUE_CHANNEL); const describeLive = runLive ? describe : describe.skip; -function commandContext(channel = process.env.OPENCLAW_LIVE_CRESTODIAN_CHANNEL ?? "whatsapp") { +function commandContext(channel = process.env.OPENCLAW_LIVE_SYSTEM_AGENT_CHANNEL ?? "whatsapp") { return { surface: channel, channel, @@ -30,8 +30,8 @@ function commandContext(channel = process.env.OPENCLAW_LIVE_CRESTODIAN_CHANNEL ? senderIsOwner: true, isAuthorizedSender: true, senderId: "user:owner", - rawBodyNormalized: "/crestodian status", - commandBodyNormalized: "/crestodian status", + rawBodyNormalized: "/openclaw status", + commandBodyNormalized: "/openclaw status", from: "user:owner", to: "account:default", } satisfies CommandContext; @@ -43,7 +43,7 @@ async function runRescue(params: { ctx?: CommandContext; }) { const ctx = params.ctx ?? commandContext(); - return await runCrestodianRescueMessage({ + return await runSystemAgentRescueMessage({ cfg: params.cfg, command: { ...ctx, commandBodyNormalized: params.commandBody }, commandBody: params.commandBody, @@ -51,7 +51,7 @@ async function runRescue(params: { }); } -describeLive("Crestodian live rescue channel smoke", () => { +describeLive("OpenClaw live rescue channel smoke", () => { afterEach(() => { clearConfigCache(); if (originalStateDir === undefined) { @@ -66,8 +66,8 @@ describeLive("Crestodian live rescue channel smoke", () => { } }); - it("handles /crestodian status and a persistent approval roundtrip", async () => { - await withTempDir({ prefix: "crestodian-live-rescue-" }, async (tempDir) => { + it("handles /openclaw status and a persistent approval roundtrip", async () => { + await withTempDir({ prefix: "openclaw-live-rescue-" }, async (tempDir) => { const configPath = path.join(tempDir, "openclaw.json"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); setTestEnvValue("OPENCLAW_CONFIG_PATH", configPath); @@ -85,17 +85,17 @@ describeLive("Crestodian live rescue channel smoke", () => { ); const cfg: OpenClawConfig = { - crestodian: { rescue: { enabled: true } }, + systemAgent: { rescue: { enabled: true } }, tools: { exec: { security: "full", ask: "off" } }, }; - await expect(runRescue({ commandBody: "/crestodian status", cfg })).resolves.toContain( - "[crestodian] done: status.check", + await expect(runRescue({ commandBody: "/openclaw status", cfg })).resolves.toContain( + "[openclaw] done: status.check", ); await expect( - runRescue({ commandBody: "/crestodian set default model openai/gpt-5.5", cfg }), - ).resolves.toContain("Reply /crestodian yes to apply"); - await expect(runRescue({ commandBody: "/crestodian yes", cfg })).resolves.toContain( + runRescue({ commandBody: "/openclaw set default model openai/gpt-5.5", cfg }), + ).resolves.toContain("Reply /openclaw yes to apply"); + await expect(runRescue({ commandBody: "/openclaw yes", cfg })).resolves.toContain( "Default model: openai/gpt-5.5", ); @@ -105,7 +105,7 @@ describeLive("Crestodian live rescue channel smoke", () => { throw new Error("expected default model object"); } expect(defaultModel.primary).toBe("openai/gpt-5.5"); - const auditPath = path.join(tempDir, "audit", "crestodian.jsonl"); + const auditPath = path.join(tempDir, "audit", "system-agent.jsonl"); const auditLines = (await fs.readFile(auditPath, "utf8")).trim().split("\n"); expect(auditLines.some((line) => line.includes('"operation":"config.setDefaultModel"'))).toBe( true, diff --git a/src/crestodian/rescue-message.test.ts b/src/system-agent/rescue-message.test.ts similarity index 72% rename from src/crestodian/rescue-message.test.ts rename to src/system-agent/rescue-message.test.ts index 5a9ad3647a86..e137f7f6ddf8 100644 --- a/src/crestodian/rescue-message.test.ts +++ b/src/system-agent/rescue-message.test.ts @@ -1,4 +1,4 @@ -// Crestodian rescue message tests cover generated rescue message content. +// OpenClaw rescue message tests cover generated rescue message content. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -7,7 +7,7 @@ import type { CommandContext } from "../auto-reply/reply/commands-types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { RuntimeEnv } from "../runtime.js"; import { withEnvAsync } from "../test-utils/env.js"; -import { extractCrestodianRescueMessage, runCrestodianRescueMessage } from "./rescue-message.js"; +import { extractSystemAgentRescueMessage, runSystemAgentRescueMessage } from "./rescue-message.js"; let tempRoot = ""; let tempDirId = 0; @@ -141,8 +141,8 @@ function commandContext(overrides: Partial = {}): CommandContext senderIsOwner: true, isAuthorizedSender: true, senderId: "user:owner", - rawBodyNormalized: "/crestodian models", - commandBodyNormalized: "/crestodian models", + rawBodyNormalized: "/openclaw models", + commandBodyNormalized: "/openclaw models", from: "user:owner", to: "account:default", ...overrides, @@ -161,9 +161,9 @@ async function runRescue( commandBody: string, cfg: OpenClawConfig, ctx = commandContext(), - deps?: Parameters[0]["deps"], + deps?: Parameters[0]["deps"], ) { - return await runCrestodianRescueMessage({ + return await runSystemAgentRescueMessage({ cfg, command: { ...ctx, commandBodyNormalized: commandBody }, commandBody, @@ -172,9 +172,9 @@ async function runRescue( }); } -describe("Crestodian rescue message", () => { +describe("OpenClaw rescue message", () => { beforeAll(async () => { - tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "crestodian-rescue-")); + tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "system-agent-rescue-")); }); beforeEach(() => { @@ -187,23 +187,23 @@ describe("Crestodian rescue message", () => { } }); - it("recognizes the Crestodian rescue command", () => { - expect(extractCrestodianRescueMessage("/crestodian status")).toBe("status"); - expect(extractCrestodianRescueMessage("/crestodian")).toBe(""); - expect(extractCrestodianRescueMessage("/status")).toBeNull(); + it("recognizes the OpenClaw rescue command", () => { + expect(extractSystemAgentRescueMessage("/openclaw status")).toBe("status"); + expect(extractSystemAgentRescueMessage("/openclaw")).toBe(""); + expect(extractSystemAgentRescueMessage("/status")).toBeNull(); }); it("denies rescue when sandboxing is active", async () => { await expect( - runRescue("/crestodian status", { - crestodian: { rescue: { enabled: true } }, + runRescue("/openclaw status", { + systemAgent: { rescue: { enabled: true } }, agents: { defaults: { sandbox: { mode: "all" } } }, }), ).resolves.toContain("sandboxing is active"); }); it("refuses TUI handoff from remote rescue", async () => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { runTui: vi.fn(async () => { throw new Error("remote rescue must not open the TUI"); @@ -211,16 +211,16 @@ describe("Crestodian rescue message", () => { }; await expect( - runRescue("/crestodian talk to agent", cfg, commandContext(), deps), + runRescue("/openclaw talk to agent", cfg, commandContext(), deps), ).resolves.toContain("cannot open the local TUI"); - await expect(runRescue("/crestodian chat", cfg, commandContext(), deps)).resolves.toContain( + await expect(runRescue("/openclaw chat", cfg, commandContext(), deps)).resolves.toContain( "cannot open the local TUI", ); expect(deps.runTui).not.toHaveBeenCalled(); }); it("rejects natural language instead of guessing an operation", async () => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { runGatewayStop: vi.fn(async () => {}), runGatewayRestart: vi.fn(async () => {}), @@ -229,49 +229,49 @@ describe("Crestodian rescue message", () => { // Questions must never become mutation plans (previously "why did my // gateway stop" keyword-matched into a gateway-stop proposal). await expect( - runRescue("/crestodian why did my gateway stop", cfg, commandContext(), deps), + runRescue("/openclaw why did my gateway stop", cfg, commandContext(), deps), ).resolves.toContain("I can run doctor/status/health"); await expect( - runRescue("/crestodian explain how restart gateway works", cfg, commandContext(), deps), + runRescue("/openclaw explain how restart gateway works", cfg, commandContext(), deps), ).resolves.toContain("I can run doctor/status/health"); expect(deps.runGatewayStop).not.toHaveBeenCalled(); expect(deps.runGatewayRestart).not.toHaveBeenCalled(); }); it("refuses channel setup from remote rescue with a local pointer", async () => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; - await expect(runRescue("/crestodian connect telegram", cfg)).resolves.toContain( + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; + await expect(runRescue("/openclaw connect telegram", cfg)).resolves.toContain( "cannot host the interactive channel setup", ); }); it("refuses model provider setup from remote rescue with a local pointer", async () => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; - const reply = await runRescue("/crestodian configure model provider", cfg); + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; + const reply = await runRescue("/openclaw configure model provider", cfg); expect(reply).toContain("cannot host model-provider credential setup"); expect(reply).toContain("openclaw onboard"); }); it("drops a pending rescue change on decline", async () => { await withRescueStateDir("decline-", async () => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { runGatewayRestart: vi.fn(async () => {}) }; await expect( - runRescue("/crestodian restart gateway", cfg, commandContext(), deps), - ).resolves.toContain("Reply /crestodian yes to apply"); - await expect(runRescue("/crestodian no", cfg, commandContext(), deps)).resolves.toContain( - "Dropped the pending Crestodian rescue change", + runRescue("/openclaw restart gateway", cfg, commandContext(), deps), + ).resolves.toContain("Reply /openclaw yes to apply"); + await expect(runRescue("/openclaw no", cfg, commandContext(), deps)).resolves.toContain( + "Dropped the pending OpenClaw rescue change", ); - await expect(runRescue("/crestodian yes", cfg, commandContext(), deps)).resolves.toBe( - "No pending Crestodian rescue change is waiting for approval.", + await expect(runRescue("/openclaw yes", cfg, commandContext(), deps)).resolves.toBe( + "No pending OpenClaw rescue change is waiting for approval.", ); expect(deps.runGatewayRestart).not.toHaveBeenCalled(); }); }); it("refuses plugin install from remote rescue", async () => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { runPluginInstall: vi.fn(async () => { throw new Error("remote rescue must not install plugins"); @@ -279,13 +279,13 @@ describe("Crestodian rescue message", () => { }; await expect( - runRescue("/crestodian plugin install clawhub:openclaw-demo", cfg, commandContext(), deps), + runRescue("/openclaw plugin install clawhub:openclaw-demo", cfg, commandContext(), deps), ).resolves.toContain("cannot install plugins from a message channel"); expect(deps.runPluginInstall).not.toHaveBeenCalled(); }); it("allows plugin list and search from remote rescue", async () => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { runPluginsList: vi.fn(async (runtime: RuntimeEnv) => { runtime.log("plugin rows"); @@ -296,10 +296,10 @@ describe("Crestodian rescue message", () => { }; await expect( - runRescue("/crestodian plugins list", cfg, commandContext(), deps), + runRescue("/openclaw plugins list", cfg, commandContext(), deps), ).resolves.toContain("plugin rows"); await expect( - runRescue("/crestodian plugins search calendar", cfg, commandContext(), deps), + runRescue("/openclaw plugins search calendar", cfg, commandContext(), deps), ).resolves.toContain("search rows: calendar"); expect(deps.runPluginsList).toHaveBeenCalledTimes(1); expect(deps.runPluginsSearch).toHaveBeenCalledTimes(1); @@ -313,7 +313,7 @@ describe("Crestodian rescue message", () => { it("queues and applies persistent writes through conversational approval", async () => { await withRescueStateDir("models-", async (tempDir) => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { verifyInferenceConfig: vi.fn(async () => ({ ok: true as const, @@ -322,9 +322,9 @@ describe("Crestodian rescue message", () => { })), }; await expect( - runRescue("/crestodian set default model openai/gpt-5.2", cfg, commandContext(), deps), - ).resolves.toContain("Reply /crestodian yes to apply"); - await expect(runRescue("/crestodian yes", cfg, commandContext(), deps)).resolves.toContain( + runRescue("/openclaw set default model openai/gpt-5.2", cfg, commandContext(), deps), + ).resolves.toContain("Reply /openclaw yes to apply"); + await expect(runRescue("/openclaw yes", cfg, commandContext(), deps)).resolves.toContain( "Default model: openai/gpt-5.2", ); @@ -333,7 +333,7 @@ describe("Crestodian rescue message", () => { }; const model = currentConfig.agents?.defaults?.model; expect(typeof model === "string" ? model : model?.primary).toBe("openai/gpt-5.2"); - const auditPath = path.join(tempDir, "audit", "crestodian.jsonl"); + const auditPath = path.join(tempDir, "audit", "system-agent.jsonl"); const audit = JSON.parse((await fs.readFile(auditPath, "utf8")).trim()) as { details?: { rescue?: boolean; channel?: string; senderId?: string }; }; @@ -345,18 +345,18 @@ describe("Crestodian rescue message", () => { it("queues and applies gateway restart through conversational approval", async () => { await withRescueStateDir("gateway-", async (tempDir) => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { runGatewayRestart: vi.fn(async () => {}) }; await expect( - runRescue("/crestodian restart gateway", cfg, commandContext(), deps), - ).resolves.toBe("Plan: restart the Gateway. Reply /crestodian yes to apply."); - await expect(runRescue("/crestodian yes", cfg, commandContext(), deps)).resolves.toContain( - "[crestodian] done: gateway.restart", + runRescue("/openclaw restart gateway", cfg, commandContext(), deps), + ).resolves.toBe("Plan: restart the Gateway. Reply /openclaw yes to apply."); + await expect(runRescue("/openclaw yes", cfg, commandContext(), deps)).resolves.toContain( + "[openclaw] done: gateway.restart", ); expect(deps.runGatewayRestart).toHaveBeenCalledTimes(1); - const auditPath = path.join(tempDir, "audit", "crestodian.jsonl"); + const auditPath = path.join(tempDir, "audit", "system-agent.jsonl"); const audit = JSON.parse((await fs.readFile(auditPath, "utf8")).trim()) as { operation?: string; details?: { rescue?: boolean; channel?: string; senderId?: string }; @@ -373,15 +373,15 @@ describe("Crestodian rescue message", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(8_640_000_000_000_000)); try { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; await expect( - runRescue("/crestodian restart gateway", cfg, commandContext()), + runRescue("/openclaw restart gateway", cfg, commandContext()), ).resolves.toContain("expiry clock is invalid"); - await expect( - fs.readdir(path.join(tempDir, "crestodian", "rescue-pending")), - ).rejects.toThrow(/ENOENT/); + await expect(fs.readdir(path.join(tempDir, "openclaw", "rescue-pending"))).rejects.toThrow( + /ENOENT/, + ); } finally { vi.useRealTimers(); } @@ -390,13 +390,13 @@ describe("Crestodian rescue message", () => { it("rejects pending rescue approvals with invalid persisted expiry", async () => { await withRescueStateDir("invalid-expiry-", async (tempDir) => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { runGatewayRestart: vi.fn(async () => {}) }; await expect( - runRescue("/crestodian restart gateway", cfg, commandContext(), deps), - ).resolves.toContain("Reply /crestodian yes to apply"); - const pendingDir = path.join(tempDir, "crestodian", "rescue-pending"); + runRescue("/openclaw restart gateway", cfg, commandContext(), deps), + ).resolves.toContain("Reply /openclaw yes to apply"); + const pendingDir = path.join(tempDir, "openclaw", "rescue-pending"); const [pendingFile] = await fs.readdir(pendingDir); if (!pendingFile) { throw new Error("expected pending rescue file"); @@ -406,8 +406,8 @@ describe("Crestodian rescue message", () => { pending.expiresAt = "not-a-date"; await fs.writeFile(pendingPath, `${JSON.stringify(pending, null, 2)}\n`, "utf8"); - await expect(runRescue("/crestodian yes", cfg, commandContext(), deps)).resolves.toBe( - "No pending Crestodian rescue change is waiting for approval.", + await expect(runRescue("/openclaw yes", cfg, commandContext(), deps)).resolves.toBe( + "No pending OpenClaw rescue change is waiting for approval.", ); expect(deps.runGatewayRestart).not.toHaveBeenCalled(); await expect(fs.stat(pendingPath)).rejects.toThrow(/ENOENT/); @@ -416,16 +416,16 @@ describe("Crestodian rescue message", () => { it("queues and applies agent creation through conversational approval", async () => { await withRescueStateDir("agent-", async (tempDir) => { - const cfg: OpenClawConfig = { crestodian: { rescue: { enabled: true } } }; + const cfg: OpenClawConfig = { systemAgent: { rescue: { enabled: true } } }; const deps = { runAgentsAdd: vi.fn(async () => {}) }; await expect( - runRescue("/crestodian create agent work workspace /tmp/work", cfg, commandContext(), deps), + runRescue("/openclaw create agent work workspace /tmp/work", cfg, commandContext(), deps), ).resolves.toBe( - "Plan: create agent work with workspace /tmp/work. Reply /crestodian yes to apply.", + "Plan: create agent work with workspace /tmp/work. Reply /openclaw yes to apply.", ); - await expect(runRescue("/crestodian yes", cfg, commandContext(), deps)).resolves.toContain( - "[crestodian] done: agents.create", + await expect(runRescue("/openclaw yes", cfg, commandContext(), deps)).resolves.toContain( + "[openclaw] done: agents.create", ); expect(deps.runAgentsAdd).toHaveBeenCalledTimes(1); @@ -444,7 +444,7 @@ describe("Crestodian rescue message", () => { }); expect(agentRuntime).toBeTypeOf("object"); expect(agentOptions).toEqual({ hasFlags: true }); - const auditPath = path.join(tempDir, "audit", "crestodian.jsonl"); + const auditPath = path.join(tempDir, "audit", "system-agent.jsonl"); const audit = JSON.parse((await fs.readFile(auditPath, "utf8")).trim()) as { operation?: string; details?: { diff --git a/src/crestodian/rescue-message.ts b/src/system-agent/rescue-message.ts similarity index 63% rename from src/crestodian/rescue-message.ts rename to src/system-agent/rescue-message.ts index fc24f9876884..fe90be8172e1 100644 --- a/src/crestodian/rescue-message.ts +++ b/src/system-agent/rescue-message.ts @@ -1,4 +1,4 @@ -// Crestodian rescue messages expose approved setup-helper commands over message channels. +// OpenClaw rescue messages expose approved setup-helper commands over message channels. import { createHash, randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; @@ -11,21 +11,21 @@ import { resolveStateDir } from "../config/paths.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { tryReadJson, writeJson } from "../infra/json-files.js"; import type { RuntimeEnv } from "../runtime.js"; -import { classifyCrestodianApprovalText } from "./approval-intent.js"; +import { classifySystemAgentApprovalText } from "./approval-intent.js"; import { - executeCrestodianOperation, - formatCrestodianPersistentPlan, - isPersistentCrestodianOperation, - parseCrestodianOperation, - type CrestodianCommandDeps, - type CrestodianOperation, + executeSystemAgentOperation, + formatSystemAgentPersistentPlan, + isPersistentSystemAgentOperation, + parseSystemAgentOperation, + type SystemAgentCommandDeps, + type SystemAgentOperation, } from "./operations.js"; -import { resolveCrestodianRescuePolicy } from "./rescue-policy.js"; +import { resolveSystemAgentRescuePolicy } from "./rescue-policy.js"; /** - * Message-channel rescue command handling for Crestodian. + * Message-channel rescue command handling for OpenClaw. * - * Rescue mode accepts `/crestodian` commands from approved message contexts, + * Rescue mode accepts `/openclaw` commands from approved message contexts, * stores pending persistent operations for explicit confirmation, and captures * command output without exposing local TUI or plugin-install flows remotely. */ @@ -33,22 +33,22 @@ type RescuePendingOperation = { id: string; createdAt: string; expiresAt: string; - operation: CrestodianOperation; + operation: SystemAgentOperation; auditDetails: Record; }; -/** Input required to process one possible `/crestodian` rescue message. */ -type CrestodianRescueMessageInput = { +/** Input required to process one possible `/openclaw` rescue message. */ +type SystemAgentRescueMessageInput = { cfg: OpenClawConfig; command: CommandContext; commandBody: string; agentId?: string; isGroup: boolean; env?: NodeJS.ProcessEnv; - deps?: CrestodianCommandDeps; + deps?: SystemAgentCommandDeps; }; -const CRESTODIAN_COMMAND = "/crestodian"; +const SYSTEM_AGENT_COMMAND = "/openclaw"; function createCaptureRuntime(): { runtime: RuntimeEnv; read: () => string } { const lines: string[] = []; @@ -60,28 +60,28 @@ function createCaptureRuntime(): { runtime: RuntimeEnv; read: () => string } { log: push, error: push, exit: (code) => { - throw new Error(`Crestodian operation exited with code ${code}`); + throw new Error(`OpenClaw operation exited with code ${code}`); }, }, read: () => lines.join("\n").trim(), }; } -/** Extract the command body after `/crestodian`, or null when the message is not for rescue. */ -export function extractCrestodianRescueMessage(commandBody: string): string | null { +/** Extract the command body after `/openclaw`, or null when the message is not for rescue. */ +export function extractSystemAgentRescueMessage(commandBody: string): string | null { const normalized = commandBody.trim(); const lower = normalized.toLowerCase(); - if (lower !== CRESTODIAN_COMMAND && !lower.startsWith(`${CRESTODIAN_COMMAND} `)) { + if (lower !== SYSTEM_AGENT_COMMAND && !lower.startsWith(`${SYSTEM_AGENT_COMMAND} `)) { return null; } - return normalized.slice(CRESTODIAN_COMMAND.length).trim(); + return normalized.slice(SYSTEM_AGENT_COMMAND.length).trim(); } function resolvePendingDir(env: NodeJS.ProcessEnv = process.env): string { - return path.join(resolveStateDir(env), "crestodian", "rescue-pending"); + return path.join(resolveStateDir(env), "openclaw", "rescue-pending"); } -function resolvePendingPath(input: CrestodianRescueMessageInput): string { +function resolvePendingPath(input: SystemAgentRescueMessageInput): string { // Pending approval is scoped by sender/channel identity so unrelated chats cannot approve it. const key = JSON.stringify({ channel: input.command.channelId ?? input.command.channel, @@ -122,7 +122,7 @@ async function writePending(pendingPath: string, pending: RescuePendingOperation }); } -function buildAuditDetails(input: CrestodianRescueMessageInput): Record { +function buildAuditDetails(input: SystemAgentRescueMessageInput): Record { return { rescue: true, channel: input.command.channelId ?? input.command.channel, @@ -132,50 +132,50 @@ function buildAuditDetails(input: CrestodianRescueMessageInput): Record { - const rescueMessage = extractCrestodianRescueMessage(input.commandBody); + const rescueMessage = extractSystemAgentRescueMessage(input.commandBody); if (rescueMessage === null) { return null; } - const policy = resolveCrestodianRescuePolicy({ + const policy = resolveSystemAgentRescuePolicy({ cfg: input.cfg, agentId: input.agentId, senderIsOwner: input.command.senderIsOwner, @@ -188,10 +188,10 @@ export async function runCrestodianRescueMessage( const pendingPath = resolvePendingPath(input); // Remote rescue never consults a model (a broken/compromised agent path must // not become a config editor); approval stays on the closed deterministic list. - if (classifyCrestodianApprovalText(rescueMessage) === "approve") { + if (classifySystemAgentApprovalText(rescueMessage) === "approve") { const pending = await readPending(pendingPath); if (!pending) { - return "No pending Crestodian rescue change is waiting for approval."; + return "No pending OpenClaw rescue change is waiting for approval."; } const unsupported = formatUnsupportedRemoteOperation(pending.operation); if (unsupported) { @@ -199,29 +199,29 @@ export async function runCrestodianRescueMessage( return unsupported; } const capture = createCaptureRuntime(); - await executeCrestodianOperation(pending.operation, capture.runtime, { + await executeSystemAgentOperation(pending.operation, capture.runtime, { approved: true, auditDetails: pending.auditDetails, deps: input.deps, }); await fs.rm(pendingPath, { force: true }); - return capture.read() || "Crestodian rescue change applied."; + return capture.read() || "OpenClaw rescue change applied."; } - if (classifyCrestodianApprovalText(rescueMessage) === "decline") { + if (classifySystemAgentApprovalText(rescueMessage) === "decline") { const pending = await readPending(pendingPath); await fs.rm(pendingPath, { force: true }); return pending - ? "Dropped the pending Crestodian rescue change." - : "No pending Crestodian rescue change is waiting for approval."; + ? "Dropped the pending OpenClaw rescue change." + : "No pending OpenClaw rescue change is waiting for approval."; } - const operation = parseCrestodianOperation(rescueMessage); + const operation = parseSystemAgentOperation(rescueMessage); const unsupported = formatUnsupportedRemoteOperation(operation); if (unsupported) { return unsupported; } - if (isPersistentCrestodianOperation(operation)) { + if (isPersistentSystemAgentOperation(operation)) { // Persistent remote operations are two-step: store the parsed operation, then require approval. const now = new Date(); const nowMs = asDateTimestampMs(now.getTime()); @@ -230,7 +230,7 @@ export async function runCrestodianRescueMessage( ? undefined : resolveExpiresAtMsFromDurationMs(policy.pendingTtlMinutes * 60_000, { nowMs }); if (expiresAtMs === undefined) { - return "Crestodian rescue could not create a pending approval because the expiry clock is invalid."; + return "OpenClaw rescue could not create a pending approval because the expiry clock is invalid."; } await writePending(pendingPath, { id: randomUUID(), @@ -243,10 +243,10 @@ export async function runCrestodianRescueMessage( } const capture = createCaptureRuntime(); - await executeCrestodianOperation(operation, capture.runtime, { + await executeSystemAgentOperation(operation, capture.runtime, { approved: true, auditDetails: buildAuditDetails(input), deps: input.deps, }); - return capture.read() || "Crestodian listened, clicked a claw, and found nothing to change."; + return capture.read() || "OpenClaw listened, clicked a claw, and found nothing to change."; } diff --git a/src/crestodian/rescue-policy.test.ts b/src/system-agent/rescue-policy.test.ts similarity index 83% rename from src/crestodian/rescue-policy.test.ts rename to src/system-agent/rescue-policy.test.ts index f940e766d7a2..9c8de23dc2e0 100644 --- a/src/crestodian/rescue-policy.test.ts +++ b/src/system-agent/rescue-policy.test.ts @@ -1,10 +1,10 @@ -// Crestodian rescue policy tests cover eligibility and safety decisions. +// OpenClaw rescue policy tests cover eligibility and safety decisions. import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { resolveCrestodianRescuePolicy } from "./rescue-policy.js"; +import { resolveSystemAgentRescuePolicy } from "./rescue-policy.js"; function decide(cfg: OpenClawConfig, overrides = {}) { - return resolveCrestodianRescuePolicy({ + return resolveSystemAgentRescuePolicy({ cfg, senderIsOwner: true, isDirectMessage: true, @@ -12,14 +12,14 @@ function decide(cfg: OpenClawConfig, overrides = {}) { }); } -describe("resolveCrestodianRescuePolicy", () => { +describe("resolveSystemAgentRescuePolicy", () => { it("allows auto rescue for owner DMs in YOLO host posture with sandboxing off", () => { expect(decide({}).allowed).toBe(true); }); it("hard-denies rescue when sandboxing is active even if explicitly enabled", () => { const decision = decide({ - crestodian: { rescue: { enabled: true } }, + systemAgent: { rescue: { enabled: true } }, agents: { defaults: { sandbox: { mode: "all" } } }, }); expect(decision.allowed).toBe(false); @@ -58,7 +58,7 @@ describe("resolveCrestodianRescuePolicy", () => { it("allows explicit group rescue when ownerDmOnly is disabled", () => { expect( - decide({ crestodian: { rescue: { ownerDmOnly: false } } }, { isDirectMessage: false }) + decide({ systemAgent: { rescue: { ownerDmOnly: false } } }, { isDirectMessage: false }) .allowed, ).toBe(true); }); diff --git a/src/crestodian/rescue-policy.ts b/src/system-agent/rescue-policy.ts similarity index 80% rename from src/crestodian/rescue-policy.ts rename to src/system-agent/rescue-policy.ts index 59e4bb587685..7742b8b5f6c4 100644 --- a/src/crestodian/rescue-policy.ts +++ b/src/system-agent/rescue-policy.ts @@ -1,14 +1,14 @@ -// Crestodian rescue policy gates remote writes by owner, DM, sandbox, and YOLO posture. +// OpenClaw rescue policy gates remote writes by owner, DM, sandbox, and YOLO posture. import type { OpenClawConfig } from "../config/types.openclaw.js"; import { normalizeAgentId } from "../routing/session-key.js"; /** - * Policy checks for remote Crestodian rescue commands. + * Policy checks for remote OpenClaw rescue commands. * * Rescue intentionally opens only for owner-controlled, non-sandboxed YOLO host * posture unless config explicitly enables it, because remote commands can write local state. */ -type CrestodianRescueDecision = +type SystemAgentRescueDecision = | { allowed: true; enabled: true; @@ -28,7 +28,7 @@ type CrestodianRescueDecision = message: string; }; -type CrestodianRescuePolicyInput = { +type SystemAgentRescuePolicyInput = { cfg: OpenClawConfig; agentId?: string; senderIsOwner: boolean; @@ -71,10 +71,10 @@ function isYoloHostPosture(cfg: OpenClawConfig, agentId?: string): boolean { } /** Decide whether a message-channel rescue command is allowed for this sender/context. */ -export function resolveCrestodianRescuePolicy( - input: CrestodianRescuePolicyInput, -): CrestodianRescueDecision { - const rescue = input.cfg.crestodian?.rescue; +export function resolveSystemAgentRescuePolicy( + input: SystemAgentRescuePolicyInput, +): SystemAgentRescueDecision { + const rescue = input.cfg.systemAgent?.rescue; const configuredEnabled = rescue?.enabled ?? "auto"; const ownerDmOnly = rescue?.ownerDmOnly ?? true; const pendingTtlMinutes = resolvePendingTtlMinutes(rescue?.pendingTtlMinutes); @@ -93,7 +93,7 @@ export function resolveCrestodianRescuePolicy( sandboxActive, reason: "disabled", message: - "Crestodian rescue is disabled. Set crestodian.rescue.enabled=true or use YOLO host posture with sandboxing off.", + "OpenClaw rescue is disabled. Set systemAgent.rescue.enabled=true or use YOLO host posture with sandboxing off.", }; } if (sandboxActive) { @@ -106,7 +106,7 @@ export function resolveCrestodianRescuePolicy( sandboxActive, reason: "sandbox-active", message: - "Crestodian rescue is blocked because OpenClaw sandboxing is active. Fix the install locally or disable sandboxing before using remote rescue.", + "OpenClaw rescue is blocked because OpenClaw sandboxing is active. Fix the install locally or disable sandboxing before using remote rescue.", }; } if (configuredEnabled === "auto" && !yolo) { @@ -119,7 +119,7 @@ export function resolveCrestodianRescuePolicy( sandboxActive, reason: "not-yolo", message: - "Crestodian rescue auto-mode only opens in YOLO host posture: tools.exec.security=full, tools.exec.ask=off, and sandboxing off.", + "OpenClaw rescue auto-mode only opens in YOLO host posture: tools.exec.security=full, tools.exec.ask=off, and sandboxing off.", }; } if (!input.senderIsOwner) { @@ -131,7 +131,7 @@ export function resolveCrestodianRescuePolicy( yolo, sandboxActive, reason: "not-owner", - message: "Crestodian rescue only accepts commands from an OpenClaw owner.", + message: "OpenClaw rescue only accepts commands from an OpenClaw owner.", }; } if (ownerDmOnly && !input.isDirectMessage) { @@ -143,7 +143,7 @@ export function resolveCrestodianRescuePolicy( yolo, sandboxActive, reason: "not-direct-message", - message: "Crestodian rescue is restricted to owner DMs by default.", + message: "OpenClaw rescue is restricted to owner DMs by default.", }; } return { diff --git a/src/crestodian/setup-apply.test.ts b/src/system-agent/setup-apply.test.ts similarity index 92% rename from src/crestodian/setup-apply.test.ts rename to src/system-agent/setup-apply.test.ts index 05f5a7aa4ed1..f6351f4aded1 100644 --- a/src/crestodian/setup-apply.test.ts +++ b/src/system-agent/setup-apply.test.ts @@ -102,7 +102,7 @@ vi.mock("../agents/agent-scope.js", async (importOriginal) => ({ config.agents?.list?.find((agent) => agent.id === agentId)?.agentDir ?? `/agents/${agentId}`, })); -import { applyCrestodianModelSelection, applyCrestodianSetup } from "./setup-apply.js"; +import { applySystemAgentModelSelection, applySystemAgentSetup } from "./setup-apply.js"; const runtime: RuntimeEnv = { log: vi.fn(), @@ -173,7 +173,7 @@ function materializePluginDefaults( return result.config; } -function baseParams(overrides: Partial[0]> = {}) { +function baseParams(overrides: Partial[0]> = {}) { return { workspace: "/tmp/openclaw-workspace", surface: "gateway" as const, @@ -182,7 +182,7 @@ function baseParams(overrides: Partial[0 }; } -describe("applyCrestodianModelSelection", () => { +describe("applySystemAgentModelSelection", () => { it("clears stale harness pins in both model scopes for a native route", async () => { const config = { agents: { @@ -207,7 +207,7 @@ describe("applyCrestodianModelSelection", () => { }, } satisfies OpenClawConfig; - const result = await applyCrestodianModelSelection({ + const result = await applySystemAgentModelSelection({ config, model: "openai/gpt-5.5", }); @@ -218,7 +218,7 @@ describe("applyCrestodianModelSelection", () => { }); it("pins the verified credential without creating a global visibility map", async () => { - const result = await applyCrestodianModelSelection({ + const result = await applySystemAgentModelSelection({ config: { agents: { defaults: { model: "openai/gpt-5.5" } } }, model: "openai/gpt-5.5", authProfileId: "openai:verified", @@ -229,7 +229,7 @@ describe("applyCrestodianModelSelection", () => { }); }); -describe("applyCrestodianSetup transaction boundaries", () => { +describe("applySystemAgentSetup transaction boundaries", () => { beforeEach(() => { vi.resetAllMocks(); mocks.events.length = 0; @@ -303,7 +303,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { mocks.state.initialSnapshot = snapshot(actual, {}); await expect( - applyCrestodianSetup(baseParams({ expectedConfigHash: expected })), + applySystemAgentSetup(baseParams({ expectedConfigHash: expected })), ).rejects.toThrow("config changed while AI access was being tested"); expect(mocks.commit).not.toHaveBeenCalled(); @@ -318,7 +318,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { mocks.state.commitSnapshot = absent; mocks.state.commitPreviousHash = null; - const result = await applyCrestodianSetup(baseParams({ expectedConfigHash: null })); + const result = await applySystemAgentSetup(baseParams({ expectedConfigHash: null })); expect(result.configHashBefore).toBeNull(); expect(mocks.state.persistedConfig).toMatchObject({ @@ -333,7 +333,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { issues: [{ path: "agents", message: "bad agent config" }], }; - await expect(applyCrestodianSetup(baseParams())).rejects.toThrow("bad agent config"); + await expect(applySystemAgentSetup(baseParams())).rejects.toThrow("bad agent config"); expect(mocks.commit).not.toHaveBeenCalled(); expect(mocks.ensureWorkspace).not.toHaveBeenCalled(); @@ -343,24 +343,39 @@ describe("applyCrestodianSetup transaction boundaries", () => { const config = { agents: { defaults: { model: "openai/gpt-5.5" }, - list: [{ id: "Crestodian" }], + list: [{ id: "OpenClaw" }], }, } satisfies OpenClawConfig; mocks.state.initialSnapshot = snapshot("reserved", config); - await expect(applyCrestodianSetup(baseParams())).rejects.toThrow( - 'Agent id "crestodian" is reserved', + await expect(applySystemAgentSetup(baseParams())).rejects.toThrow( + 'Agent id "openclaw" is reserved', ); expect(mocks.commit).not.toHaveBeenCalled(); }); + it("rejects a configured user agent with the retired id", async () => { + const config = { + agents: { + defaults: { model: "openai/gpt-5.5" }, + list: [{ id: "crestodian" }], // reserved retired id + }, + } satisfies OpenClawConfig; + mocks.state.initialSnapshot = snapshot("reserved-retired", config); + + await expect(applySystemAgentSetup(baseParams())).rejects.toThrow( + 'Agent id "crestodian" is reserved', // reserved retired id + ); + expect(mocks.commit).not.toHaveBeenCalled(); + }); + it("rechecks the probed revision inside the final transform", async () => { mocks.state.commitPreviousHash = "concurrent"; - await expect(applyCrestodianSetup(baseParams({ expectedConfigHash: "probe" }))).rejects.toThrow( - "config changed while AI access was being tested", - ); + await expect( + applySystemAgentSetup(baseParams({ expectedConfigHash: "probe" })), + ).rejects.toThrow("config changed while AI access was being tested"); expect(mocks.state.persistedConfig).toBeUndefined(); expect(mocks.ensureWorkspace).not.toHaveBeenCalled(); @@ -388,7 +403,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { mocks.state.commitSnapshot = snapshot("probe", runtimeConfig); await expect( - applyCrestodianSetup( + applySystemAgentSetup( baseParams({ expectedConfigHash: "probe", expectedAgentId: "main", @@ -409,7 +424,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { }); await expect( - applyCrestodianSetup( + applySystemAgentSetup( baseParams({ expectedConfigHash: "probe", expectedAgentId: "main", @@ -428,7 +443,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { }; mocks.state.commitSnapshot = snapshot("probe", mocks.state.commitConfig); - const result = await applyCrestodianSetup( + const result = await applySystemAgentSetup( baseParams({ expectedConfigHash: "probe", expectedAgentId: "main", @@ -464,7 +479,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { mocks.readVerifiedSnapshot.mockResolvedValue(snapshot("probe", current)); await expect( - applyCrestodianSetup( + applySystemAgentSetup( baseParams({ expectedInferenceRoute: await projectDefaultInferenceRoute(verified) }), ), ).rejects.toThrow("changed before setup could start"); @@ -485,7 +500,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { mocks.readVerifiedSnapshot.mockResolvedValue(snapshot("same-root", current)); await expect( - applyCrestodianSetup( + applySystemAgentSetup( baseParams({ expectedInferenceRoute: await projectDefaultInferenceRoute(current) }), ), ).rejects.toThrow("changed before setup could start"); @@ -504,7 +519,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { mocks.readVerifiedSnapshot.mockResolvedValue(initialSnapshot); await expect( - applyCrestodianSetup( + applySystemAgentSetup( baseParams({ model: "anthropic/claude-opus-4-8", expectedInferenceRoute: await projectDefaultInferenceRoute(initial), @@ -580,7 +595,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { }); const expectedInferenceRoute = await projectDefaultInferenceRoute(initial); - await applyCrestodianSetup(baseParams({ expectedInferenceRoute, surface: "cli" })); + await applySystemAgentSetup(baseParams({ expectedInferenceRoute, surface: "cli" })); expect(mocks.configureGateway).toHaveBeenCalledTimes(2); expect(mocks.configureGateway).toHaveBeenLastCalledWith( @@ -637,7 +652,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { }); await expect( - applyCrestodianSetup( + applySystemAgentSetup( baseParams({ expectedInferenceRoute: await projectDefaultInferenceRoute(initial) }), ), ).rejects.toThrow("changed after the config write"); @@ -680,7 +695,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { snapshot: persistedSnapshot(), pluginMetadataSnapshot, })); - await applyCrestodianSetup( + await applySystemAgentSetup( baseParams({ expectedInferenceRoute: await projectDefaultInferenceRoute(initialSnapshot.runtimeConfig), }), @@ -720,7 +735,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { try { await expect( - applyCrestodianSetup( + applySystemAgentSetup( baseParams({ expectedInferenceRoute: await projectDefaultInferenceRoute(sourceConfig), }), @@ -786,7 +801,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { }; await expect( - applyCrestodianSetup(baseParams({ expectedInferenceRoute }), { commit: authorityCommit }), + applySystemAgentSetup(baseParams({ expectedInferenceRoute }), { commit: authorityCommit }), ).rejects.toThrow("verified inference binding changed"); expect(guardCalls).toBe(3); @@ -807,7 +822,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { plugins: source.plugins, })); - await applyCrestodianSetup(baseParams({ expectedConfigHash: "probe", finalizeConfig })); + await applySystemAgentSetup(baseParams({ expectedConfigHash: "probe", finalizeConfig })); expect(finalizeConfig).toHaveBeenCalledWith(expect.any(Object), sourceConfig); expect(mocks.state.persistedConfig?.plugins).toEqual(sourceConfig.plugins); @@ -819,7 +834,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { mocks.refreshPluginRegistry.mockRejectedValueOnce(new Error("registry exploded")); mocks.ensureGatewayService.mockRejectedValueOnce(new Error("service exploded")); - const result = await applyCrestodianSetup( + const result = await applySystemAgentSetup( baseParams({ expectedConfigHash: "probe", enablePluginId: "codex", @@ -832,7 +847,7 @@ describe("applyCrestodianSetup transaction boundaries", () => { expect(result.lines).toEqual( expect.arrayContaining([ "Workspace files: workspace exploded", - "Crestodian exec approval: approval exploded; local model harnesses may ask again.", + "OpenClaw exec approval: approval exploded; local model harnesses may ask again.", "Plugin registry refresh failed: registry exploded", "Gateway service: service exploded", ]), diff --git a/src/crestodian/setup-apply.ts b/src/system-agent/setup-apply.ts similarity index 91% rename from src/crestodian/setup-apply.ts rename to src/system-agent/setup-apply.ts index dff987473dfa..ff2370a4db0f 100644 --- a/src/crestodian/setup-apply.ts +++ b/src/system-agent/setup-apply.ts @@ -1,4 +1,4 @@ -// Applies Crestodian's conversational setup: config, workspace files, gateway. +// Applies OpenClaw's conversational setup: config, workspace files, gateway. import { isDeepStrictEqual } from "node:util"; import { readConfigFileSnapshot, @@ -21,7 +21,7 @@ import { sameDefaultInferenceRoute, type DefaultInferenceRouteProjection, } from "./inference-route.js"; -import { requireValidCrestodianSetupSnapshot } from "./setup-config-snapshot.js"; +import { requireValidSystemAgentSetupSnapshot } from "./setup-config-snapshot.js"; /** * The whole first-run setup as one approved operation: the user says "yes" in @@ -30,7 +30,7 @@ import { requireValidCrestodianSetupSnapshot } from "./setup-config-snapshot.js" * and starts the gateway service. No interactive prompts may occur here — * everything uses quickstart defaults, so the conversation stays the only UI. */ -export type CrestodianSetupApplyParams = { +export type SystemAgentSetupApplyParams = { workspace: string; model?: string; agentRuntimeId?: string; @@ -60,14 +60,14 @@ export type CrestodianSetupApplyParams = { runtime: RuntimeEnv; }; -export type CrestodianSetupApplyResult = { +export type SystemAgentSetupApplyResult = { configPath: string; configHashBefore: string | null; configHashAfter: string | null; lines: string[]; }; -type CrestodianSetupApplyHooks = { +type SystemAgentSetupApplyHooks = { /** Host-owned authority seam; called at every persistent setup boundary. */ commit(effect: () => Promise | T): Promise; }; @@ -75,7 +75,7 @@ type CrestodianSetupApplyHooks = { /** Prompter for quickstart-only flows: notes go to the log, prompts fail loud. */ export function createQuickstartNotePrompter(runtime: RuntimeEnv): WizardPrompter { const unexpected = (kind: string) => { - throw new Error(`crestodian setup hit an interactive ${kind} prompt; quickstart must not ask`); + throw new Error(`openclaw setup hit an interactive ${kind} prompt; quickstart must not ask`); }; return { intro: async () => {}, @@ -120,7 +120,7 @@ function applySecurityAcknowledgement(config: OpenClawConfig): OpenClawConfig { }; } -type CrestodianModelSelectionParams = { +type SystemAgentModelSelectionParams = { config: OpenClawConfig; model: string; agentRuntimeId?: string; @@ -128,15 +128,15 @@ type CrestodianModelSelectionParams = { authProfileId?: string; }; -type CrestodianModelSelectionModules = { +type SystemAgentModelSelectionModules = { agentScope: typeof import("../agents/agent-scope.js"); modelConfig: typeof import("../commands/models/shared.js"); runtimePolicy: typeof import("../agents/model-runtime-policy.js"); }; -function applyCrestodianModelSelectionWithModules( - params: CrestodianModelSelectionParams, - modules: CrestodianModelSelectionModules, +function applySystemAgentModelSelectionWithModules( + params: SystemAgentModelSelectionParams, + modules: SystemAgentModelSelectionModules, ): OpenClawConfig { const { agentScope, modelConfig, runtimePolicy } = modules; const nextConfig = structuredClone(params.config); @@ -213,8 +213,8 @@ function applyCrestodianModelSelectionWithModules( return nextConfig; } -export async function createCrestodianModelSelectionUpdater( - params: Omit, +export async function createSystemAgentModelSelectionUpdater( + params: Omit, ): Promise<(config: OpenClawConfig) => OpenClawConfig> { const [agentScope, modelConfig, runtimePolicy] = await Promise.all([ import("../agents/agent-scope.js"), @@ -222,20 +222,20 @@ export async function createCrestodianModelSelectionUpdater( import("../agents/model-runtime-policy.js"), ]); const modules = { agentScope, modelConfig, runtimePolicy }; - return (config) => applyCrestodianModelSelectionWithModules({ ...params, config }, modules); + return (config) => applySystemAgentModelSelectionWithModules({ ...params, config }, modules); } -export async function applyCrestodianModelSelection( - params: CrestodianModelSelectionParams, +export async function applySystemAgentModelSelection( + params: SystemAgentModelSelectionParams, ): Promise { - const update = await createCrestodianModelSelectionUpdater(params); + const update = await createSystemAgentModelSelectionUpdater(params); return update(params.config); } -export async function applyCrestodianSetup( - params: CrestodianSetupApplyParams, - hooks?: CrestodianSetupApplyHooks, -): Promise { +export async function applySystemAgentSetup( + params: SystemAgentSetupApplyParams, + hooks?: SystemAgentSetupApplyHooks, +): Promise { const { workspace, model, @@ -254,7 +254,7 @@ export async function applyCrestodianSetup( runtime, } = params; const hasExpectedConfigHash = Object.hasOwn(params, "expectedConfigHash"); - const commit: CrestodianSetupApplyHooks["commit"] = hooks + const commit: SystemAgentSetupApplyHooks["commit"] = hooks ? async (effect) => await hooks.commit(effect) : async (effect) => await effect(); const [ @@ -270,7 +270,7 @@ export async function applyCrestodianSetup( ]); const snapshot = await readSetupConfigFileSnapshot(); - const snapshotConfig = requireValidCrestodianSetupSnapshot(snapshot); + const snapshotConfig = requireValidSystemAgentSetupSnapshot(snapshot); if (hasExpectedConfigHash && resolveConfigSnapshotHash(snapshot) !== expectedConfigHash) { throw new Error("OpenClaw config changed while AI access was being tested. Try setup again."); @@ -343,8 +343,8 @@ export async function applyCrestodianSetup( if (!currentRoute || !sameDefaultInferenceRoute(currentRoute, expectedRoute)) { throw new Error( phase === "before" - ? "The default-agent inference route changed before setup could start, so no workspace or Gateway settings were changed. Retry setup from the current Crestodian session." - : "The default-agent inference route changed after the config write, so no further setup effects were applied. Retry setup from the current Crestodian session.", + ? "The default-agent inference route changed before setup could start, so no workspace or Gateway settings were changed. Retry setup from the current OpenClaw session." + : "The default-agent inference route changed after the config write, so no further setup effects were applied. Retry setup from the current OpenClaw session.", ); } }; @@ -367,7 +367,7 @@ export async function applyCrestodianSetup( let candidate = applyLocalSetupWorkspaceConfig(setupBaseConfig, workspace); if (model) { - candidate = await applyCrestodianModelSelection({ + candidate = await applySystemAgentModelSelection({ config: candidate, model, ...(agentRuntimeId ? { agentRuntimeId } : {}), @@ -398,7 +398,7 @@ export async function applyCrestodianSetup( afterWrite: { mode: "auto" }, writeOptions: { allowConfigSizeDrop: false }, transform: async (currentConfig, context) => { - const currentSnapshot = requireValidCrestodianSetupSnapshot(context.snapshot); + const currentSnapshot = requireValidSystemAgentSetupSnapshot(context.snapshot); if (hasExpectedConfigHash && context.previousHash !== expectedConfigHash) { throw new Error( "OpenClaw config changed while AI access was being tested. Try setup again.", @@ -424,7 +424,7 @@ export async function applyCrestodianSetup( !isDeepStrictEqual(expectedSourceRoute.route, params.expectedInferenceRoute.route)) ) { throw new Error( - "The setup candidate no longer preserves the exact verified inference route, so it was not saved. Retry setup from the current Crestodian session.", + "The setup candidate no longer preserves the exact verified inference route, so it was not saved. Retry setup from the current OpenClaw session.", ); } // This is the auth/config operation's linearization point. Never hold @@ -440,12 +440,12 @@ export async function applyCrestodianSetup( const nextConfig = committed.nextConfig; const settings = committed.result?.settings; if (!settings) { - throw new Error("Crestodian setup committed without resolved Gateway settings."); + throw new Error("OpenClaw setup committed without resolved Gateway settings."); } if (params.expectedInferenceRoute) { const afterRead = await readConfigFileSnapshotWithPluginMetadata(); const afterSnapshot = afterRead.snapshot; - requireValidCrestodianSetupSnapshot(afterSnapshot); + requireValidSystemAgentSetupSnapshot(afterSnapshot); const expectedRuntime = validateConfigObjectWithPlugins(committed.nextConfig, { env: process.env, pluginMetadataSnapshot: afterRead.pluginMetadataSnapshot, @@ -454,7 +454,7 @@ export async function applyCrestodianSetup( const issue = expectedRuntime.issues[0]; const detail = issue ? ` (${issue.path ? `${issue.path}: ` : ""}${issue.message})` : ""; throw new Error( - `OpenClaw could not validate the setup route after its config write${detail}. No further setup effects were applied. Retry setup from the current Crestodian session.`, + `OpenClaw could not validate the setup route after its config write${detail}. No further setup effects were applied. Retry setup from the current OpenClaw session.`, ); } const expectedPersistedRoute = await projectDefaultInferenceRoute(expectedRuntime.config); @@ -463,7 +463,7 @@ export async function applyCrestodianSetup( // metadata change that would make the committed config run differently. if (!isDeepStrictEqual(expectedPersistedRoute.route, params.expectedInferenceRoute.route)) { throw new Error( - "The materialized inference route no longer matches the exact verified route, so no further setup effects were applied. Retry setup from the current Crestodian session.", + "The materialized inference route no longer matches the exact verified route, so no further setup effects were applied. Retry setup from the current OpenClaw session.", ); } } @@ -504,27 +504,27 @@ export async function applyCrestodianSetup( (error) => lines.push(`Workspace files: ${formatErrorMessage(error)}`), ); - // Setup approval includes consent for Crestodian's local model harnesses. + // Setup approval includes consent for OpenClaw's local model harnesses. // Keep the grant agent-scoped; regular agents retain interactive approvals. await runCommittedFollowUp( async () => { const { updateExecApprovals } = await import("../infra/exec-approvals.js"); await updateExecApprovals({ update: (approvals) => - approvals.agents?.crestodian + approvals.agents?.openclaw ? null : { ...approvals, agents: { ...approvals.agents, - crestodian: { security: "full", ask: "off" }, + openclaw: { security: "full", ask: "off" }, }, }, }); }, (error) => lines.push( - `Crestodian exec approval: ${formatErrorMessage(error)}; local model harnesses may ask again.`, + `OpenClaw exec approval: ${formatErrorMessage(error)}; local model harnesses may ask again.`, ), ); @@ -537,7 +537,7 @@ export async function applyCrestodianSetup( config: nextConfig, reason: "source-changed", workspaceDir: workspace, - traceCommand: "crestodian-setup", + traceCommand: "openclaw-setup", logger: { warn: (message) => lines.push(message), }, diff --git a/src/crestodian/setup-config-snapshot.ts b/src/system-agent/setup-config-snapshot.ts similarity index 66% rename from src/crestodian/setup-config-snapshot.ts rename to src/system-agent/setup-config-snapshot.ts index cfb7dc6e4d80..e3b67d1e8a4c 100644 --- a/src/crestodian/setup-config-snapshot.ts +++ b/src/system-agent/setup-config-snapshot.ts @@ -1,10 +1,9 @@ import type { ConfigFileSnapshot, OpenClawConfig } from "../config/types.openclaw.js"; import { normalizeAgentId } from "../routing/session-key.js"; import { shortenHomePath } from "../utils.js"; +import { isReservedSystemAgentId } from "./agent-id.js"; -const CRESTODIAN_AGENT_ID = normalizeAgentId("crestodian"); - -export function requireValidCrestodianSetupSnapshot(snapshot: ConfigFileSnapshot): { +export function requireValidSystemAgentSetupSnapshot(snapshot: ConfigFileSnapshot): { sourceConfig: OpenClawConfig; runtimeConfig: OpenClawConfig; } { @@ -17,11 +16,12 @@ export function requireValidCrestodianSetupSnapshot(snapshot: ConfigFileSnapshot } const sourceConfig = snapshot.exists ? (snapshot.sourceConfig ?? snapshot.config) : {}; const runtimeConfig = snapshot.exists ? (snapshot.runtimeConfig ?? snapshot.config) : {}; - if ( - runtimeConfig.agents?.list?.some((entry) => normalizeAgentId(entry.id) === CRESTODIAN_AGENT_ID) - ) { + const reservedAgent = runtimeConfig.agents?.list?.find((entry) => + isReservedSystemAgentId(entry.id), + ); + if (reservedAgent) { throw new Error( - 'Agent id "crestodian" is reserved for the setup assistant. Rename that configured agent, then retry setup.', + `Agent id "${normalizeAgentId(reservedAgent.id)}" is reserved for the system agent. Rename that configured agent, then retry setup.`, ); } return { sourceConfig, runtimeConfig }; diff --git a/src/crestodian/setup-inference-probe.ts b/src/system-agent/setup-inference-probe.ts similarity index 100% rename from src/crestodian/setup-inference-probe.ts rename to src/system-agent/setup-inference-probe.ts diff --git a/src/crestodian/setup-inference.test.ts b/src/system-agent/setup-inference.test.ts similarity index 97% rename from src/crestodian/setup-inference.test.ts rename to src/system-agent/setup-inference.test.ts index cf57bdeba24c..bb7f59eaf49d 100644 --- a/src/crestodian/setup-inference.test.ts +++ b/src/system-agent/setup-inference.test.ts @@ -33,12 +33,12 @@ import { ensurePluginRegistryLoaded } from "../plugins/runtime/runtime-registry- import type { ProviderPlugin } from "../plugins/types.js"; import { disposeOpenClawAgentDatabaseByPath } from "../state/openclaw-agent-db.js"; import { - cleanupCrestodianAgentSession, - createCrestodianAgentSession, - runCrestodianAgentTurnWithDeps, + cleanupSystemAgentSession, + createSystemAgentSession, + runSystemAgentTurnWithDeps, } from "./agent-turn.js"; -import { resolveCrestodianConfiguredRouteFromConfig } from "./inference-route.js"; -import { applyCrestodianModelSelection } from "./setup-apply.js"; +import { resolveSystemAgentConfiguredRouteFromConfig } from "./inference-route.js"; +import { applySystemAgentModelSelection } from "./setup-apply.js"; import { resolveSetupInferenceProbeStreamParams } from "./setup-inference-probe.js"; import { SetupInferenceActivationIndeterminateError, @@ -46,13 +46,13 @@ import { detectSetupInference, listSetupInferenceAuthOptions, listSetupInferenceManualProviders, - resolveCrestodianInferenceForPersistentApply, + resolveSystemAgentInferenceForPersistentApply, verifySetupInference, verifySetupInferenceConfig, } from "./setup-inference.js"; import { - createCrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceBinding, + createSystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceBinding, } from "./verified-inference.js"; const mocks = vi.hoisted(() => ({ @@ -62,7 +62,7 @@ const mocks = vi.hoisted(() => ({ })); vi.mock("./audit.js", () => ({ - appendCrestodianAuditEntry: mocks.appendAudit, + appendSystemAgentAuditEntry: mocks.appendAudit, })); vi.mock("../agents/harness/runtime-plugin.js", async (importOriginal) => ({ @@ -127,13 +127,13 @@ async function activateSetupInference( ) as NonNullable; const ownerPluginArtifacts = { ownerPluginIds: [], ownerPluginArtifacts: [] } as const; const usesRealOwnerBinding = - params.deps?.createCrestodianVerifiedInferenceBinding === - createCrestodianVerifiedInferenceBinding; - if (!deps.captureCrestodianOwnerPluginArtifacts && !usesRealOwnerBinding) { - deps.captureCrestodianOwnerPluginArtifacts = () => ownerPluginArtifacts; + params.deps?.createSystemAgentVerifiedInferenceBinding === + createSystemAgentVerifiedInferenceBinding; + if (!deps.captureSystemAgentOwnerPluginArtifacts && !usesRealOwnerBinding) { + deps.captureSystemAgentOwnerPluginArtifacts = () => ownerPluginArtifacts; } - if (!deps.createCrestodianVerifiedInferenceBinding) { - deps.createCrestodianVerifiedInferenceBinding = async () => ownerPluginArtifacts as never; + if (!deps.createSystemAgentVerifiedInferenceBinding) { + deps.createSystemAgentVerifiedInferenceBinding = async () => ownerPluginArtifacts as never; } if (!deps.ensurePluginRegistryLoaded) { deps.ensurePluginRegistryLoaded = () => {}; @@ -257,9 +257,9 @@ function createConfigTransformHarness( }; } -describe("applyCrestodianModelSelection", () => { +describe("applySystemAgentModelSelection", () => { it("pins a verified credential without putting the profile suffix in model metadata", async () => { - const result = await applyCrestodianModelSelection({ + const result = await applySystemAgentModelSelection({ config: {}, model: "openai/gpt-5.5", authProfileId: "openai:setup-123", @@ -285,7 +285,7 @@ describe("applyCrestodianModelSelection", () => { }, } satisfies OpenClawConfig; - const result = await applyCrestodianModelSelection({ + const result = await applySystemAgentModelSelection({ config, model: "openai/gpt-5.5", agentRuntimeId: "codex", @@ -730,7 +730,7 @@ describe("activateSetupInference", () => { tools: { allow: ["read"], deny: ["exec"] }, }, { - id: "crestodian", + id: "openclaw", params: { temperature: 1.7 }, tools: { allow: ["exec"] }, }, @@ -764,7 +764,7 @@ describe("activateSetupInference", () => { expect(runCliAgent).toHaveBeenCalledOnce(); expect(runCliAgent).toHaveBeenCalledWith( expect.objectContaining({ - agentId: "crestodian", + agentId: "openclaw", agentDir: "/tmp/openclaw-ops-agent", executionMode: "side-question", disableTools: true, @@ -772,15 +772,13 @@ describe("activateSetupInference", () => { }), ); const probeConfig = runCliAgent.mock.calls[0]?.[0].config; - expect(probeConfig?.agents?.list?.find((agent) => agent.id === "crestodian")).toEqual({ - id: "crestodian", + expect(probeConfig?.agents?.list?.find((agent) => agent.id === "openclaw")).toEqual({ + id: "openclaw", params: { temperature: 0.2 }, tools: { allow: ["read"], deny: ["exec"] }, }); - expect( - configHarness.current().agents?.list?.find((agent) => agent.id === "crestodian"), - ).toEqual({ - id: "crestodian", + expect(configHarness.current().agents?.list?.find((agent) => agent.id === "openclaw")).toEqual({ + id: "openclaw", params: { temperature: 1.7 }, tools: { allow: ["exec"] }, }); @@ -809,7 +807,7 @@ describe("activateSetupInference", () => { expect(configHarness.current()).toEqual({}); }); - it("rejects an unattested existing route before handing off to Crestodian", async () => { + it("rejects an unattested existing route before handing off to OpenClaw", async () => { const config = { agents: { defaults: { model: "openai/gpt-5.5" } }, } satisfies OpenClawConfig; @@ -935,7 +933,7 @@ describe("activateSetupInference", () => { expect(committedConfig?.agents?.defaults?.model).toBe("claude-cli/claude-opus-4-8"); }); - it("persists only the verified model before Crestodian configures the rest", async () => { + it("persists only the verified model before OpenClaw configures the rest", async () => { const configHarness = createConfigTransformHarness(); const result = await activateSetupInference({ @@ -1242,7 +1240,7 @@ describe("activateSetupInference", () => { runCliAgent: vi.fn(successfulRunner("claude-cli", "claude-opus-4-8")) as never, resolveCliRuntimeOwnerFingerprint: resolveCliRuntimeOwnerFingerprint as never, fingerprintPluginRuntimeArtifact: ({ pluginId }) => `${pluginId}-runtime-v1`, - createCrestodianVerifiedInferenceBinding, + createSystemAgentVerifiedInferenceBinding, transformConfigWithPendingPluginInstalls: configHarness.transform as never, createTempDir: makeTempDir, }, @@ -1277,7 +1275,7 @@ describe("activateSetupInference", () => { })) as never, runCliAgent: vi.fn(successfulRunner("claude-cli", "claude-opus-4-8")) as never, resolveCliRuntimeOwnerFingerprint: vi.fn(async () => "changed-runtime-owner") as never, - createCrestodianVerifiedInferenceBinding, + createSystemAgentVerifiedInferenceBinding, createTempDir: makeTempDir, }, }); @@ -1319,7 +1317,7 @@ describe("activateSetupInference", () => { source: "env:ANTHROPIC_API_KEY", mode: "api-key", })) as never, - createCrestodianVerifiedInferenceBinding, + createSystemAgentVerifiedInferenceBinding, transformConfigWithPendingPluginInstalls: configHarness.transform as never, createTempDir: makeTempDir, }, @@ -1620,7 +1618,7 @@ describe("activateSetupInference", () => { expect(result).toMatchObject({ ok: true, modelRef: "anthropic/claude-opus-4-8" }); expect(runEmbeddedAgent).toHaveBeenCalledWith( expect.objectContaining({ - agentId: "crestodian", + agentId: "openclaw", provider: "anthropic", model: "claude-opus-4-8", agentHarnessRuntimeOverride: "openclaw", @@ -1916,7 +1914,7 @@ describe("activateSetupInference", () => { expect(activatedProfileId).toMatch(/^groq:setup-/); expect(runEmbeddedAgent).toHaveBeenCalledWith( expect.objectContaining({ - agentId: "crestodian", + agentId: "openclaw", provider: "groq", model: "llama-3.3-70b-versatile", authProfileId: activatedProfileId, @@ -1944,7 +1942,7 @@ describe("activateSetupInference", () => { ); expect(readAuthProfileStoreForTest(agentDir).order?.groq).toEqual(["groq:legacy"]); expect( - (await resolveCrestodianConfiguredRouteFromConfig(configHarness.current())) + (await resolveSystemAgentConfiguredRouteFromConfig(configHarness.current())) ?.authProfileId, ).toBe(activatedProfileId); } finally { @@ -2254,7 +2252,7 @@ describe("activateSetupInference", () => { source: `profile:${params.profileId}`, mode: "api-key", })) as never, - createCrestodianVerifiedInferenceBinding, + createSystemAgentVerifiedInferenceBinding, transformConfigWithPendingPluginInstalls: configHarness.transform as never, createTempDir: makeTempDir, }, @@ -3039,7 +3037,7 @@ describe("activateSetupInference", () => { expect(activatedProfileId).toMatch(/^github-copilot:setup-/); expect(runEmbeddedAgent).toHaveBeenCalledWith( expect.objectContaining({ - agentId: "crestodian", + agentId: "openclaw", agentDir: expect.stringContaining("setup-inference-test-"), authProfileId: activatedProfileId, provider: "github-copilot", @@ -3316,7 +3314,7 @@ describe("activateSetupInference", () => { expect.objectContaining({ reason: "source-changed", policyPluginIds: ["codex"], - traceCommand: "crestodian-setup-probe", + traceCommand: "openclaw-setup-probe", workspaceDir: "/tmp/openclaw-workspace", }), ); @@ -3334,7 +3332,7 @@ describe("activateSetupInference", () => { expect.objectContaining({ afterWrite: { mode: "none", - reason: "Crestodian activates verified inference", + reason: "OpenClaw activates verified inference", }, }), ); @@ -3352,7 +3350,7 @@ describe("activateSetupInference", () => { }); // Harness selection: codex tests run embedded with the codex harness. expect(runEmbeddedAgent.mock.calls[0]?.[0]).toMatchObject({ - agentId: "crestodian", + agentId: "openclaw", agentDir: resolveAgentDir(initialConfig, "ops"), provider: "openai", authProfileStateMode: "read-only", @@ -3514,7 +3512,7 @@ describe("activateSetupInference", () => { expect.objectContaining({ reason: "source-changed", policyPluginIds: ["codex"], - traceCommand: "crestodian-setup-probe", + traceCommand: "openclaw-setup-probe", workspaceDir: "/tmp/work", }), ); @@ -4143,17 +4141,17 @@ describe("activateSetupInference", () => { expect(markRetained).toHaveBeenNthCalledWith(1, { packageDir: expectDefined(installRecords[0], "installRecords[0] test invariant").installPath, pluginId: "codex", - reason: "crestodian-inference-activation-not-committed", + reason: "openclaw-inference-activation-not-committed", }); expect(markRetained).toHaveBeenNthCalledWith(2, { packageDir: expectDefined(installRecords[0], "installRecords[0] test invariant").installPath, pluginId: "codex", - reason: "crestodian-inference-activation-not-committed", + reason: "openclaw-inference-activation-not-committed", }); expect(markRetained).toHaveBeenNthCalledWith(3, { packageDir: expectDefined(installRecords[1], "installRecords[1] test invariant").installPath, pluginId: "codex", - reason: "crestodian-inference-activation-not-committed", + reason: "openclaw-inference-activation-not-committed", }); expect(clearInstallRecords).toHaveBeenCalledTimes(3); expect(clearMetadata).toHaveBeenCalledTimes(3); @@ -4256,10 +4254,10 @@ describe("activateSetupInference", () => { }); }); -describe("resolveCrestodianInferenceForPersistentApply", () => { +describe("resolveSystemAgentInferenceForPersistentApply", () => { function createBinding( proofKind: "runtime-owner" | "credential" = "runtime-owner", - ): CrestodianVerifiedInferenceBinding { + ): SystemAgentVerifiedInferenceBinding { const execution = { runner: "embedded" as const, runConfig: { agents: { defaults: { model: "openai/gpt-5.5" } } }, @@ -4313,7 +4311,7 @@ describe("resolveCrestodianInferenceForPersistentApply", () => { const resolveVerifiedInferenceRoute = vi.fn(async () => binding.execution); const verifyBoundInference = vi.fn(); - const route = await resolveCrestodianInferenceForPersistentApply({ + const route = await resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: { @@ -4333,7 +4331,7 @@ describe("resolveCrestodianInferenceForPersistentApply", () => { const resolveVerifiedInferenceRoute = vi.fn(async () => null); const verifyBoundInference = vi.fn(); - const route = await resolveCrestodianInferenceForPersistentApply({ + const route = await resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: { @@ -4358,7 +4356,7 @@ describe("resolveCrestodianInferenceForPersistentApply", () => { })); await expect( - resolveCrestodianInferenceForPersistentApply({ + resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: { @@ -4381,7 +4379,7 @@ describe("resolveCrestodianInferenceForPersistentApply", () => { const resolveVerifiedInferenceRoute = vi.fn(async () => binding.execution); await expect( - resolveCrestodianInferenceForPersistentApply({ + resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: { @@ -4407,7 +4405,7 @@ describe("resolveCrestodianInferenceForPersistentApply", () => { .mockResolvedValueOnce(null); await expect( - resolveCrestodianInferenceForPersistentApply({ + resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: { @@ -4640,7 +4638,7 @@ describe("verifySetupInference", () => { expect(result).toMatchObject({ ok: true, modelRef: "openai/gpt-5.5" }); }); - it("locks the exact winning profile into a bound Crestodian session", async () => { + it("locks the exact winning profile into a bound OpenClaw session", async () => { const config = { agents: { defaults: { @@ -4731,7 +4729,7 @@ describe("verifySetupInference", () => { agents: { defaults: { model: `openai/gpt-5.5@${profileId}` } }, auth: { profiles: { [profileId]: { provider: "openai", mode: "api_key" } } }, } satisfies OpenClawConfig; - const captureCrestodianOwnerPluginArtifacts = vi.fn(() => ({ + const captureSystemAgentOwnerPluginArtifacts = vi.fn(() => ({ ownerPluginIds: ["openai"], ownerPluginArtifacts: [{ pluginId: "openai", fingerprint: "openai-runtime-v1" }], })); @@ -4767,8 +4765,8 @@ describe("verifySetupInference", () => { source: `profile:${profileId}`, mode: "api-key" as const, })), - captureCrestodianOwnerPluginArtifacts, - createCrestodianVerifiedInferenceBinding: createChangedVerifiedInferenceBinding, + captureSystemAgentOwnerPluginArtifacts, + createSystemAgentVerifiedInferenceBinding: createChangedVerifiedInferenceBinding, runEmbeddedAgent: runEmbeddedAgent as never, createTempDir: makeTempDir, }, @@ -4779,11 +4777,11 @@ describe("verifySetupInference", () => { status: "auth", error: expect.stringContaining("owner changed"), }); - expect(captureCrestodianOwnerPluginArtifacts).toHaveBeenCalledOnce(); + expect(captureSystemAgentOwnerPluginArtifacts).toHaveBeenCalledOnce(); expect(createChangedVerifiedInferenceBinding).toHaveBeenCalledOnce(); }); - it("binds a runtime-only Codex profile after activation and runs the first Crestodian turn", async () => { + it("binds a runtime-only Codex profile after activation and runs the first OpenClaw turn", async () => { const stateDir = await makeTempDir(); vi.stubEnv("OPENCLAW_STATE_DIR", stateDir); const profileId = "openai:default"; @@ -4841,8 +4839,8 @@ describe("verifySetupInference", () => { })); const validateAgentHarnessRuntimeArtifact = vi.fn(async () => true); const createVerifiedInferenceBinding = vi.fn( - (params: Parameters[0]) => - createCrestodianVerifiedInferenceBinding({ + (params: Parameters[0]) => + createSystemAgentVerifiedInferenceBinding({ ...params, deps: { ...params.deps, validateAgentHarnessRuntimeArtifact }, }), @@ -4857,7 +4855,7 @@ describe("verifySetupInference", () => { loadAuthProfileStoreForRuntime: externalStore as never, ensureAuthProfileStore: externalStore as never, runEmbeddedAgent: runEmbeddedAgent as never, - createCrestodianVerifiedInferenceBinding: createVerifiedInferenceBinding, + createSystemAgentVerifiedInferenceBinding: createVerifiedInferenceBinding, createTempDir: makeTempDir, }, }); @@ -4876,9 +4874,9 @@ describe("verifySetupInference", () => { throw new Error(verification.error); } - const session = createCrestodianAgentSession(verification.binding); + const session = createSystemAgentSession(verification.binding); try { - const reply = await runCrestodianAgentTurnWithDeps( + const reply = await runSystemAgentTurnWithDeps( { input: "continue setup", overview: { defaultModel: "openai/gpt-5.6-sol" } as never, @@ -4895,7 +4893,7 @@ describe("verifySetupInference", () => { ); expect(reply).toMatchObject({ text: "OK", modelLabel: "openai/gpt-5.6-sol" }); } finally { - await cleanupCrestodianAgentSession(session); + await cleanupSystemAgentSession(session); } expect(runEmbeddedAgent).toHaveBeenCalledTimes(3); @@ -4908,12 +4906,12 @@ describe("verifySetupInference", () => { authProfileId: profileId, authProfileIdSource: "user", agentHarnessRuntimeOverride: "codex", - agentId: "crestodian", - toolsAllow: ["crestodian"], + agentId: "openclaw", + toolsAllow: ["openclaw"], }); - const crestodianTurnParams = runEmbeddedAgent.mock.calls[2]?.[0]; - expect(crestodianTurnParams).toBeDefined(); - expect((crestodianTurnParams as { config?: OpenClawConfig }).config).toBe( + const systemAgentTurnParams = runEmbeddedAgent.mock.calls[2]?.[0]; + expect(systemAgentTurnParams).toBeDefined(); + expect((systemAgentTurnParams as { config?: OpenClawConfig }).config).toBe( verification.binding.execution.runConfig, ); expect(validateAgentHarnessRuntimeArtifact).toHaveBeenCalledWith({ @@ -5029,7 +5027,7 @@ describe("verifySetupInference", () => { expect(result).toMatchObject({ ok: true, modelRef: "openai/gpt-5.5" }); expect(runEmbeddedAgent).toHaveBeenCalledWith( expect.objectContaining({ - agentId: "crestodian", + agentId: "openclaw", provider: "openai", model: "gpt-5.5", agentHarnessRuntimeOverride: "codex", @@ -5218,7 +5216,7 @@ describe("verifySetupInference", () => { }); try { - const route = await resolveCrestodianConfiguredRouteFromConfig(config); + const route = await resolveSystemAgentConfiguredRouteFromConfig(config); expect(route).toMatchObject({ runner: "cli", provider: "google-gemini-cli", diff --git a/src/crestodian/setup-inference.ts b/src/system-agent/setup-inference.ts similarity index 95% rename from src/crestodian/setup-inference.ts rename to src/system-agent/setup-inference.ts index a726d74736f8..e29f1c2b1f28 100644 --- a/src/crestodian/setup-inference.ts +++ b/src/system-agent/setup-inference.ts @@ -61,34 +61,34 @@ import { normalizeAgentId } from "../routing/session-key.js"; import type { RuntimeEnv } from "../runtime.js"; import { resolveUserPath } from "../utils.js"; import type { WizardPrompter } from "../wizard/prompts.js"; -import { appendCrestodianAuditEntry } from "./audit.js"; +import { appendSystemAgentAuditEntry } from "./audit.js"; import { projectDefaultInferenceRoute, - resolveCrestodianConfiguredRouteFromConfig, + resolveSystemAgentConfiguredRouteFromConfig, sameDefaultInferenceRoute, - type CrestodianConfiguredRoute, + type SystemAgentConfiguredRoute, } from "./inference-route.js"; import { loadAuthoredSetupConfig } from "./onboarding-welcome.js"; import { - applyCrestodianModelSelection, - createCrestodianModelSelectionUpdater, + applySystemAgentModelSelection, + createSystemAgentModelSelectionUpdater, createQuickstartNotePrompter, } from "./setup-apply.js"; import { resolveSetupInferenceProbeStreamParams } from "./setup-inference-probe.js"; import { - captureCrestodianOwnerPluginArtifacts, - createCrestodianVerifiedInferenceBinding, - hasCurrentCrestodianOwnerPluginArtifacts, - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceDeps, - type CrestodianOwnerPluginArtifactSnapshot, + captureSystemAgentOwnerPluginArtifacts, + createSystemAgentVerifiedInferenceBinding, + hasCurrentSystemAgentOwnerPluginArtifacts, + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceDeps, + type SystemAgentOwnerPluginArtifactSnapshot, } from "./verified-inference.js"; -const log = createSubsystemLogger("crestodian/setup-inference"); +const log = createSubsystemLogger("system-agent/setup-inference"); /** - * Inference is the one required onboarding step (docs/cli/crestodian.md + * Inference is the one required onboarding step (docs/cli/setup.md * "Setup bootstrap"). This module gives structured clients (macOS app) the * same ladder the conversation uses, with one hard guarantee: a candidate is * persisted as the default model only after a real completion round-trips. @@ -108,14 +108,14 @@ export type SetupInferenceCandidate = { }; export type SetupInferenceManualProvider = { - /** Provider-auth choice id sent back to `crestodian.setup.activate`. */ + /** Provider-auth choice id sent back to `openclaw.setup.activate`. */ id: string; label: string; hint?: string; }; export type SetupInferenceAuthOption = { - /** Provider-auth choice id sent to `crestodian.setup.auth.start`. */ + /** Provider-auth choice id sent to `openclaw.setup.auth.start`. */ id: string; label: string; hint?: string; @@ -174,7 +174,7 @@ export type BoundVerifySetupInferenceResult = ok: true; modelRef: string; latencyMs: number; - binding: CrestodianVerifiedInferenceBinding; + binding: SystemAgentVerifiedInferenceBinding; } | { ok: false; status: SetupInferenceFailureStatus; error: string }; @@ -262,10 +262,10 @@ export type ActivateSetupInferenceDeps = { resolveCliRuntimeArtifactFingerprint?: typeof import("../agents/cli-auth-epoch.js").resolveCliRuntimeArtifactFingerprint; resolveCliRuntimeOwnerFingerprint?: typeof import("../agents/cli-auth-epoch.js").resolveCliRuntimeOwnerFingerprint; resolveApiKeyForProvider?: typeof import("../agents/model-auth.js").resolveApiKeyForProvider; - loadPluginRegistrySnapshot?: CrestodianVerifiedInferenceDeps["loadPluginRegistrySnapshot"]; - fingerprintPluginRuntimeArtifact?: CrestodianVerifiedInferenceDeps["fingerprintPluginRuntimeArtifact"]; - captureCrestodianOwnerPluginArtifacts?: typeof captureCrestodianOwnerPluginArtifacts; - createCrestodianVerifiedInferenceBinding?: typeof createCrestodianVerifiedInferenceBinding; + loadPluginRegistrySnapshot?: SystemAgentVerifiedInferenceDeps["loadPluginRegistrySnapshot"]; + fingerprintPluginRuntimeArtifact?: SystemAgentVerifiedInferenceDeps["fingerprintPluginRuntimeArtifact"]; + captureSystemAgentOwnerPluginArtifacts?: typeof captureSystemAgentOwnerPluginArtifacts; + createSystemAgentVerifiedInferenceBinding?: typeof createSystemAgentVerifiedInferenceBinding; readPersistedInstalledPluginIndexInstallRecords?: typeof import("../plugins/installed-plugin-index-records.js").readPersistedInstalledPluginIndexInstallRecords; markRetainedManagedNpmInstall?: typeof import("../plugins/managed-npm-retention.js").markRetainedManagedNpmInstall; clearLoadInstalledPluginIndexInstallRecordsCache?: typeof import("../plugins/installed-plugin-index-records.js").clearLoadInstalledPluginIndexInstallRecordsCache; @@ -415,7 +415,7 @@ type SetupInferenceTestPlan = { model: string; modelRef: string; config: OpenClawConfig; - /** Execution identity used by the real Crestodian turn. */ + /** Execution identity used by the real OpenClaw turn. */ agentId?: string; /** Default-agent owner whose model/runtime config is being selected. */ routeAgentId?: string; @@ -766,7 +766,7 @@ function findSelectedProviderConfigKey( /** * Provider auth hooks are untrusted setup input. Carry only the selected - * inference route's config into the probe; Crestodian owns every other setup + * inference route's config into the probe; OpenClaw owns every other setup * surface after intelligence exists. */ function projectManualInferenceConfig(params: { @@ -875,7 +875,7 @@ async function buildTestPlan(params: { }; switch (kind) { case "existing-model": { - const route = await resolveCrestodianConfiguredRouteFromConfig(cfg); + const route = await resolveSystemAgentConfiguredRouteFromConfig(cfg); if (!route) { return { error: "No configured default-agent inference route is available." }; } @@ -898,7 +898,7 @@ async function buildTestPlan(params: { model: route.model, modelRef: route.modelLabel, config: route.runConfig, - agentId: "crestodian", + agentId: "openclaw", routeAgentId: route.agentId, agentDir: route.agentDir, ...(route.runner === "embedded" @@ -918,7 +918,7 @@ async function buildTestPlan(params: { ...ref, modelRef, config: cfg, - agentId: "crestodian", + agentId: "openclaw", routeAgentId: resolveDefaultAgentId(cfg), persistModelRef: modelRef, }; @@ -934,7 +934,7 @@ async function buildTestPlan(params: { ...ref, modelRef, config: cfg, - agentId: "crestodian", + agentId: "openclaw", routeAgentId: resolveDefaultAgentId(cfg), persistModelRef: modelRef, }; @@ -951,7 +951,7 @@ async function buildTestPlan(params: { modelRef, agentHarnessRuntimeOverride: "codex", config: cfg, - agentId: "crestodian", + agentId: "openclaw", routeAgentId: resolveDefaultAgentId(cfg), agentDir: params.agentDir, cleanupBundleMcpOnRunEnd: true, @@ -969,7 +969,7 @@ async function buildTestPlan(params: { ...ref, modelRef, config: cfg, - agentId: "crestodian", + agentId: "openclaw", routeAgentId: resolveDefaultAgentId(cfg), persistModelRef: modelRef, }; @@ -985,7 +985,7 @@ async function buildTestPlan(params: { ...ref, modelRef, config: cfg, - agentId: "crestodian", + agentId: "openclaw", routeAgentId: resolveDefaultAgentId(cfg), persistModelRef: modelRef, }; @@ -1157,7 +1157,7 @@ async function buildTestPlan(params: { modelRef, agentDir: params.agentDir, config: preparedAuth.config, - agentId: "crestodian", + agentId: "openclaw", routeAgentId: resolveDefaultAgentId(preparedAuth.config), authProfileId: preparedAuth.selectedProfileId, persistModelRef: modelRef, @@ -1348,7 +1348,7 @@ async function activateSetupInferenceUnredacted( let testPlan = plan; if (plan.persistModelRef) { const agentRuntimeId = resolveSetupAgentRuntimeId(params.kind); - const stagedConfig = await applyCrestodianModelSelection({ + const stagedConfig = await applySystemAgentModelSelection({ config: plan.config, model: plan.persistModelRef, ...(agentRuntimeId ? { agentRuntimeId } : {}), @@ -1457,7 +1457,7 @@ async function activateSetupInferenceUnredacted( reason: "source-changed", workspaceDir: workspace, policyPluginIds: ["codex"], - traceCommand: "crestodian-setup-probe", + traceCommand: "openclaw-setup-probe", logger: { warn: (message) => (registryRefreshWarning = message) }, }); const ensureHarnessPlugin = @@ -1484,7 +1484,7 @@ async function activateSetupInferenceUnredacted( const baselineRoute = await projectDefaultInferenceRoute(cfg); const verifiedRoute = await projectDefaultInferenceRoute(testPlan.config); const stagedRoute = verifiedRoute.route; - const stagedExecutionRoute = await resolveCrestodianConfiguredRouteFromConfig(testPlan.config); + const stagedExecutionRoute = await resolveSystemAgentConfiguredRouteFromConfig(testPlan.config); if ( !stagedRoute || !stagedExecutionRoute || @@ -1509,7 +1509,7 @@ async function activateSetupInferenceUnredacted( sourceCfg, stagedRoute.modelLabel, ); - // Crestodian executes through the reserved agent id but reuses the default + // OpenClaw executes through the reserved agent id but reuses the default // route's agent directory. Only a submitted key stays in the isolated store. if (testPlan.runner === "embedded" && stagedRoute.runner === "embedded") { testPlan = { @@ -1542,10 +1542,10 @@ async function activateSetupInferenceUnredacted( } } - let stagedOwnerPluginArtifacts: CrestodianOwnerPluginArtifactSnapshot; + let stagedOwnerPluginArtifacts: SystemAgentOwnerPluginArtifactSnapshot; try { stagedOwnerPluginArtifacts = ( - deps.captureCrestodianOwnerPluginArtifacts ?? captureCrestodianOwnerPluginArtifacts + deps.captureSystemAgentOwnerPluginArtifacts ?? captureSystemAgentOwnerPluginArtifacts )({ config: stagedExecutionRoute.runConfig, executionRoute: stagedExecutionRoute, @@ -1608,7 +1608,7 @@ async function activateSetupInferenceUnredacted( ok: false, status: "unknown", error: - "Inference succeeded, but its runtime did not report an owner that Crestodian can safely reuse. No model or credential route was saved.", + "Inference succeeded, but its runtime did not report an owner that OpenClaw can safely reuse. No model or credential route was saved.", }; } if ( @@ -1670,7 +1670,7 @@ async function activateSetupInferenceUnredacted( }; } const latestResolvedRoute = latestRuntime - ? await resolveCrestodianConfiguredRouteFromConfig(latestRuntime) + ? await resolveSystemAgentConfiguredRouteFromConfig(latestRuntime) : null; if (!latestResolvedRoute) { return { @@ -1703,7 +1703,7 @@ async function activateSetupInferenceUnredacted( await import("../plugins/install-record-commit.js"); const agentRuntimeId = resolveSetupAgentRuntimeId(params.kind); const selectModel = plan.persistModelRef - ? await createCrestodianModelSelectionUpdater({ + ? await createSystemAgentModelSelectionUpdater({ model: plan.persistModelRef, ...(agentRuntimeId ? { agentRuntimeId } : {}), ...(plan.manualAuth && plan.authProfileId ? { authProfileId: plan.authProfileId } : {}), @@ -1769,7 +1769,7 @@ async function activateSetupInferenceUnredacted( throwIfSetupInferenceCancelled(params); const initialCandidate = stageCandidate(cfg); const initialRoute = await projectDefaultInferenceRoute(initialCandidate); - const resolvedRoute = await resolveCrestodianConfiguredRouteFromConfig(initialCandidate); + const resolvedRoute = await resolveSystemAgentConfiguredRouteFromConfig(initialCandidate); if ( !sameDefaultInferenceRoute(initialRoute, verifiedRoute) || !resolvedRoute || @@ -1815,7 +1815,7 @@ async function activateSetupInferenceUnredacted( base: "source", // The transform stays side-effect free so a config conflict can retry // without replaying credential writes in another agent directory. - afterWrite: { mode: "none", reason: "Crestodian activates verified inference" }, + afterWrite: { mode: "none", reason: "OpenClaw activates verified inference" }, transform: async (current, context) => { const latestRuntime = context.snapshot.runtimeConfig ?? context.snapshot.config; // Validate that the candidate is still admissible before reporting @@ -1843,7 +1843,7 @@ async function activateSetupInferenceUnredacted( "The default-agent inference route changed during its live test, so the verified candidate was not saved. Review the current model/auth/runtime settings and retry.", ); } - const resolvedRoute = await resolveCrestodianConfiguredRouteFromConfig(stagedRuntime); + const resolvedRoute = await resolveSystemAgentConfiguredRouteFromConfig(stagedRuntime); if ( !resolvedRoute || resolvedRoute.modelLabel !== plan.modelRef || @@ -1865,7 +1865,7 @@ async function activateSetupInferenceUnredacted( } const nextConfig = stageCandidate(current); const nextRouteProjection = await projectDefaultInferenceRoute(nextConfig); - const nextResolvedRoute = await resolveCrestodianConfiguredRouteFromConfig(nextConfig); + const nextResolvedRoute = await resolveSystemAgentConfiguredRouteFromConfig(nextConfig); if ( !sameDefaultInferenceRoute(nextRouteProjection, expectedSourceCandidateRoute) || !nextResolvedRoute || @@ -1966,9 +1966,9 @@ async function activateSetupInferenceUnredacted( if (params.surface === "gateway" && params.recordSetupAudit !== false) { const after = await readSnapshot().catch(() => null); try { - await appendCrestodianAuditEntry({ - operation: "crestodian.setup", - summary: "Verified and configured AI access through Crestodian setup", + await appendSystemAgentAuditEntry({ + operation: "openclaw.setup", + summary: "Verified and configured AI access through OpenClaw setup", configPath: after?.path ?? snapshot.path, configHashBefore: snapshot.hash ?? null, configHashAfter: after?.hash ?? null, @@ -2039,13 +2039,13 @@ async function redactSetupInferenceError(message: string, apiKey?: string): Prom } async function revalidateSetupInferenceOwner(params: { - route: NonNullable>>; + route: NonNullable>>; auth: AgentExecutionAuthBinding; deps: ActivateSetupInferenceDeps; -}): Promise { +}): Promise { const createBinding = - params.deps.createCrestodianVerifiedInferenceBinding ?? - createCrestodianVerifiedInferenceBinding; + params.deps.createSystemAgentVerifiedInferenceBinding ?? + createSystemAgentVerifiedInferenceBinding; return await createBinding({ configuredRoute: params.route, executionRoute: params.route, @@ -2055,8 +2055,8 @@ async function revalidateSetupInferenceOwner(params: { } function hasSameOwnerPluginArtifacts( - binding: CrestodianVerifiedInferenceBinding, - snapshot: CrestodianOwnerPluginArtifactSnapshot, + binding: SystemAgentVerifiedInferenceBinding, + snapshot: SystemAgentOwnerPluginArtifactSnapshot, ): boolean { return ( isDeepStrictEqual(binding.ownerPluginIds, snapshot.ownerPluginIds) && @@ -2104,7 +2104,7 @@ export async function verifySetupInference( } const cfg: OpenClawConfig = snapshot.runtimeConfig ?? snapshot.config; const baselineRoute = await projectDefaultInferenceRoute(cfg); - let verifiedBinding: CrestodianVerifiedInferenceBinding | undefined; + let verifiedBinding: SystemAgentVerifiedInferenceBinding | undefined; const verification = await verifySetupInferenceConfig({ config: cfg, runtime: params.runtime, @@ -2115,7 +2115,7 @@ export async function verifySetupInference( ? { onVerifiedExecution: ( _auth: AgentExecutionAuthBinding, - binding: CrestodianVerifiedInferenceBinding, + binding: SystemAgentVerifiedInferenceBinding, ) => { verifiedBinding = binding; }, @@ -2142,13 +2142,13 @@ export async function verifySetupInference( if (!params.bindSession) { return verification; } - const configuredRoute = await resolveCrestodianConfiguredRouteFromConfig(cfg); + const configuredRoute = await resolveSystemAgentConfiguredRouteFromConfig(cfg); if (!configuredRoute || !verifiedBinding) { return { ok: false, status: "unknown", error: - "The successful inference run did not report an exact execution binding. Retry setup before starting Crestodian.", + "The successful inference run did not report an exact execution binding. Retry setup before starting OpenClaw.", }; } return { ...verification, binding: verifiedBinding }; @@ -2160,13 +2160,13 @@ type BoundSetupInferenceVerifier = (params: { deps?: ActivateSetupInferenceDeps; }) => Promise; -export type ResolveCrestodianInferenceForPersistentApplyDeps = CrestodianVerifiedInferenceDeps & { - resolveVerifiedInferenceRoute?: typeof resolveCrestodianVerifiedInferenceRoute; - hasCurrentOwnerPluginArtifacts?: typeof hasCurrentCrestodianOwnerPluginArtifacts; +export type ResolveSystemAgentInferenceForPersistentApplyDeps = SystemAgentVerifiedInferenceDeps & { + resolveVerifiedInferenceRoute?: typeof resolveSystemAgentVerifiedInferenceRoute; + hasCurrentOwnerPluginArtifacts?: typeof hasCurrentSystemAgentOwnerPluginArtifacts; verifyBoundInference?: BoundSetupInferenceVerifier; }; -function executionRouteIdentity(route: CrestodianConfiguredRoute): unknown { +function executionRouteIdentity(route: SystemAgentConfiguredRoute): unknown { const { runConfig: _runConfig, ...identity } = route; return identity; } @@ -2176,20 +2176,20 @@ function executionRouteIdentity(route: CrestodianConfiguredRoute): unknown { * prove liveness only by completing another exact turn at the side-effect * boundary; the result must still be the original frozen route. */ -export async function resolveCrestodianInferenceForPersistentApply(params: { - binding: CrestodianVerifiedInferenceBinding; +export async function resolveSystemAgentInferenceForPersistentApply(params: { + binding: SystemAgentVerifiedInferenceBinding; runtime: RuntimeEnv; - deps?: ResolveCrestodianInferenceForPersistentApplyDeps; -}): Promise { + deps?: ResolveSystemAgentInferenceForPersistentApplyDeps; +}): Promise { const deps = params.deps ?? {}; const resolveVerified = - deps.resolveVerifiedInferenceRoute ?? resolveCrestodianVerifiedInferenceRoute; + deps.resolveVerifiedInferenceRoute ?? resolveSystemAgentVerifiedInferenceRoute; const initialRoute = await resolveVerified(params.binding, deps); if (!initialRoute) { return null; } const hasCurrentOwnerPluginArtifacts = - deps.hasCurrentOwnerPluginArtifacts ?? hasCurrentCrestodianOwnerPluginArtifacts; + deps.hasCurrentOwnerPluginArtifacts ?? hasCurrentSystemAgentOwnerPluginArtifacts; if (!(await hasCurrentOwnerPluginArtifacts(params.binding, deps))) { return null; } @@ -2235,7 +2235,7 @@ export async function verifySetupInferenceConfig(params: { /** Internal session gate: capture only the final exact successful credential. */ onVerifiedExecution?: ( auth: AgentExecutionAuthBinding, - binding: CrestodianVerifiedInferenceBinding, + binding: SystemAgentVerifiedInferenceBinding, ) => void; /** Reject a successful turn unless its runner reports the exact execution owner. */ requireExecutionOwner?: boolean; @@ -2272,11 +2272,11 @@ export async function verifySetupInferenceConfig(params: { const requiresExecutionOwner = params.requireExecutionOwner === true || params.onVerifiedExecution !== undefined; let configuredRoute: - | NonNullable>> + | NonNullable>> | undefined; - let stagedOwnerPluginArtifacts: CrestodianOwnerPluginArtifactSnapshot | undefined; + let stagedOwnerPluginArtifacts: SystemAgentOwnerPluginArtifactSnapshot | undefined; if (requiresExecutionOwner) { - configuredRoute = (await resolveCrestodianConfiguredRouteFromConfig(cfg)) ?? undefined; + configuredRoute = (await resolveSystemAgentConfiguredRouteFromConfig(cfg)) ?? undefined; if (!configuredRoute) { return { ok: false, @@ -2286,7 +2286,7 @@ export async function verifySetupInferenceConfig(params: { } try { stagedOwnerPluginArtifacts = ( - deps.captureCrestodianOwnerPluginArtifacts ?? captureCrestodianOwnerPluginArtifacts + deps.captureSystemAgentOwnerPluginArtifacts ?? captureSystemAgentOwnerPluginArtifacts )({ config: cfg, executionRoute: configuredRoute, @@ -2442,7 +2442,7 @@ async function retainUnownedCodexInstall(params: { const marked = await markRetained({ packageDir: params.record.installPath, pluginId: "codex", - reason: "crestodian-inference-activation-not-committed", + reason: "openclaw-inference-activation-not-committed", }); if (!marked) { log.warn("Could not retain the uncommitted Codex runtime package generation."); @@ -2829,7 +2829,7 @@ async function runSetupInferenceTest(params: { result = (await runCli({ sessionId, sessionKey: `temp:setup-inference:${runId}`, - agentId: plan.agentId ?? "crestodian", + agentId: plan.agentId ?? "openclaw", trigger: "manual", sessionFile, workspaceDir: tempDir, @@ -2841,8 +2841,8 @@ async function runSetupInferenceTest(params: { ...(plan.authProfileId ? { authProfileId: plan.authProfileId } : {}), timeoutMs, runId, - messageChannel: "crestodian", - messageProvider: "crestodian", + messageChannel: "openclaw", + messageProvider: "openclaw", executionMode: "side-question", disableTools: true, cleanupCliLiveSessionOnRunEnd: true, @@ -2857,7 +2857,7 @@ async function runSetupInferenceTest(params: { result = (await runEmbedded({ sessionId, sessionKey: `temp:setup-inference:${runId}`, - agentId: plan.agentId ?? "crestodian", + agentId: plan.agentId ?? "openclaw", trigger: "manual", sessionFile, workspaceDir: tempDir, @@ -2883,8 +2883,8 @@ async function runSetupInferenceTest(params: { ...resolveSetupInferenceProbeStreamParams(plan.agentHarnessRuntimeOverride), disableTools: true, modelRun: true, - messageChannel: "crestodian", - messageProvider: "crestodian", + messageChannel: "openclaw", + messageProvider: "openclaw", onSuccessfulAuthBinding: (binding) => { successfulAuth = binding; }, @@ -2920,7 +2920,7 @@ async function runSetupInferenceTest(params: { ok: false, status: "unknown", error: - "Inference succeeded, but its runtime did not report an owner that Crestodian can safely reuse.", + "Inference succeeded, but its runtime did not report an owner that OpenClaw can safely reuse.", }; } return { diff --git a/src/crestodian/crestodian.test-helpers.ts b/src/system-agent/system-agent.test-helpers.ts similarity index 86% rename from src/crestodian/crestodian.test-helpers.ts rename to src/system-agent/system-agent.test-helpers.ts index 7e47a915adda..20696ca2e0d9 100644 --- a/src/crestodian/crestodian.test-helpers.ts +++ b/src/system-agent/system-agent.test-helpers.ts @@ -1,5 +1,5 @@ import { resolveCliBackendConfig } from "../agents/cli-backends.js"; -// Crestodian test helpers build runtime environments for rescue tests. +// OpenClaw test helpers build runtime environments for rescue tests. import { fingerprintAuthProfileOwnerShape, fingerprintOpaqueRuntimeOwner, @@ -8,23 +8,23 @@ import { } from "../agents/execution-auth-binding.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { RuntimeEnv } from "../runtime.js"; -import { resolveCrestodianConfiguredRouteFromConfig } from "./inference-route.js"; +import { resolveSystemAgentConfiguredRouteFromConfig } from "./inference-route.js"; import { - createCrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceBinding, - type CrestodianVerifiedInferenceDeps, + createSystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceBinding, + type SystemAgentVerifiedInferenceDeps, } from "./verified-inference.js"; -export type CrestodianVerifiedInferenceTestFixture = { - binding: CrestodianVerifiedInferenceBinding; - deps: CrestodianVerifiedInferenceDeps; +export type SystemAgentVerifiedInferenceTestFixture = { + binding: SystemAgentVerifiedInferenceBinding; + deps: SystemAgentVerifiedInferenceDeps; }; /** Build exact, revalidatable proof for a test config without reading host credentials. */ -export async function createCrestodianVerifiedInferenceTestFixture( +export async function createSystemAgentVerifiedInferenceTestFixture( config: OpenClawConfig, -): Promise { - const configuredRoute = await resolveCrestodianConfiguredRouteFromConfig(config); +): Promise { + const configuredRoute = await resolveSystemAgentConfiguredRouteFromConfig(config); if (!configuredRoute) { throw new Error("missing test route"); } @@ -52,7 +52,7 @@ export async function createCrestodianVerifiedInferenceTestFixture( configuredRoute.provider === "openai" ? "codex" : undefined, configuredRoute.provider === "claude-cli" ? "anthropic" : undefined, ].filter((id, index, ids): id is string => Boolean(id) && ids.indexOf(id) === index); - const deps: CrestodianVerifiedInferenceDeps = { + const deps: SystemAgentVerifiedInferenceDeps = { ensureAuthProfileStore: (() => ({ version: 1, profiles: profileId ? { [profileId]: credential } : {}, @@ -87,7 +87,7 @@ export async function createCrestodianVerifiedInferenceTestFixture( : undefined; const resolveRuntimeOwnerFingerprint = (currentConfig: OpenClawConfig) => { const backend = resolveCliBackendConfig(configuredRoute.provider, currentConfig, { - agentId: "crestodian", + agentId: "openclaw", }); if (!backend || backend.id !== runtimeArtifactId) { return undefined; @@ -119,7 +119,7 @@ export async function createCrestodianVerifiedInferenceTestFixture( params.runtimeArtifactFingerprint === runtimeArtifactFingerprint ? resolveRuntimeOwnerFingerprint(params.config) : undefined; - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute, executionRoute: configuredRoute, auth: { @@ -147,7 +147,7 @@ export async function createCrestodianVerifiedInferenceTestFixture( throw new Error("missing test embedded auth fingerprint"); } deps.resolveAgentHarnessAuthBindingFingerprint = async () => authFingerprint; - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute, executionRoute: configuredRoute, auth: { @@ -169,13 +169,13 @@ export async function createCrestodianVerifiedInferenceTestFixture( } /** - * Test helpers for capturing Crestodian runtime output. + * Test helpers for capturing OpenClaw runtime output. * * Tests use this lightweight runtime instead of the real CLI runtime so exits * become thrown errors and logs are easy to assert. */ /** Create a RuntimeEnv that records log/error lines for tests. */ -export function createCrestodianTestRuntime(): { runtime: RuntimeEnv; lines: string[] } { +export function createSystemAgentTestRuntime(): { runtime: RuntimeEnv; lines: string[] } { const lines: string[] = []; return { lines, diff --git a/src/crestodian/crestodian.test.ts b/src/system-agent/system-agent.test.ts similarity index 75% rename from src/crestodian/crestodian.test.ts rename to src/system-agent/system-agent.test.ts index fb8de172ce91..12a5c53aec27 100644 --- a/src/crestodian/crestodian.test.ts +++ b/src/system-agent/system-agent.test.ts @@ -1,14 +1,14 @@ -// Crestodian tests cover main rescue and audit command behavior. +// OpenClaw tests cover main rescue and audit command behavior. import { describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { runCrestodian, type RunCrestodianOptions } from "./crestodian.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; +import type { SystemAgentCommandDeps } from "./operations.js"; +import type { SystemAgentOverview } from "./overview.js"; +import { runSystemAgent, type RunSystemAgentOptions } from "./system-agent.js"; import { - createCrestodianTestRuntime, - createCrestodianVerifiedInferenceTestFixture, -} from "./crestodian.test-helpers.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; -import type { CrestodianCommandDeps } from "./operations.js"; -import type { CrestodianOverview } from "./overview.js"; + createSystemAgentTestRuntime, + createSystemAgentVerifiedInferenceTestFixture, +} from "./system-agent.test-helpers.js"; vi.mock("../plugins/providers.js", async (importOriginal) => ({ ...(await importOriginal()), @@ -16,7 +16,7 @@ vi.mock("../plugins/providers.js", async (importOriginal) => ({ resolveOwningPluginIdsForProviderRef: vi.fn(() => []), })); -const overview: CrestodianOverview = { +const overview: SystemAgentOverview = { defaultAgentId: "main", defaultModel: "openai/gpt-5.5", agents: [{ id: "main", isDefault: true, model: "openai/gpt-5.5" }], @@ -39,7 +39,7 @@ const overview: CrestodianOverview = { }, }; -const crestodianOverviewDeps = { +const systemAgentOverviewDeps = { formatOverview: () => "Default model: openai/gpt-5.5", loadOverview: async () => overview, }; @@ -71,8 +71,8 @@ function configSnapshot(config: OpenClawConfig) { }; } -async function createVerifiedRunOptions(deps: CrestodianCommandDeps = {}) { - const fixture = await createCrestodianVerifiedInferenceTestFixture(verifiedConfig); +async function createVerifiedRunOptions(deps: SystemAgentCommandDeps = {}) { + const fixture = await createSystemAgentVerifiedInferenceTestFixture(verifiedConfig); return { verifiedInference: fixture.binding, deps: { @@ -83,9 +83,9 @@ async function createVerifiedRunOptions(deps: CrestodianCommandDeps = {}) { }; } -describe("runCrestodian", () => { +describe("runSystemAgent", () => { it("rejects a missing inference binding before any runner side effect", async () => { - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const loadOverview = vi.fn(async () => overview); const planWithAssistant = vi.fn(async () => ({ command: "restart gateway" })); const runGatewayRestart = vi.fn(async () => {}); @@ -97,20 +97,20 @@ describe("runCrestodian", () => { input: { isTTY: true } as unknown as NodeJS.ReadableStream, output: { isTTY: true } as unknown as NodeJS.WritableStream, }; - const withoutBinding = (opts: Omit) => - opts as RunCrestodianOptions; + const withoutBinding = (opts: Omit) => + opts as RunSystemAgentOptions; await expect( - runCrestodian(withoutBinding({ ...common, json: true }), runtime), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + runSystemAgent(withoutBinding({ ...common, json: true }), runtime), + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); await expect( - runCrestodian(withoutBinding({ ...common, message: "please make things nicer" }), runtime), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + runSystemAgent(withoutBinding({ ...common, message: "please make things nicer" }), runtime), + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); await expect( - runCrestodian(withoutBinding({ ...common, message: "restart gateway", yes: true }), runtime), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); - await expect(runCrestodian(withoutBinding(common), runtime)).rejects.toBeInstanceOf( - CrestodianInferenceUnavailableError, + runSystemAgent(withoutBinding({ ...common, message: "restart gateway", yes: true }), runtime), + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); + await expect(runSystemAgent(withoutBinding(common), runtime)).rejects.toBeInstanceOf( + SystemAgentInferenceUnavailableError, ); expect(loadOverview).not.toHaveBeenCalled(); @@ -120,7 +120,7 @@ describe("runCrestodian", () => { }); it("uses the assistant planner only to choose typed operations", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); let runGatewayRestartCalls = 0; let onReadyCalls = 0; const verified = await createVerifiedRunOptions({ @@ -129,7 +129,7 @@ describe("runCrestodian", () => { }, }); - await runCrestodian( + await runSystemAgent( { ...verified, message: "the local bridge looks sleepy, poke it", @@ -141,23 +141,23 @@ describe("runCrestodian", () => { command: "restart gateway", modelLabel: "openai/gpt-5.5", }), - ...crestodianOverviewDeps, + ...systemAgentOverviewDeps, }, runtime, ); expect(runGatewayRestartCalls).toBe(0); expect(onReadyCalls).toBe(0); - expect(lines.join("\n")).toContain("[crestodian] planner: openai/gpt-5.5"); - expect(lines.join("\n")).toContain("[crestodian] interpreted: restart gateway"); + expect(lines.join("\n")).toContain("[openclaw] planner: openai/gpt-5.5"); + expect(lines.join("\n")).toContain("[openclaw] interpreted: restart gateway"); expect(lines.join("\n")).toContain("Plan: restart the Gateway. Say yes to apply."); expect(lines.indexOf("Default model: openai/gpt-5.5")).toBeLessThan( - lines.findIndex((line) => line.includes("[crestodian] planner:")), + lines.findIndex((line) => line.includes("[openclaw] planner:")), ); }); it("does not apply a one-shot plan after the verified route changes", async () => { - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const changedConfig = { agents: { defaults: { model: "anthropic/claude-opus-4-8" } }, } satisfies OpenClawConfig; @@ -172,22 +172,22 @@ describe("runCrestodian", () => { }); await expect( - runCrestodian( + runSystemAgent( { ...verified, message: "the bridge looks sleepy, restart it", yes: true, planWithAssistant: async () => ({ command: "restart gateway" }), - ...crestodianOverviewDeps, + ...systemAgentOverviewDeps, }, runtime, ), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(runGatewayRestart).not.toHaveBeenCalled(); }); it("rechecks one-shot authority at the persistent apply boundary", async () => { - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const changedConfig = { agents: { defaults: { model: "anthropic/claude-opus-4-8" } }, } satisfies OpenClawConfig; @@ -203,29 +203,29 @@ describe("runCrestodian", () => { }); await expect( - runCrestodian( + runSystemAgent( { ...verified, message: "the bridge looks sleepy, restart it", yes: true, planWithAssistant: async () => ({ command: "restart gateway" }), - ...crestodianOverviewDeps, + ...systemAgentOverviewDeps, }, runtime, ), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(readConfigFileSnapshot).toHaveBeenCalledTimes(3); expect(runGatewayRestart).not.toHaveBeenCalled(); }); it("keeps exact one-shot parsing ahead of the assistant planner", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); let plannerCalls = 0; let onReadyCalls = 0; const verified = await createVerifiedRunOptions(); - await runCrestodian( + await runSystemAgent( { ...verified, message: "models", @@ -236,7 +236,7 @@ describe("runCrestodian", () => { onReady: () => { onReadyCalls += 1; }, - ...crestodianOverviewDeps, + ...systemAgentOverviewDeps, }, runtime, ); @@ -247,10 +247,10 @@ describe("runCrestodian", () => { }); it("prints an explicit one-shot overview exactly once", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const verified = await createVerifiedRunOptions(); - await runCrestodian( + await runSystemAgent( { ...verified, message: "overview", @@ -267,32 +267,32 @@ describe("runCrestodian", () => { { name: "no plan", plan: null }, { name: "invalid command", plan: { command: "invent a new operation" } }, ])("fails a fuzzy one-shot when inference returns $name", async ({ plan }) => { - const { runtime } = createCrestodianTestRuntime(); + const { runtime } = createSystemAgentTestRuntime(); const verified = await createVerifiedRunOptions(); await expect( - runCrestodian( + runSystemAgent( { ...verified, message: "please make things nicer", planWithAssistant: async () => plan, - ...crestodianOverviewDeps, + ...systemAgentOverviewDeps, }, runtime, ), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); }); it("prints a valid reply-only one-shot plan", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const verified = await createVerifiedRunOptions(); - await runCrestodian( + await runSystemAgent( { ...verified, message: "explain the current setup", planWithAssistant: async () => ({ reply: "The current setup is healthy." }), - ...crestodianOverviewDeps, + ...systemAgentOverviewDeps, }, runtime, ); @@ -301,7 +301,7 @@ describe("runCrestodian", () => { }); it("does not print a reply-only plan after its inference owner drifts", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); const changedConfig = { agents: { defaults: { model: "anthropic/claude-opus-4-8" } }, } satisfies OpenClawConfig; @@ -311,7 +311,7 @@ describe("runCrestodian", () => { }); await expect( - runCrestodian( + runSystemAgent( { ...verified, message: "explain the current setup", @@ -319,21 +319,21 @@ describe("runCrestodian", () => { currentConfig = changedConfig; return { reply: "stale reply" }; }, - ...crestodianOverviewDeps, + ...systemAgentOverviewDeps, }, runtime, ), - ).rejects.toBeInstanceOf(CrestodianInferenceUnavailableError); + ).rejects.toBeInstanceOf(SystemAgentInferenceUnavailableError); expect(lines).not.toContain("stale reply"); }); - it("starts interactive Crestodian in the TUI shell", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + it("starts interactive OpenClaw in the TUI shell", async () => { + const { runtime, lines } = createSystemAgentTestRuntime(); let runInteractiveTuiCalls = 0; let onReadyCalls = 0; const verified = await createVerifiedRunOptions(); - await runCrestodian( + await runSystemAgent( { ...verified, input: { isTTY: true } as unknown as NodeJS.ReadableStream, @@ -354,12 +354,12 @@ describe("runCrestodian", () => { }); it("prints the formatted overview exactly once when interactive mode is disabled", async () => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); let loadOverviewCalls = 0; let runInteractiveTuiCalls = 0; const verified = await createVerifiedRunOptions(); - await runCrestodian( + await runSystemAgent( { ...verified, interactive: false, @@ -394,12 +394,12 @@ describe("runCrestodian", () => { interactive: true, }, ])("exits non-zero when $name", async ({ input, output, interactive }) => { - const { runtime, lines } = createCrestodianTestRuntime(); + const { runtime, lines } = createSystemAgentTestRuntime(); let runInteractiveTuiCalls = 0; const verified = await createVerifiedRunOptions(); await expect( - runCrestodian( + runSystemAgent( { ...verified, input, @@ -415,7 +415,7 @@ describe("runCrestodian", () => { expect(runInteractiveTuiCalls).toBe(0); expect(lines.join("\n")).toContain( - "Crestodian needs an interactive TTY. Use --message for one command.", + "OpenClaw needs an interactive TTY. Use --message for one command.", ); }); }); diff --git a/src/crestodian/crestodian.ts b/src/system-agent/system-agent.ts similarity index 51% rename from src/crestodian/crestodian.ts rename to src/system-agent/system-agent.ts index 7803ddcba51d..7290ed7b9f31 100644 --- a/src/crestodian/crestodian.ts +++ b/src/system-agent/system-agent.ts @@ -1,40 +1,40 @@ -// Crestodian CLI runner selects JSON, one-shot, or interactive setup-helper mode. +// OpenClaw CLI runner selects JSON, one-shot, or interactive setup-helper mode. import { stdin as defaultStdin, stdout as defaultStdout } from "node:process"; import { withProgress } from "../cli/progress.js"; import { defaultRuntime, writeRuntimeJson, type RuntimeEnv } from "../runtime.js"; -import type { CrestodianAssistantPlanner } from "./assistant.js"; -import { resolveCrestodianOperation } from "./dialogue.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; +import type { SystemAgentAssistantPlanner } from "./assistant.js"; +import { resolveSystemAgentOperation } from "./dialogue.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; import { - executeCrestodianOperation, - isPersistentCrestodianOperation, - parseCrestodianOperation, - type CrestodianCommandDeps, - type CrestodianOperation, + executeSystemAgentOperation, + isPersistentSystemAgentOperation, + parseSystemAgentOperation, + type SystemAgentCommandDeps, + type SystemAgentOperation, } from "./operations.js"; import { - formatCrestodianOverview, - loadCrestodianOverview, - type CrestodianOverview, + formatSystemAgentOverview, + loadSystemAgentOverview, + type SystemAgentOverview, } from "./overview.js"; import { - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceBinding, + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceBinding, } from "./verified-inference.js"; /** - * CLI entry point for Crestodian. + * CLI entry point for OpenClaw. * * This module chooses JSON, one-shot, or interactive TUI mode and delegates all * command parsing/execution to dialogue and operation modules. */ -type CrestodianInteractiveRunner = ( - opts: RunCrestodianOptions, +type SystemAgentInteractiveRunner = ( + opts: RunSystemAgentOptions, runtime: RuntimeEnv, ) => Promise; -/** Options accepted by the Crestodian command runner. */ -export type RunCrestodianOptions = { +/** Options accepted by the OpenClaw command runner. */ +export type RunSystemAgentOptions = { message?: string; yes?: boolean; json?: boolean; @@ -44,23 +44,23 @@ export type RunCrestodianOptions = { /** Workspace override for the proposed first-run setup (from --workspace). */ setupWorkspace?: string; onReady?: () => void; - deps?: CrestodianCommandDeps; - formatOverview?: (overview: CrestodianOverview) => string; - loadOverview?: typeof loadCrestodianOverview; - planWithAssistant?: CrestodianAssistantPlanner; + deps?: SystemAgentCommandDeps; + formatOverview?: (overview: SystemAgentOverview) => string; + loadOverview?: typeof loadSystemAgentOverview; + planWithAssistant?: SystemAgentAssistantPlanner; input?: NodeJS.ReadableStream; output?: NodeJS.WritableStream; - runInteractiveTui?: CrestodianInteractiveRunner; + runInteractiveTui?: SystemAgentInteractiveRunner; /** Exact live-tested route supplied by the inference gate. */ - readonly verifiedInference: CrestodianVerifiedInferenceBinding; + readonly verifiedInference: SystemAgentVerifiedInferenceBinding; }; /** User-supplied command options before the inference gate binds the run. */ -export type CrestodianCommandOptions = Omit; +export type SystemAgentCommandOptions = Omit; -function crestodianCommandDepsFromOptions( - opts: RunCrestodianOptions, -): CrestodianCommandDeps | undefined { +function systemAgentCommandDepsFromOptions( + opts: RunSystemAgentOptions, +): SystemAgentCommandDeps | undefined { if (!opts.deps && !opts.formatOverview && !opts.loadOverview) { return undefined; } @@ -71,31 +71,31 @@ function crestodianCommandDepsFromOptions( }; } -async function requireVerifiedInference(opts: RunCrestodianOptions): Promise { +async function requireVerifiedInference(opts: RunSystemAgentOptions): Promise { if (!opts.verifiedInference) { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } try { - const route = await resolveCrestodianVerifiedInferenceRoute(opts.verifiedInference, opts.deps); + const route = await resolveSystemAgentVerifiedInferenceRoute(opts.verifiedInference, opts.deps); if (route) { return; } } catch (error) { - throw new CrestodianInferenceUnavailableError("conversation", [error]); + throw new SystemAgentInferenceUnavailableError("conversation", [error]); } - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } async function requirePersistentApplyInference( - opts: RunCrestodianOptions, + opts: RunSystemAgentOptions, runtime: RuntimeEnv, ): Promise { if (!opts.verifiedInference) { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } try { - const { resolveCrestodianInferenceForPersistentApply } = await import("./setup-inference.js"); - const route = await resolveCrestodianInferenceForPersistentApply({ + const { resolveSystemAgentInferenceForPersistentApply } = await import("./setup-inference.js"); + const route = await resolveSystemAgentInferenceForPersistentApply({ binding: opts.verifiedInference, runtime, deps: opts.deps, @@ -104,18 +104,18 @@ async function requirePersistentApplyInference( return; } } catch (error) { - if (error instanceof CrestodianInferenceUnavailableError) { + if (error instanceof SystemAgentInferenceUnavailableError) { throw error; } - throw new CrestodianInferenceUnavailableError("conversation", [error]); + throw new SystemAgentInferenceUnavailableError("conversation", [error]); } - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } async function runOneShot( - operation: CrestodianOperation, + operation: SystemAgentOperation, runtime: RuntimeEnv, - opts: RunCrestodianOptions, + opts: RunSystemAgentOptions, ): Promise { if (operation.kind === "none" && operation.message === "") { return; @@ -123,36 +123,36 @@ async function runOneShot( // The planner may take long enough for the verified route to change. Never // apply its result under a different inference owner. await requireVerifiedInference(opts); - await executeCrestodianOperation(operation, runtime, { - approved: opts.yes === true || !isPersistentCrestodianOperation(operation), - deps: crestodianCommandDepsFromOptions(opts), + await executeSystemAgentOperation(operation, runtime, { + approved: opts.yes === true || !isPersistentSystemAgentOperation(operation), + deps: systemAgentCommandDepsFromOptions(opts), beforePersistentApply: async () => { await requirePersistentApplyInference(opts, runtime); }, }); } -/** Run Crestodian in JSON, one-shot message, or interactive TUI mode. */ -export async function runCrestodian( - opts: RunCrestodianOptions, +/** Run OpenClaw in JSON, one-shot message, or interactive TUI mode. */ +export async function runSystemAgent( + opts: RunSystemAgentOptions, runtime: RuntimeEnv = defaultRuntime, ): Promise { const binding = opts?.verifiedInference; if (!binding) { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } // Hold one immutable authority snapshot for the whole run. A caller that // mutates its input object cannot swap inference owners between planning and apply. - const boundOpts: RunCrestodianOptions = { ...opts, verifiedInference: binding }; + const boundOpts: RunSystemAgentOptions = { ...opts, verifiedInference: binding }; await requireVerifiedInference(boundOpts); if (boundOpts.json) { - const overview = await (boundOpts.loadOverview ?? loadCrestodianOverview)(); + const overview = await (boundOpts.loadOverview ?? loadSystemAgentOverview)(); writeRuntimeJson(runtime, overview); return; } if (boundOpts.message?.trim()) { - const parsed = parseCrestodianOperation(boundOpts.message); + const parsed = parseSystemAgentOperation(boundOpts.message); if (parsed.kind === "overview") { await runOneShot(parsed, runtime, boundOpts); return; @@ -161,16 +161,16 @@ export async function runCrestodian( // same snapshot for planning so reply-only plans do not print before it. const overview = await withProgress( { - label: "Loading Crestodian overview…", + label: "Loading OpenClaw overview…", indeterminate: true, delayMs: 0, fallback: "none", }, - async () => await (boundOpts.loadOverview ?? loadCrestodianOverview)(), + async () => await (boundOpts.loadOverview ?? loadSystemAgentOverview)(), ); - runtime.log((boundOpts.formatOverview ?? formatCrestodianOverview)(overview)); + runtime.log((boundOpts.formatOverview ?? formatSystemAgentOverview)(overview)); runtime.log(""); - const operation = await resolveCrestodianOperation(boundOpts.message, runtime, { + const operation = await resolveSystemAgentOperation(boundOpts.message, runtime, { ...boundOpts, loadOverview: async () => overview, }); @@ -179,8 +179,8 @@ export async function runCrestodian( } if (boundOpts.interactive === false) { - const overview = await (boundOpts.loadOverview ?? loadCrestodianOverview)(); - runtime.log((boundOpts.formatOverview ?? formatCrestodianOverview)(overview)); + const overview = await (boundOpts.loadOverview ?? loadSystemAgentOverview)(); + runtime.log((boundOpts.formatOverview ?? formatSystemAgentOverview)(overview)); return; } @@ -189,14 +189,14 @@ export async function runCrestodian( const inputIsTty = (input as { isTTY?: boolean }).isTTY === true; const outputIsTty = (output as { isTTY?: boolean }).isTTY === true; if (!inputIsTty || !outputIsTty) { - // Without a TTY, Crestodian cannot safely ask for confirmation; require --message instead. - runtime.error("Crestodian needs an interactive TTY. Use --message for one command."); + // Without a TTY, OpenClaw cannot safely ask for confirmation; require --message instead. + runtime.error("OpenClaw needs an interactive TTY. Use --message for one command."); runtime.exit(1); return; } const runInteractiveTui = - boundOpts.runInteractiveTui ?? (await import("./tui-backend.js")).runCrestodianTui; + boundOpts.runInteractiveTui ?? (await import("./tui-backend.js")).runSystemAgentTui; boundOpts.onReady?.(); await runInteractiveTui(boundOpts, runtime); } diff --git a/src/crestodian/tui-backend.test.ts b/src/system-agent/tui-backend.test.ts similarity index 86% rename from src/crestodian/tui-backend.test.ts rename to src/system-agent/tui-backend.test.ts index a7c9e51e1274..e43a69d4de1a 100644 --- a/src/crestodian/tui-backend.test.ts +++ b/src/system-agent/tui-backend.test.ts @@ -1,12 +1,12 @@ -// Crestodian TUI backend tests cover rescue status integration with the TUI backend. +// OpenClaw TUI backend tests cover rescue status integration with the TUI backend. import { describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { RuntimeEnv } from "../runtime.js"; -import { createCrestodianVerifiedInferenceTestFixture } from "./crestodian.test-helpers.js"; -import { CrestodianInferenceUnavailableError } from "./inference-error.js"; -import type { CrestodianCommandDeps, CrestodianOperation } from "./operations.js"; -import type { CrestodianOverview } from "./overview.js"; -import { runCrestodianTui, type CrestodianTuiOptions } from "./tui-backend.js"; +import { SystemAgentInferenceUnavailableError } from "./inference-error.js"; +import type { SystemAgentCommandDeps, SystemAgentOperation } from "./operations.js"; +import type { SystemAgentOverview } from "./overview.js"; +import { createSystemAgentVerifiedInferenceTestFixture } from "./system-agent.test-helpers.js"; +import { runSystemAgentTui, type SystemAgentTuiOptions } from "./tui-backend.js"; vi.mock("../plugins/providers.js", async (importOriginal) => ({ ...(await importOriginal()), @@ -14,7 +14,7 @@ vi.mock("../plugins/providers.js", async (importOriginal) => ({ resolveOwningPluginIdsForProviderRef: vi.fn(() => []), })); -const overview: CrestodianOverview = { +const overview: SystemAgentOverview = { defaultAgentId: "main", defaultModel: "openai/gpt-5.5", agents: [{ id: "main", isDefault: true, model: "openai/gpt-5.5" }], @@ -64,8 +64,8 @@ function configSnapshot(config: OpenClawConfig) { }; } -async function createVerifiedTuiOptions(deps: CrestodianCommandDeps = {}) { - const fixture = await createCrestodianVerifiedInferenceTestFixture(verifiedConfig); +async function createVerifiedTuiOptions(deps: SystemAgentCommandDeps = {}) { + const fixture = await createSystemAgentVerifiedInferenceTestFixture(verifiedConfig); return { verifiedInference: fixture.binding, deps: { @@ -86,14 +86,14 @@ function createRuntime(): RuntimeEnv { }; } -describe("runCrestodianTui", () => { +describe("runSystemAgentTui", () => { it("rejects a missing inference binding before overview, planner, TUI, or setup", async () => { const loadOverview = vi.fn(async () => overview); const planWithAssistant = vi.fn(async () => ({ reply: "ready" })); const runTui = vi.fn(async () => ({ exitReason: "exit" as const })); const runChannelsAdd = vi.fn(async () => undefined); - const fixture = await createCrestodianVerifiedInferenceTestFixture(verifiedConfig); - const options: CrestodianTuiOptions = { + const fixture = await createSystemAgentVerifiedInferenceTestFixture(verifiedConfig); + const options: SystemAgentTuiOptions = { verifiedInference: fixture.binding, deps: { loadOverview }, planWithAssistant, @@ -102,8 +102,8 @@ describe("runCrestodianTui", () => { }; Reflect.deleteProperty(options, "verifiedInference"); - await expect(runCrestodianTui(options, createRuntime())).rejects.toBeInstanceOf( - CrestodianInferenceUnavailableError, + await expect(runSystemAgentTui(options, createRuntime())).rejects.toBeInstanceOf( + SystemAgentInferenceUnavailableError, ); expect(loadOverview).not.toHaveBeenCalled(); @@ -112,12 +112,12 @@ describe("runCrestodianTui", () => { expect(runChannelsAdd).not.toHaveBeenCalled(); }); - it("runs Crestodian inside the shared TUI shell", async () => { + it("runs OpenClaw inside the shared TUI shell", async () => { let runTuiCalls = 0; let runTuiOptions: unknown; const verified = await createVerifiedTuiOptions({ loadOverview: async () => overview }); - await runCrestodianTui( + await runSystemAgentTui( { ...verified, runTui: async (opts) => { @@ -139,12 +139,12 @@ describe("runCrestodianTui", () => { backend?: unknown; }; expect(options.local).toBe(true); - expect(options.session).toBe("agent:crestodian:main"); + expect(options.session).toBe("agent:openclaw:main"); expect(options.historyLimit).toBe(200); expect(options.config).toEqual({}); - expect(options.title).toBe("openclaw crestodian"); + expect(options.title).toBe("openclaw setup"); if (!options.backend || typeof options.backend !== "object") { - throw new Error("expected crestodian TUI backend"); + throw new Error("expected openclaw TUI backend"); } }); @@ -164,7 +164,7 @@ describe("runCrestodianTui", () => { }; dispose: () => Promise; }>((resolve) => { - void runCrestodianTui( + void runSystemAgentTui( { ...verified, runTui: async (opts) => { @@ -215,7 +215,7 @@ describe("runCrestodianTui", () => { const events: Array<{ payload?: { state?: string; errorMessage?: string } }> = []; const verified = await createVerifiedTuiOptions({ loadOverview: async () => overview }); - await runCrestodianTui( + await runSystemAgentTui( { ...verified, runTui: async (opts) => { @@ -225,7 +225,7 @@ describe("runCrestodianTui", () => { engine: { handle: () => Promise }; }; backend.engine.handle = async () => { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); }; backend.onEvent = (event) => events.push(event); @@ -252,13 +252,13 @@ describe("runCrestodianTui", () => { it("retires the local session before a queued exact mutation can run", async () => { const handle = vi .fn() - .mockRejectedValueOnce(new CrestodianInferenceUnavailableError("conversation")) + .mockRejectedValueOnce(new SystemAgentInferenceUnavailableError("conversation")) .mockResolvedValue({ text: "mutation ran", action: "none" }); const dispose = vi.fn(async () => undefined); const events: Array<{ payload?: { state?: string; errorMessage?: string } }> = []; const verified = await createVerifiedTuiOptions({ loadOverview: async () => overview }); - await runCrestodianTui( + await runSystemAgentTui( { ...verified, runTui: async (opts) => { @@ -308,7 +308,7 @@ describe("runCrestodianTui", () => { it("launches setup handoffs after the chat TUI is disposed", async () => { const cases: Array<{ - handoff: Extract; + handoff: Extract; expected: string; }> = [ { @@ -320,7 +320,7 @@ describe("runCrestodianTui", () => { for (const { handoff, expected } of cases) { const events: string[] = []; const verified = await createVerifiedTuiOptions({ loadOverview: async () => overview }); - await runCrestodianTui( + await runSystemAgentTui( { ...verified, setupWorkspace: "/tmp/custom-workspace", @@ -332,7 +332,7 @@ describe("runCrestodianTui", () => { handle: () => Promise<{ text: string; action: "open-setup"; - handoff: CrestodianOperation; + handoff: SystemAgentOperation; }>; dispose: () => Promise; }; diff --git a/src/crestodian/tui-backend.ts b/src/system-agent/tui-backend.ts similarity index 71% rename from src/crestodian/tui-backend.ts rename to src/system-agent/tui-backend.ts index 594cf7859d0c..a2c63e60661c 100644 --- a/src/crestodian/tui-backend.ts +++ b/src/system-agent/tui-backend.ts @@ -1,4 +1,4 @@ -// Crestodian TUI backend runs setup-helper dialogue inside the shared local TUI shell. +// OpenClaw TUI backend runs setup-helper dialogue inside the shared local TUI shell. import { randomUUID } from "node:crypto"; import type { SessionsPatchParams, @@ -18,56 +18,56 @@ import type { TuiSessionCreateOptions, } from "../tui/tui-backend.js"; import { runTui as defaultRunTui } from "../tui/tui.js"; -import type { CrestodianAssistantPlanner } from "./assistant.js"; -import { CrestodianChatEngine, type CrestodianChatEngineOptions } from "./chat-engine.js"; +import { SYSTEM_AGENT_ID } from "./agent-id.js"; +import type { SystemAgentAssistantPlanner } from "./assistant.js"; +import { SystemAgentChatEngine, type SystemAgentChatEngineOptions } from "./chat-engine.js"; import { - CrestodianInferenceUnavailableError, - isCrestodianInferenceUnavailableError, + SystemAgentInferenceUnavailableError, + isSystemAgentInferenceUnavailableError, } from "./inference-error.js"; import { buildOnboardingWelcome } from "./onboarding-welcome.js"; import { - executeCrestodianOperation, - type CrestodianCommandDeps, - type CrestodianOperation, + executeSystemAgentOperation, + type SystemAgentCommandDeps, + type SystemAgentOperation, } from "./operations.js"; -import { formatCrestodianStartupMessage, loadCrestodianOverview } from "./overview.js"; +import { formatSystemAgentStartupMessage, loadSystemAgentOverview } from "./overview.js"; import { - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceBinding, + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceBinding, } from "./verified-inference.js"; type RunTui = typeof defaultRunTui; -export type CrestodianTuiOptions = { +export type SystemAgentTuiOptions = { yes?: boolean; - deps?: CrestodianCommandDeps; - planWithAssistant?: CrestodianAssistantPlanner; + deps?: SystemAgentCommandDeps; + planWithAssistant?: SystemAgentAssistantPlanner; runTui?: RunTui; /** "onboarding" swaps the greeting for the first-run setup proposal. */ welcomeVariant?: "onboarding"; /** Workspace override for the proposed first-run setup (from --workspace). */ setupWorkspace?: string; /** Test seam for the channel-setup wizard hosted by the chat bridge. */ - runChannelSetupWizard?: CrestodianChatEngineOptions["runChannelSetupWizard"]; + runChannelSetupWizard?: SystemAgentChatEngineOptions["runChannelSetupWizard"]; runChannelsAdd?: ( opts: ChannelsAddOptions, runtime: RuntimeEnv, params?: { hasFlags?: boolean; beforePersistentEffect?: () => Promise }, ) => Promise; - readonly verifiedInference: CrestodianVerifiedInferenceBinding; + readonly verifiedInference: SystemAgentVerifiedInferenceBinding; }; -type CrestodianHistoryMessage = { +type SystemAgentHistoryMessage = { role: "assistant" | "user"; content: Array<{ type: "text"; text: string }>; timestamp: number; }; -const CRESTODIAN_AGENT_ID = "crestodian"; -const CRESTODIAN_SESSION_KEY = buildAgentMainSessionKey({ agentId: CRESTODIAN_AGENT_ID }); +const SYSTEM_AGENT_SESSION_KEY = buildAgentMainSessionKey({ agentId: SYSTEM_AGENT_ID }); -function createChatEngine(opts: CrestodianTuiOptions): CrestodianChatEngine { - return new CrestodianChatEngine({ +function createChatEngine(opts: SystemAgentTuiOptions): SystemAgentChatEngine { + return new SystemAgentChatEngine({ yes: opts.yes, deps: opts.deps, planWithAssistant: opts.planWithAssistant, @@ -77,14 +77,14 @@ function createChatEngine(opts: CrestodianTuiOptions): CrestodianChatEngine { }); } -async function loadOverviewForTui(opts: CrestodianTuiOptions) { +async function loadOverviewForTui(opts: SystemAgentTuiOptions) { if (opts.deps?.loadOverview) { return await opts.deps.loadOverview(); } - return await loadCrestodianOverview(); + return await loadSystemAgentOverview(); } -function message(role: "assistant" | "user", text: string): CrestodianHistoryMessage { +function message(role: "assistant" | "user", text: string): SystemAgentHistoryMessage { return { role, content: [{ type: "text", text }], @@ -107,8 +107,8 @@ function splitModelRef(ref: string | undefined): { provider?: string; model?: st }; } -class CrestodianTuiBackend implements TuiBackend { - readonly connection = { url: "crestodian local" }; +class SystemAgentTuiBackend implements TuiBackend { + readonly connection = { url: "openclaw local" }; onEvent?: (evt: TuiEvent) => void; onConnected?: () => void; @@ -116,18 +116,18 @@ class CrestodianTuiBackend implements TuiBackend { onGap?: (info: { expected: number; received: number }) => void; private seq = 0; - private engine: CrestodianChatEngine; + private engine: SystemAgentChatEngine; private engineDisposal: Promise | null = null; - private inferenceFailure: CrestodianInferenceUnavailableError | null = null; - private handoff: CrestodianOperation | null = null; + private inferenceFailure: SystemAgentInferenceUnavailableError | null = null; + private handoff: SystemAgentOperation | null = null; private requestExit: (() => void) | null = null; private responseQueue: Promise = Promise.resolve(); - private readonly messages: CrestodianHistoryMessage[] = []; + private readonly messages: SystemAgentHistoryMessage[] = []; constructor( - private readonly opts: CrestodianTuiOptions, + private readonly opts: SystemAgentTuiOptions, welcome: string, - engine: CrestodianChatEngine, + engine: SystemAgentChatEngine, ) { this.engine = engine; this.messages.push(message("assistant", welcome)); @@ -140,7 +140,7 @@ class CrestodianTuiBackend implements TuiBackend { } } - consumeHandoff(): CrestodianOperation | null { + consumeHandoff(): SystemAgentOperation | null { const handoff = this.handoff; this.handoff = null; return handoff; @@ -153,7 +153,7 @@ class CrestodianTuiBackend implements TuiBackend { } stop(): void { - // The enclosing TUI owns terminal shutdown; Crestodian has no transport to close. + // The enclosing TUI owns terminal shutdown; OpenClaw has no transport to close. } async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> { @@ -173,12 +173,12 @@ class CrestodianTuiBackend implements TuiBackend { async loadHistory(): Promise<{ sessionId: string; - messages: CrestodianHistoryMessage[]; + messages: SystemAgentHistoryMessage[]; thinkingLevel: string; verboseLevel: string; }> { return { - sessionId: "crestodian", + sessionId: "openclaw", messages: this.messages, thinkingLevel: "off", verboseLevel: "off", @@ -190,7 +190,7 @@ class CrestodianTuiBackend implements TuiBackend { const model = splitModelRef(overview.defaultModel); return { ts: Date.now(), - path: "crestodian", + path: "openclaw", count: 1, defaults: { model: model.model ?? null, @@ -199,9 +199,9 @@ class CrestodianTuiBackend implements TuiBackend { }, sessions: [ { - key: CRESTODIAN_SESSION_KEY, - sessionId: "crestodian", - displayName: "Crestodian", + key: SYSTEM_AGENT_SESSION_KEY, + sessionId: "openclaw", + displayName: "OpenClaw", updatedAt: Date.now(), thinkingLevel: "off", verboseLevel: "off", @@ -214,10 +214,10 @@ class CrestodianTuiBackend implements TuiBackend { async listAgents(): Promise { return { - defaultId: CRESTODIAN_AGENT_ID, + defaultId: SYSTEM_AGENT_ID, mainKey: "main", scope: "per-sender", - agents: [{ id: CRESTODIAN_AGENT_ID, name: "Crestodian" }], + agents: [{ id: SYSTEM_AGENT_ID, name: "OpenClaw" }], }; } @@ -225,11 +225,11 @@ class CrestodianTuiBackend implements TuiBackend { const model = splitModelRef(typeof opts.model === "string" ? opts.model : undefined); return { ok: true, - path: "crestodian", - key: CRESTODIAN_SESSION_KEY, + path: "openclaw", + key: SYSTEM_AGENT_SESSION_KEY, entry: { - sessionId: "crestodian", - displayName: "Crestodian", + sessionId: "openclaw", + displayName: "OpenClaw", updatedAt: Date.now(), ...(model.model ? { model: model.model } : {}), ...(model.provider ? { modelProvider: model.provider } : {}), @@ -253,7 +253,7 @@ class CrestodianTuiBackend implements TuiBackend { this.messages.splice( 0, this.messages.length, - message("assistant", formatCrestodianStartupMessage(overview)), + message("assistant", formatSystemAgentStartupMessage(overview)), ); return { ok: true }; } @@ -262,8 +262,8 @@ class CrestodianTuiBackend implements TuiBackend { await this.resetSession(); return { ok: true as const, - key: CRESTODIAN_SESSION_KEY, - entry: { sessionId: "crestodian", updatedAt: Date.now() }, + key: SYSTEM_AGENT_SESSION_KEY, + entry: { sessionId: "openclaw", updatedAt: Date.now() }, }; } @@ -313,7 +313,7 @@ class CrestodianTuiBackend implements TuiBackend { private emitFinal(runId: string, sessionKey: string, text: string): void { const assistant = message( "assistant", - text || "Crestodian listened and found nothing to change.", + text || "OpenClaw listened and found nothing to change.", ); this.messages.push(assistant); this.emit("chat", { @@ -343,7 +343,7 @@ class CrestodianTuiBackend implements TuiBackend { try { const reply = await this.engine.handle(text); if ((reply.action === "open-tui" || reply.action === "open-setup") && reply.handoff) { - // The outer loop owns interactive handoffs after the Crestodian TUI exits. + // The outer loop owns interactive handoffs after the OpenClaw TUI exits. this.handoff = reply.handoff; queueMicrotask(() => this.requestExit?.()); } else if (reply.action === "exit") { @@ -351,7 +351,7 @@ class CrestodianTuiBackend implements TuiBackend { } this.emitFinal(runId, sessionKey, reply.text); } catch (error) { - if (isCrestodianInferenceUnavailableError(error)) { + if (isSystemAgentInferenceUnavailableError(error)) { // Match the Gateway session boundary: the failed conversation is dead. // Clear handoffs and dispose before exit so no queued exact command can // bypass the inference-first gate through this backend instance. @@ -372,13 +372,13 @@ class CrestodianTuiBackend implements TuiBackend { } async function runSetupHandoff( - handoff: Extract, - opts: CrestodianTuiOptions, + handoff: Extract, + opts: SystemAgentTuiOptions, runtime: RuntimeEnv, ): Promise { if (handoff.target !== "channels") { runtime.error( - "Setup cannot replace the inference route powering Crestodian. Exit and run `openclaw onboard`, then start Crestodian again.", + "Setup cannot replace the inference route powering OpenClaw. Exit and run `openclaw onboard`, then start OpenClaw again.", ); return; } @@ -387,11 +387,12 @@ async function runSetupHandoff( const beforePersistentEffect = async () => { const binding = opts?.verifiedInference; if (!binding) { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } try { - const { resolveCrestodianInferenceForPersistentApply } = await import("./setup-inference.js"); - const route = await resolveCrestodianInferenceForPersistentApply({ + const { resolveSystemAgentInferenceForPersistentApply } = + await import("./setup-inference.js"); + const route = await resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: opts.deps, @@ -400,12 +401,12 @@ async function runSetupHandoff( return; } } catch (error) { - if (isCrestodianInferenceUnavailableError(error)) { + if (isSystemAgentInferenceUnavailableError(error)) { throw error; } - throw new CrestodianInferenceUnavailableError("conversation", [error]); + throw new SystemAgentInferenceUnavailableError("conversation", [error]); } - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); }; await runChannelsAdd(handoff.channel ? { channel: handoff.channel } : {}, runtime, { hasFlags: false, @@ -413,23 +414,23 @@ async function runSetupHandoff( }); } -export async function runCrestodianTui( - opts: CrestodianTuiOptions, +export async function runSystemAgentTui( + opts: SystemAgentTuiOptions, runtime: RuntimeEnv, ): Promise { const binding = opts?.verifiedInference; if (!binding) { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } // Snapshot the verified owner so an external options mutation cannot swap // authority between the chat shell and a later host-owned wizard handoff. - const boundOpts: CrestodianTuiOptions = { ...opts, verifiedInference: binding }; + const boundOpts: SystemAgentTuiOptions = { ...opts, verifiedInference: binding }; let nextInput: string | undefined; let welcomeVariant = boundOpts.welcomeVariant; for (;;) { await requireTuiVerifiedInference(boundOpts); // A returned agent request is single-use; a later wizard handoff must not - // replay it when Crestodian re-enters the chat shell. + // replay it when OpenClaw re-enters the chat shell. const initialMessage = nextInput; const engine = createChatEngine(boundOpts); let welcome: string; @@ -439,22 +440,22 @@ export async function runCrestodianTui( ...(boundOpts.setupWorkspace ? { workspace: boundOpts.setupWorkspace } : {}), }); } else { - welcome = formatCrestodianStartupMessage(await loadOverviewForTui(boundOpts)); + welcome = formatSystemAgentStartupMessage(await loadOverviewForTui(boundOpts)); engine.noteAssistantMessage(welcome); } // The onboarding greeting applies to the first shell only; re-entry after // an agent handoff uses the normal repair-oriented startup message. welcomeVariant = undefined; - const backend = new CrestodianTuiBackend(boundOpts, welcome, engine); + const backend = new SystemAgentTuiBackend(boundOpts, welcome, engine); const runTui = boundOpts.runTui ?? defaultRunTui; try { await runTui({ local: true, - session: CRESTODIAN_SESSION_KEY, + session: SYSTEM_AGENT_SESSION_KEY, historyLimit: 200, backend, config: {}, - title: "openclaw crestodian", + title: "openclaw setup", ...(initialMessage ? { message: initialMessage } : {}), }); } finally { @@ -467,7 +468,7 @@ export async function runCrestodianTui( } if (handoff.kind === "model-setup") { runtime.error( - "Crestodian cannot replace its active inference route. Run `openclaw onboard` outside this session, then start Crestodian again.", + "OpenClaw cannot replace its active inference route. Run `openclaw onboard` outside this session, then start OpenClaw again.", ); return; } @@ -475,7 +476,7 @@ export async function runCrestodianTui( await runSetupHandoff(handoff, boundOpts, runtime); return; } - const result = await executeCrestodianOperation(handoff, runtime, { + const result = await executeSystemAgentOperation(handoff, runtime, { approved: true, deps: boundOpts.deps, }); @@ -486,18 +487,18 @@ export async function runCrestodianTui( } } -async function requireTuiVerifiedInference(opts: CrestodianTuiOptions): Promise { +async function requireTuiVerifiedInference(opts: SystemAgentTuiOptions): Promise { const binding = opts?.verifiedInference; if (!binding) { - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } try { - const route = await resolveCrestodianVerifiedInferenceRoute(binding, opts.deps); + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, opts.deps); if (route) { return; } } catch (error) { - throw new CrestodianInferenceUnavailableError("conversation", [error]); + throw new SystemAgentInferenceUnavailableError("conversation", [error]); } - throw new CrestodianInferenceUnavailableError("conversation"); + throw new SystemAgentInferenceUnavailableError("conversation"); } diff --git a/src/crestodian/verified-inference.test.ts b/src/system-agent/verified-inference.test.ts similarity index 90% rename from src/crestodian/verified-inference.test.ts rename to src/system-agent/verified-inference.test.ts index 24976f8a933b..6dcf6d0b8429 100644 --- a/src/crestodian/verified-inference.test.ts +++ b/src/system-agent/verified-inference.test.ts @@ -11,12 +11,12 @@ import { } from "../agents/execution-auth-binding.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { PluginOrigin } from "../plugins/types.js"; -import { resolveCrestodianConfiguredRouteFromConfig } from "./inference-route.js"; -import { resolveCrestodianInferenceForPersistentApply } from "./setup-inference.js"; +import { resolveSystemAgentConfiguredRouteFromConfig } from "./inference-route.js"; +import { resolveSystemAgentInferenceForPersistentApply } from "./setup-inference.js"; import { - createCrestodianVerifiedInferenceBinding, - resolveCrestodianVerifiedInferenceRoute, - type CrestodianVerifiedInferenceDeps, + createSystemAgentVerifiedInferenceBinding, + resolveSystemAgentVerifiedInferenceRoute, + type SystemAgentVerifiedInferenceDeps, } from "./verified-inference.js"; const pluginRegistryState = vi.hoisted(() => ({ @@ -134,7 +134,7 @@ function authDeps(apiKey = "verified-key") { resolveAgentHarnessAuthBindingFingerprint: vi.fn( async ( params: Parameters< - NonNullable + NonNullable >[0], ) => { const credential = params.authProfileStore.profiles[params.authProfileId]; @@ -191,9 +191,9 @@ function config(model = "openai/gpt-5.5@openai:verified"): OpenClawConfig { async function bindingFor( baseConfig: OpenClawConfig, - deps: CrestodianVerifiedInferenceDeps = { ...authDeps(), ...pluginArtifactDeps() }, + deps: SystemAgentVerifiedInferenceDeps = { ...authDeps(), ...pluginArtifactDeps() }, ) { - const route = await resolveCrestodianConfiguredRouteFromConfig(baseConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(baseConfig); if (!route) { throw new Error("missing test route"); } @@ -210,7 +210,7 @@ async function bindingFor( ? "openclaw" : route.agentHarnessRuntimeOverride : undefined; - return await createCrestodianVerifiedInferenceBinding({ + return await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -233,13 +233,13 @@ async function bindingFor( }); } -describe("verified Crestodian inference binding", () => { +describe("verified OpenClaw inference binding", () => { it("invalidates an identity-less OAuth binding when its grant changes", async () => { const oauthConfig = { agents: { defaults: { model: "anthropic/claude-opus-4-8@anthropic:oauth" } }, auth: { profiles: { "anthropic:oauth": { provider: "anthropic", mode: "oauth" } } }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(oauthConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(oauthConfig); if (!route) { throw new Error("missing test OAuth route"); } @@ -257,7 +257,7 @@ describe("verified Crestodian inference binding", () => { if (!authFingerprint) { throw new Error("missing test OAuth fingerprint"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -274,7 +274,7 @@ describe("verified Crestodian inference binding", () => { }, }); - const current = await resolveCrestodianVerifiedInferenceRoute(binding, { + const current = await resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -296,13 +296,13 @@ describe("verified Crestodian inference binding", () => { }); it("rejects a binding when no credential fingerprint can be observed", async () => { - const route = await resolveCrestodianConfiguredRouteFromConfig(config()); + const route = await resolveSystemAgentConfiguredRouteFromConfig(config()); if (!route) { throw new Error("missing test route"); } await expect( - createCrestodianVerifiedInferenceBinding({ + createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -337,12 +337,12 @@ describe("verified Crestodian inference binding", () => { const cliConfig = { agents: { defaults: { model: "claude-cli/claude-opus-4-8" } }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(cliConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(cliConfig); if (!route || route.runner !== "cli") { throw new Error("missing test CLI route"); } const resolveOwner = vi.fn(async () => "opaque-cli-owner"); - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -363,7 +363,7 @@ describe("verified Crestodian inference binding", () => { proofKind: "runtime-owner", }); await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -376,7 +376,7 @@ describe("verified Crestodian inference binding", () => { resolveOwner.mockResolvedValue("replacement-owner"); await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -405,11 +405,11 @@ describe("verified Crestodian inference binding", () => { }, }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(cliConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(cliConfig); if (!route || route.runner !== "cli") { throw new Error("missing test CLI route"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -426,7 +426,7 @@ describe("verified Crestodian inference binding", () => { }); await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -441,13 +441,13 @@ describe("verified Crestodian inference binding", () => { const cliConfig = { agents: { defaults: { model: "claude-cli/claude-opus-4-8" } }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(cliConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(cliConfig); if (!route || route.runner !== "cli") { throw new Error("missing test CLI route"); } const resolveAuth = vi.fn(() => "strict-cli-credential"); const resolveArtifact = vi.fn(async () => "claude-cli-artifact-v1"); - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -463,7 +463,7 @@ describe("verified Crestodian inference binding", () => { resolveArtifact.mockResolvedValue("claude-cli-artifact-v2"); await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -489,7 +489,7 @@ describe("verified Crestodian inference binding", () => { }, auth: { profiles: { [profileId]: { provider: "claude-cli", mode: "api_key" } } }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(cliConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(cliConfig); if (!route || route.runner !== "cli" || route.authProfileId !== profileId) { throw new Error("missing test CLI SecretRef route"); } @@ -513,7 +513,7 @@ describe("verified Crestodian inference binding", () => { (params: { resolvedAuth?: { apiKey?: string } }) => params.resolvedAuth?.apiKey && `strict:${params.resolvedAuth.apiKey}`, ); - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -540,7 +540,7 @@ describe("verified Crestodian inference binding", () => { ); activeKey = "materialized-b"; await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -575,7 +575,7 @@ describe("verified Crestodian inference binding", () => { ], }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!route || route.runner !== "embedded" || route.agentHarnessRuntimeOverride !== "codex") { throw new Error("missing test plugin harness route"); } @@ -589,7 +589,7 @@ describe("verified Crestodian inference binding", () => { if (!runtimeOwnerFingerprint) { throw new Error("missing test harness owner"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -605,7 +605,7 @@ describe("verified Crestodian inference binding", () => { expect(binding.ownerPluginIds).toEqual(["codex", "provider-owner"]); await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -628,7 +628,7 @@ describe("verified Crestodian inference binding", () => { ], }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!route || route.runner !== "embedded") { throw new Error("missing test plugin harness route"); } @@ -642,7 +642,7 @@ describe("verified Crestodian inference binding", () => { if (!runtimeOwnerFingerprint) { throw new Error("missing test harness owner"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -657,7 +657,7 @@ describe("verified Crestodian inference binding", () => { harnessRuntimeArtifactState.fingerprint = "codex-runtime-v2"; await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -683,7 +683,7 @@ describe("verified Crestodian inference binding", () => { profiles: { "openai:verified": { provider: "openai", mode: "api_key" } }, }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!route || route.runner !== "embedded") { throw new Error("missing test plugin harness route"); } @@ -696,7 +696,7 @@ describe("verified Crestodian inference binding", () => { } await expect( - createCrestodianVerifiedInferenceBinding({ + createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -708,7 +708,7 @@ describe("verified Crestodian inference binding", () => { ).rejects.toThrow("did not report its exact runtime artifact"); await expect( - createCrestodianVerifiedInferenceBinding({ + createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -723,7 +723,7 @@ describe("verified Crestodian inference binding", () => { ).rejects.toThrow("did not report its exact runtime artifact"); await expect( - createCrestodianVerifiedInferenceBinding({ + createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -755,7 +755,7 @@ describe("verified Crestodian inference binding", () => { ], }, } satisfies OpenClawConfig; - const resolved = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const resolved = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!resolved || resolved.runner !== "embedded") { throw new Error("missing test plugin harness route"); } @@ -774,7 +774,7 @@ describe("verified Crestodian inference binding", () => { throw new Error("missing test harness owner"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute, executionRoute: configuredRoute, auth: { @@ -804,7 +804,7 @@ describe("verified Crestodian inference binding", () => { profiles: { "openai:verified": { provider: "openai", mode: "api_key" } }, }, } satisfies OpenClawConfig; - const resolved = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const resolved = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!resolved || resolved.runner !== "embedded") { throw new Error("missing test embedded route"); } @@ -823,7 +823,7 @@ describe("verified Crestodian inference binding", () => { } await expect( - createCrestodianVerifiedInferenceBinding({ + createSystemAgentVerifiedInferenceBinding({ configuredRoute, executionRoute: configuredRoute, auth: { authProfileId: "openai:verified", authFingerprint }, @@ -831,7 +831,7 @@ describe("verified Crestodian inference binding", () => { }), ).rejects.toThrow("did not report its exact agent harness"); - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute, executionRoute: configuredRoute, auth: { @@ -859,7 +859,7 @@ describe("verified Crestodian inference binding", () => { ], }, } satisfies OpenClawConfig; - const resolved = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const resolved = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!resolved || resolved.runner !== "embedded") { throw new Error("missing test plugin harness route"); } @@ -879,7 +879,7 @@ describe("verified Crestodian inference binding", () => { } await expect( - createCrestodianVerifiedInferenceBinding({ + createSystemAgentVerifiedInferenceBinding({ configuredRoute, executionRoute: configuredRoute, auth: { @@ -910,7 +910,7 @@ describe("verified Crestodian inference binding", () => { }, plugins: { entries: { codex: { config: { appServer: { command: "codex" } } } } }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!route || route.runner !== "embedded" || route.agentHarnessRuntimeOverride !== "codex") { throw new Error("missing test plugin harness route"); } @@ -924,7 +924,7 @@ describe("verified Crestodian inference binding", () => { if (!runtimeOwnerFingerprint) { throw new Error("missing test harness owner"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -940,7 +940,7 @@ describe("verified Crestodian inference binding", () => { changed.plugins!.entries!.codex!.config = { appServer: { command: "/opt/other/codex" } }; await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -965,7 +965,7 @@ describe("verified Crestodian inference binding", () => { }, auth: { profiles: { "openai:verified": { provider: "openai", mode: "api_key" } } }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!route || route.runner !== "embedded") { throw new Error("missing test plugin harness route"); } @@ -996,7 +996,7 @@ describe("verified Crestodian inference binding", () => { })) as never, resolveApiKeyForProvider: resolveAuth, }; - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -1011,7 +1011,7 @@ describe("verified Crestodian inference binding", () => { }); await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { ...deps, readConfigFileSnapshot: vi.fn(async () => ({ exists: true, @@ -1043,7 +1043,7 @@ describe("verified Crestodian inference binding", () => { }, auth: { profiles: { "openai:work": { provider: "openai", mode: "api_key" } } }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(harnessConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(harnessConfig); if (!route || route.runner !== "embedded" || route.authProfileId !== "openai:work") { throw new Error("missing test plugin harness profile route"); } @@ -1078,7 +1078,7 @@ describe("verified Crestodian inference binding", () => { if (!authFingerprint) { throw new Error("missing test profile owner"); } - const binding = await createCrestodianVerifiedInferenceBinding({ + const binding = await createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: { @@ -1100,7 +1100,7 @@ describe("verified Crestodian inference binding", () => { }); await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -1116,7 +1116,7 @@ describe("verified Crestodian inference binding", () => { activeKey = "replacement-key"; await expect( - resolveCrestodianVerifiedInferenceRoute(binding, { + resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -1150,7 +1150,7 @@ describe("verified Crestodian inference binding", () => { }, }, } satisfies OpenClawConfig; - const route = await resolveCrestodianConfiguredRouteFromConfig(bedrockConfig); + const route = await resolveSystemAgentConfiguredRouteFromConfig(bedrockConfig); if (!route || route.runner !== "embedded") { throw new Error("missing test AWS route"); } @@ -1179,7 +1179,7 @@ describe("verified Crestodian inference binding", () => { ).toBeUndefined(); await expect( - createCrestodianVerifiedInferenceBinding({ + createSystemAgentVerifiedInferenceBinding({ configuredRoute: route, executionRoute: route, auth: {}, @@ -1194,7 +1194,7 @@ describe("verified Crestodian inference binding", () => { const binding = await bindingFor(config()); const changed = config("anthropic/claude-opus-4-8"); - const route = await resolveCrestodianVerifiedInferenceRoute(binding, { + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -1222,7 +1222,7 @@ describe("verified Crestodian inference binding", () => { pluginRegistryState.providerOwnerIds = ownerIds; pluginRegistryState.records = records; - const route = await resolveCrestodianVerifiedInferenceRoute(binding, { + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -1239,7 +1239,7 @@ describe("verified Crestodian inference binding", () => { const binding = await bindingFor(baseConfig); pluginRegistryState.records = []; - const route = await resolveCrestodianVerifiedInferenceRoute(binding, { + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -1273,7 +1273,7 @@ describe("verified Crestodian inference binding", () => { const binding = await bindingFor(baseConfig); pluginRegistryState.records = [pluginRecord("provider-owner", replacement)]; - const route = await resolveCrestodianVerifiedInferenceRoute(binding, { + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -1303,7 +1303,7 @@ describe("verified Crestodian inference binding", () => { ])( "invalidates a strict credential after an in-place $name change with stable registry identity", async ({ origin, sourcePath, runtimePath, installRecordHash }) => { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-crestodian-plugin-")); + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-openclaw-plugin-")); try { const rootDir = path.join(tempDir, "provider-owner"); const source = path.join(rootDir, sourcePath); @@ -1348,7 +1348,7 @@ describe("verified Crestodian inference binding", () => { const binding = await bindingFor(baseConfig, deps); await expect( - resolveCrestodianInferenceForPersistentApply({ + resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: { @@ -1365,7 +1365,7 @@ describe("verified Crestodian inference binding", () => { fs.writeFileSync(runtimeSource, "export const runtimeRevision = 2;\n", "utf8"); await expect( - resolveCrestodianInferenceForPersistentApply({ + resolveSystemAgentInferenceForPersistentApply({ binding, runtime, deps: { @@ -1393,7 +1393,7 @@ describe("verified Crestodian inference binding", () => { plugins: { entries: { discord: { enabled: true } } }, } satisfies OpenClawConfig; - const route = await resolveCrestodianVerifiedInferenceRoute(binding, { + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -1410,7 +1410,7 @@ describe("verified Crestodian inference binding", () => { it("fails closed when the selected credential content changes", async () => { const binding = await bindingFor(config()); - const route = await resolveCrestodianVerifiedInferenceRoute(binding, { + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, @@ -1463,7 +1463,7 @@ describe("verified Crestodian inference binding", () => { const binding = await bindingFor(baseConfig); const changed = { ...config(), plugins } satisfies OpenClawConfig; - const route = await resolveCrestodianVerifiedInferenceRoute(binding, { + const route = await resolveSystemAgentVerifiedInferenceRoute(binding, { readConfigFileSnapshot: vi.fn(async () => ({ exists: true, valid: true, diff --git a/src/crestodian/verified-inference.ts b/src/system-agent/verified-inference.ts similarity index 89% rename from src/crestodian/verified-inference.ts rename to src/system-agent/verified-inference.ts index afe7e93677e0..77b17a0fe441 100644 --- a/src/crestodian/verified-inference.ts +++ b/src/system-agent/verified-inference.ts @@ -39,19 +39,19 @@ import { } from "../plugins/providers.js"; import { projectDefaultInferenceRoute, - resolveCrestodianConfiguredRouteFromConfig, - type CrestodianConfiguredRoute, - type CrestodianConfiguredRouteDeps, + resolveSystemAgentConfiguredRouteFromConfig, + type SystemAgentConfiguredRoute, + type SystemAgentConfiguredRouteDeps, } from "./inference-route.js"; type DistributiveOmit = T extends unknown ? Omit : never; -type CrestodianConfiguredRouteIdentity = DistributiveOmit< - CrestodianConfiguredRoute, +type SystemAgentConfiguredRouteIdentity = DistributiveOmit< + SystemAgentConfiguredRoute, "runConfig" | "authProfileId" >; -type CrestodianVerifiedExecutionFingerprint = { +type SystemAgentVerifiedExecutionFingerprint = { route: unknown; defaultSelection: unknown; auth: unknown; @@ -59,10 +59,10 @@ type CrestodianVerifiedExecutionFingerprint = { defaults: unknown; agent?: unknown; plugins: unknown; - ownerPluginRuntimes: readonly CrestodianOwnerPluginRuntimeIdentity[]; + ownerPluginRuntimes: readonly SystemAgentOwnerPluginRuntimeIdentity[]; }; -type CrestodianOwnerPluginRuntimeIdentity = Readonly<{ +type SystemAgentOwnerPluginRuntimeIdentity = Readonly<{ pluginId: string; origin: string; rootDir: string; @@ -75,17 +75,17 @@ type CrestodianOwnerPluginRuntimeIdentity = Readonly<{ packageJson: Readonly<{ path: string; hash: string }> | null; }>; -type CrestodianOwnerPluginArtifactIdentity = Readonly<{ +type SystemAgentOwnerPluginArtifactIdentity = Readonly<{ pluginId: string; fingerprint: string; }>; -export type CrestodianOwnerPluginArtifactSnapshot = Readonly<{ +export type SystemAgentOwnerPluginArtifactSnapshot = Readonly<{ ownerPluginIds: readonly string[]; - ownerPluginArtifacts: readonly CrestodianOwnerPluginArtifactIdentity[]; + ownerPluginArtifacts: readonly SystemAgentOwnerPluginArtifactIdentity[]; }>; -type CrestodianOwnerPluginRegistryRecord = { +type SystemAgentOwnerPluginRegistryRecord = { pluginId: string; origin: PluginOrigin; rootDir: string; @@ -99,19 +99,19 @@ type CrestodianOwnerPluginRegistryRecord = { packageBuild?: OpenClawPackageBuild; }; -type CrestodianOwnerPluginRegistryLoader = (params: { +type SystemAgentOwnerPluginRegistryLoader = (params: { config: OpenClawConfig; workspaceDir: string; env: NodeJS.ProcessEnv; -}) => { plugins: readonly CrestodianOwnerPluginRegistryRecord[] }; +}) => { plugins: readonly SystemAgentOwnerPluginRegistryRecord[] }; /** Server-local proof returned only after the exact route completes a live turn. */ -export type CrestodianVerifiedInferenceBinding = Readonly<{ - configuredRoute: CrestodianConfiguredRouteIdentity; - execution: CrestodianConfiguredRoute; - executionFingerprint: CrestodianVerifiedExecutionFingerprint; +export type SystemAgentVerifiedInferenceBinding = Readonly<{ + configuredRoute: SystemAgentConfiguredRouteIdentity; + execution: SystemAgentConfiguredRoute; + executionFingerprint: SystemAgentVerifiedExecutionFingerprint; ownerPluginIds: readonly string[]; - ownerPluginArtifacts: readonly CrestodianOwnerPluginArtifactIdentity[]; + ownerPluginArtifacts: readonly SystemAgentOwnerPluginArtifactIdentity[]; auth: Readonly<{ authProfileId?: string; agentHarnessId?: string; @@ -125,7 +125,7 @@ export type CrestodianVerifiedInferenceBinding = Readonly<{ }>; }>; -export type CrestodianVerifiedInferenceDeps = CrestodianConfiguredRouteDeps & { +export type SystemAgentVerifiedInferenceDeps = SystemAgentConfiguredRouteDeps & { ensureAuthProfileStore?: typeof ensureAuthProfileStore; resolveCliAuthBindingFingerprint?: typeof resolveCliAuthBindingFingerprint; resolveCliRuntimeOwnerFingerprint?: typeof resolveCliRuntimeOwnerFingerprint; @@ -138,13 +138,13 @@ export type CrestodianVerifiedInferenceDeps = CrestodianConfiguredRouteDeps & { resolveAgentHarnessAuthBindingFingerprint?: ( params: AgentHarnessAuthBindingFingerprintParams & { harnessId: string }, ) => Promise; - loadPluginRegistrySnapshot?: CrestodianOwnerPluginRegistryLoader; + loadPluginRegistrySnapshot?: SystemAgentOwnerPluginRegistryLoader; fingerprintPluginRuntimeArtifact?: (record: PluginRuntimeArtifactIdentitySource) => string; }; -/** Exact child harness artifact every verified embedded Crestodian call must carry. */ -export function resolveCrestodianExpectedAgentHarnessRuntimeArtifact( - binding: CrestodianVerifiedInferenceBinding, +/** Exact child harness artifact every verified embedded OpenClaw call must carry. */ +export function resolveSystemAgentExpectedAgentHarnessRuntimeArtifact( + binding: SystemAgentVerifiedInferenceBinding, ): ExpectedAgentHarnessRuntimeArtifact | undefined { if ( binding.execution.runner !== "embedded" || @@ -167,7 +167,7 @@ export function resolveCrestodianExpectedAgentHarnessRuntimeArtifact( async function validateAgentHarnessRuntimeArtifact(params: { harnessId: string; artifact: AgentHarnessRuntimeArtifactBinding; - deps: CrestodianVerifiedInferenceDeps; + deps: SystemAgentVerifiedInferenceDeps; }): Promise { try { if (params.deps.validateAgentHarnessRuntimeArtifact) { @@ -189,7 +189,7 @@ async function resolveAgentHarnessAuthBindingFingerprint(params: { authProfileStore: AgentHarnessAuthBindingFingerprintParams["authProfileStore"]; agentDir: string; config: OpenClawConfig; - deps: CrestodianVerifiedInferenceDeps; + deps: SystemAgentVerifiedInferenceDeps; }): Promise { const input = { harnessId: params.harnessId, @@ -204,21 +204,21 @@ async function resolveAgentHarnessAuthBindingFingerprint(params: { return getRegisteredAgentHarness(params.harnessId)?.harness.authBinding?.fingerprint(input); } -function crestodianRouteIdentity( - route: CrestodianConfiguredRoute, -): CrestodianConfiguredRouteIdentity { +function systemAgentRouteIdentity( + route: SystemAgentConfiguredRoute, +): SystemAgentConfiguredRouteIdentity { const { runConfig: _runConfig, authProfileId: _authProfileId, ...identity } = route; return identity; } async function resolveCurrentRuntimeOwnerFingerprint(params: { - route: CrestodianConfiguredRoute; + route: SystemAgentConfiguredRoute; kind: OpaqueRuntimeOwnerKind; runtimeOwnerId: string; authProfileId?: string; skipLocalCredential?: boolean; runtimeArtifactFingerprint?: string; - deps: CrestodianVerifiedInferenceDeps; + deps: SystemAgentVerifiedInferenceDeps; }): Promise { if (params.route.runner === "cli") { if (params.kind !== "cli-runtime") { @@ -230,7 +230,7 @@ async function resolveCurrentRuntimeOwnerFingerprint(params: { provider: params.route.provider, config: params.route.runConfig, agentDir: params.route.agentDir, - agentId: "crestodian", + agentId: "openclaw", runtimeOwnerId: params.runtimeOwnerId, ...(params.authProfileId ? { authProfileId: params.authProfileId } : {}), ...(params.skipLocalCredential ? { skipLocalCredential: true } : {}), @@ -302,7 +302,7 @@ async function resolveCurrentRuntimeOwnerFingerprint(params: { function projectRelevantPlugins( config: OpenClawConfig, - route: CrestodianConfiguredRouteIdentity | null, + route: SystemAgentConfiguredRouteIdentity | null, ownerPluginIds: readonly string[], ): unknown { if (!route || ownerPluginIds.length === 0) { @@ -324,8 +324,8 @@ function projectRelevantPlugins( } function projectOwnerPluginRuntime( - record: CrestodianOwnerPluginRegistryRecord, -): CrestodianOwnerPluginRuntimeIdentity { + record: SystemAgentOwnerPluginRegistryRecord, +): SystemAgentOwnerPluginRuntimeIdentity { return { pluginId: record.pluginId, origin: record.origin, @@ -346,10 +346,10 @@ function projectOwnerPluginRuntime( // installed source and package identity so a stale inference proof cannot write. function projectOwnerPluginRuntimes(params: { config: OpenClawConfig; - route: CrestodianConfiguredRoute; + route: SystemAgentConfiguredRoute; ownerPluginIds: readonly string[]; - deps: CrestodianVerifiedInferenceDeps; -}): CrestodianOwnerPluginRuntimeIdentity[] { + deps: SystemAgentVerifiedInferenceDeps; +}): SystemAgentOwnerPluginRuntimeIdentity[] { if (params.ownerPluginIds.length === 0) { return []; } @@ -368,10 +368,10 @@ function projectOwnerPluginRuntimes(params: { function projectOwnerPluginArtifacts(params: { config: OpenClawConfig; - route: CrestodianConfiguredRoute; + route: SystemAgentConfiguredRoute; ownerPluginIds: readonly string[]; - deps: CrestodianVerifiedInferenceDeps; -}): CrestodianOwnerPluginArtifactIdentity[] { + deps: SystemAgentVerifiedInferenceDeps; +}): SystemAgentOwnerPluginArtifactIdentity[] { if (params.ownerPluginIds.length === 0) { return []; } @@ -401,10 +401,10 @@ function projectOwnerPluginArtifacts(params: { async function projectVerifiedExecutionFingerprint( config: OpenClawConfig, - route: CrestodianConfiguredRoute, + route: SystemAgentConfiguredRoute, ownerPluginIds: readonly string[], - deps: CrestodianVerifiedInferenceDeps, -): Promise { + deps: SystemAgentVerifiedInferenceDeps, +): Promise { const projection = await projectDefaultInferenceRoute(config); return { route: projection.route @@ -430,7 +430,7 @@ async function projectVerifiedExecutionFingerprint( function resolveRouteHarnessOwnerPluginIds( config: OpenClawConfig, - route: CrestodianConfiguredRoute, + route: SystemAgentConfiguredRoute, ): string[] { if (route.runner !== "embedded" || route.agentHarnessRuntimeOverride === "openclaw") { return []; @@ -446,7 +446,7 @@ function resolveRouteHarnessOwnerPluginIds( function resolveRouteOwnerPluginIds( config: OpenClawConfig, - route: CrestodianConfiguredRoute, + route: SystemAgentConfiguredRoute, ): string[] { const workspaceDir = resolveAgentWorkspaceDir(config, route.agentId, process.env); return [ @@ -469,11 +469,11 @@ function resolveRouteOwnerPluginIds( } /** Capture once immediately before a live setup turn. */ -export function captureCrestodianOwnerPluginArtifacts(params: { +export function captureSystemAgentOwnerPluginArtifacts(params: { config: OpenClawConfig; - executionRoute: CrestodianConfiguredRoute; - deps?: CrestodianVerifiedInferenceDeps; -}): CrestodianOwnerPluginArtifactSnapshot { + executionRoute: SystemAgentConfiguredRoute; + deps?: SystemAgentVerifiedInferenceDeps; +}): SystemAgentOwnerPluginArtifactSnapshot { const deps = params.deps ?? {}; const ownerPluginIds = resolveRouteOwnerPluginIds(params.config, params.executionRoute); return { @@ -488,10 +488,10 @@ export function captureCrestodianOwnerPluginArtifacts(params: { } async function resolveCurrentAuthFingerprint(params: { - route: CrestodianConfiguredRoute; + route: SystemAgentConfiguredRoute; authProfileId?: string; skipLocalCredential?: boolean; - deps: CrestodianVerifiedInferenceDeps; + deps: SystemAgentVerifiedInferenceDeps; }): Promise { if (params.route.runner === "cli") { const resolveBinding = @@ -624,15 +624,15 @@ async function resolveCurrentAuthFingerprint(params: { return fingerprintResolvedProviderAuth(auth); } -export async function createCrestodianVerifiedInferenceBinding(params: { - configuredRoute: CrestodianConfiguredRoute; - executionRoute: CrestodianConfiguredRoute; +export async function createSystemAgentVerifiedInferenceBinding(params: { + configuredRoute: SystemAgentConfiguredRoute; + executionRoute: SystemAgentConfiguredRoute; auth: AgentExecutionAuthBinding; - deps?: CrestodianVerifiedInferenceDeps; -}): Promise { + deps?: SystemAgentVerifiedInferenceDeps; +}): Promise { const deps = params.deps ?? {}; const runConfig = structuredClone(params.executionRoute.runConfig); - const execution = { ...params.executionRoute, runConfig } as CrestodianConfiguredRoute; + const execution = { ...params.executionRoute, runConfig } as SystemAgentConfiguredRoute; const authProfileId = params.auth.authProfileId ?? execution.authProfileId; if (authProfileId) { execution.authProfileId = authProfileId; @@ -680,7 +680,7 @@ export async function createCrestodianVerifiedInferenceBinding(params: { currentRuntimeArtifactFingerprint = await resolveArtifact({ provider: execution.provider, config: execution.runConfig, - agentId: "crestodian", + agentId: "openclaw", runtimeArtifactId: params.auth.runtimeArtifactId.trim(), }); if (currentRuntimeArtifactFingerprint !== params.auth.runtimeArtifactFingerprint) { @@ -746,14 +746,14 @@ export async function createCrestodianVerifiedInferenceBinding(params: { ) { throw new Error("The successful inference harness has no trusted manifest owner."); } - const ownerPluginArtifactSnapshot = captureCrestodianOwnerPluginArtifacts({ + const ownerPluginArtifactSnapshot = captureSystemAgentOwnerPluginArtifacts({ config: params.configuredRoute.runConfig, executionRoute: execution, deps, }); const { ownerPluginIds, ownerPluginArtifacts } = ownerPluginArtifactSnapshot; return { - configuredRoute: crestodianRouteIdentity(params.configuredRoute), + configuredRoute: systemAgentRouteIdentity(params.configuredRoute), execution, executionFingerprint: await projectVerifiedExecutionFingerprint( params.configuredRoute.runConfig, @@ -782,9 +782,9 @@ export async function createCrestodianVerifiedInferenceBinding(params: { } /** Re-hash plugin-owned runtime files only at a persistent side-effect boundary. */ -export async function hasCurrentCrestodianOwnerPluginArtifacts( - binding: CrestodianVerifiedInferenceBinding, - deps: CrestodianVerifiedInferenceDeps = {}, +export async function hasCurrentSystemAgentOwnerPluginArtifacts( + binding: SystemAgentVerifiedInferenceBinding, + deps: SystemAgentVerifiedInferenceDeps = {}, ): Promise { const readSnapshot = deps.readConfigFileSnapshot ?? (await import("../config/config.js")).readConfigFileSnapshot; @@ -818,10 +818,10 @@ export async function hasCurrentCrestodianOwnerPluginArtifacts( * switch this frozen run, while relevant runtime plugin membership and the * actual selected credential are checked explicitly. */ -export async function resolveCrestodianVerifiedInferenceRoute( - binding: CrestodianVerifiedInferenceBinding, - deps: CrestodianVerifiedInferenceDeps = {}, -): Promise { +export async function resolveSystemAgentVerifiedInferenceRoute( + binding: SystemAgentVerifiedInferenceBinding, + deps: SystemAgentVerifiedInferenceDeps = {}, +): Promise { const readSnapshot = deps.readConfigFileSnapshot ?? (await import("../config/config.js")).readConfigFileSnapshot; const snapshot = await readSnapshot(); @@ -829,21 +829,21 @@ export async function resolveCrestodianVerifiedInferenceRoute( return null; } const config = snapshot.runtimeConfig ?? snapshot.config; - const currentRoute = await resolveCrestodianConfiguredRouteFromConfig(config); + const currentRoute = await resolveSystemAgentConfiguredRouteFromConfig(config); if ( !currentRoute || - !isDeepStrictEqual(crestodianRouteIdentity(currentRoute), binding.configuredRoute) + !isDeepStrictEqual(systemAgentRouteIdentity(currentRoute), binding.configuredRoute) ) { return null; } // Keep the live-tested runner/harness selection frozen, but reproject its // owner through current config so policy/backend changes cannot reuse proof. - const currentExecution: CrestodianConfiguredRoute = { + const currentExecution: SystemAgentConfiguredRoute = { ...binding.execution, runConfig: currentRoute.runConfig, }; let currentOwnerPluginIds: string[]; - let currentFingerprint: CrestodianVerifiedExecutionFingerprint; + let currentFingerprint: SystemAgentVerifiedExecutionFingerprint; try { currentOwnerPluginIds = resolveRouteOwnerPluginIds(config, currentExecution); if (!isDeepStrictEqual(currentOwnerPluginIds, binding.ownerPluginIds)) { @@ -874,7 +874,7 @@ export async function resolveCrestodianVerifiedInferenceRoute( currentRuntimeArtifactFingerprint = await resolveArtifact({ provider: currentExecution.provider, config: currentExecution.runConfig, - agentId: "crestodian", + agentId: "openclaw", runtimeArtifactId: binding.auth.runtimeArtifactId, }).catch(() => undefined); if (currentRuntimeArtifactFingerprint !== binding.auth.runtimeArtifactFingerprint) { diff --git a/src/tui/commands.test.ts b/src/tui/commands.test.ts index 43b85fd35593..f8fa432061e8 100644 --- a/src/tui/commands.test.ts +++ b/src/tui/commands.test.ts @@ -17,6 +17,16 @@ describe("parseCommand", () => { expect(parseCommand("/gwstatus")).toEqual({ name: "gateway-status", args: "" }); }); + it("accepts the hidden retired-name alias", () => { + const retiredCommand = "/crestodian repair gateway"; // hidden alias + expect(parseCommand(retiredCommand)).toEqual({ + name: "openclaw", + args: "repair gateway", + }); + expect(getSlashCommands().map((command) => command.name)).not.toContain("crestodian"); // hidden alias + expect(helpText()).not.toContain("/crestodian"); // hidden alias + }); + it("returns empty name for empty input", () => { expect(parseCommand(" ")).toEqual({ name: "", args: "" }); }); @@ -45,10 +55,10 @@ describe("getSlashCommands", () => { const commands = getSlashCommands(); const status = commands.find((command) => command.name === "status"); const gatewayStatus = commands.find((command) => command.name === "gateway-status"); - const crestodian = commands.find((command) => command.name === "crestodian"); + const openclaw = commands.find((command) => command.name === "openclaw"); expect(status?.description).toBe("Show current status."); expect(gatewayStatus?.description).toBe("Show gateway status summary"); - expect(crestodian?.description).toBe("Return to Crestodian"); + expect(openclaw?.description).toBe("Return to OpenClaw"); }); it("distinguishes new-session and reset command descriptions", () => { @@ -155,7 +165,7 @@ describe("helpText", () => { expect(output).toContain("/fast "); expect(output).toContain("/gateway-status"); expect(output).toContain("/gwstatus"); - expect(output).toContain("/crestodian [request]"); + expect(output).toContain("/openclaw [request]"); }); it("does not advertise Gateway-owned commands in local mode", () => { diff --git a/src/tui/commands.ts b/src/tui/commands.ts index 3be6eb4e06e4..53a1327d98c3 100644 --- a/src/tui/commands.ts +++ b/src/tui/commands.ts @@ -34,6 +34,7 @@ type SlashCommandOptions = { }; const COMMAND_ALIASES: Record = { + crestodian: "openclaw", // hidden alias gwstatus: "gateway-status", }; @@ -115,7 +116,7 @@ export function getSlashCommands(options: SlashCommandOptions = {}): SlashComman ...(options.local ? [{ name: "auth", description: "Run provider auth/login flow" }] : []), { name: "agent", description: "Switch agent (or open picker)" }, { name: "agents", description: "Open agent picker" }, - { name: "crestodian", description: "Return to Crestodian" }, + { name: "openclaw", description: "Return to OpenClaw" }, { name: "session", description: "Switch session (or open picker)" }, { name: "sessions", description: "Open session picker" }, { @@ -221,7 +222,7 @@ export function helpText(options: SlashCommandOptions = {}): string { "/gwstatus", ...(options.local ? ["/auth [provider]"] : []), "/agent (or /agents)", - "/crestodian [request]", + "/openclaw [request]", "/session (or /sessions)", "/model (or /models)", `/think <${thinkLevels}>`, diff --git a/src/tui/tui-command-handlers.test.ts b/src/tui/tui-command-handlers.test.ts index 77479f4af5bd..45ca7e7f941b 100644 --- a/src/tui/tui-command-handlers.test.ts +++ b/src/tui/tui-command-handlers.test.ts @@ -628,16 +628,16 @@ describe("tui command handlers", () => { expect(addSystem).toHaveBeenCalledWith("Version: 1.2.3"); }); - it("returns to Crestodian with an optional request", async () => { + it("returns to OpenClaw with an optional request", async () => { const { handleCommand, addSystem, requestExit, sendChat } = createHarness(); - await handleCommand("/crestodian restart gateway"); + await handleCommand("/openclaw restart gateway"); expect(sendChat).not.toHaveBeenCalled(); - expect(addSystem).toHaveBeenCalledWith("returning to Crestodian with request: restart gateway"); + expect(addSystem).toHaveBeenCalledWith("returning to OpenClaw with request: restart gateway"); expect(requestExit).toHaveBeenCalledWith({ - exitReason: "return-to-crestodian", - crestodianMessage: "restart gateway", + exitReason: "return-to-system-agent", + systemAgentMessage: "restart gateway", }); }); @@ -652,14 +652,14 @@ describe("tui command handlers", () => { expect(addSystem).not.toHaveBeenCalled(); }); - it("leaves a Crestodian breadcrumb after switching agents", async () => { + it("leaves a OpenClaw breadcrumb after switching agents", async () => { const { handleCommand, addSystem, setSession, state } = createHarness(); await handleCommand("/agent Work"); expect(state.currentAgentId).toBe("work"); expect(setSession).toHaveBeenCalledWith(""); - expect(addSystem).toHaveBeenCalledWith("agent set to work; use /crestodian to return"); + expect(addSystem).toHaveBeenCalledWith("agent set to work; use /openclaw to return"); }); it("marks the generated runId as local before gateway events arrive", async () => { diff --git a/src/tui/tui-command-handlers.ts b/src/tui/tui-command-handlers.ts index 459aa5885afc..53d517474cab 100644 --- a/src/tui/tui-command-handlers.ts +++ b/src/tui/tui-command-handlers.ts @@ -161,7 +161,7 @@ export function createCommandHandlers(context: CommandHandlerContext) { const setAgent = async (id: string) => { state.currentAgentId = normalizeAgentId(id); await setSession(""); - chatLog.addSystem(`agent set to ${state.currentAgentId}; use /crestodian to return`); + chatLog.addSystem(`agent set to ${state.currentAgentId}; use /openclaw to return`); }; const closeOverlayAndRender = (handle: OverlayHandle) => { @@ -490,13 +490,13 @@ export function createCommandHandlers(context: CommandHandlerContext) { chatLog.addSystem("Usage: /btw [side question]"); } break; - case "crestodian": + case "openclaw": chatLog.addSystem( - args ? `returning to Crestodian with request: ${args}` : "returning to Crestodian", + args ? `returning to OpenClaw with request: ${args}` : "returning to OpenClaw", ); requestExit({ - exitReason: "return-to-crestodian", - ...(args ? { crestodianMessage: args } : {}), + exitReason: "return-to-system-agent", + ...(args ? { systemAgentMessage: args } : {}), }); break; case "session": diff --git a/src/tui/tui-types.ts b/src/tui/tui-types.ts index e71dc7c11016..927705dcdab1 100644 --- a/src/tui/tui-types.ts +++ b/src/tui/tui-types.ts @@ -23,11 +23,11 @@ export type TuiOptions = { forceProcessExitOnReturn?: boolean; }; -type TuiExitReason = "exit" | "return-to-crestodian"; +type TuiExitReason = "exit" | "return-to-system-agent"; export type TuiResult = { exitReason: TuiExitReason; - crestodianMessage?: string; + systemAgentMessage?: string; }; export type TuiHistoryLoadResult = diff --git a/src/tui/tui.ts b/src/tui/tui.ts index dc13b9a138da..c9bb9bfb5e2b 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -1368,7 +1368,7 @@ export async function runTui(opts: RunTuiOptions): Promise { exitRequested = true; exitResult = { exitReason: result?.exitReason ?? "exit", - ...(result?.crestodianMessage ? { crestodianMessage: result.crestodianMessage } : {}), + ...(result?.systemAgentMessage ? { systemAgentMessage: result.systemAgentMessage } : {}), }; pluginApprovals?.dispose(); taskSuggestions?.dispose(); diff --git a/src/wizard/i18n/locales/en.ts b/src/wizard/i18n/locales/en.ts index c85eef3e5e96..d3516769493e 100644 --- a/src/wizard/i18n/locales/en.ts +++ b/src/wizard/i18n/locales/en.ts @@ -262,7 +262,7 @@ export const en = { invalidConfigUnknown: "- The config could not be parsed.", manualChoice: "How would you like to connect AI?", nextSteps: - "Workspace: {workspace}\nAdd a channel: `openclaw channels add`\nPrefer chatting? Run `openclaw crestodian` and say `connect telegram` (or `connect slack`).\nOpen the dashboard: `openclaw dashboard`\nChat later: `openclaw`", + "Workspace: {workspace}\nAdd a channel: `openclaw channels add`\nPrefer chatting? Run `openclaw setup` and say `connect telegram` (or `connect slack`).\nOpen the dashboard: `openclaw dashboard`\nChat later: `openclaw`", nextStepsWithoutAi: "Workspace: {workspace}\nAdd AI later: re-run `openclaw onboard`\nAfter AI connects, add a channel: `openclaw channels add`\nOpen the dashboard: `openclaw dashboard`", nextStepsTitle: "Next steps", diff --git a/src/wizard/setup.test.ts b/src/wizard/setup.test.ts index ffddad7efe85..12ac32300e41 100644 --- a/src/wizard/setup.test.ts +++ b/src/wizard/setup.test.ts @@ -111,7 +111,7 @@ const detectSetupMigrationSources = vi.hoisted(() => vi.fn(async () => [])); const listSetupMigrationOptions = vi.hoisted(() => vi.fn(async () => [])); const runSetupMigrationImport = vi.hoisted(() => vi.fn(async () => {})); const verifySetupInference = vi.hoisted(() => - vi.fn<() => Promise>( + vi.fn<() => Promise>( async () => ({ ok: true, modelRef: "openai/gpt-5.5", latencyMs: 250 }), ), ); @@ -288,7 +288,7 @@ vi.mock("./setup.migration-import.js", () => ({ runSetupMigrationImport, })); -vi.mock("../crestodian/setup-inference.js", () => ({ +vi.mock("../system-agent/setup-inference.js", () => ({ verifySetupInference, })); diff --git a/src/wizard/setup.ts b/src/wizard/setup.ts index a7ecc2d98294..980f3deefbef 100644 --- a/src/wizard/setup.ts +++ b/src/wizard/setup.ts @@ -61,7 +61,7 @@ async function offerLiveModelVerification(params: { return params.config; } - const { verifySetupInference } = await import("../crestodian/setup-inference.js"); + const { verifySetupInference } = await import("../system-agent/setup-inference.js"); const verify = async () => { const progress = params.prompter.progress(t("wizard.setup.testAiProgress")); const result = await withConsoleSubsystemsSuppressed(() => diff --git a/taxonomy.yaml b/taxonomy.yaml index 348a8f55c84a..6392e5d2e2d8 100644 --- a/taxonomy.yaml +++ b/taxonomy.yaml @@ -835,7 +835,7 @@ surfaces: coverageIds: [cli.service-install-and-control] description: The CLI documents install, status, start, stop, restart, and run flows for managed gateway services. - name: Service auth wiring - coverageIds: [agents.create, channels.discord-config, config.crestodian-setup] + coverageIds: [agents.create, channels.discord-config, config.system-agent-setup] description: Gateway service installation documents how auth tokens and other sensitive values are handled. - name: Drift and reinstall recovery coverageIds: [cli.drift-and-reinstall-recovery] @@ -1356,7 +1356,7 @@ surfaces: agents.create, agents.instructions, channels.discord-config, - config.crestodian-setup, + config.system-agent-setup, runtime.first-action, runtime.multi-turn-continuity, runtime.long-context, @@ -2017,7 +2017,7 @@ surfaces: coverageIds: [channels.status-taxonomy-in-channels-list] description: Channel status taxonomy in channels list, channels status, and setup status output - name: Setup/onboarding flows - coverageIds: [agents.create, channels.discord-config, config.crestodian-setup] + coverageIds: [agents.create, channels.discord-config, config.system-agent-setup] description: Setup/onboarding flows, including first-run channel selection and channel account setup - name: Install-on-demand coverageIds: [channels.install-on-demand] diff --git a/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.test.ts b/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.test.ts index 5bea84557175..281c155a797e 100644 --- a/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.test.ts +++ b/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.test.ts @@ -13,7 +13,7 @@ describe("QA Docker E2E lane fixture", () => { expect(listQaDockerE2eLaneNames()).toEqual( expect.arrayContaining([ "agent-bundle-mcp-tools", - "crestodian-first-run", + "system-agent-first-run", "gateway-network", "release-plugin-marketplace", "update-migration", diff --git a/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.ts b/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.ts index 86e595341293..14a3757b83bc 100644 --- a/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.ts +++ b/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.ts @@ -28,8 +28,8 @@ export const QA_DOCKER_E2E_LANES = { "bundled-plugin-install-uninstall": { script: "scripts/e2e/bundled-plugin-install-uninstall-docker.sh", }, - "crestodian-first-run": { - script: "scripts/e2e/crestodian-first-run-docker.sh", + "system-agent-first-run": { + script: "scripts/e2e/system-agent-first-run-docker.sh", }, "docker-build-image": { script: "scripts/e2e/build-image.sh", diff --git a/test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts b/test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts similarity index 84% rename from test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts rename to test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts index 78d169eb8de7..791c79f2fd5c 100644 --- a/test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts +++ b/test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts @@ -1,4 +1,4 @@ -// Crestodian first-run Docker harness. +// OpenClaw first-run Docker harness. // Imports packaged dist modules so the Docker lane verifies the npm tarball, // while this small test driver stays mounted from the checkout. import { spawn } from "node:child_process"; @@ -7,11 +7,11 @@ import path from "node:path"; import { shouldStartOnboardingForFreshInstall } from "../../../../dist/cli/run-main.js"; import { clearConfigCache } from "../../../../dist/config/config.js"; import type { OpenClawConfig } from "../../../../dist/config/types.openclaw.js"; -import { activateSetupInference } from "../../../../dist/crestodian/setup-inference.js"; import type { RuntimeEnv } from "../../../../dist/runtime.js"; +import { activateSetupInference } from "../../../../dist/system-agent/setup-inference.js"; import { createE2eStateDir } from "../../../../scripts/e2e/lib/temp-state-dir.ts"; -type CrestodianFirstRunCommand = { +type SystemAgentFirstRunCommand = { id: string; message: string; expectOutput: string; @@ -19,14 +19,14 @@ type CrestodianFirstRunCommand = { planner?: boolean; }; -type CrestodianFirstRunSpec = { +type SystemAgentFirstRunSpec = { dockerDefaultWorkspace: string; dockerAgentWorkspace: string; agentId: string; model: string; telegramEnv: string; telegramToken: string; - commands: CrestodianFirstRunCommand[]; + commands: SystemAgentFirstRunCommand[]; auditOperations: string[]; }; @@ -54,13 +54,13 @@ function createRuntime(): { runtime: RuntimeEnv; lines: string[] } { }; } -async function readFirstRunSpec(): Promise { +async function readFirstRunSpec(): Promise { return JSON.parse( await fs.readFile( - path.join(process.cwd(), "scripts", "e2e", "crestodian-first-run-spec.json"), + path.join(process.cwd(), "scripts", "e2e", "system-agent-first-run-spec.json"), "utf8", ), - ) as CrestodianFirstRunSpec; + ) as SystemAgentFirstRunSpec; } function renderCommandTemplate(template: string, vars: Record): string { @@ -178,7 +178,7 @@ async function runPackagedCli(args: string[]): Promise<{ async function main() { const spec = await readFirstRunSpec(); - const tempState = await createE2eStateDir("openclaw-crestodian-first-run-"); + const tempState = await createE2eStateDir("openclaw-system-agent-first-run-"); tempState.registerExitCleanup(); const stateDir = tempState.stateDir; const configPath = process.env.OPENCLAW_CONFIG_PATH ?? path.join(stateDir, "openclaw.json"); @@ -200,11 +200,11 @@ async function main() { "fresh bare OpenClaw invocation did not route to onboarding", ); - const blocked = await runPackagedCli(["crestodian", "--message", "overview"]); - assert(blocked.code === 1, "Crestodian did not fail closed without inference"); + const blocked = await runPackagedCli(["openclaw", "--message", "overview"]); + assert(blocked.code === 1, "OpenClaw did not fail closed without inference"); assert( `${blocked.stdout}\n${blocked.stderr}`.includes("openclaw onboard"), - "blocked Crestodian did not direct the user to inference onboarding", + "blocked OpenClaw did not direct the user to inference onboarding", ); const blockedModern = await runPackagedCli([ "onboard", @@ -241,7 +241,7 @@ async function main() { assert( inferenceConfig.agents?.defaults?.workspace === undefined && inferenceConfig.gateway === undefined, - "inference activation configured the rest before Crestodian started", + "inference activation configured the rest before OpenClaw started", ); const activationPrompts = await fs.readFile(promptLogPath, "utf8"); assert( @@ -258,12 +258,12 @@ async function main() { ]); assert( modern.code === 0 && `${modern.stdout}\n${modern.stderr}`.includes(activation.modelRef), - "modern compatibility entrypoint did not expose Crestodian after activation", + "modern compatibility entrypoint did not expose OpenClaw after activation", ); - const overview = await runPackagedCli(["crestodian", "--message", "overview"]); + const overview = await runPackagedCli(["openclaw", "--message", "overview"]); const overviewOutput = `${overview.stdout}\n${overview.stderr}`; - assert(overview.code === 0, `verified Crestodian CLI failed: ${overviewOutput}`); + assert(overview.code === 0, `verified OpenClaw CLI failed: ${overviewOutput}`); assert( overviewOutput.includes("claude-cli/claude-opus-4-8"), "verified overview did not report the activated model", @@ -282,7 +282,7 @@ async function main() { const message = renderCommandTemplate(command.message, commandVars); const probesBefore = countInferencePrompts(await readFakeClaudePromptLines(promptLogPath)); const result = await runPackagedCli([ - "crestodian", + "openclaw", "--message", message, ...(command.approve ? ["--yes"] : []), @@ -290,14 +290,14 @@ async function main() { const output = `${result.stdout}\n${result.stderr}`; assert( result.code === 0 && output.includes(command.expectOutput), - `Crestodian first-run command ${command.id} did not apply: ${output}`, + `OpenClaw first-run command ${command.id} did not apply: ${output}`, ); if (command.planner) { assert( - output.includes(`[crestodian] planner: ${spec.model}`) && + output.includes(`[openclaw] planner: ${spec.model}`) && output.includes(FAKE_PLANNER_REPLY) && - output.includes(`[crestodian] interpreted: ${plannerCommand}`), - `Crestodian first-run command ${command.id} did not use the verified planner: ${output}`, + output.includes(`[openclaw] interpreted: ${plannerCommand}`), + `OpenClaw first-run command ${command.id} did not use the verified planner: ${output}`, ); } const probesAfter = countInferencePrompts(await readFakeClaudePromptLines(promptLogPath)); @@ -305,7 +305,7 @@ async function main() { const minimumProbes = command.approve ? 2 : 1; assert( probeDelta >= minimumProbes, - `Crestodian command ${command.id} ran ${probeDelta} inference probes; expected at least ${minimumProbes} for preflight${command.approve ? " plus its persistent boundary" : ""}`, + `OpenClaw command ${command.id} ran ${probeDelta} inference probes; expected at least ${minimumProbes} for preflight${command.approve ? " plus its persistent boundary" : ""}`, ); } @@ -348,13 +348,13 @@ async function main() { ); assert(resolveDefaultModel(config) === spec.model, "first-run setup did not write default model"); const reef = config.agents?.list?.find((agent) => agent.id === spec.agentId); - assert(reef, "Crestodian did not create reef agent"); - assert(reef.workspace === spec.dockerAgentWorkspace, "Crestodian did not write reef workspace"); + assert(reef, "OpenClaw did not create reef agent"); + assert(reef.workspace === spec.dockerAgentWorkspace, "OpenClaw did not write reef workspace"); assert( reef.model === undefined, - "Crestodian wrote a per-agent model instead of inheriting the verified default", + "OpenClaw wrote a per-agent model instead of inheriting the verified default", ); - assert(config.channels?.telegram?.enabled === true, "Crestodian did not enable Telegram"); + assert(config.channels?.telegram?.enabled === true, "OpenClaw did not enable Telegram"); const telegramToken = config.channels?.telegram?.botToken; assert( telegramToken && @@ -363,20 +363,20 @@ async function main() { telegramToken.source === "env" && "id" in telegramToken && telegramToken.id === spec.telegramEnv, - "Crestodian did not write Telegram token SecretRef", + "OpenClaw did not write Telegram token SecretRef", ); assert( !JSON.stringify(config.channels.telegram).includes(spec.telegramToken), - "Crestodian persisted the raw Telegram token", + "OpenClaw persisted the raw Telegram token", ); - const auditPath = path.join(stateDir, "audit", "crestodian.jsonl"); + const auditPath = path.join(stateDir, "audit", "system-agent.jsonl"); const audit = (await fs.readFile(auditPath, "utf8")).trim(); for (const operation of spec.auditOperations) { assert(audit.includes(`"operation":"${operation}"`), `${operation} audit entry missing`); } - console.log("Crestodian first-run Docker E2E passed"); + console.log("OpenClaw first-run Docker E2E passed"); } main().catch((err: unknown) => { diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index f3812e4eab95..18a0a371e5a7 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -60,8 +60,8 @@ const BUNDLED_PLUGIN_INSTALL_UNINSTALL_E2E_PATH = "scripts/e2e/bundled-plugin-install-uninstall-docker.sh"; const AGENT_BUNDLE_MCP_TOOLS_DOCKER_E2E_PATH = "scripts/e2e/agent-bundle-mcp-tools-docker.sh"; const COMMITMENTS_SAFETY_DOCKER_E2E_PATH = "scripts/e2e/commitments-safety-docker.sh"; -const CRESTODIAN_FIRST_RUN_DOCKER_E2E_PATH = "scripts/e2e/crestodian-first-run-docker.sh"; -const CRESTODIAN_RESCUE_DOCKER_E2E_PATH = "scripts/e2e/crestodian-rescue-docker.sh"; +const SYSTEM_AGENT_FIRST_RUN_DOCKER_E2E_PATH = "scripts/e2e/system-agent-first-run-docker.sh"; +const SYSTEM_AGENT_RESCUE_DOCKER_E2E_PATH = "scripts/e2e/system-agent-rescue-docker.sh"; const SESSION_RUNTIME_CONTEXT_DOCKER_E2E_PATH = "scripts/e2e/session-runtime-context-docker.sh"; const BUNDLED_PLUGIN_INSTALL_UNINSTALL_SWEEP_PATH = "scripts/e2e/lib/bundled-plugin-install-uninstall/sweep.sh"; @@ -3469,8 +3469,8 @@ heartbeat_elapsed="\${BASH_REMATCH[1]}" for (const path of [ AGENT_BUNDLE_MCP_TOOLS_DOCKER_E2E_PATH, COMMITMENTS_SAFETY_DOCKER_E2E_PATH, - CRESTODIAN_FIRST_RUN_DOCKER_E2E_PATH, - CRESTODIAN_RESCUE_DOCKER_E2E_PATH, + SYSTEM_AGENT_FIRST_RUN_DOCKER_E2E_PATH, + SYSTEM_AGENT_RESCUE_DOCKER_E2E_PATH, PLUGIN_BINDING_COMMAND_ESCAPE_DOCKER_E2E_PATH, SESSION_RUNTIME_CONTEXT_DOCKER_E2E_PATH, ]) { diff --git a/test/scripts/docker-e2e-plan.test.ts b/test/scripts/docker-e2e-plan.test.ts index 0b6d304448e7..e9fface6b08d 100644 --- a/test/scripts/docker-e2e-plan.test.ts +++ b/test/scripts/docker-e2e-plan.test.ts @@ -1090,8 +1090,8 @@ describe("scripts/lib/docker-e2e-plan", () => { "mcp-code-mode-gateway", "cron-mcp-cleanup", "agent-bundle-mcp-tools", - "crestodian-first-run", - "crestodian-rescue", + "system-agent-first-run", + "system-agent-rescue", "config-reload", "plugin-update", "plugins", @@ -1119,8 +1119,8 @@ describe("scripts/lib/docker-e2e-plan", () => { { name: "mcp-code-mode-gateway", stateScenario: "empty" }, { name: "cron-mcp-cleanup", stateScenario: "empty" }, { name: "agent-bundle-mcp-tools", stateScenario: "empty" }, - { name: "crestodian-first-run", stateScenario: "empty" }, - { name: "crestodian-rescue", stateScenario: "empty" }, + { name: "system-agent-first-run", stateScenario: "empty" }, + { name: "system-agent-rescue", stateScenario: "empty" }, { name: "config-reload", stateScenario: "empty" }, { name: "plugin-update", stateScenario: "empty" }, { name: "plugins", stateScenario: "empty" }, diff --git a/test/scripts/docker-e2e-crestodian.test.ts b/test/scripts/docker-e2e-system-agent.test.ts similarity index 55% rename from test/scripts/docker-e2e-crestodian.test.ts rename to test/scripts/docker-e2e-system-agent.test.ts index ef7ced909899..7c2dec0c713d 100644 --- a/test/scripts/docker-e2e-crestodian.test.ts +++ b/test/scripts/docker-e2e-system-agent.test.ts @@ -1,4 +1,4 @@ -// Crestodian Docker E2E tests cover packaged-dist harness wiring. +// OpenClaw Docker E2E tests cover packaged-dist harness wiring. import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; @@ -6,31 +6,31 @@ function readScript(pathname: string): string { return readFileSync(pathname, "utf8"); } -describe("Crestodian Docker E2E scripts", () => { - it("keeps first-run checks wired to packaged CLI and Crestodian behavior", () => { - const source = readScript("test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts"); - const spec = readScript("scripts/e2e/crestodian-first-run-spec.json"); +describe("OpenClaw Docker E2E scripts", () => { + it("keeps first-run checks wired to packaged CLI and OpenClaw behavior", () => { + const source = readScript("test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts"); + const spec = readScript("scripts/e2e/system-agent-first-run-spec.json"); expect(source).toContain("../../../../dist/cli/run-main.js"); - expect(source).toContain("../../../../dist/crestodian/setup-inference.js"); + expect(source).toContain("../../../../dist/system-agent/setup-inference.js"); expect(source).toContain("shouldStartOnboardingForFreshInstall"); - expect(source).toContain("Crestodian did not fail closed without inference"); + expect(source).toContain("OpenClaw did not fail closed without inference"); expect(source).toContain("activateSetupInference({"); - expect(source).toContain('runPackagedCli(["crestodian", "--message", "overview"])'); + expect(source).toContain('runPackagedCli(["openclaw", "--message", "overview"])'); expect(source).toContain("const PACKAGED_CLI_TIMEOUT_MS = 60_000"); expect(source).toContain("inference activation did not send the live model probe"); expect(source).toContain("function resolveDefaultModel(config: OpenClawConfig)"); expect(source).toContain("resolveDefaultModel(config) === spec.model"); expect(source).toContain("Fake Claude planner selected an inference-backed typed setup."); - expect(source).toContain("[crestodian] interpreted: ${plannerCommand}"); + expect(source).toContain("[openclaw] interpreted: ${plannerCommand}"); expect(source).toContain("expected one fuzzy setup planner prompt"); expect(source).toContain('runPackagedCli(["plugins", "list", "--json"])'); expect(source).toContain( "Telegram channel config did not auto-enable the packaged Telegram plugin", ); - expect(source).toContain("Crestodian first-run Docker E2E passed"); + expect(source).toContain("OpenClaw first-run Docker E2E passed"); expect(spec).toContain('"auditOperations"'); - expect(spec).toContain('"crestodian.setup"'); + expect(spec).toContain('"openclaw.setup"'); expect(spec).toContain('"model": "claude-cli/claude-opus-4-8"'); expect(spec).toContain('"planner": true'); expect(spec).toContain('"telegramEnv": "TELEGRAM_BOT_TOKEN"'); @@ -41,18 +41,18 @@ describe("Crestodian Docker E2E scripts", () => { }); it("keeps rescue checks wired through auto-reply command handling", () => { - const shell = readScript("scripts/e2e/crestodian-rescue-docker.sh"); - const source = readScript("scripts/e2e/crestodian-rescue-docker-client.ts"); + const shell = readScript("scripts/e2e/system-agent-rescue-docker.sh"); + const source = readScript("scripts/e2e/system-agent-rescue-docker-client.ts"); - expect(shell).toContain("OPENCLAW_GATEWAY_TOKEN=crestodian-rescue-token"); - expect(source).toContain("../../dist/auto-reply/reply/commands-crestodian.js"); - expect(source).toContain("../../dist/crestodian/rescue-message.js"); - expect(source).toContain("handleCrestodianCommand("); - expect(source).toContain("runCrestodianRescueMessage({"); + expect(shell).toContain("OPENCLAW_GATEWAY_TOKEN=system-agent-rescue-token"); + expect(source).toContain("../../dist/auto-reply/reply/commands-system-agent.js"); + expect(source).toContain("../../dist/system-agent/rescue-message.js"); + expect(source).toContain("handleSystemAgentCommand("); + expect(source).toContain("runSystemAgentRescueMessage({"); expect(source).toContain("sandboxing is active"); expect(source).toContain("cannot open the local TUI"); - expect(source).toContain("[crestodian] done: gateway.restart"); - expect(source).toContain("[crestodian] done: doctor.fix"); - expect(source).toContain("Crestodian rescue Docker E2E passed"); + expect(source).toContain("[openclaw] done: gateway.restart"); + expect(source).toContain("[openclaw] done: doctor.fix"); + expect(source).toContain("OpenClaw rescue Docker E2E passed"); }); }); diff --git a/test/scripts/lint-suppressions.test.ts b/test/scripts/lint-suppressions.test.ts index bdb4220e10c1..2e2417c843b9 100644 --- a/test/scripts/lint-suppressions.test.ts +++ b/test/scripts/lint-suppressions.test.ts @@ -208,8 +208,8 @@ describe("production lint suppressions", () => { "src/cli/command-options.ts|typescript/no-unnecessary-type-parameters|1", "src/cli/plugins-cli-test-helpers.ts|typescript/no-unnecessary-type-parameters|1", "src/cli/test-runtime-capture.ts|typescript/no-unnecessary-type-parameters|1", - "src/crestodian/setup-inference.ts|no-unsafe-finally|1", - "src/crestodian/setup-inference.ts|preserve-caught-error|1", + "src/system-agent/setup-inference.ts|no-unsafe-finally|1", + "src/system-agent/setup-inference.ts|preserve-caught-error|1", "src/gateway/test-helpers.server.ts|typescript/no-unnecessary-type-parameters|1", "src/hooks/module-loader.ts|typescript/no-unnecessary-type-parameters|1", "src/infra/device-pairing-store.ts|typescript/no-unnecessary-type-parameters|1", diff --git a/test/scripts/test-live-shard.test.ts b/test/scripts/test-live-shard.test.ts index 2e24cc70fa39..ad37341ed501 100644 --- a/test/scripts/test-live-shard.test.ts +++ b/test/scripts/test-live-shard.test.ts @@ -101,7 +101,7 @@ describe("scripts/test-live-shard", () => { "src/gateway/gateway-codex-harness.live.test.ts", ]); expect(selectLiveShardFiles("native-live-src-gateway-core", allFiles)).toEqual([ - "src/crestodian/rescue-channel.live.test.ts", + "src/system-agent/rescue-channel.live.test.ts", "src/gateway/android-node.capabilities.live.test.ts", "src/gateway/gateway-acp-spawn-defaults.live.test.ts", "src/gateway/gateway-trajectory-export.live.test.ts", diff --git a/test/scripts/test-projects.test.ts b/test/scripts/test-projects.test.ts index e626434ee2e7..cf9bd852d60a 100644 --- a/test/scripts/test-projects.test.ts +++ b/test/scripts/test-projects.test.ts @@ -631,54 +631,54 @@ describe("scripts/test-projects changed-target routing", () => { ["test/scripts/docker-build-helper.test.ts", "test/scripts/docker-e2e-plan.test.ts"], ], [ - "scripts/e2e/crestodian-first-run-docker.sh", + "scripts/e2e/system-agent-first-run-docker.sh", [ "test/scripts/docker-build-helper.test.ts", "test/scripts/docker-e2e-plan.test.ts", - "test/scripts/docker-e2e-crestodian.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", ], ], [ - "test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts", + "test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts", [ - "test/scripts/docker-e2e-crestodian.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", "src/cli/program/register.onboard.test.ts", "src/cli/run-main.test.ts", "src/cli/run-main.exit.test.ts", - "src/commands/crestodian-with-inference.test.ts", - "src/crestodian/assistant.configured.test.ts", - "src/crestodian/assistant.test.ts", - "src/crestodian/crestodian.test.ts", - "src/crestodian/operations.test.ts", - "src/crestodian/overview.test.ts", - "src/crestodian/setup-inference.test.ts", - "src/crestodian/audit.test.ts", + "src/commands/system-agent-with-inference.test.ts", + "src/system-agent/assistant.configured.test.ts", + "src/system-agent/assistant.test.ts", + "src/system-agent/system-agent.test.ts", + "src/system-agent/operations.test.ts", + "src/system-agent/overview.test.ts", + "src/system-agent/setup-inference.test.ts", + "src/system-agent/audit.test.ts", ], ], [ - "scripts/e2e/crestodian-first-run-spec.json", + "scripts/e2e/system-agent-first-run-spec.json", [ - "test/scripts/docker-e2e-crestodian.test.ts", - "src/crestodian/operations.test.ts", - "src/crestodian/audit.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", + "src/system-agent/operations.test.ts", + "src/system-agent/audit.test.ts", ], ], [ - "scripts/e2e/crestodian-rescue-docker.sh", + "scripts/e2e/system-agent-rescue-docker.sh", [ "test/scripts/docker-build-helper.test.ts", "test/scripts/docker-e2e-plan.test.ts", - "test/scripts/docker-e2e-crestodian.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", ], ], [ - "scripts/e2e/crestodian-rescue-docker-client.ts", + "scripts/e2e/system-agent-rescue-docker-client.ts", [ - "test/scripts/docker-e2e-crestodian.test.ts", - "src/crestodian/rescue-policy.test.ts", - "src/crestodian/rescue-message.test.ts", - "src/crestodian/operations.test.ts", - "src/crestodian/audit.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", + "src/system-agent/rescue-policy.test.ts", + "src/system-agent/rescue-message.test.ts", + "src/system-agent/operations.test.ts", + "src/system-agent/audit.test.ts", ], ], [ @@ -2643,13 +2643,13 @@ describe("scripts/test-projects changed-target routing", () => { }); }); - it("routes Crestodian Docker E2E script targets instead of skipping changed tests", () => { + it("routes OpenClaw Docker E2E script targets instead of skipping changed tests", () => { const targets = [ - "scripts/e2e/crestodian-first-run-docker.sh", - "test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts", - "scripts/e2e/crestodian-first-run-spec.json", - "scripts/e2e/crestodian-rescue-docker.sh", - "scripts/e2e/crestodian-rescue-docker-client.ts", + "scripts/e2e/system-agent-first-run-docker.sh", + "test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts", + "scripts/e2e/system-agent-first-run-spec.json", + "scripts/e2e/system-agent-rescue-docker.sh", + "scripts/e2e/system-agent-rescue-docker-client.ts", ]; expect(findUnmatchedExplicitTestTargets(targets)).toEqual([]); @@ -2658,20 +2658,20 @@ describe("scripts/test-projects changed-target routing", () => { targets: [ "test/scripts/docker-build-helper.test.ts", "test/scripts/docker-e2e-plan.test.ts", - "test/scripts/docker-e2e-crestodian.test.ts", + "test/scripts/docker-e2e-system-agent.test.ts", "src/cli/program/register.onboard.test.ts", "src/cli/run-main.test.ts", "src/cli/run-main.exit.test.ts", - "src/commands/crestodian-with-inference.test.ts", - "src/crestodian/assistant.configured.test.ts", - "src/crestodian/assistant.test.ts", - "src/crestodian/crestodian.test.ts", - "src/crestodian/operations.test.ts", - "src/crestodian/overview.test.ts", - "src/crestodian/setup-inference.test.ts", - "src/crestodian/audit.test.ts", - "src/crestodian/rescue-policy.test.ts", - "src/crestodian/rescue-message.test.ts", + "src/commands/system-agent-with-inference.test.ts", + "src/system-agent/assistant.configured.test.ts", + "src/system-agent/assistant.test.ts", + "src/system-agent/system-agent.test.ts", + "src/system-agent/operations.test.ts", + "src/system-agent/overview.test.ts", + "src/system-agent/setup-inference.test.ts", + "src/system-agent/audit.test.ts", + "src/system-agent/rescue-policy.test.ts", + "src/system-agent/rescue-message.test.ts", ], }); }); diff --git a/test/tsconfig/tsconfig.test.root.json b/test/tsconfig/tsconfig.test.root.json index 1dbcd89f37b9..fd1f5e547ffe 100644 --- a/test/tsconfig/tsconfig.test.root.json +++ b/test/tsconfig/tsconfig.test.root.json @@ -14,7 +14,7 @@ // These Docker E2E clients statically import built dist/** artifacts and are // validated by their Docker lanes, so they cannot join this source-only program // (same rationale as the scripts/e2e exclusion in tsconfig.scripts.json). - "../../test/e2e/qa-lab/runtime/crestodian-first-run-docker-client.ts", + "../../test/e2e/qa-lab/runtime/system-agent-first-run-docker-client.ts", "../../test/e2e/qa-lab/runtime/agent-bundle-mcp-tools-docker-client.ts" ] } diff --git a/test/vitest-unit-fast-config.test.ts b/test/vitest-unit-fast-config.test.ts index 96fab31d073d..3aa0a4af937f 100644 --- a/test/vitest-unit-fast-config.test.ts +++ b/test/vitest-unit-fast-config.test.ts @@ -172,8 +172,8 @@ describe("unit-fast vitest lane", () => { expect(testConfig.include).toContain("src/commands/status-overview-values.test.ts"); expect(testConfig.include).toContain("src/entry.version-fast-path.test.ts"); expect(testConfig.include).toContain("src/flows/doctor-startup-channel-maintenance.test.ts"); - expect(testConfig.include).toContain("src/crestodian/rescue-policy.test.ts"); - expect(testConfig.include).toContain("src/crestodian/assistant.configured.test.ts"); + expect(testConfig.include).toContain("src/system-agent/rescue-policy.test.ts"); + expect(testConfig.include).toContain("src/system-agent/assistant.configured.test.ts"); expect(testConfig.include).toContain("src/flows/search-setup.test.ts"); expect(testConfig.include).toContain("src/plugins/config-policy.test.ts"); expect(testConfig.include).toContain("src/proxy-capture/proxy-server.test.ts"); @@ -215,7 +215,7 @@ describe("unit-fast vitest lane", () => { expect(isUnitFastTestFile("src/auto-reply/reply/dispatch-from-config.test.ts")).toBe(false); expect(isUnitFastTestFile("src/agents/sandbox.resolveSandboxContext.test.ts")).toBe(false); expect(isUnitFastTestFile("src/acp/runtime/session-meta.test.ts")).toBe(false); - expect(isUnitFastTestFile("src/crestodian/assistant.test.ts")).toBe(false); + expect(isUnitFastTestFile("src/system-agent/assistant.test.ts")).toBe(false); expect(isUnitFastTestFile("src/flows/channel-setup.test.ts")).toBe(false); expect(isUnitFastTestFile("src/flows/doctor-health-contributions.test.ts")).toBe(false); expect(isUnitFastTestFile("src/plugins/install.npm-spec.test.ts")).toBe(false); diff --git a/test/vitest/vitest.unit-fast-paths.mjs b/test/vitest/vitest.unit-fast-paths.mjs index f6df3564890f..e0dcdf0c8460 100644 --- a/test/vitest/vitest.unit-fast-paths.mjs +++ b/test/vitest/vitest.unit-fast-paths.mjs @@ -94,14 +94,14 @@ export const forcedUnitFastTestFiles = [ "src/acp/translator.tool-streaming.test.ts", "src/browser-lifecycle-cleanup.test.ts", "extensions/canvas/src/host/server.test.ts", - "src/crestodian/audit.test.ts", - "src/crestodian/assistant.configured.test.ts", - "src/crestodian/crestodian.test.ts", - "src/crestodian/operations.test.ts", - "src/crestodian/overview.test.ts", - "src/crestodian/rescue-policy.test.ts", - "src/crestodian/rescue-message.test.ts", - "src/crestodian/tui-backend.test.ts", + "src/system-agent/audit.test.ts", + "src/system-agent/assistant.configured.test.ts", + "src/system-agent/system-agent.test.ts", + "src/system-agent/operations.test.ts", + "src/system-agent/overview.test.ts", + "src/system-agent/rescue-policy.test.ts", + "src/system-agent/rescue-message.test.ts", + "src/system-agent/tui-backend.test.ts", "src/flows/channel-setup.status.test.ts", "src/flows/provider-flow.test.ts", "src/context-engine/context-engine.test.ts", diff --git a/tsdown.config.ts b/tsdown.config.ts index 04e384ec39de..8bfec4995fbc 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -320,14 +320,14 @@ function buildDockerE2eHarnessEntries(): Record { "agents/embedded-agent-runner/tool-split": "src/agents/embedded-agent-runner/tool-split.ts", "agents/embedded-agent-runner/run/runtime-context-prompt": "src/agents/embedded-agent-runner/run/runtime-context-prompt.ts", - "auto-reply/reply/commands-crestodian": "src/auto-reply/reply/commands-crestodian.ts", + "auto-reply/reply/commands-system-agent": "src/auto-reply/reply/commands-system-agent.ts", "cli/run-main": "src/cli/run-main.ts", "commitments/runtime": "src/commitments/runtime.ts", "commitments/store": "src/commitments/store.ts", "config/config": "src/config/config.ts", - "crestodian/crestodian": "src/crestodian/crestodian.ts", - "crestodian/rescue-message": "src/crestodian/rescue-message.ts", - "crestodian/setup-inference": "src/crestodian/setup-inference.ts", + "system-agent/system-agent": "src/system-agent/system-agent.ts", + "system-agent/rescue-message": "src/system-agent/rescue-message.ts", + "system-agent/setup-inference": "src/system-agent/setup-inference.ts", "gateway/protocol/index": "packages/gateway-protocol/src/index.ts", "infra/errors": "src/infra/errors.ts", "infra/ws": "src/infra/ws.ts",