| |
| |
| |
| import { beforeEach, describe, expect, it, vi } from "vitest"; |
| import { deliverSubagentAnnouncement } from "../agents/subagents/announce/subagent-announce-delivery.js"; |
| import { getPluginRuntimeGatewayRequestScope } from "../plugins/runtime/gateway-request-scope.js"; |
| import { createAgentHarnessTaskRuntimeScope } from "../tasks/agent-harness-task-runtime-scope.js"; |
| import { createRunningTaskRun, finalizeTaskRunByRunId } from "../tasks/detached-task-runtime.js"; |
| import { listTaskRecords } from "../tasks/runtime-internal.js"; |
| import { captureTaskExecutionOwner } from "../tasks/task-execution-owner.js"; |
| import { |
| createAgentHarnessTaskRuntime, |
| deliverAgentHarnessTaskCompletion, |
| isDurableAgentHarnessCompletionDelivery, |
| } from "./agent-harness-task-runtime.js"; |
|
|
| vi.mock("../agents/subagents/announce/subagent-announce-delivery.js", async (importOriginal) => { |
| const actual = |
| await importOriginal< |
| typeof import("../agents/subagents/announce/subagent-announce-delivery.js") |
| >(); |
| return { |
| ...actual, |
| deliverSubagentAnnouncement: vi.fn(async () => ({ delivered: true, path: "steered" })), |
| isInternalAnnounceRequesterSession: vi.fn(() => true), |
| }; |
| }); |
|
|
| vi.mock("../tasks/detached-task-runtime.js", () => ({ |
| createRunningTaskRun: vi.fn((params) => ({ taskId: "task-1", ...params })), |
| recordTaskRunProgressByRunId: vi.fn(() => []), |
| finalizeTaskRunByRunId: vi.fn(() => []), |
| setDetachedTaskDeliveryStatusByRunId: vi.fn(() => []), |
| })); |
|
|
| vi.mock("../tasks/runtime-internal.js", () => ({ |
| listTaskRecords: vi.fn(() => []), |
| })); |
|
|
| vi.mock("../tasks/task-execution-owner.js", () => ({ |
| captureTaskExecutionOwner: vi.fn(), |
| })); |
|
|
| describe("agent-harness-task-runtime", () => { |
| beforeEach(() => { |
| vi.clearAllMocks(); |
| vi.mocked(listTaskRecords).mockReturnValue([]); |
| }); |
|
|
| function createScope(requesterSessionKey = "agent:main:channel:C123") { |
| return createAgentHarnessTaskRuntimeScope({ requesterSessionKey }); |
| } |
|
|
| it("records the scoped harness process identity without recapturing a reused PID", () => { |
| const executionOwner = { host: "gateway-host", pid: 4321, startIdentity: 100 }; |
| vi.mocked(captureTaskExecutionOwner).mockReturnValue(executionOwner); |
| const runtime = createAgentHarnessTaskRuntime({ |
| runtime: "subagent", |
| taskKind: "example-harness", |
| scope: createScope(), |
| executionPid: executionOwner.pid, |
| }); |
| vi.mocked(captureTaskExecutionOwner).mockReturnValue({ |
| ...executionOwner, |
| startIdentity: 200, |
| }); |
|
|
| for (const runId of ["child-1", "child-2"]) { |
| const task = runtime.createRunningTaskRun({ runId, task: "do work" }); |
| expect(task.executionOwner).toEqual(executionOwner); |
| } |
| expect(captureTaskExecutionOwner).toHaveBeenCalledExactlyOnceWith(executionOwner.pid); |
| }); |
|
|
| it("keeps an unidentified or remote harness owner unknown", () => { |
| const runtime = createAgentHarnessTaskRuntime({ |
| runtime: "subagent", |
| taskKind: "example-harness", |
| scope: createScope(), |
| }); |
| const task = runtime.createRunningTaskRun({ runId: "child-1", task: "remote work" }); |
|
|
| expect(task.executionOwner).toBeUndefined(); |
| expect(captureTaskExecutionOwner).not.toHaveBeenCalled(); |
| }); |
|
|
| it("scopes task lifecycle mutations to the owning requester session", () => { |
| const runtime = createAgentHarnessTaskRuntime({ |
| runtime: "subagent", |
| taskKind: "example-harness", |
| scope: createScope(), |
| runIdPrefix: "example:", |
| }); |
|
|
| runtime.createRunningTaskRun({ |
| runId: "example:child-1", |
| sourceId: "example:child-1", |
| task: "do work", |
| label: "worker", |
| }); |
| runtime.finalizeTaskRunByRunId({ |
| runId: "example:child-1", |
| status: "succeeded", |
| endedAt: 1, |
| }); |
|
|
| expect(createRunningTaskRun).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| runtime: "subagent", |
| taskKind: "example-harness", |
| requesterSessionKey: "agent:main:channel:C123", |
| ownerKey: "agent:main:channel:C123", |
| scopeKind: "session", |
| runId: "example:child-1", |
| }), |
| ); |
| expect(finalizeTaskRunByRunId).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| runtime: "subagent", |
| sessionKey: "agent:main:channel:C123", |
| runId: "example:child-1", |
| }), |
| ); |
| }); |
|
|
| it("rejects task run ids outside the configured harness scope", () => { |
| const runtime = createAgentHarnessTaskRuntime({ |
| runtime: "subagent", |
| taskKind: "example-harness", |
| scope: createScope(), |
| runIdPrefix: "example:", |
| }); |
|
|
| expect(() => |
| runtime.finalizeTaskRunByRunId({ |
| runId: "other:child-1", |
| status: "succeeded", |
| endedAt: 1, |
| }), |
| ).toThrow(/outside the configured scope/); |
| }); |
|
|
| it("rejects caller-forged task runtime scopes", async () => { |
| const forgedScope = { |
| requesterSessionKey: "agent:other:channel:C999", |
| } as ReturnType<typeof createScope>; |
| expect(() => |
| createAgentHarnessTaskRuntime({ |
| runtime: "subagent", |
| taskKind: "example-harness", |
| scope: forgedScope, |
| }), |
| ).toThrow(/host-issued scope/); |
| await expect( |
| deliverAgentHarnessTaskCompletion({ |
| scope: forgedScope, |
| childSessionKey: "harness-thread:child", |
| childSessionId: "child", |
| announceId: "harness:parent:child:succeeded", |
| status: "succeeded", |
| result: "child final answer", |
| }), |
| ).rejects.toThrow(/host-issued scope/); |
| }); |
|
|
| it("lists only task records owned by the scoped requester session", () => { |
| vi.mocked(listTaskRecords).mockReturnValue([ |
| { |
| taskId: "task-1", |
| runtime: "subagent", |
| taskKind: "example-harness", |
| requesterSessionKey: "agent:main:channel:C123", |
| ownerKey: "agent:main:channel:C123", |
| scopeKind: "session", |
| runId: "example:child-1", |
| task: "owned", |
| status: "running", |
| deliveryStatus: "not_applicable", |
| notifyPolicy: "silent", |
| createdAt: 1, |
| }, |
| { |
| taskId: "task-2", |
| runtime: "subagent", |
| taskKind: "example-harness", |
| requesterSessionKey: "agent:other:channel:C999", |
| ownerKey: "agent:other:channel:C999", |
| scopeKind: "session", |
| runId: "example:child-2", |
| task: "other", |
| status: "running", |
| deliveryStatus: "not_applicable", |
| notifyPolicy: "silent", |
| createdAt: 1, |
| }, |
| ]); |
| const runtime = createAgentHarnessTaskRuntime({ |
| runtime: "subagent", |
| taskKind: "example-harness", |
| scope: createScope(), |
| runIdPrefix: "example:", |
| }); |
|
|
| expect(runtime.listTaskRecords().map((task) => task.taskId)).toEqual(["task-1"]); |
| }); |
|
|
| it("delivers a generic harness completion through subagent announcement delivery", async () => { |
| const gatewayContextResolver = vi.fn(); |
| vi.mocked(deliverSubagentAnnouncement).mockImplementationOnce(async () => { |
| expect(getPluginRuntimeGatewayRequestScope()?.resolveGatewayContext).toBe( |
| gatewayContextResolver, |
| ); |
| return { delivered: true, path: "steered" }; |
| }); |
| await deliverAgentHarnessTaskCompletion({ |
| scope: createAgentHarnessTaskRuntimeScope({ |
| requesterSessionKey: "agent:main:main", |
| gatewayContextResolver, |
| }), |
| childSessionKey: "harness-thread:child", |
| childSessionId: "child", |
| announceId: "harness:parent:child:succeeded", |
| announceType: "Example harness worker", |
| taskLabel: "Example worker", |
| status: "succeeded", |
| statusLabel: "task_complete", |
| result: "child final answer", |
| }); |
|
|
| expect(deliverSubagentAnnouncement).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| requesterSessionKey: "agent:main:main", |
| sourceSessionKey: "harness-thread:child", |
| sourceTool: "agent_harness_task", |
| expectsCompletionMessage: true, |
| directIdempotencyKey: "announce:harness:parent:child:succeeded", |
| }), |
| ); |
| expect(vi.mocked(deliverSubagentAnnouncement).mock.calls[0]?.[0]).not.toHaveProperty( |
| "resolveGatewayContext", |
| ); |
| }); |
|
|
| it("checks durable direct delivery phases", () => { |
| expect( |
| isDurableAgentHarnessCompletionDelivery({ |
| delivered: true, |
| path: "direct", |
| phases: [{ phase: "direct-primary", delivered: true, path: "direct" }], |
| }), |
| ).toBe(true); |
| expect( |
| isDurableAgentHarnessCompletionDelivery({ |
| delivered: true, |
| path: "direct", |
| phases: [{ phase: "steer-fallback", delivered: true, path: "steered" }], |
| }), |
| ).toBe(false); |
| }); |
| }); |
|
|