refactor(agents): move announce, completion, recovery, and registry families into concept directories (#121553)

* refactor(agents): move announce family into subagents/announce/

* refactor(agents): move completion and main-session recovery families

* refactor(agents): move registry family into subagents/registry

* refactor(agents): update registry state type imports

* style(agents): format moved subagent imports

* test(agents): preserve isolated registry test routing

* fix(scripts): recognize relocated subagent announce seams
This commit is contained in:
Peter Steinberger
2026-08-10 16:06:39 -07:00
committed by GitHub
parent 2132c15bd6
commit 1ced7441eb
356 changed files with 1410 additions and 1290 deletions
+2 -2
View File
@@ -156,9 +156,9 @@ const rootEntries = [
// Loaded by URL from setup-inference-detection.ts; no static import edge exists.
"src/system-agent/setup-inference-detection.worker.ts!",
// Split runtime loaded through a path assembled in subagent-registry.ts.
"src/agents/subagent-registry.runtime.ts!",
"src/agents/subagents/registry/subagent-registry.runtime.ts!",
// Loaded lazily by the sweeper only when a receipt-bearing or interrupted row is found.
"src/agents/subagent-registry-restart-recovery.ts!",
"src/agents/subagents/registry/subagent-registry-restart-recovery.ts!",
// Task cancellation loads this control facade by string path to avoid a registry cycle.
"src/tasks/task-registry-control.runtime.ts!",
// Human plugin listing lazily loads its formatter to keep JSON startup lean.
+10 -10
View File
@@ -409,7 +409,7 @@ src/agents/failover-error.ts
src/agents/harness/native-hook-relay.test.ts
src/agents/harness/selection.test.ts
src/agents/harness/selection.ts
src/agents/main-session-restart-recovery.test.ts
src/agents/main-session-recovery/main-session-restart-recovery.test.ts
src/agents/model-auth-availability.ts
src/agents/model-auth.profiles.test.ts
src/agents/model-auth.test.ts
@@ -441,15 +441,15 @@ src/agents/sessions/model-registry.ts
src/agents/sessions/package-manager.ts
src/agents/sessions/resource-loader.ts
src/agents/sessions/settings-manager.ts
src/agents/subagent-announce-delivery.test.ts
src/agents/subagent-announce-delivery.ts
src/agents/subagent-announce.format.e2e.test.ts
src/agents/subagent-control.test.ts
src/agents/subagent-control.ts
src/agents/subagent-registry-lifecycle.test.ts
src/agents/subagent-registry-run-manager.ts
src/agents/subagent-registry.steer-restart.test.ts
src/agents/subagent-registry.test.ts
src/agents/subagents/announce/subagent-announce-delivery.test.ts
src/agents/subagents/announce/subagent-announce-delivery.ts
src/agents/subagents/announce/subagent-announce.format.e2e.test.ts
src/agents/subagents/registry/subagent-control.test.ts
src/agents/subagents/registry/subagent-control.ts
src/agents/subagents/registry/subagent-registry-lifecycle.test.ts
src/agents/subagents/registry/subagent-registry-run-manager.ts
src/agents/subagents/registry/subagent-registry.steer-restart.test.ts
src/agents/subagents/registry/subagent-registry.test.ts
src/agents/subagents/spawn/subagent-spawn.test.ts
src/agents/system-prompt.test.ts
src/agents/system-prompt.ts
+2 -2
View File
@@ -50,7 +50,7 @@ This table records the implementation state when #103505 was opened. The rollout
| Portable actions | `src/interactive/payload.ts`, `src/plugin-sdk/interactive-runtime.ts`, `src/plugin-sdk/approval-reply-runtime.ts` | Approval buttons are command actions containing `/approve ...`; URL and Web App targets are untyped button fields. |
| Telegram | `extensions/telegram/src/approval-handler.runtime.ts`, `extensions/telegram/src/button-types.ts` | The renderer parses command text to recognize approval semantics before producing private callback data. |
| Control UI | `ui/src/app/exec-approval.ts`, `ui/src/app/overlays.ts`, `ui/src/components/exec-approval.ts` | Approval UI is a global modal. `ui/src/app-route-paths.ts` and `ui/src/app-routes.ts` use exact routes and rewrite unknown paths to Chat. |
| Session ownership | `src/agents/subagent-registry.types.ts`, `src/agents/subagent-registry-read.ts`, `src/config/sessions/types.ts` | Controller, requester, explicit parent, and legacy spawn ownership exist, but approval events are not projected to those session streams. |
| Session ownership | `src/agents/subagents/registry/subagent-registry.types.ts`, `src/agents/subagents/registry/subagent-registry-read.ts`, `src/config/sessions/types.ts` | Controller, requester, explicit parent, and legacy spawn ownership exist, but approval events are not projected to those session streams. |
| Shared state | `src/state/openclaw-state-schema.sql`, `src/state/openclaw-state-db.ts` | Existing immediate transactions and Kysely conditional updates support durable compare-and-set in `state/openclaw.sqlite`. |
Representative current tests include `src/gateway/exec-approval-manager.test.ts`, `src/gateway/server-methods/approval-shared.test.ts`, `src/agents/bash-tools.exec-gateway-approval.e2e.test.ts`, `extensions/telegram/src/approval-handler.runtime.test.ts`, and `ui/src/e2e/approval-flow.e2e.test.ts`.
@@ -294,7 +294,7 @@ Use a deterministic breadth-first walk:
4. Normalize and de-duplicate on enqueue so the first, shortest path wins.
5. Stop at 64 unique keys; this audience-size cap also bounds traversal depth.
The registry source is `src/agents/subagent-registry-read.ts`; ownership fields are defined in `src/agents/subagent-registry.types.ts`. Session fallback fields are defined in `src/config/sessions/types.ts`.
The registry source is `src/agents/subagents/registry/subagent-registry-read.ts`; ownership fields are defined in `src/agents/subagents/registry/subagent-registry.types.ts`. Session fallback fields are defined in `src/config/sessions/types.ts`.
Requested and terminal projections use the same persisted audience even if focus/controller ownership changes while the approval is pending. This guarantees terminal cleanup for every audience session stream that received the request projection. Resolution always targets the source approval ID; audience sessions never receive cloned approval state. Forwarded channel-message cleanup remains the separate delivery-locator follow-up below.
@@ -37,8 +37,8 @@ scenario:
- docs/help/testing.md
codeRefs:
- src/agents/requester-tool-policy.ts
- src/agents/subagent-announce-delivery.ts
- src/agents/subagent-capabilities.ts
- src/agents/subagents/announce/subagent-announce-delivery.ts
- src/agents/subagents/spawn/subagent-capabilities.ts
- src/agents/tools/sessions-yield-tool.ts
execution:
kind: flow
@@ -30,7 +30,7 @@ scenario:
- src/agents/conversation-capability-profile.ts
- src/agents/requester-tool-policy.ts
- src/agents/sender-tool-policy.ts
- src/agents/subagent-capabilities.ts
- src/agents/subagents/spawn/subagent-capabilities.ts
execution:
kind: flow
suiteIsolation: isolated
@@ -32,8 +32,8 @@ scenario:
- docs/channels/qa-channel.md
codeRefs:
- src/agents/agent-run-terminal-reply.ts
- src/agents/subagent-announce-delivery.ts
- src/agents/subagent-registry-lifecycle.ts
- src/agents/subagents/announce/subagent-announce-delivery.ts
- src/agents/subagents/registry/subagent-registry-lifecycle.ts
- extensions/qa-lab/src/providers/mock-openai/server.ts
execution:
kind: flow
@@ -20,7 +20,7 @@ scenario:
- docs/help/testing.md
codeRefs:
- src/gateway/session-utils.ts
- src/agents/subagent-run-liveness.ts
- src/agents/subagents/registry/subagent-run-liveness.ts
- extensions/qa-lab/src/gateway-child.ts
execution:
kind: flow
@@ -31,7 +31,7 @@ scenario:
codeRefs:
- extensions/qa-lab/src/suite-runtime-agent-session.ts
- extensions/qa-lab/src/suite-runtime-gateway.ts
- src/agents/main-session-restart-recovery.ts
- src/agents/main-session-recovery/main-session-restart-recovery.ts
execution:
kind: flow
summary: Run a fixture-backed audit with GPT-5.4 while replacing the Gateway child three times at durable wait checkpoints.
+4 -2
View File
@@ -686,8 +686,8 @@ function describeSubagentSeamKinds(relativePath: string, source: string) {
const seamKinds = [];
const isAnnounceDispatchPath =
relativePath === "src/agents/subagent-announce.ts" ||
relativePath === "src/agents/subagent-announce-dispatch.ts";
relativePath === "src/agents/subagents/announce/subagent-announce.ts" ||
relativePath === "src/agents/subagents/announce/subagent-announce-dispatch.ts";
const importsSpawnRuntime = hasAnyImportSource(source, [
"./subagent-spawn.js",
"./acp-spawn.js",
@@ -718,6 +718,8 @@ function describeSubagentSeamKinds(relativePath: string, source: string) {
const importsAnnounceDelivery = hasAnyImportSource(source, [
"./subagent-announce.js",
"./subagent-announce-dispatch.js",
"../announce/subagent-announce.js",
"../announce/subagent-announce-dispatch.js",
"./subagent-announce-queue.js",
"../infra/outbound/bound-delivery-router.js",
"../utils/delivery-context.shared.js",
+8 -8
View File
@@ -3,7 +3,7 @@ import os from "node:os";
import path from "node:path";
import { performance } from "node:perf_hooks";
import { pathToFileURL } from "node:url";
import type { SubagentRunRecord } from "../src/agents/subagent-registry.types.js";
import type { SubagentRunRecord } from "../src/agents/subagents/registry/subagent-registry.types.js";
import type { DB as OpenClawStateKyselyDatabase } from "../src/state/openclaw-state-db.generated.js";
import {
WORKER_RESULT_SENTINEL,
@@ -102,7 +102,7 @@ async function drainSpawnSampleRootWork(
async function resetRuntime(persist: boolean): Promise<void> {
const [subagents, tasks, stateDb, agentDb] = await Promise.all([
import("../src/agents/subagent-registry.test-helpers.js"),
import("../src/agents/subagents/registry/subagent-registry.test-helpers.js"),
import("../src/tasks/task-runtime.test-helpers.js"),
import("../src/state/openclaw-state-db.js"),
import("../src/state/openclaw-agent-db.js"),
@@ -184,8 +184,8 @@ async function configureSpawnRuntime(
callGateway: typeof import("../src/gateway/call.js").callGateway,
): Promise<void> {
const [subagents, registry, taskStore, flowStore] = await Promise.all([
import("../src/agents/subagent-registry.test-helpers.js"),
import("../src/agents/subagent-registry-memory.js"),
import("../src/agents/subagents/registry/subagent-registry.test-helpers.js"),
import("../src/agents/subagents/registry/subagent-registry-memory.js"),
import("../src/tasks/task-registry.store.js"),
import("../src/tasks/task-flow-registry.store.test-support.js"),
]);
@@ -315,7 +315,7 @@ async function runSpawnSample(
): Promise<Sample> {
const [pipeline, registry] = await Promise.all([
import("../src/agents/spawn-pipeline.js"),
import("../src/agents/subagent-registry-memory.js"),
import("../src/agents/subagents/registry/subagent-registry-memory.js"),
]);
await resetRuntime(mode === "durable");
const barrier = createTerminalWaitBarrier();
@@ -595,8 +595,8 @@ async function runSweepSample(childCount: number): Promise<Sample> {
{ getSubagentRunsForChildSession, subagentRuns: runs },
{ createSubagentRegistrySweeper },
] = await Promise.all([
import("../src/agents/subagent-registry-memory.js"),
import("../src/agents/subagent-registry-sweeper.js"),
import("../src/agents/subagents/registry/subagent-registry-memory.js"),
import("../src/agents/subagents/registry/subagent-registry-sweeper.js"),
]);
const now = Date.now();
runs.clear();
@@ -694,7 +694,7 @@ async function runSweepSample(childCount: number): Promise<Sample> {
async function runDedupeSample(childCount: number): Promise<Sample> {
const { dedupeLatestChildCompletionRows } =
await import("../src/agents/subagent-announce-output.js");
await import("../src/agents/subagents/announce/subagent-announce-output.js");
const rowsForOrder = (generations: number[]) =>
Array.from({ length: childCount }, (_, child) =>
generations.map((generation) => ({
+7 -7
View File
@@ -109,8 +109,8 @@ export const migratedSessionAccessorFiles = new Set([
"src/agents/embedded-agent-runner/transcript-rewrite.ts",
"src/agents/embedded-agent-runner/transcript-runtime-state.ts",
"src/agents/live-model-switch.ts",
"src/agents/subagent-control.ts",
"src/agents/subagent-registry-helpers.ts",
"src/agents/subagents/registry/subagent-control.ts",
"src/agents/subagents/registry/subagent-registry-helpers.ts",
"src/auto-reply/reply/abort.ts",
"src/auto-reply/reply/agent-runner-helpers.ts",
"src/auto-reply/reply/agent-runner.ts",
@@ -187,13 +187,13 @@ export const migratedSessionAccessorWriteFiles = new Set([
"src/agents/embedded-agent-subscribe.handlers.compaction.runtime.ts",
"src/agents/embedded-agent-runner/run/attempt.ts",
"src/agents/live-model-switch.ts",
"src/agents/main-session-restart-recovery-checkpoint.ts",
"src/agents/main-session-restart-recovery-marking.ts",
"src/agents/main-session-restart-recovery-store.ts",
"src/agents/main-session-recovery/main-session-restart-recovery-checkpoint.ts",
"src/agents/main-session-recovery/main-session-restart-recovery-marking.ts",
"src/agents/main-session-recovery/main-session-restart-recovery-store.ts",
"src/agents/session-suspension.ts",
"src/auto-reply/reply/abort.ts",
"src/agents/subagent-control.ts",
"src/agents/subagent-registry-helpers.ts",
"src/agents/subagents/registry/subagent-control.ts",
"src/agents/subagents/registry/subagent-registry-helpers.ts",
"src/agents/tools/session-status-tool.ts",
"src/auto-reply/reply/abort-cutoff.runtime.ts",
"src/auto-reply/reply/agent-runner-cli-dispatch.ts",
@@ -69,12 +69,12 @@ const gatewaySessionServerMethodFiles = [
];
export const migratedSessionTranscriptReaderFiles = new Set([
"src/agents/main-session-restart-recovery-store.ts",
"src/agents/subagent-announce-output.test.ts",
"src/agents/subagent-announce-output.ts",
"src/agents/subagent-announce.runtime.ts",
"src/agents/subagent-registry-restart-recovery.test.ts",
"src/agents/subagent-registry-restart-recovery.ts",
"src/agents/main-session-recovery/main-session-restart-recovery-store.ts",
"src/agents/subagents/announce/subagent-announce-output.test.ts",
"src/agents/subagents/announce/subagent-announce-output.ts",
"src/agents/subagents/announce/subagent-announce.runtime.ts",
"src/agents/subagents/registry/subagent-registry-restart-recovery.test.ts",
"src/agents/subagents/registry/subagent-registry-restart-recovery.ts",
"src/agents/tools/embedded-gateway-stub.runtime.ts",
"src/agents/tools/embedded-gateway-stub.test.ts",
"src/agents/tools/embedded-gateway-stub.ts",
+14 -14
View File
@@ -200,15 +200,15 @@
{
"name": "countActiveDescendantRuns",
"files": [
"src/agents/subagent-registry-read.ts",
"src/agents/subagent-registry.ts"
"src/agents/subagents/registry/subagent-registry-read.ts",
"src/agents/subagents/registry/subagent-registry.ts"
]
},
{
"name": "countPendingDescendantRuns",
"files": [
"src/agents/subagent-registry-announce-read.ts",
"src/agents/subagent-registry.ts"
"src/agents/subagents/registry/subagent-registry-announce-read.ts",
"src/agents/subagents/registry/subagent-registry.ts"
]
},
{
@@ -524,8 +524,8 @@
{
"name": "getLatestSubagentRunByChildSessionKey",
"files": [
"src/agents/subagent-registry-read.ts",
"src/agents/subagent-registry.ts"
"src/agents/subagents/registry/subagent-registry-read.ts",
"src/agents/subagents/registry/subagent-registry.ts"
]
},
{
@@ -672,8 +672,8 @@
{
"name": "listDescendantRunsForRequester",
"files": [
"src/agents/subagent-registry-read.ts",
"src/agents/subagent-registry.ts"
"src/agents/subagents/registry/subagent-registry-read.ts",
"src/agents/subagents/registry/subagent-registry.ts"
]
},
{
@@ -702,8 +702,8 @@
{
"name": "listSubagentRunsForController",
"files": [
"src/agents/subagent-registry-read.ts",
"src/agents/subagent-registry.ts"
"src/agents/subagents/registry/subagent-registry-read.ts",
"src/agents/subagents/registry/subagent-registry.ts"
]
},
{
@@ -806,7 +806,7 @@
"files": [
"src/agents/auth-profiles/constants.ts",
"src/agents/embedded-agent-runner/logger.ts",
"src/agents/main-session-restart-recovery-shared.ts",
"src/agents/main-session-recovery/main-session-restart-recovery-shared.ts",
"src/system-agent/setup-inference-core.ts",
"src/tasks/task-registry-state.ts"
]
@@ -1081,7 +1081,7 @@
{
"name": "readSessionEntry",
"files": [
"src/agents/subagent-announce.runtime.ts",
"src/agents/subagents/announce/subagent-announce.runtime.ts",
"src/cron/isolated-agent.turn-test-helpers.ts"
]
},
@@ -1184,8 +1184,8 @@
{
"name": "replaceSubagentRunAfterSteer",
"files": [
"src/agents/subagent-registry-runtime.ts",
"src/agents/subagent-registry.ts"
"src/agents/subagents/registry/subagent-registry-runtime.ts",
"src/agents/subagents/registry/subagent-registry.ts"
]
},
{
@@ -2,8 +2,8 @@
"embeddedAgentSessionTarget": {},
"memoryHostSessionCorpus": {},
"sessionAccessorRead": {
"src/agents/subagent-announce-output.ts": 3,
"src/agents/subagent-announce.test-support.ts": 2,
"src/agents/subagents/announce/subagent-announce-output.ts": 3,
"src/agents/subagents/announce/subagent-announce.test-support.ts": 2,
"src/agents/tools/transcripts-tool.ts": 2,
"src/auto-reply/reply/commands-session-store.ts": 2,
"src/config/sessions/session-accessor.entry.ts": 2
+1 -1
View File
@@ -26,7 +26,7 @@ const OPTIONAL_LIVE_SHARD_FILE_ENVS = new Map([
["src/agents/embedded-agent-runner.cache.live.test.ts", ["OPENCLAW_LIVE_CACHE_TEST"]],
["src/agents/live-cache-regression.live.test.ts", ["OPENCLAW_LIVE_CACHE_TEST"]],
["src/agents/provider-headers.live.test.ts", ["OPENCLAW_LIVE_CACHE_TEST"]],
["src/agents/subagent-announce.live.test.ts", ["OPENCLAW_LIVE_SUBAGENT_E2E"]],
["src/agents/subagents/announce/subagent-announce.live.test.ts", ["OPENCLAW_LIVE_SUBAGENT_E2E"]],
["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"]],
[
@@ -85,7 +85,7 @@ cd "$tmp_dir"
OPENCLAW_LIVE_TEST=1 \
OPENCLAW_LIVE_SUBAGENT_E2E=1 \
OPENCLAW_VITEST_MAX_WORKERS="${OPENCLAW_VITEST_MAX_WORKERS:-1}" \
node --import tsx scripts/test-live.mts -- src/agents/subagent-announce.live.test.ts -- --reporter=verbose
node --import tsx scripts/test-live.mts -- src/agents/subagents/announce/subagent-announce.live.test.ts -- --reporter=verbose
EOF
OPENCLAW_LIVE_DOCKER_REPO_ROOT="$ROOT_DIR" "$TRUSTED_HARNESS_DIR/scripts/test-live-build-docker.sh"
@@ -98,7 +98,7 @@ if openclaw_live_uses_managed_bind_dirs; then
fi
echo "==> Run subagent announce live test in Docker"
echo "==> Target: src/agents/subagent-announce.live.test.ts"
echo "==> Target: src/agents/subagents/announce/subagent-announce.live.test.ts"
echo "==> Model: ${OPENCLAW_LIVE_SUBAGENT_E2E_MODEL:-openai/gpt-5.6-luna}"
echo "==> Profile file: $PROFILE_STATUS"
DOCKER_RUN_ARGS=()
@@ -6,13 +6,13 @@ import { loadSessionEntry, replaceSessionEntry } from "../config/sessions/sessio
import { getAgentEventLifecycleGeneration } from "../infra/agent-events.js";
import { runWithAgentCommandRecoveryOwner } from "./agent-command-recovery-owner.js";
import type { AgentCommandOpts } from "./command/types.js";
import { claimMainSessionRecoveryOwner } from "./main-session-recovery-store.js";
import { claimMainSessionRecoveryOwner } from "./main-session-recovery/main-session-recovery-store.js";
const recoveryOwnerMocks = vi.hoisted(() => ({
scheduleMainSessionRecoveryPendingTarget: vi.fn(),
}));
vi.mock("./main-session-recovery-owner-release.js", () => ({
vi.mock("./main-session-recovery/main-session-recovery-owner-release.js", () => ({
scheduleMainSessionRecoveryPendingTarget:
recoveryOwnerMocks.scheduleMainSessionRecoveryPendingTarget,
}));
+3 -3
View File
@@ -3,8 +3,8 @@ import type { InternalSessionEntry } from "../config/sessions.js";
import { formatErrorMessage } from "../infra/errors.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import type { AgentCommandOpts } from "./command/types.js";
import { repairMainSessionRecoveryMutation } from "./main-session-recovery-lifecycle.js";
import { scheduleMainSessionRecoveryPendingTarget } from "./main-session-recovery-owner-release.js";
import { repairMainSessionRecoveryMutation } from "./main-session-recovery/main-session-recovery-lifecycle.js";
import { scheduleMainSessionRecoveryPendingTarget } from "./main-session-recovery/main-session-recovery-owner-release.js";
import {
claimMainSessionRecoveryOwner,
inspectMainSessionRecoveryRequired,
@@ -12,7 +12,7 @@ import {
releaseMainSessionRecoveryOwner,
type MainSessionRecoveryOwnerLease,
type MainSessionRecoveryPendingTarget,
} from "./main-session-recovery-store.js";
} from "./main-session-recovery/main-session-recovery-store.js";
const log = createSubsystemLogger("agents/agent-command");
@@ -240,7 +240,7 @@ vi.mock("./command/types.js", () => ({}));
// Recovery ownership has dedicated store-backed coverage. This command suite
// uses an intentionally synthetic session resolver with no durable store path.
vi.mock("./main-session-recovery-store.js", () => ({
vi.mock("./main-session-recovery/main-session-recovery-store.js", () => ({
claimMainSessionRecoveryOwner: vi.fn(async () => ({ kind: "not_required" })),
inspectMainSessionRecoveryRequired: vi.fn(async () => ({ kind: "not_required" })),
releaseMainSessionRecoveryOwner: vi.fn(async () => undefined),
+1 -1
View File
@@ -59,7 +59,7 @@ import {
resolveInternalSessionEffectsTarget,
} from "./internal-session-effects.js";
import { AGENT_LANE_SUBAGENT } from "./lanes.js";
import type { MainSessionRecoveryPendingTarget } from "./main-session-recovery-store.js";
import type { MainSessionRecoveryPendingTarget } from "./main-session-recovery/main-session-recovery-store.js";
import type { AgentRunSessionTarget } from "./run-session-target.js";
import { createAgentRunRestartAbortError } from "./run-termination.js";
import { measureAgentStartup } from "./startup-timing.js";
+4 -1
View File
@@ -6,7 +6,10 @@ import {
prependAgentSteeringPrompt,
releaseLeasedAgentSteeringItemsFromSubagentRuns,
} from "./agent-steering-queue.js";
import type { PendingFinalDeliveryPayload, SubagentRunRecord } from "./subagent-registry.types.js";
import type {
PendingFinalDeliveryPayload,
SubagentRunRecord,
} from "./subagents/registry/subagent-registry.types.js";
const requesterSessionKey = "agent:main:main";
+1 -1
View File
@@ -5,7 +5,7 @@ import type {
PendingFinalDeliveryPayload,
SubagentCompletionDeliveryState,
SubagentRunRecord,
} from "./subagent-registry.types.js";
} from "./subagents/registry/subagent-registry.types.js";
import { selectDeliverableSessionsReply } from "./tools/sessions-send-tokens.js";
// Steering queue utilities for delivering completed subagent results back into
+1 -1
View File
@@ -34,7 +34,7 @@ import {
resolveStoredSubagentCapabilities,
type SessionCapabilityStore,
type SubagentSessionRole,
} from "./subagent-capabilities.js";
} from "./subagents/spawn/subagent-capabilities.js";
import { isToolAllowedByPolicyName } from "./tool-policy-match.js";
import { mergeAlsoAllowPolicy, resolveToolProfilePolicy } from "./tool-policy.js";
import { AUTOMATIONS_TOOL_NAME } from "./tools/automations-tool-name.js";
+1 -1
View File
@@ -77,7 +77,7 @@ import {
createReadTool,
createWriteTool,
} from "./sessions/index.js";
import type { TrustedSubagentCompletionHandoff } from "./subagent-announce-handoff.js";
import type { TrustedSubagentCompletionHandoff } from "./subagents/announce/subagent-announce-handoff.js";
import { createToolFsPolicy, resolveToolFsConfig } from "./tool-fs-policy.js";
import { resolveToolLoopDetectionConfig } from "./tool-loop-detection-config.js";
import { buildDeclaredToolAllowlistContext } from "./tool-policy-declared-context.js";
+5 -2
View File
@@ -11,8 +11,11 @@ import type { CodeModeNamespaceRuntime } from "./code-mode-namespaces.js";
import type { PendingBridgeRequest, SettledBridgeRequest } from "./code-mode-runtime.js";
import { readCodeModeSkill } from "./code-mode-skills.js";
import type { AgentToolUpdateCallback } from "./runtime/index.js";
import { getSwarmRunByLaunchReplayKey, initSubagentRegistry } from "./subagent-registry.js";
import type { SubagentRunRecord } from "./subagent-registry.types.js";
import {
getSwarmRunByLaunchReplayKey,
initSubagentRegistry,
} from "./subagents/registry/subagent-registry.js";
import type { SubagentRunRecord } from "./subagents/registry/subagent-registry.types.js";
import {
SWARM_CODE_MODE_IDEMPOTENCY_KEY,
SWARM_CODE_MODE_REQUEST_FINGERPRINT,
+1 -1
View File
@@ -92,7 +92,7 @@ import { buildUsageWithNoCost } from "../stream-message-shared.js";
import {
isSubagentAnnounceCompletionHandoff,
isTrustedSubagentCompletionHandoffForRun,
} from "../subagent-announce-handoff.js";
} from "../subagents/announce/subagent-announce-handoff.js";
import { isRuntimeToolAllowed, isToolAllowedByPolicies } from "../tool-policy-match.js";
import { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS } from "../tool-result-limits.js";
import type { ContextUsage } from "../usage.js";
+1 -1
View File
@@ -20,7 +20,7 @@ import {
setCliSessionId,
} from "../cli-session.js";
import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js";
import { clearMainSessionRecoveryAfterAgentRun } from "../main-session-recovery-clear.js";
import { clearMainSessionRecoveryAfterAgentRun } from "../main-session-recovery/main-session-recovery-clear.js";
import { isCliProvider } from "../model-selection.js";
import { deriveSessionTotalTokens, hasNonzeroUsage } from "../usage.js";
+1 -1
View File
@@ -41,7 +41,7 @@ import { sessionDeliveryChannel } from "../../utils/delivery-context.shared.js";
import { listAgentIds, resolveDefaultAgentId } from "../agent-scope.js";
import { clearBootstrapSnapshotOnSessionRollover } from "../bootstrap-cache.js";
import { clearAllCliSessions } from "../cli-session.js";
import { transitionMainSessionRecovery } from "../main-session-recovery-state.js";
import { transitionMainSessionRecovery } from "../main-session-recovery/main-session-recovery-state.js";
/** Resolved command session identity plus backing store metadata. */
type SessionResolution = {
+2 -2
View File
@@ -21,9 +21,9 @@ import type { ExecApprovalContinuationPromptRange } from "../bash-tools.exec-app
import type { ExecElevatedDefaults } from "../bash-tools.exec-types.js";
import type { BootstrapContextRunKind } from "../bootstrap-mode.js";
import type { CliSessionBindingFacts } from "../cli-runner/types.js";
import type { MainSessionRecoveryOwnerLease } from "../main-session-recovery-store.js";
import type { MainSessionRecoveryOwnerLease } from "../main-session-recovery/main-session-recovery-store.js";
import type { ScheduledToolPolicyContext } from "../scheduled-tool-policy.js";
import type { TrustedSubagentCompletionHandoff } from "../subagent-announce-handoff.js";
import type { TrustedSubagentCompletionHandoff } from "../subagents/announce/subagent-announce-handoff.js";
import type { AgentStreamParams, ClientToolDefinition } from "./shared-types.js";
/** Image content block for Claude API multimodal messages. */
@@ -25,7 +25,7 @@ import {
import { pickSandboxToolPolicy } from "./sandbox-tool-policy.js";
import type { SandboxToolPolicy } from "./sandbox/types.js";
import type { ScheduledToolPolicyContext } from "./scheduled-tool-policy.js";
import type { TrustedSubagentCompletionHandoff } from "./subagent-announce-handoff.js";
import type { TrustedSubagentCompletionHandoff } from "./subagents/announce/subagent-announce-handoff.js";
import type { PromptMode } from "./system-prompt.types.js";
import {
collectExplicitAllowlist,
@@ -15,7 +15,7 @@ import type { SkillSnapshot } from "../../skills/types.js";
import type { ExecElevatedDefaults, ExecToolDefaults } from "../bash-tools.exec-types.js";
import type { AgentRunSessionTarget } from "../run-session-target.js";
import type { AgentRuntimeAuthPlan, AgentRuntimePlan } from "../runtime-plan/types.js";
import type { TrustedSubagentCompletionHandoff } from "../subagent-announce-handoff.js";
import type { TrustedSubagentCompletionHandoff } from "../subagents/announce/subagent-announce-handoff.js";
export type CompactEmbeddedAgentSessionParams = {
sessionId: string;
@@ -36,7 +36,7 @@ vi.mock("../harness/selection.js", () => ({
runAgentHarnessSettledTurnFinalization: vi.fn(),
}));
vi.mock("../subagent-registry.js", () => ({
vi.mock("../subagents/registry/subagent-registry.js", () => ({
settleRequesterAfterSessionSpawns: mocks.settleRequesterAfterSessionSpawns,
}));
@@ -13,7 +13,7 @@ const mocks = vi.hoisted(() => ({
vi.mock("../logger.js", () => ({
log: { debug: mocks.logDebug, error: mocks.logError },
}));
vi.mock("../../subagent-registry.js", () => ({
vi.mock("../../subagents/registry/subagent-registry.js", () => ({
settleRequesterAfterSessionSpawns: mocks.settleRequesterAfterSessionSpawns,
}));
vi.mock("../runs.js", () => ({ clearActiveEmbeddedRun: mocks.clearActiveEmbeddedRun }));
@@ -27,7 +27,7 @@ import { DEFAULT_CONTEXT_TOKENS } from "../../defaults.js";
import type { AgentRuntimePlan } from "../../runtime-plan/types.js";
import type { AgentMessage } from "../../runtime/index.js";
import type { AgentSession, SessionManager } from "../../sessions/index.js";
import { buildActiveSubagentSystemPromptAddition } from "../../subagent-active-context.js";
import { buildActiveSubagentSystemPromptAddition } from "../../subagents/registry/subagent-active-context.js";
import { resolveTranscriptPolicy, type TranscriptPolicy } from "../../transcript-policy.js";
import { getHistoryLimitFromSessionKey, limitHistoryTurns } from "../history.js";
import { log } from "../logger.js";
@@ -29,7 +29,7 @@ import type { AgentSession, SessionManager } from "../../sessions/index.js";
import {
leasePendingAgentSteeringItems,
prependAgentSteeringPrompt,
} from "../../subagent-registry.js";
} from "../../subagents/registry/subagent-registry.js";
import {
appendModelIdentitySystemPrompt,
buildModelIdentityPromptLine,
@@ -15,7 +15,7 @@ const mocks = vi.hoisted(() => ({
warn: vi.fn(),
}));
vi.mock("../../subagent-registry.js", () => ({
vi.mock("../../subagents/registry/subagent-registry.js", () => ({
releasePendingAgentSteeringItems: mocks.releasePendingSteering,
}));
vi.mock("../google-prompt-cache.js", () => ({
@@ -4,7 +4,7 @@ import {
buildHeartbeatOutcomeContext,
claimHeartbeatOutcomeForRun,
} from "../../../infra/heartbeat-outcome-store.js";
import { releasePendingAgentSteeringItems } from "../../subagent-registry.js";
import { releasePendingAgentSteeringItems } from "../../subagents/registry/subagent-registry.js";
import { prepareGooglePromptCacheStreamFn } from "../google-prompt-cache.js";
import { log } from "../logger.js";
import { resolveEmbeddedAgentApiKey } from "../stream-resolution.js";
@@ -11,7 +11,7 @@ import { resolveImageSanitizationLimits } from "../../image-sanitization.js";
import type { AgentMessage } from "../../runtime/index.js";
import type { SandboxContext } from "../../sandbox/types.js";
import type { AgentSession } from "../../sessions/index.js";
import { ackPendingAgentSteeringItems } from "../../subagent-registry.js";
import { ackPendingAgentSteeringItems } from "../../subagents/registry/subagent-registry.js";
import { normalizeAssistantReplayContent } from "../replay-history.js";
import { updateActiveEmbeddedRunSnapshot } from "../runs.js";
import {
@@ -10,7 +10,7 @@ import {
type AgentRunAttemptFailureSource,
} from "../../agent-run-terminal-outcome.js";
import type { AgentMessage } from "../../runtime/index.js";
import { settleRequesterAfterSessionSpawns } from "../../subagent-registry.js";
import { settleRequesterAfterSessionSpawns } from "../../subagents/registry/subagent-registry.js";
import type { NormalizedUsage } from "../../usage.js";
import { log } from "../logger.js";
import type { PromptCacheBreak, PromptCacheChange } from "../prompt-cache-observability.js";
@@ -6,7 +6,7 @@ import {
runAgentHarnessSettledTurnFinalization,
} from "../../harness/selection.js";
import type { AgentHarness } from "../../harness/types.js";
import { settleRequesterAfterSessionSpawns } from "../../subagent-registry.js";
import { settleRequesterAfterSessionSpawns } from "../../subagents/registry/subagent-registry.js";
import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js";
/**
@@ -50,7 +50,7 @@ import type { AgentRunSessionTarget } from "../../run-session-target.js";
import type { AgentMessage } from "../../runtime/index.js";
import type { ScheduledToolPolicyContext } from "../../scheduled-tool-policy.js";
import type { SessionManager } from "../../sessions/index.js";
import type { TrustedSubagentCompletionHandoff } from "../../subagent-announce-handoff.js";
import type { TrustedSubagentCompletionHandoff } from "../../subagents/announce/subagent-announce-handoff.js";
import type { SilentReplyPromptMode } from "../../system-prompt.types.js";
import type { PromptMode } from "../../system-prompt.types.js";
import type { EmbeddedAgentExecutionPhase } from "../execution-phase.js";
@@ -1,4 +1,4 @@
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
type MainRecoveryStateFields = Pick<
SessionEntry,
@@ -1,6 +1,6 @@
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import { mergeRestartRecoveryTerminalRunIds } from "../config/sessions/restart-recovery-state.js";
import { retryAsync } from "../infra/retry.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import { mergeRestartRecoveryTerminalRunIds } from "../../config/sessions/restart-recovery-state.js";
import { retryAsync } from "../../infra/retry.js";
import {
buildMainSessionRecoveryClearPatch,
type MainRecoveryStateFields,
@@ -19,5 +19,5 @@ export function scheduleMainSessionRecoveryPendingTarget(
() => {}, // Startup recovery remains the fallback if this optional module cannot load.
);
}
import { getRuntimeConfig } from "../config/io.js";
import { getGatewayRecoveryRuntime } from "../gateway/server-recovery-runtime-context.js";
import { getRuntimeConfig } from "../../config/io.js";
import { getGatewayRecoveryRuntime } from "../../gateway/server-recovery-runtime-context.js";
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import { projectMainSessionRecoveryLifecycle } from "./main-session-recovery-lifecycle.js";
function recoveryEntry(params?: {
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import type {
InternalSessionEntry as SessionEntry,
MainRestartRecoveryState,
} from "../config/sessions.js";
} from "../../config/sessions.js";
import { transitionMainSessionRecovery } from "./main-session-recovery-state.js";
const executionIdentity = (runId: string) => ({
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import type {
InternalSessionEntry as SessionEntry,
MainRestartRecoveryState,
} from "../config/sessions.js";
} from "../../config/sessions.js";
import { buildMainSessionRecoveryClearPatch } from "./main-session-recovery-clear.js";
import { projectMainSessionRecoveryLifecycle } from "./main-session-recovery-lifecycle.js";
import { transitionMainSessionRecovery } from "./main-session-recovery-state.js";
@@ -1,14 +1,18 @@
import {
PENDING_FINAL_DELIVERY_CLEAR_PATCH,
sanitizePendingFinalDeliveryText,
} from "../auto-reply/reply/pending-final-delivery.js";
} from "../../auto-reply/reply/pending-final-delivery.js";
import type {
InternalSessionEntry as SessionEntry,
MainRestartRecoveryState,
RestartRecoveryRun,
} from "../config/sessions.js";
import { buildRestartRecoveryClaimCleanupPatch } from "../config/sessions/restart-recovery-state.js";
import { isAcpSessionKey, isCronSessionKey, isSubagentSessionKey } from "../routing/session-key.js";
} from "../../config/sessions.js";
import { buildRestartRecoveryClaimCleanupPatch } from "../../config/sessions/restart-recovery-state.js";
import {
isAcpSessionKey,
isCronSessionKey,
isSubagentSessionKey,
} from "../../routing/session-key.js";
import { buildMainSessionRecoveryClearPatch } from "./main-session-recovery-clear.js";
import type {
MainSessionRecoveryCommand,
@@ -1,16 +1,16 @@
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js";
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import * as sessionAccessor from "../config/sessions/session-accessor.js";
import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import * as sessionAccessor from "../../config/sessions/session-accessor.js";
import {
applySessionEntryLifecycleMutation,
listSessionEntries,
} from "../config/sessions/session-accessor.js";
} from "../../config/sessions/session-accessor.js";
import {
getAgentEventLifecycleGeneration,
rotateAgentEventLifecycleGeneration,
} from "../infra/agent-events.js";
} from "../../infra/agent-events.js";
import {
claimMainSessionRecoveryOwner,
commitMainSessionRecovery,
@@ -1,7 +1,7 @@
import { randomUUID } from "node:crypto";
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import { applySessionEntryReplacements } from "../config/sessions/session-accessor.js";
import { getAgentEventLifecycleGeneration } from "../infra/agent-events.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import { applySessionEntryReplacements } from "../../config/sessions/session-accessor.js";
import { getAgentEventLifecycleGeneration } from "../../infra/agent-events.js";
import {
retryMainSessionRecoveryMutation,
scheduleMainSessionRecoveryMutation,
@@ -2,7 +2,7 @@ import type {
InternalSessionEntry as SessionEntry,
MainRestartRecoveryState,
RestartRecoveryRun,
} from "../config/sessions.js";
} from "../../config/sessions.js";
type MainSessionRecoveryExecutionIdentity = NonNullable<
MainRestartRecoveryState["executionIdentity"]
@@ -1,6 +1,6 @@
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import type { SessionEntry } from "../config/sessions.js";
import { loadExactSessionEntry } from "../config/sessions/session-accessor.js";
import type { SessionEntry } from "../../config/sessions.js";
import { loadExactSessionEntry } from "../../config/sessions/session-accessor.js";
export type ExpectedRestartRecoveryClaim = {
canonicalSessionKey?: string;
@@ -1,35 +1,36 @@
import { randomUUID } from "node:crypto";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { GatewayClientRequestError } from "../../packages/gateway-client/src/index.js";
import { isExecutionIdentityCollectionEnabled } from "../audit/audit-config.js";
import { createExecutionIdentityAdmissionToken } from "../audit/execution-identity-admission.js";
import { sanitizePendingFinalDeliveryText } from "../auto-reply/reply/pending-final-delivery.js";
import type { SessionEntry } from "../config/sessions.js";
import { GatewayClientRequestError } from "../../../packages/gateway-client/src/index.js";
import { isExecutionIdentityCollectionEnabled } from "../../audit/audit-config.js";
import { createExecutionIdentityAdmissionToken } from "../../audit/execution-identity-admission.js";
import { sanitizePendingFinalDeliveryText } from "../../auto-reply/reply/pending-final-delivery.js";
import type { SessionEntry } from "../../config/sessions.js";
import {
buildRestartRecoveryClaimCleanupPatch,
hasRestartRecoveryTerminalRun,
resolveRestartRecoveryChannelAuthority,
} from "../config/sessions/restart-recovery-state.js";
import { applySessionEntryReplacements } from "../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { isTrustedMessageActionTurnIngress } from "../gateway/message-action-turn-capability.js";
import type { GatewayRecoveryRuntime } from "../gateway/server-instance-runtime.types.js";
import type { AgentRunRequest } from "../gateway/server-methods/agent-request-types.js";
import { getAgentEventLifecycleGeneration } from "../infra/agent-events.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { findRestartRecoveryUnsafeReplyHook } from "../plugins/restart-recovery-hook-safety.js";
import { withPluginRuntimeRegistryScope } from "../plugins/runtime/gateway-request-scope.js";
import { CommandLane } from "../process/lanes.js";
import { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
import { MAIN_SESSION_RESTART_RECOVERY_SOURCE_TOOL } from "../sessions/input-provenance.js";
import { resolveSendPolicy } from "../sessions/send-policy.js";
} from "../../config/sessions/restart-recovery-state.js";
import { applySessionEntryReplacements } from "../../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { isTrustedMessageActionTurnIngress } from "../../gateway/message-action-turn-capability.js";
import type { GatewayRecoveryRuntime } from "../../gateway/server-instance-runtime.types.js";
import type { AgentRunRequest } from "../../gateway/server-methods/agent-request-types.js";
import { getAgentEventLifecycleGeneration } from "../../infra/agent-events.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { findRestartRecoveryUnsafeReplyHook } from "../../plugins/restart-recovery-hook-safety.js";
import { withPluginRuntimeRegistryScope } from "../../plugins/runtime/gateway-request-scope.js";
import { CommandLane } from "../../process/lanes.js";
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import { MAIN_SESSION_RESTART_RECOVERY_SOURCE_TOOL } from "../../sessions/input-provenance.js";
import { resolveSendPolicy } from "../../sessions/send-policy.js";
import {
deliveryContextFromSession,
normalizeDeliveryContext,
type DeliveryContext,
} from "../utils/delivery-context.shared.js";
import { isDeliverableMessageChannel } from "../utils/message-channel.js";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "./agent-scope.js";
} from "../../utils/delivery-context.shared.js";
import { isDeliverableMessageChannel } from "../../utils/message-channel.js";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agent-scope.js";
import { loadAgentRuntimePluginRegistryHandle } from "../runtime-plugins.js";
import { buildMainSessionRecoveryClearPatch } from "./main-session-recovery-clear.js";
import {
repairMainSessionRecoveryMutation,
@@ -44,7 +45,6 @@ import {
} from "./main-session-recovery-state.js";
import { commitMainSessionRecovery } from "./main-session-recovery-store.js";
import { normalizeFiniteTimestamp } from "./main-session-restart-recovery-shared.js";
import { loadAgentRuntimePluginRegistryHandle } from "./runtime-plugins.js";
const log = createSubsystemLogger("main-session-restart-recovery");
const RESTART_RECOVERY_RESUME_MESSAGE =
@@ -1,23 +1,23 @@
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import { buildRestartRecoveryClaimCleanupPatch } from "../config/sessions/restart-recovery-state.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import { buildRestartRecoveryClaimCleanupPatch } from "../../config/sessions/restart-recovery-state.js";
import {
applySessionEntryReplacements,
persistSessionTranscriptTurn,
type SessionTranscriptTurnLifecyclePatch,
updateSessionEntry,
} from "../config/sessions/session-accessor.js";
} from "../../config/sessions/session-accessor.js";
import {
hasInterSessionUserProvenance,
isCompletionReportInputProvenance,
} from "../sessions/input-provenance.js";
import { buildRunUserTurnIdempotencyKey } from "../sessions/user-turn-transcript.js";
import { isAnnounceRunId } from "./announce-idempotency.js";
} from "../../sessions/input-provenance.js";
import { buildRunUserTurnIdempotencyKey } from "../../sessions/user-turn-transcript.js";
import { isAnnounceRunId } from "../announce-idempotency.js";
import {
getTranscriptMessageRole as getMessageRole,
isTerminalSilentAssistantMessage,
readTerminalSourceReplyDeliveryMirror,
} from "./embedded-agent-runner/message-visibility.js";
} from "../embedded-agent-runner/message-visibility.js";
import { buildMainSessionRecoveryClearPatch } from "./main-session-recovery-clear.js";
import { isRestartAbortTailArtifact } from "./main-session-restart-recovery-resume-policy.js";
import { buildRestartRecoveryExpectedState, log } from "./main-session-restart-recovery-shared.js";
@@ -1,8 +1,8 @@
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import { loadSessionEntry } from "../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { GatewayRecoveryRuntime } from "../gateway/server-instance-runtime.types.js";
import { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import { loadSessionEntry } from "../../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { GatewayRecoveryRuntime } from "../../gateway/server-instance-runtime.types.js";
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import type { MainSessionRecoveryObservation } from "./main-session-recovery-state.js";
import { commitMainSessionRecovery } from "./main-session-recovery-store.js";
import { resolveRestartRecoveryDeliveryContext } from "./main-session-restart-dispatch.js";
@@ -1,20 +1,21 @@
import { randomUUID } from "node:crypto";
import path from "node:path";
import { resolveStateDir } from "../config/paths.js";
import { resolveStateDir } from "../../config/paths.js";
import {
type InternalSessionEntry as SessionEntry,
type RestartRecoveryRun,
resolveAllAgentSessionStoreTargetsSync,
} from "../config/sessions.js";
import { applySessionEntryReplacements } from "../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { resolveGatewaySessionStoreTarget } from "../gateway/session-utils.js";
import { getAgentEventLifecycleGeneration } from "../infra/agent-events.js";
import { listAgentRunsForSession } from "../infra/agent-run-registry.js";
} from "../../config/sessions.js";
import { applySessionEntryReplacements } from "../../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { resolveGatewaySessionStoreTarget } from "../../gateway/session-utils.js";
import { getAgentEventLifecycleGeneration } from "../../infra/agent-events.js";
import { listAgentRunsForSession } from "../../infra/agent-run-registry.js";
import {
listActiveEmbeddedRunSessionIds,
listActiveEmbeddedRunSessionKeys,
} from "./embedded-agent-runner/run-state.js";
} from "../embedded-agent-runner/run-state.js";
import { resolveAgentSessionDirs } from "../session-dirs.js";
import {
isMainRestartRecoveryCandidate,
normalizeMainSessionRecoveryRunFences,
@@ -27,7 +28,6 @@ import {
normalizeStringSet,
resolveRestartRecoveryStorePaths,
} from "./main-session-restart-recovery-shared.js";
import { resolveAgentSessionDirs } from "./session-dirs.js";
async function markRecoveryStore(params: {
storePath: string;
@@ -1,14 +1,14 @@
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import type {
SessionTranscriptTurnExpectedState,
SessionTranscriptTurnLifecyclePatch,
} from "../config/sessions/session-accessor.js";
import { appendAssistantMessageToSessionTranscript } from "../config/sessions/transcript.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { GatewayRecoveryRuntime } from "../gateway/server-instance-runtime.types.js";
import { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
import type { DeliveryContext } from "../utils/delivery-context.shared.js";
import { resolveDefaultAgentId } from "./agent-scope-config.js";
} from "../../config/sessions/session-accessor.js";
import { appendAssistantMessageToSessionTranscript } from "../../config/sessions/transcript.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { GatewayRecoveryRuntime } from "../../gateway/server-instance-runtime.types.js";
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import type { DeliveryContext } from "../../utils/delivery-context.shared.js";
import { resolveDefaultAgentId } from "../agent-scope-config.js";
import type { MainSessionRecoveryObservation } from "./main-session-recovery-state.js";
import { commitMainSessionRecovery } from "./main-session-recovery-store.js";
import { buildUnresumableSessionNoticeIdempotencyKey } from "./main-session-restart-claim.js";
@@ -1,16 +1,16 @@
import { describe, expect, it, vi } from "vitest";
import { resolveMainSessionResumePolicy } from "./main-session-restart-recovery-resume-policy.js";
vi.mock("./code-mode-control-tools.js", () => ({
vi.mock("../code-mode-control-tools.js", () => ({
CODE_MODE_EXEC_TOOL_NAME: "exec",
CODE_MODE_WAIT_TOOL_NAME: "wait",
}));
vi.mock("./tool-replay-safety.js", () => ({
vi.mock("../tool-replay-safety.js", () => ({
isAgentToolReplaySafe: ({ name }: { name?: string }) => name === "read",
}));
vi.mock("./run-termination.js", () => ({
vi.mock("../run-termination.js", () => ({
AGENT_RUN_RESTART_ABORT_ERROR: "agent run aborted for restart",
AGENT_RUN_RESTART_ABORT_ERROR_CODE: "OPENCLAW_RESTART_ABORT",
}));
@@ -1,17 +1,17 @@
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import { CODE_MODE_EXEC_TOOL_NAME, CODE_MODE_WAIT_TOOL_NAME } from "./code-mode-control-tools.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import { CODE_MODE_EXEC_TOOL_NAME, CODE_MODE_WAIT_TOOL_NAME } from "../code-mode-control-tools.js";
import {
getTranscriptMessageRole as getMessageRole,
isIntermediateAssistantTranscriptMessage,
isMeaningfulTranscriptMessage,
readTerminalSourceReplyDeliveryMirror,
} from "./embedded-agent-runner/message-visibility.js";
} from "../embedded-agent-runner/message-visibility.js";
import {
AGENT_RUN_RESTART_ABORT_ERROR,
AGENT_RUN_RESTART_ABORT_ERROR_CODE,
} from "./run-termination.js";
import { isAgentToolReplaySafe } from "./tool-replay-safety.js";
} from "../run-termination.js";
import { isAgentToolReplaySafe } from "../tool-replay-safety.js";
function readDeliveredTerminalSourceReplyToolCallId(
messages: readonly unknown[],
@@ -1,15 +1,15 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { GatewayRecoveryRuntime } from "../gateway/server-instance-runtime.types.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { GatewayRecoveryRuntime } from "../../gateway/server-instance-runtime.types.js";
import {
getAgentEventLifecycleGeneration,
isAgentEventLifecycleGenerationCurrent,
} from "../infra/agent-events.js";
import { sleepWithAbort } from "../infra/backoff.js";
import { runWithGatewayIndependentRootWorkAdmission } from "../process/gateway-work-admission.js";
} from "../../infra/agent-events.js";
import { sleepWithAbort } from "../../infra/backoff.js";
import { runWithGatewayIndependentRootWorkAdmission } from "../../process/gateway-work-admission.js";
import {
beginSessionWorkAdmission,
cancelSessionWorkAdmissionHandoff,
} from "../sessions/session-lifecycle-admission.js";
} from "../../sessions/session-lifecycle-admission.js";
import {
loadExpectedRestartRecoveryClaim,
type ExpectedRestartRecoveryClaim,
@@ -1,18 +1,18 @@
import path from "node:path";
import { resolveStateDir } from "../config/paths.js";
import { resolveStateDir } from "../../config/paths.js";
import {
listConfiguredSessionStoreAgentIds,
resolveStorePath,
type InternalSessionEntry as SessionEntry,
resolveAllAgentSessionStoreTargetsSync,
} from "../config/sessions.js";
} from "../../config/sessions.js";
import {
hasSessionEntriesByStatusReadOnly,
type SessionTranscriptTurnExpectedState,
} from "../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveAgentSessionDirs } from "./session-dirs.js";
} from "../../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { resolveAgentSessionDirs } from "../session-dirs.js";
export const log = createSubsystemLogger("main-session-restart-recovery");
export const DEFAULT_RECOVERY_DELAY_MS = 5_000;
@@ -4,22 +4,22 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coe
import {
type InternalSessionEntry as SessionEntry,
resolveSessionWorkStartError,
} from "../config/sessions.js";
} from "../../config/sessions.js";
import {
listSessionEntriesByStatus,
loadExactSessionEntry,
} from "../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { GatewayRecoveryRuntime } from "../gateway/server-instance-runtime.types.js";
import { readSessionMessagesAsync } from "../gateway/session-transcript-readers.js";
import { resolveGatewaySessionStoreTarget } from "../gateway/session-utils.js";
import { getAgentEventLifecycleGeneration } from "../infra/agent-events.js";
import { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
import { resolveDefaultAgentId } from "./agent-scope-config.js";
} from "../../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { GatewayRecoveryRuntime } from "../../gateway/server-instance-runtime.types.js";
import { readSessionMessagesAsync } from "../../gateway/session-transcript-readers.js";
import { resolveGatewaySessionStoreTarget } from "../../gateway/session-utils.js";
import { getAgentEventLifecycleGeneration } from "../../infra/agent-events.js";
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import { resolveDefaultAgentId } from "../agent-scope-config.js";
import {
listActiveEmbeddedRunSessionIds,
listActiveEmbeddedRunSessionKeys,
} from "./embedded-agent-runner/run-state.js";
} from "../embedded-agent-runner/run-state.js";
import { isMainRestartRecoveryCandidate } from "./main-session-recovery-state.js";
import { commitMainSessionRecovery } from "./main-session-recovery-store.js";
import {
@@ -3,66 +3,76 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { GatewayClientRequestError } from "../../packages/gateway-client/src/index.js";
import { createDeferred } from "../../test/helpers/promise.js";
import { markInboundContextLabel } from "../auto-reply/reply/inbound-context-marker.js";
import type { ChannelOutboundAdapter } from "../channels/plugins/types.public.js";
import type { CliDeps } from "../cli/outbound-send-deps.js";
import type { OpenClawConfig } from "../config/config.js";
import type { InternalSessionEntry as SessionEntry } from "../config/sessions.js";
import * as sessionAccessor from "../config/sessions/session-accessor.js";
import { GatewayClientRequestError } from "../../../packages/gateway-client/src/index.js";
import { createDeferred } from "../../../test/helpers/promise.js";
import { markInboundContextLabel } from "../../auto-reply/reply/inbound-context-marker.js";
import type { ChannelOutboundAdapter } from "../../channels/plugins/types.public.js";
import type { CliDeps } from "../../cli/outbound-send-deps.js";
import type { OpenClawConfig } from "../../config/config.js";
import type { InternalSessionEntry as SessionEntry } from "../../config/sessions.js";
import * as sessionAccessor from "../../config/sessions/session-accessor.js";
import {
appendTranscriptMessage,
listSessionEntries,
loadSessionEntry as loadSessionEntryRaw,
loadTranscriptEvents,
replaceSessionEntry,
} from "../config/sessions/session-accessor.js";
import { callGateway } from "../gateway/call.js";
import type { GatewayRecoveryRuntime } from "../gateway/server-instance-runtime.types.js";
} from "../../config/sessions/session-accessor.js";
import { callGateway } from "../../gateway/call.js";
import type { GatewayRecoveryRuntime } from "../../gateway/server-instance-runtime.types.js";
import {
getAgentEventLifecycleGeneration,
resetAgentEventsForTest,
rotateAgentEventLifecycleGeneration,
} from "../infra/agent-events.js";
import { registerAgentRunContext } from "../infra/agent-run-registry.js";
} from "../../infra/agent-events.js";
import { registerAgentRunContext } from "../../infra/agent-run-registry.js";
import {
initializeGlobalHookRunner,
resetGlobalHookRunner,
} from "../plugins/hook-runner-global.js";
import { addTestHook } from "../plugins/hooks.test-fixtures.js";
import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { getPluginRuntimeGatewayRequestScope } from "../plugins/runtime/gateway-request-scope.js";
} from "../../plugins/hook-runner-global.js";
import { addTestHook } from "../../plugins/hooks.test-fixtures.js";
import { createEmptyPluginRegistry } from "../../plugins/registry-empty.js";
import { setActivePluginRegistry } from "../../plugins/runtime.js";
import { getPluginRuntimeGatewayRequestScope } from "../../plugins/runtime/gateway-request-scope.js";
import {
getActiveGatewayRootWorkCount,
resetGatewayWorkAdmission,
tryBeginGatewaySuspendAdmission,
} from "../process/gateway-work-admission.js";
} from "../../process/gateway-work-admission.js";
import {
interruptSessionWorkAdmissions,
isSessionLifecycleMutationActive,
isSessionWorkAdmissionActive,
runExclusiveSessionLifecycleMutation,
} from "../sessions/session-lifecycle-admission.js";
} from "../../sessions/session-lifecycle-admission.js";
import {
closeOpenClawAgentDatabasesForTest,
openOpenClawAgentDatabase,
} from "../state/openclaw-agent-db.js";
import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js";
import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js";
import { normalizeSessionDeliveryState } from "../utils/delivery-context.shared.js";
import { deliverAgentCommandResult } from "./command/delivery.js";
import { setActiveEmbeddedRunLifecycleGeneration } from "./embedded-agent-runner/run-state.js";
} from "../../state/openclaw-agent-db.js";
import { closeOpenClawStateDatabaseForTest } from "../../state/openclaw-state-db.js";
import { createOutboundTestPlugin, createTestRegistry } from "../../test-utils/channel-plugins.js";
import { normalizeSessionDeliveryState } from "../../utils/delivery-context.shared.js";
import { deliverAgentCommandResult } from "../command/delivery.js";
import { setActiveEmbeddedRunLifecycleGeneration } from "../embedded-agent-runner/run-state.js";
import {
clearActiveEmbeddedRun,
setActiveEmbeddedRun,
type EmbeddedAgentQueueHandle,
} from "./embedded-agent-runner/runs.js";
} from "../embedded-agent-runner/runs.js";
import {
INTERNAL_RUNTIME_CONTEXT_BEGIN,
INTERNAL_RUNTIME_CONTEXT_END,
} from "./internal-runtime-context.js";
} from "../internal-runtime-context.js";
import { AGENT_RUN_RESTART_ABORT_ERROR_CODE } from "../run-termination.js";
import {
createAssistantToolCallMessage,
createSessionEntry,
createSessionStore,
type SessionEntryFixture,
expectRecord,
mockCallArg,
waitForFast,
} from "../subagent-test-fixtures.test-helpers.js";
import * as recoveryOwnerRelease from "./main-session-recovery-owner-release.js";
import { claimMainSessionRecoveryOwner } from "./main-session-recovery-store.js";
import { resolveRestartRecoveryStorePaths } from "./main-session-restart-recovery-shared.js";
@@ -74,16 +84,6 @@ import {
scheduleRestartAbortedMainSessionRecoveryAfterOwnerRelease,
scheduleRestartAbortedMainSessionRecovery as scheduleRestartAbortedMainSessionRecoveryBase,
} from "./main-session-restart-recovery.js";
import { AGENT_RUN_RESTART_ABORT_ERROR_CODE } from "./run-termination.js";
import {
createAssistantToolCallMessage,
createSessionEntry,
createSessionStore,
type SessionEntryFixture,
expectRecord,
mockCallArg,
waitForFast,
} from "./subagent-test-fixtures.test-helpers.js";
const transcriptMocks = vi.hoisted(() => ({
appendAssistantMessageToSessionTranscript: vi.fn(),
@@ -101,7 +101,7 @@ const executionIdentityEnabledConfig = {
logging: { audit: { executionIdentity: true } },
} satisfies OpenClawConfig;
vi.mock("../gateway/call.js", () => ({
vi.mock("../../gateway/call.js", () => ({
callGateway: vi.fn(async () => ({ runId: "run-resumed" })),
}));
@@ -144,8 +144,8 @@ function gatewayParams(): Record<string, unknown> {
return expectRecord(mockCallArg(callGateway).params, "gateway params");
}
vi.mock("../config/sessions/transcript.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/sessions/transcript.js")>();
vi.mock("../../config/sessions/transcript.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../config/sessions/transcript.js")>();
transcriptMocks.appendAssistantMessageToSessionTranscript.mockImplementation(
actual.appendAssistantMessageToSessionTranscript,
);
@@ -156,11 +156,11 @@ vi.mock("../config/sessions/transcript.js", async (importOriginal) => {
};
});
vi.mock("../plugins/restart-recovery-hook-safety.js", () => ({
vi.mock("../../plugins/restart-recovery-hook-safety.js", () => ({
findRestartRecoveryUnsafeReplyHook: runtimePluginMocks.findRestartRecoveryUnsafeReplyHook,
}));
vi.mock("./runtime-plugins.js", () => ({
vi.mock("../runtime-plugins.js", () => ({
loadAgentRuntimePluginRegistryHandle: runtimePluginMocks.loadAgentRuntimePluginRegistryHandle,
}));
@@ -529,7 +529,7 @@ describe("sessions_yield completion ownership", () => {
["the controller when the run owner is blank", " ", controllerSessionKey],
["the controller when the run owner is absent", undefined, controllerSessionKey],
] as const)("records yield intent against %s", async (_, runSessionKey, expectedSessionKey) => {
const registry = await import("./subagent-registry.js");
const registry = await import("./subagents/registry/subagent-registry.js");
const markRequesterTurnYielded = vi
.spyOn(registry, "markRequesterTurnYielded")
.mockReturnValue(1);
+1 -1
View File
@@ -25,7 +25,7 @@ vi.mock("../gateway/call.js", () => ({
callGateway: (opts: unknown) => callGatewayMock(opts),
}));
const loadSessionEntryByKeyMock = vi.fn();
vi.mock("./subagent-announce-delivery.js", () => ({
vi.mock("./subagents/announce/subagent-announce-delivery.js", () => ({
loadSessionEntryByKey: (sessionKey: string) => loadSessionEntryByKeyMock(sessionKey),
}));
@@ -11,7 +11,7 @@ import {
import {
addSubagentRunForTests,
resetSubagentRegistryForTests,
} from "./subagent-registry.test-helpers.js";
} from "./subagents/registry/subagent-registry.test-helpers.js";
import { createPerSenderSessionConfig } from "./test-helpers/session-config.js";
import { createSubagentsTool } from "./tools/subagents-tool.js";
@@ -11,7 +11,7 @@ const hoisted = vi.hoisted(() => ({
configOverride: {} as Record<string, unknown>,
}));
let resetSubagentRegistryForTests: typeof import("./subagent-registry.test-helpers.js").resetSubagentRegistryForTests;
let resetSubagentRegistryForTests: typeof import("./subagents/registry/subagent-registry.test-helpers.js").resetSubagentRegistryForTests;
let spawnSubagentDirect: typeof import("./subagents/spawn/subagent-spawn.js").spawnSubagentDirect;
function resolveAgentConfigFromList(cfg: Record<string, unknown>, agentId: string) {
@@ -19,7 +19,7 @@ import {
import {
getLatestSubagentRunByChildSessionKey,
resetSubagentRegistryForTests,
} from "./subagent-registry.test-helpers.js";
} from "./subagents/registry/subagent-registry.test-helpers.js";
const fastModeEnv = vi.hoisted(() => {
const previous = process.env.OPENCLAW_TEST_FAST;
@@ -2,18 +2,20 @@
import { vi, type Mock } from "vitest";
import type { SessionRunStatus } from "../../packages/gateway-protocol/src/schema/sessions-row.js";
import type { SubagentLifecycleHookRunner } from "../plugins/hooks.js";
import { resolveRequesterStoreKey } from "./subagent-requester-store-key.js";
import { resolveRequesterStoreKey } from "./subagents/announce/subagent-requester-store-key.js";
type SessionsSpawnTestConfig = ReturnType<
(typeof import("../config/config.js"))["getRuntimeConfig"]
>;
type SessionsSpawnHookRunner = SubagentLifecycleHookRunner | null;
type CaptureSubagentCompletionReply =
(typeof import("./subagent-announce.js"))["captureSubagentCompletionReply"];
type RunSubagentAnnounceFlow = (typeof import("./subagent-announce.js"))["runSubagentAnnounceFlow"];
(typeof import("./subagents/announce/subagent-announce.js"))["captureSubagentCompletionReply"];
type RunSubagentAnnounceFlow =
(typeof import("./subagents/announce/subagent-announce.js"))["runSubagentAnnounceFlow"];
type CreateSessionsSpawnTool =
(typeof import("./tools/sessions-spawn-tool.js"))["createSessionsSpawnTool"];
type SubagentRegistryTesting = (typeof import("./subagent-registry.test-helpers.js"))["testing"];
type SubagentRegistryTesting =
(typeof import("./subagents/registry/subagent-registry.test-helpers.js"))["testing"];
type SubagentSpawnTesting =
(typeof import("./subagents/spawn/subagent-spawn.test-support.js"))["testing"];
type CreateOpenClawToolsOpts = Parameters<CreateSessionsSpawnTool>[0];
@@ -196,7 +198,7 @@ export async function getSessionsSpawnTool(opts: CreateOpenClawToolsOpts) {
const [{ testing: subagentSpawnTesting }, { testing: subagentRegistryTesting }] =
await Promise.all([
import("./subagents/spawn/subagent-spawn.test-support.js"),
import("./subagent-registry.test-helpers.js"),
import("./subagents/registry/subagent-registry.test-helpers.js"),
]);
cachedSubagentSpawnTesting = subagentSpawnTesting;
cachedSubagentRegistryTesting = subagentRegistryTesting;
@@ -3,8 +3,8 @@ import { vi } from "vitest";
import { testing as queueCleanupTesting } from "../auto-reply/reply/queue/cleanup.test-support.js";
import type { CallGatewayOptions } from "../gateway/call.js";
import type { MockFn } from "../test-utils/vitest-mock-fn.js";
import { testing as subagentAnnounceTesting } from "./subagent-announce.js";
import { testing as subagentControlTesting } from "./subagent-control.test-support.js";
import { testing as subagentAnnounceTesting } from "./subagents/announce/subagent-announce.js";
import { testing as subagentControlTesting } from "./subagents/registry/subagent-control.test-support.js";
type LoadedConfig = ReturnType<(typeof import("../config/config.js"))["getRuntimeConfig"]>;
+1 -1
View File
@@ -3,7 +3,7 @@ import {
getLatestSubagentRunByChildSessionKey,
getSubagentRunByRunId,
recordSwarmStructuredOutput,
} from "./subagent-registry.js";
} from "./subagents/registry/subagent-registry.js";
import { resolveSwarmConfig } from "./subagents/swarm/swarm-config.js";
import { createAgentsWaitTool } from "./tools/agents-wait-tool.js";
import type { AnyAgentTool } from "./tools/common.js";
+2 -1
View File
@@ -691,7 +691,8 @@ export function createOpenClawTools(
onBeforeYield:
requesterSessionKey && requesterTurnRunId
? async () => {
const { markRequesterTurnYielded } = await import("./subagent-registry.js");
const { markRequesterTurnYielded } =
await import("./subagents/registry/subagent-registry.js");
markRequesterTurnYielded({ requesterSessionKey, requesterTurnRunId });
}
: undefined,
+3 -3
View File
@@ -16,14 +16,14 @@ import { resolveSenderToolPolicy } from "./sender-tool-policy.js";
import {
isTrustedSubagentCompletionHandoffForRun,
type TrustedSubagentCompletionHandoff,
} from "./subagent-announce-handoff.js";
} from "./subagents/announce/subagent-announce-handoff.js";
import { resolveRequesterStoreKey } from "./subagents/announce/subagent-requester-store-key.js";
import {
isSubagentEnvelopeSession,
resolvePersistedSubagentToolPolicyEnvelope,
resolveSubagentCapabilityStore,
type SessionCapabilityStore,
} from "./subagent-capabilities.js";
import { resolveRequesterStoreKey } from "./subagent-requester-store-key.js";
} from "./subagents/spawn/subagent-capabilities.js";
const MAX_DELEGATION_LINEAGE_DEPTH = 32;
@@ -1,6 +1,6 @@
import { failTransportStream, finalizeTransportStream } from "@openclaw/ai/transports";
import { describe, expect, it, vi } from "vitest";
import { resolveMainSessionResumePolicy } from "./main-session-restart-recovery-resume-policy.js";
import { resolveMainSessionResumePolicy } from "./main-session-recovery/main-session-restart-recovery-resume-policy.js";
import {
createAssistantOutput,
makeCompletionsModel,
+1 -1
View File
@@ -56,7 +56,7 @@ const bindingMocks = vi.hoisted(() => ({
listBySession: vi.fn(() => []),
}));
let resetSubagentRegistryForTests: typeof import("./subagent-registry.test-helpers.js").resetSubagentRegistryForTests;
let resetSubagentRegistryForTests: typeof import("./subagents/registry/subagent-registry.test-helpers.js").resetSubagentRegistryForTests;
let spawnSubagentDirect: typeof import("./subagents/spawn/subagent-spawn.js").spawnSubagentDirect;
function getGatewayRequests(): GatewayRequest[] {
+1 -1
View File
@@ -1,5 +1,5 @@
import type { SubagentLifecycleHookRunner } from "../plugins/hooks.js";
import { registerSubagentRun } from "./subagent-registry.js";
import { registerSubagentRun } from "./subagents/registry/subagent-registry.js";
type SpawnPipelinePhase = "initialize" | "dispatch" | "register";
+4 -4
View File
@@ -20,10 +20,10 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { getSessionBindingService } from "../infra/outbound/session-binding-service.js";
import { resolveAgentConfig } from "./agent-scope.js";
import { resolveChildAdmission, type ChildAdmissionCap } from "./child-admission.js";
import { resolveSubagentCapabilities } from "./subagent-capabilities.js";
import { getSubagentDepthFromSessionStore } from "./subagent-depth.js";
import { countActiveRunsForSession } from "./subagent-registry.js";
import { resolveSubagentTargetPolicy } from "./subagent-target-policy.js";
import { countActiveRunsForSession } from "./subagents/registry/subagent-registry.js";
import { resolveSubagentCapabilities } from "./subagents/spawn/subagent-capabilities.js";
import { getSubagentDepthFromSessionStore } from "./subagents/spawn/subagent-depth.js";
import { resolveSubagentTargetPolicy } from "./subagents/spawn/subagent-target-policy.js";
type SpawnMode = "run" | "session";
type SpawnBackendKind = "subagent" | "acp";
@@ -1,24 +0,0 @@
/**
* Runtime dependency barrel for subagent announcement delivery.
*
* Tests mock this module to isolate delivery logic from gateway, outbound
* message routing, queue settings, hooks, and embedded-run state.
*/
export { getRuntimeConfig } from "../config/config.js";
export { resolveAgentIdFromSessionKey, resolveStorePath } from "../config/sessions.js";
export { loadSessionEntryReadOnly as loadSessionEntry } from "../config/sessions/session-accessor.js";
export { callGateway } from "../gateway/call.js";
export { dispatchGatewayMethodInProcess } from "../gateway/server-plugins.js";
export { resolveQueueSettings } from "../auto-reply/reply/queue.js";
export { resolveExternalBestEffortDeliveryTarget } from "../infra/outbound/best-effort-delivery.js";
export { sendMessage } from "../infra/outbound/message.js";
export { createBoundDeliveryRouter } from "../infra/outbound/bound-delivery-router.js";
export { resolveConversationIdFromTargets } from "../infra/outbound/conversation-id.js";
export { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
export {
formatEmbeddedAgentQueueFailureSummary,
isEmbeddedAgentRunActive,
isEmbeddedRunAbandoned,
queueEmbeddedAgentMessageWithOutcomeAsync,
resolveActiveEmbeddedRunSessionId,
} from "./embedded-agent-runner/runs.js";
-5
View File
@@ -1,5 +0,0 @@
/**
* Runtime seams used by subagent control for queue and embedded-run cancellation.
*/
export { clearSessionQueues } from "../auto-reply/reply/queue.js";
export { abortEmbeddedAgentRun, isEmbeddedAgentRunActive } from "./embedded-agent-runner/runs.js";
@@ -23,15 +23,19 @@ import {
} from "../tasks/task-runtime.test-helpers.js";
import { captureEnv } from "../test-utils/env.js";
import { cleanupSessionStateForTest } from "../test-utils/session-state-cleanup.js";
import { subagentRuns } from "./subagent-registry-memory.js";
import { persistSubagentRunsToDiskOrThrow } from "./subagent-registry-state.js";
import {
createSubagentRunRecord,
type SubagentRunRecordOverrides,
} from "./subagent-test-fixtures.test-helpers.js";
import { subagentRuns } from "./subagents/registry/subagent-registry-memory.js";
import { persistSubagentRunsToDiskOrThrow } from "./subagents/registry/subagent-registry-state.js";
import {
createCanonicalSubagentRunFixture,
createSubagentRegistryTestDeps,
readSubagentSessionStore,
writeSubagentSessionEntry,
} from "./subagent-registry.persistence.test-support.js";
import { loadSubagentRegistryFromSqlite } from "./subagent-registry.store.sqlite.js";
} from "./subagents/registry/subagent-registry.persistence.test-support.js";
import { loadSubagentRegistryFromSqlite } from "./subagents/registry/subagent-registry.store.sqlite.js";
import {
addSubagentRunForTests,
getSubagentRunByChildSessionKey,
@@ -39,12 +43,8 @@ import {
listSubagentRunsForRequester,
resetSubagentRegistryForTests,
testing,
} from "./subagent-registry.test-helpers.js";
import type { SubagentRunRecord } from "./subagent-registry.types.js";
import {
createSubagentRunRecord,
type SubagentRunRecordOverrides,
} from "./subagent-test-fixtures.test-helpers.js";
} from "./subagents/registry/subagent-registry.test-helpers.js";
import type { SubagentRunRecord } from "./subagents/registry/subagent-registry.types.js";
function consumeRecoveryAdmission(payload: Record<string, unknown>): SessionWorkAdmissionLease {
const sessionKey = String(payload.sessionKey);
-5
View File
@@ -1,5 +0,0 @@
/**
* Runtime seams used by subagent registry code for plugin/context-engine initialization.
*/
export { ensureContextEnginesInitialized } from "../context-engine/init.js";
export { resolveContextEngine } from "../context-engine/registry.js";
@@ -4,8 +4,8 @@ import type { SessionOrigin } from "../config/sessions/types.js";
import { normalizeLegacySessionEntryDelivery } from "../infra/state-migrations.legacy-session-store.js";
import type { DeliveryContext } from "../utils/delivery-context.types.js";
import type { AgentInternalEvent } from "./internal-events.js";
import type { RegisterSubagentRunParams } from "./subagent-registry-run-manager.js";
import type { SubagentRunRecord } from "./subagent-registry.types.js";
import type { RegisterSubagentRunParams } from "./subagents/registry/subagent-registry-run-manager.js";
import type { SubagentRunRecord } from "./subagents/registry/subagent-registry.types.js";
type GatewayRequest = { method?: string };
type GatewayResponse<TRequest, TResult> =
@@ -0,0 +1,24 @@
/**
* Runtime dependency barrel for subagent announcement delivery.
*
* Tests mock this module to isolate delivery logic from gateway, outbound
* message routing, queue settings, hooks, and embedded-run state.
*/
export { getRuntimeConfig } from "../../../config/config.js";
export { resolveAgentIdFromSessionKey, resolveStorePath } from "../../../config/sessions.js";
export { loadSessionEntryReadOnly as loadSessionEntry } from "../../../config/sessions/session-accessor.js";
export { callGateway } from "../../../gateway/call.js";
export { dispatchGatewayMethodInProcess } from "../../../gateway/server-plugins.js";
export { resolveQueueSettings } from "../../../auto-reply/reply/queue.js";
export { resolveExternalBestEffortDeliveryTarget } from "../../../infra/outbound/best-effort-delivery.js";
export { sendMessage } from "../../../infra/outbound/message.js";
export { createBoundDeliveryRouter } from "../../../infra/outbound/bound-delivery-router.js";
export { resolveConversationIdFromTargets } from "../../../infra/outbound/conversation-id.js";
export { getGlobalHookRunner } from "../../../plugins/hook-runner-global.js";
export {
formatEmbeddedAgentQueueFailureSummary,
isEmbeddedAgentRunActive,
isEmbeddedRunAbandoned,
queueEmbeddedAgentMessageWithOutcomeAsync,
resolveActiveEmbeddedRunSessionId,
} from "../../embedded-agent-runner/runs.js";
@@ -1,9 +1,9 @@
export * from "./subagent-announce-delivery.js";
type QueueMessageOptions =
import("./embedded-agent-runner/runs.js").EmbeddedAgentQueueMessageOptions;
import("../../embedded-agent-runner/runs.js").EmbeddedAgentQueueMessageOptions;
type QueueMessageOutcome =
import("./embedded-agent-runner/runs.js").EmbeddedAgentQueueMessageOutcome;
import("../../embedded-agent-runner/runs.js").EmbeddedAgentQueueMessageOutcome;
type DeliveryDeps = {
callGateway: typeof import("./subagent-announce-delivery.runtime.js").callGateway;
dispatchGatewayMethodInProcess: typeof import("./subagent-announce-delivery.runtime.js").dispatchGatewayMethodInProcess;
@@ -1,24 +1,36 @@
// Subagent announce delivery tests cover the last-mile routing used when child
// runs report progress or completion back to the requester session.
import { afterEach, describe, expect, it, vi } from "vitest";
import type { SessionEntry } from "../config/sessions.js";
import { OutboundDeliveryError } from "../infra/outbound/deliver-types.js";
import type { SessionEntry } from "../../../config/sessions.js";
import { OutboundDeliveryError } from "../../../infra/outbound/deliver-types.js";
import {
testing as sessionBindingServiceTesting,
registerSessionBindingAdapter,
} from "../infra/outbound/session-binding-service.js";
import { normalizeLegacySessionEntryDelivery } from "../infra/state-migrations.legacy-session-store.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
} from "../../../infra/outbound/session-binding-service.js";
import { normalizeLegacySessionEntryDelivery } from "../../../infra/state-migrations.legacy-session-store.js";
import { setActivePluginRegistry } from "../../../plugins/runtime.js";
import {
createChannelTestPluginBase,
createTestRegistry,
} from "../../../test-utils/channel-plugins.js";
import type {
EmbeddedAgentQueueMessageOptions,
EmbeddedAgentQueueMessageOutcome,
} from "./embedded-agent-runner/runs.js";
import type { AgentInternalEvent } from "./internal-events.js";
} from "../../embedded-agent-runner/runs.js";
import type { AgentInternalEvent } from "../../internal-events.js";
import {
INTERNAL_RUNTIME_CONTEXT_BEGIN,
INTERNAL_RUNTIME_CONTEXT_END,
} from "./internal-runtime-context.js";
} from "../../internal-runtime-context.js";
import {
createTaskCompletionEvent,
expectDeliveryPath,
expectRecordFields,
imageCompletionEvents,
mockCallArg,
musicCompletionEvents,
taskCompletionEvents,
} from "../../subagent-test-fixtures.test-helpers.js";
import {
callGateway as runtimeCallGateway,
dispatchGatewayMethodInProcess as runtimeDispatchGatewayMethodInProcess,
@@ -29,15 +41,6 @@ import {
resolveAnnounceOrigin,
resolveSubagentCompletionOrigin,
} from "./subagent-announce-origin.js";
import {
createTaskCompletionEvent,
expectDeliveryPath,
expectRecordFields,
imageCompletionEvents,
mockCallArg,
musicCompletionEvents,
taskCompletionEvents,
} from "./subagent-test-fixtures.test-helpers.js";
const sessionDeliveryQueueMocks = vi.hoisted(() => ({
enqueueClaimedSessionDelivery: vi.fn((_payload: unknown, _leaseMs: number) => ({
@@ -49,20 +52,20 @@ const sessionDeliveryQueueMocks = vi.hoisted(() => ({
scheduleSessionDelivery: vi.fn(async () => true),
}));
vi.mock("./subagent-completion-delivery.js", async (importOriginal) => ({
...(await importOriginal<typeof import("./subagent-completion-delivery.js")>()),
vi.mock("../completion/subagent-completion-delivery.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../completion/subagent-completion-delivery.js")>()),
admitCorrelatedSubagentSessionDelivery: (params: { payload: Record<string, unknown> }) =>
sessionDeliveryQueueMocks.enqueueClaimedSessionDelivery(params.payload, 125_000),
}));
vi.mock("../infra/session-delivery-queue.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../infra/session-delivery-queue.js")>()),
vi.mock("../../../infra/session-delivery-queue.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../../../infra/session-delivery-queue.js")>()),
enqueueClaimedSessionDelivery: sessionDeliveryQueueMocks.enqueueClaimedSessionDelivery,
releaseSessionDeliveryClaim: sessionDeliveryQueueMocks.releaseSessionDeliveryClaim,
}));
vi.mock("../infra/session-delivery-queue-runtime.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../infra/session-delivery-queue-runtime.js")>()),
vi.mock("../../../infra/session-delivery-queue-runtime.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../../../infra/session-delivery-queue-runtime.js")>()),
scheduleSessionDelivery: sessionDeliveryQueueMocks.scheduleSessionDelivery,
}));
@@ -6,34 +6,34 @@
import { clampTimerTimeoutMs } from "@openclaw/normalization-core/number-coercion";
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
import { normalizeUniqueTrimmedStringList } from "@openclaw/normalization-core/string-normalization";
import { completionRequiresMessageToolDelivery } from "../auto-reply/reply/completion-delivery-policy.js";
import { sanitizePendingFinalDeliveryText } from "../auto-reply/reply/pending-final-delivery.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { isFastTestRuntimeEnv } from "../infra/env.js";
import { isOutboundDeliveryError } from "../infra/outbound/deliver-types.js";
import { sourceDeliveryTargetsMatch } from "../infra/outbound/source-delivery-plan.js";
import { scheduleSessionDelivery } from "../infra/session-delivery-queue-runtime.js";
import { completionRequiresMessageToolDelivery } from "../../../auto-reply/reply/completion-delivery-policy.js";
import { sanitizePendingFinalDeliveryText } from "../../../auto-reply/reply/pending-final-delivery.js";
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
import { isFastTestRuntimeEnv } from "../../../infra/env.js";
import { isOutboundDeliveryError } from "../../../infra/outbound/deliver-types.js";
import { sourceDeliveryTargetsMatch } from "../../../infra/outbound/source-delivery-plan.js";
import { scheduleSessionDelivery } from "../../../infra/session-delivery-queue-runtime.js";
import {
enqueueClaimedSessionDelivery,
releaseSessionDeliveryClaim,
} from "../infra/session-delivery-queue.js";
import { stringifyRouteThreadId } from "../plugin-sdk/channel-route.js";
import { defaultRuntime } from "../runtime.js";
} from "../../../infra/session-delivery-queue.js";
import { stringifyRouteThreadId } from "../../../plugin-sdk/channel-route.js";
import { defaultRuntime } from "../../../runtime.js";
import {
isAgentMediatedCompletionSourceTool,
shouldPreserveUserFacingSessionStateForInputProvenance,
} from "../sessions/input-provenance.js";
import { deriveSessionChatTypeFromKey } from "../sessions/session-chat-type-shared.js";
import { isCronRunSessionKey, isCronSessionKey } from "../sessions/session-key-utils.js";
import { isNonTerminalAgentRunStatus } from "../shared/agent-run-status.js";
import { sessionDeliveryChannel } from "../utils/delivery-context.shared.js";
} from "../../../sessions/input-provenance.js";
import { deriveSessionChatTypeFromKey } from "../../../sessions/session-chat-type-shared.js";
import { isCronRunSessionKey, isCronSessionKey } from "../../../sessions/session-key-utils.js";
import { isNonTerminalAgentRunStatus } from "../../../shared/agent-run-status.js";
import { sessionDeliveryChannel } from "../../../utils/delivery-context.shared.js";
import {
INTERNAL_MESSAGE_CHANNEL,
isGatewayMessageChannel,
normalizeMessageChannel,
} from "../utils/message-channel.js";
import { sanitizeAgentRunTerminalReplyText } from "./agent-run-terminal-reply.js";
import { resolveDefaultAgentId } from "./agent-scope-config.js";
} from "../../../utils/message-channel.js";
import { sanitizeAgentRunTerminalReplyText } from "../../agent-run-terminal-reply.js";
import { resolveDefaultAgentId } from "../../agent-scope-config.js";
import {
getAgentCommandDeliveryFailure,
getGatewayAgentResult,
@@ -43,20 +43,25 @@ import {
hasPayloadOutcomeSendEvidence,
hasUnaccountedMessagingToolAggregateEvidence,
resolveExplicitFinalSourceReplyDeliveryEvidence,
} from "./embedded-agent-runner/delivery-evidence.js";
} from "../../embedded-agent-runner/delivery-evidence.js";
import {
hasIntentionalSilentAgentPayload,
hasVisibleAgentPayload,
} from "./embedded-agent-runner/message-visibility.js";
import type { EmbeddedAgentQueueMessageOptions } from "./embedded-agent-runner/run-state.js";
import type { EmbeddedAgentQueueMessageOutcome } from "./embedded-agent-runner/runs.js";
import { isFailoverError } from "./failover-error.js";
import { mediaUrlsFromGeneratedAttachments } from "./generated-attachments.js";
} from "../../embedded-agent-runner/message-visibility.js";
import type { EmbeddedAgentQueueMessageOptions } from "../../embedded-agent-runner/run-state.js";
import type { EmbeddedAgentQueueMessageOutcome } from "../../embedded-agent-runner/runs.js";
import { isFailoverError } from "../../failover-error.js";
import { mediaUrlsFromGeneratedAttachments } from "../../generated-attachments.js";
import {
AGENT_INTERNAL_EVENT_TYPE_TASK_COMPLETION,
hasGeneratedMediaCompletionEvent,
} from "./internal-event-contract.js";
import { formatAgentInternalEventsForPrompt, type AgentInternalEvent } from "./internal-events.js";
} from "../../internal-event-contract.js";
import {
formatAgentInternalEventsForPrompt,
type AgentInternalEvent,
} from "../../internal-events.js";
import { admitCorrelatedSubagentSessionDelivery } from "../completion/subagent-completion-delivery.js";
import { getSubagentDepthFromSessionStore } from "../spawn/subagent-depth.js";
import {
callGateway,
dispatchGatewayMethodInProcess,
@@ -84,8 +89,6 @@ import {
resolveGeneratedMediaSessionDeliveryRoute,
type DeliveryContext,
} from "./subagent-announce-origin.js";
import { admitCorrelatedSubagentSessionDelivery } from "./subagent-completion-delivery.js";
import { getSubagentDepthFromSessionStore } from "./subagent-depth.js";
import { resolveRequesterStoreKey } from "./subagent-requester-store-key.js";
const DEFAULT_SUBAGENT_ANNOUNCE_TIMEOUT_MS = 120_000;
@@ -1,9 +1,10 @@
// Descendant-settle wake replaces an ended nested orchestrator run while
// preserving lifecycle ownership.
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { getAgentEventLifecycleGeneration } from "../infra/agent-events.js";
import { INTERNAL_MESSAGE_CHANNEL } from "../utils/message-channel.js";
import { buildAnnounceIdempotencyKey } from "./announce-idempotency.js";
import { getAgentEventLifecycleGeneration } from "../../../infra/agent-events.js";
import { INTERNAL_MESSAGE_CHANNEL } from "../../../utils/message-channel.js";
import { buildAnnounceIdempotencyKey } from "../../announce-idempotency.js";
import { terminateAcceptedCollectorRun } from "../spawn/subagent-spawn-cleanup.js";
import {
loadSessionEntryByKey,
runAnnounceDeliveryWithRetry,
@@ -14,13 +15,12 @@ import type {
dispatchGatewayMethodInProcess,
getRuntimeConfig,
} from "./subagent-announce.runtime.js";
import { terminateAcceptedCollectorRun } from "./subagents/spawn/subagent-spawn-cleanup.js";
type DescendantWakeDeps = {
callGateway: typeof callGateway;
dispatchGatewayMethodInProcess: typeof dispatchGatewayMethodInProcess;
getRuntimeConfig: typeof getRuntimeConfig;
replaceSubagentRunAfterSteer: typeof import("./subagent-registry-runtime.js").replaceSubagentRunAfterSteer;
replaceSubagentRunAfterSteer: typeof import("../registry/subagent-registry-runtime.js").replaceSubagentRunAfterSteer;
};
type UsableSessionEntryGuard = (entry: unknown) => entry is Record<string, unknown>;
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { AGENT_INTERNAL_EVENT_TYPE_TASK_COMPLETION } from "./internal-event-contract.js";
import type { AgentInternalEvent } from "./internal-events.js";
import { AGENT_INTERNAL_EVENT_TYPE_TASK_COMPLETION } from "../../internal-event-contract.js";
import type { AgentInternalEvent } from "../../internal-events.js";
import {
isSubagentAnnounceCompletionHandoff,
isTrustedSubagentCompletionHandoffForRun,
@@ -1,6 +1,6 @@
import type { InputProvenance } from "../sessions/input-provenance.js";
import { AGENT_INTERNAL_EVENT_TYPE_TASK_COMPLETION } from "./internal-event-contract.js";
import type { AgentInternalEvent } from "./internal-events.js";
import type { InputProvenance } from "../../../sessions/input-provenance.js";
import { AGENT_INTERNAL_EVENT_TYPE_TASK_COMPLETION } from "../../internal-event-contract.js";
import type { AgentInternalEvent } from "../../internal-events.js";
export type TrustedSubagentCompletionHandoff = {
kind: "subagent-completion";
@@ -7,40 +7,43 @@ import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "@openclaw/normalization-core/string-coerce";
import { getLoadedChannelPluginForRead } from "../channels/plugins/registry-loaded.js";
import type { ChannelId } from "../channels/plugins/types.public.js";
import { routeFromConversationRef, routeToDeliveryFields } from "../channels/route-projection.js";
import type { SessionEntry } from "../config/sessions/types.js";
import { getLoadedChannelPluginForRead } from "../../../channels/plugins/registry-loaded.js";
import type { ChannelId } from "../../../channels/plugins/types.public.js";
import {
routeFromConversationRef,
routeToDeliveryFields,
} from "../../../channels/route-projection.js";
import type { SessionEntry } from "../../../config/sessions/types.js";
import {
stripTargetKindPrefix,
stripTargetProviderPrefix,
stripTargetTopicSuffix,
} from "../infra/outbound/channel-target-prefix.js";
import type { ConversationRef } from "../infra/outbound/session-binding-service.js";
import type { SessionDeliveryRoute } from "../infra/session-delivery-queue.js";
import { stringifyRouteThreadId } from "../plugin-sdk/channel-route.js";
import { normalizeAccountId } from "../routing/session-key.js";
import { deriveSessionChatTypeFromKey } from "../sessions/session-chat-type-shared.js";
} from "../../../infra/outbound/channel-target-prefix.js";
import type { ConversationRef } from "../../../infra/outbound/session-binding-service.js";
import type { SessionDeliveryRoute } from "../../../infra/session-delivery-queue.js";
import { stringifyRouteThreadId } from "../../../plugin-sdk/channel-route.js";
import { normalizeAccountId } from "../../../routing/session-key.js";
import { deriveSessionChatTypeFromKey } from "../../../sessions/session-chat-type-shared.js";
import {
deliveryContextFromSession,
mergeDeliveryContext,
normalizeDeliveryContext,
} from "../utils/delivery-context.shared.js";
import type { DeliveryContext } from "../utils/delivery-context.types.js";
} from "../../../utils/delivery-context.shared.js";
import type { DeliveryContext } from "../../../utils/delivery-context.types.js";
import {
INTERNAL_MESSAGE_CHANNEL,
isDeliverableMessageChannel,
isGatewayMessageChannel,
isInternalMessageChannel,
normalizeMessageChannel,
} from "../utils/message-channel.js";
} from "../../../utils/message-channel.js";
import type { SpawnSubagentMode } from "../spawn/subagent-spawn.types.js";
import {
createBoundDeliveryRouter,
getGlobalHookRunner,
resolveConversationIdFromTargets,
} from "./subagent-announce-delivery.runtime.js";
import type { SpawnSubagentMode } from "./subagents/spawn/subagent-spawn.types.js";
export type { DeliveryContext } from "../utils/delivery-context.types.js";
export type { DeliveryContext } from "../../../utils/delivery-context.types.js";
function normalizeAnnounceRouteTarget(context?: DeliveryContext): string | undefined {
const rawTo = normalizeOptionalString(context?.to);
@@ -10,7 +10,7 @@ import {
readSubagentOutput,
} from "./subagent-announce-output.test-support.js";
type CallGateway = typeof import("../gateway/call.js").callGateway;
type CallGateway = typeof import("../../../gateway/call.js").callGateway;
type GetRuntimeConfig = typeof import("./subagent-announce.runtime.js").getRuntimeConfig;
type ReadSessionEntry = typeof import("./subagent-announce.runtime.js").readSessionEntry;
type ReadSessionMessagesAsync =
@@ -5,13 +5,19 @@
*/
import { asFiniteNumber } from "@openclaw/normalization-core/number-coercion";
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
import type { SessionTranscriptRuntimeTarget } from "../config/sessions/session-accessor.js";
import { resolveFreshSessionTotalTokens } from "../config/sessions/types.js";
import { isFastTestRuntimeEnv } from "../infra/env.js";
import { formatDurationCompact } from "../infra/format-time/format-duration.js";
import { buildAgentRunTerminalOutcomeFromWaitResult } from "./agent-run-terminal-outcome.js";
import { wrapPromptDataBlock } from "./sanitize-for-prompt.js";
import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../../../auto-reply/tokens.js";
import type { SessionTranscriptRuntimeTarget } from "../../../config/sessions/session-accessor.js";
import { resolveFreshSessionTotalTokens } from "../../../config/sessions/types.js";
import { isFastTestRuntimeEnv } from "../../../infra/env.js";
import { formatDurationCompact } from "../../../infra/format-time/format-duration.js";
import { buildAgentRunTerminalOutcomeFromWaitResult } from "../../agent-run-terminal-outcome.js";
import { wrapPromptDataBlock } from "../../sanitize-for-prompt.js";
import { extractAssistantText, sanitizeTextContent } from "../../tools/chat-history-text.js";
import {
isAnnounceSkip,
selectDeliverableSessionsReply,
} from "../../tools/sessions-send-tokens.js";
import { compareSubagentRunGeneration } from "../registry/subagent-run-generation.js";
import {
captureSubagentCompletionReplyUsing,
readLatestSubagentOutputWithRetryUsing,
@@ -24,10 +30,7 @@ import {
resolveAgentIdFromSessionKey,
resolveStorePath,
} from "./subagent-announce.runtime.js";
import { compareSubagentRunGeneration } from "./subagent-run-generation.js";
import { assistantCallsSessionsYield, isSessionsYieldToolResult } from "./subagent-yield-output.js";
import { extractAssistantText, sanitizeTextContent } from "./tools/chat-history-text.js";
import { isAnnounceSkip, selectDeliverableSessionsReply } from "./tools/sessions-send-tokens.js";
const FAST_TEST_RETRY_INTERVAL_MS = 8;
const MAX_CHILD_COMPLETION_RESULT_CHARS = 512;
@@ -1,31 +1,34 @@
// Subagent announce format e2e tests exercise the full announce flow with
// channel fixtures, session stores, hooks, and gateway calls wired together.
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
import { SILENT_REPLY_TOKEN } from "../../../auto-reply/tokens.js";
import {
clearRuntimeConfigSnapshot,
setRuntimeConfigSnapshot,
type OpenClawConfig,
} from "../config/config.js";
import * as configSessions from "../config/sessions.js";
import type { SessionEntry } from "../config/sessions/types.js";
import * as gatewayCall from "../gateway/call.js";
import { getAgentEventLifecycleGeneration } from "../infra/agent-events.js";
} from "../../../config/config.js";
import * as configSessions from "../../../config/sessions.js";
import type { SessionEntry } from "../../../config/sessions/types.js";
import * as gatewayCall from "../../../gateway/call.js";
import { getAgentEventLifecycleGeneration } from "../../../infra/agent-events.js";
import {
testing as sessionBindingServiceTesting,
registerSessionBindingAdapter,
} from "../infra/outbound/session-binding-service.js";
import { normalizeLegacySessionEntryDelivery } from "../infra/state-migrations.legacy-session-store.js";
import * as hookRunnerGlobal from "../plugins/hook-runner-global.js";
import type { HookRunner } from "../plugins/hooks.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
} from "../../../infra/outbound/session-binding-service.js";
import { normalizeLegacySessionEntryDelivery } from "../../../infra/state-migrations.legacy-session-store.js";
import * as hookRunnerGlobal from "../../../plugins/hook-runner-global.js";
import type { HookRunner } from "../../../plugins/hooks.js";
import { setActivePluginRegistry } from "../../../plugins/runtime.js";
import {
createChannelTestPluginBase,
createTestRegistry,
} from "../../../test-utils/channel-plugins.js";
import {
buildAnnounceIdFromChildRun,
buildAnnounceIdempotencyKey,
} from "./announce-idempotency.js";
import * as embeddedRuns from "./embedded-agent-runner/runs.js";
import { FailoverError } from "./failover-error.js";
} from "../../announce-idempotency.js";
import * as embeddedRuns from "../../embedded-agent-runner/runs.js";
import { FailoverError } from "../../failover-error.js";
import { testing as subagentAnnounceDeliveryTesting } from "./subagent-announce-delivery.test-support.js";
import { runSubagentAnnounceDispatch } from "./subagent-announce-dispatch.js";
import { testing as subagentAnnounceOutputTesting } from "./subagent-announce-output.test-support.js";
@@ -332,8 +335,8 @@ function loadSessionStoreFixture(): Record<string, SessionEntry> {
}) as unknown as Record<string, SessionEntry>;
}
vi.mock("./subagent-registry.js", () => subagentRegistryMock);
vi.mock("./subagent-registry-runtime.js", () => subagentRegistryMock);
vi.mock("../registry/subagent-registry.js", () => subagentRegistryMock);
vi.mock("../registry/subagent-registry-runtime.js", () => subagentRegistryMock);
describe("subagent announce formatting", () => {
let previousFastTestEnv: string | undefined;
@@ -4,25 +4,28 @@ import { randomBytes, randomUUID } from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { clearRuntimeConfigSnapshot, type OpenClawConfig } from "../config/config.js";
import { callGateway as realCallGateway } from "../gateway/call.js";
import { GatewayClient } from "../gateway/client.js";
import { dispatchGatewayMethodInProcess as realDispatchGatewayMethodInProcess } from "../gateway/server-plugins.js";
import { startGatewayServer, type GatewayServer } from "../gateway/server.js";
import { extractPayloadText } from "../gateway/test-helpers.agent-results.js";
import { onAgentEvent, type AgentEventPayload } from "../infra/agent-events.js";
import { isTruthyEnvValue } from "../infra/env.js";
import { clearCurrentPluginMetadataSnapshot } from "../plugins/current-plugin-metadata-state.js";
import { clearRuntimeConfigSnapshot, type OpenClawConfig } from "../../../config/config.js";
import { callGateway as realCallGateway } from "../../../gateway/call.js";
import { GatewayClient } from "../../../gateway/client.js";
import { dispatchGatewayMethodInProcess as realDispatchGatewayMethodInProcess } from "../../../gateway/server-plugins.js";
import { startGatewayServer, type GatewayServer } from "../../../gateway/server.js";
import { extractPayloadText } from "../../../gateway/test-helpers.agent-results.js";
import { onAgentEvent, type AgentEventPayload } from "../../../infra/agent-events.js";
import { isTruthyEnvValue } from "../../../infra/env.js";
import { clearCurrentPluginMetadataSnapshot } from "../../../plugins/current-plugin-metadata-state.js";
import {
createOpenClawTestState,
type OpenClawTestState,
} from "../test-utils/openclaw-test-state.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import { isLiveTestEnabled, readLiveTestConfig } from "./live-test-helpers.js";
} from "../../../test-utils/openclaw-test-state.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../../utils/message-channel.js";
import { isLiveTestEnabled, readLiveTestConfig } from "../../live-test-helpers.js";
import {
resolveSubagentController,
steerControlledSubagentRun,
} from "../registry/subagent-control.js";
import { listSubagentRunsForRequester } from "../registry/subagent-registry.test-helpers.js";
import { testing as subagentAnnounceDeliveryTesting } from "./subagent-announce-delivery.test-support.js";
import { testing as subagentAnnounceTesting } from "./subagent-announce.js";
import { resolveSubagentController, steerControlledSubagentRun } from "./subagent-control.js";
import { listSubagentRunsForRequester } from "./subagent-registry.test-helpers.js";
const LIVE = isLiveTestEnabled() && isTruthyEnvValue(process.env.OPENCLAW_LIVE_SUBAGENT_E2E);
const describeLive = LIVE ? describe : describe.skip;

Some files were not shown because too many files have changed in this diff Show More