/**
 * Built-artifact guard for the scope carrier shared by `dsh-subagent` and
 * `dsh-sdk-jsonrpc-server`. The carrier registry is module-local, so both bundles must
 * externalize `dsh-scope`; source-mode tests cannot expose an accidentally
 * inlined second registry. This test runs the real `lib/index.js` bundles in a
 * plain Node subprocess, disposes the child before settlement, and requires the
 * SDK completion notification to retain the delegating parent.
 */

import { execFile } from 'node:child_process'
import { existsSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { promisify } from 'node:util'
import { describe, expect, it } from 'vitest'

const repoRoot = fileURLToPath(new URL('../../../../', import.meta.url))
const jsonrpcBundle = fileURLToPath(new URL('../lib/index.js', import.meta.url))
const execFileAsync = promisify(execFile)

const builtRuntimeProbe = String.raw`
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";

const load = (path) => import(pathToFileURL(resolve(path)).href);
const [
  { Context },
  agentCore,
  { default: SubagentRuntime },
  { default: JsonlSessionPersistence },
  { HarnessSdkJsonRpcServer },
  { SessionId },
] = await Promise.all([
  load("vendor/cordis/lib/index.js"),
  load("packages/examples/agent-spine-demo/lib/index.js"),
  load("packages/subagent/subagent/lib/index.js"),
  load("packages/session/session-persistence-jsonl/lib/index.js"),
  load("packages/sdk/server/lib/index.js"),
  load("packages/core/session/lib/index.js"),
]);

const storageRoot = await mkdtemp(join(tmpdir(), "jsonrpc-built-scope-"));
const ctx = new Context();
try {
  await ctx.plugin(agentCore, { workspaceContext: false });
  await ctx.plugin(SubagentRuntime);
  await ctx.plugin(JsonlSessionPersistence, { root: storageRoot });
  await new Promise((ready) => setTimeout(ready, 50));

  const notifications = [];
  const server = new HarnessSdkJsonRpcServer(ctx, {
    request() { return Promise.reject(new Error("unexpected host request")); },
    notify(method, params) { notifications.push({ method, params }); },
  });
  const parent = await ctx.agents.create({
    sessionId: SessionId("built-parent"),
    meta: { cwd: storageRoot },
    agentOptions: { model: "test" },
  });
  const child = await parent.agent.ctx.agents.create({
    sessionId: SessionId("built-child"),
    meta: { cwd: storageRoot, parentSession: SessionId("built-parent") },
    agentOptions: { model: "test" },
  });
  const result = Promise.withResolvers();
  const unregister = ctx.subagents.registerProvider({
    name: "built-local",
    capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
    inheritsParentContext: false,
    start() {
      return Promise.resolve({
        id: child.agent.id,
        localAgent: child.agent,
        result: result.promise,
        dispose() { return Promise.resolve(); },
      });
    },
  });
  const run = await ctx.subagents.start("built-local", {
    parent: parent.agent,
    prompt: [],
    signal: new AbortController().signal,
  });
  await child.dispose();
  result.resolve({ output: [], stopReason: "completed" });
  await run.result;
  await Promise.resolve();

  console.log(JSON.stringify(notifications.filter(({ method }) => method === "subagent.finished")));
  await run.dispose();
  unregister();
  await parent.dispose();
  await server.shutdown();
} finally {
  await ctx.fiber.dispose();
  await rm(storageRoot, { recursive: true, force: true });
}
`

describe.skipIf(!existsSync(jsonrpcBundle))('dsh-sdk-jsonrpc-server BUILT scope carrier', () => {
  it('preserves parent-scoped completion after child disposal', async () => {
    const { stdout, stderr } = await execFileAsync(process.execPath, ['--input-type=module', '-e', builtRuntimeProbe], {
      cwd: repoRoot,
      timeout: 15_000,
    })

    expect(stderr).not.toContain('listener threw')
    // A result without output omits lastAssistantMessage from the wire; it
    // never sends `[]`.
    expect(JSON.parse(stdout) as unknown).toEqual([{
      method: 'subagent.finished',
      params: {
        provider: 'built-local',
        agentId: 'built-child',
        parentSessionId: 'built-parent',
        childSessionId: 'built-child',
        status: 'ok',
        stopReason: 'completed',
      },
    }])
  })
})
