



























@@ -431,9 +431,9 @@ function writeLegacyTaskStateSidecars(root: string): {
431431.prepare(
432432`
433433 INSERT INTO task_runs (
434- task_id, runtime, source_id, requester_session_key, child_session_key, run_id, task,
435- status, delivery_status, notify_policy, created_at, last_event_at
436- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
434+ task_id, runtime, source_id, requester_session_key, child_session_key, agent_id, run_id,
435+ task, status, delivery_status, notify_policy, created_at, last_event_at
436+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
437437 `,
438438)
439439.run(
@@ -442,6 +442,7 @@ function writeLegacyTaskStateSidecars(root: string): {
442442"nightly",
443443"",
444444"agent:main:cron:nightly",
445+"ops",
445446"legacy-task-run",
446447"Legacy cron task",
447448"running",
@@ -507,6 +508,36 @@ function writeLegacyTaskStateSidecars(root: string): {
507508return { taskRunsPath, flowRunsPath };
508509}
509510511+function appendLegacyCrossAgentTask(taskRunsPath: string): void {
512+const sqlite = requireNodeSqlite();
513+const db = new sqlite.DatabaseSync(taskRunsPath);
514+try {
515+db.prepare(
516+`
517+ INSERT INTO task_runs (
518+ task_id, runtime, requester_session_key, child_session_key, agent_id, run_id, task,
519+ status, delivery_status, notify_policy, created_at, last_event_at
520+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
521+ `,
522+).run(
523+"legacy-cross-agent",
524+"subagent",
525+"agent:main:main",
526+"agent:worker:subagent:child",
527+"main",
528+"legacy-cross-agent-run",
529+"Inspect worker state",
530+"running",
531+"pending",
532+"done_only",
533+130,
534+140,
535+);
536+} finally {
537+db.close();
538+}
539+}
540+510541async function detectAndRunMigrations(params: {
511542root: string;
512543cfg: OpenClawConfig;
@@ -2271,6 +2302,7 @@ describe("doctor legacy state migrations", () => {
22712302ownerKey: "system:cron:nightly",
22722303scopeKind: "system",
22732304requesterSessionKey: "",
2305+agentId: "ops",
22742306runId: "legacy-task-run",
22752307});
22762308expect(taskState.deliveryStates.get("legacy-task")).toMatchObject({
@@ -2345,6 +2377,90 @@ describe("doctor legacy state migrations", () => {
23452377});
23462378});
234723792380+it("canonicalizes cross-agent attribution while importing task sidecars", async () => {
2381+const root = await makeTempRoot();
2382+const { taskRunsPath } = writeLegacyTaskStateSidecars(root);
2383+appendLegacyCrossAgentTask(taskRunsPath);
2384+2385+const result = await autoMigrateLegacyTaskStateSidecars({
2386+env: { OPENCLAW_STATE_DIR: root } as NodeJS.ProcessEnv,
2387+});
2388+2389+expect(result.warnings).toStrictEqual([]);
2390+expect(result.changes).toContain("Migrated 2 task registry sidecar rows → shared SQLite state");
2391+2392+await withStateDir(root, async () => {
2393+expect(loadTaskRegistryStateFromSqlite().tasks.get("legacy-cross-agent")).toMatchObject({
2394+taskId: "legacy-cross-agent",
2395+agentId: "worker",
2396+requesterAgentId: "main",
2397+requesterSessionKey: "agent:main:main",
2398+childSessionKey: "agent:worker:subagent:child",
2399+});
2400+});
2401+});
2402+2403+it("keeps task sidecars when only requester attribution conflicts", async () => {
2404+const root = await makeTempRoot();
2405+const { taskRunsPath } = writeLegacyTaskStateSidecars(root);
2406+appendLegacyCrossAgentTask(taskRunsPath);
2407+2408+await withStateDir(root, async () => {
2409+loadTaskRegistryStateFromSqlite();
2410+closeOpenClawStateDatabaseForTest();
2411+const sqlite = requireNodeSqlite();
2412+const db = new sqlite.DatabaseSync(path.join(root, "state", "openclaw.sqlite"));
2413+try {
2414+db.prepare(
2415+`INSERT INTO task_runs (
2416+ task_id,
2417+ runtime,
2418+ requester_session_key,
2419+ owner_key,
2420+ scope_kind,
2421+ child_session_key,
2422+ agent_id,
2423+ requester_agent_id,
2424+ run_id,
2425+ task,
2426+ status,
2427+ delivery_status,
2428+ notify_policy,
2429+ created_at,
2430+ last_event_at
2431+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2432+).run(
2433+"legacy-cross-agent",
2434+"subagent",
2435+"agent:main:main",
2436+"agent:main:main",
2437+"session",
2438+"agent:worker:subagent:child",
2439+"worker",
2440+"other-requester",
2441+"legacy-cross-agent-run",
2442+"Inspect worker state",
2443+"running",
2444+"pending",
2445+"done_only",
2446+130,
2447+140,
2448+);
2449+} finally {
2450+db.close();
2451+}
2452+});
2453+2454+const result = await autoMigrateLegacyTaskStateSidecars({
2455+env: { OPENCLAW_STATE_DIR: root } as NodeJS.ProcessEnv,
2456+});
2457+2458+expect(result.warnings).toContain(
2459+"Left task registry sidecar in place because 1 row already existed in shared state: legacy-cross-agent",
2460+);
2461+expect(fs.existsSync(taskRunsPath)).toBe(true);
2462+});
2463+23482464it("keeps task sidecars when shared state already has conflicting task rows", async () => {
23492465const root = await makeTempRoot();
23502466const { taskRunsPath, flowRunsPath } = writeLegacyTaskStateSidecars(root);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。