@stigmer/server 3.36.0 → 3.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/agentexecution/artifacts.d.ts.map +1 -1
- package/dist/domain/agentexecution/artifacts.js +15 -6
- package/dist/domain/agentexecution/artifacts.js.map +1 -1
- package/dist/domain/agentexecution/constants.d.ts +0 -10
- package/dist/domain/agentexecution/constants.d.ts.map +1 -1
- package/dist/domain/agentexecution/constants.js +0 -12
- package/dist/domain/agentexecution/constants.js.map +1 -1
- package/dist/domain/agentexecution/lifecycle.d.ts.map +1 -1
- package/dist/domain/agentexecution/lifecycle.js +5 -3
- package/dist/domain/agentexecution/lifecycle.js.map +1 -1
- package/dist/domain/agentexecution/platform-rows.d.ts +63 -0
- package/dist/domain/agentexecution/platform-rows.d.ts.map +1 -0
- package/dist/domain/agentexecution/platform-rows.js +106 -0
- package/dist/domain/agentexecution/platform-rows.js.map +1 -0
- package/dist/domain/agentexecution/subscribe.d.ts.map +1 -1
- package/dist/domain/agentexecution/subscribe.js +7 -4
- package/dist/domain/agentexecution/subscribe.js.map +1 -1
- package/dist/domain/agentexecution/update-status.d.ts.map +1 -1
- package/dist/domain/agentexecution/update-status.js +48 -17
- package/dist/domain/agentexecution/update-status.js.map +1 -1
- package/dist/domain/agentexecution/usage.d.ts +0 -2
- package/dist/domain/agentexecution/usage.d.ts.map +1 -1
- package/dist/domain/agentexecution/usage.js +12 -10
- package/dist/domain/agentexecution/usage.js.map +1 -1
- package/dist/domain/organization/controller.d.ts +4 -1
- package/dist/domain/organization/controller.d.ts.map +1 -1
- package/dist/domain/organization/controller.js +30 -8
- package/dist/domain/organization/controller.js.map +1 -1
- package/dist/domain/workflow/controller.js +2 -1
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/steps.d.ts +7 -4
- package/dist/domain/workflow/steps.d.ts.map +1 -1
- package/dist/domain/workflow/steps.js +29 -17
- package/dist/domain/workflow/steps.js.map +1 -1
- package/dist/domain/workflowexecution/create-steps.d.ts.map +1 -1
- package/dist/domain/workflowexecution/create-steps.js +6 -2
- package/dist/domain/workflowexecution/create-steps.js.map +1 -1
- package/dist/domain/workflowexecution/lifecycle.d.ts.map +1 -1
- package/dist/domain/workflowexecution/lifecycle.js +5 -2
- package/dist/domain/workflowexecution/lifecycle.js.map +1 -1
- package/dist/domain/workflowexecution/send-signal.d.ts.map +1 -1
- package/dist/domain/workflowexecution/send-signal.js +6 -3
- package/dist/domain/workflowexecution/send-signal.js.map +1 -1
- package/dist/domain/workflowexecution/submit-approval.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-approval.js +6 -2
- package/dist/domain/workflowexecution/submit-approval.js.map +1 -1
- package/dist/domain/workflowexecution/submit-file-decision.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-file-decision.js +6 -2
- package/dist/domain/workflowexecution/submit-file-decision.js.map +1 -1
- package/dist/domain/workflowexecution/submit-workflow-task-approval.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-workflow-task-approval.js +6 -2
- package/dist/domain/workflowexecution/submit-workflow-task-approval.js.map +1 -1
- package/dist/domain/workflowexecution/subscribe-events.d.ts.map +1 -1
- package/dist/domain/workflowexecution/subscribe-events.js +6 -2
- package/dist/domain/workflowexecution/subscribe-events.js.map +1 -1
- package/dist/domain/workflowexecution/subscribe.d.ts.map +1 -1
- package/dist/domain/workflowexecution/subscribe.js +7 -4
- package/dist/domain/workflowexecution/subscribe.js.map +1 -1
- package/dist/extensions/organization-directory.d.ts +29 -6
- package/dist/extensions/organization-directory.d.ts.map +1 -1
- package/dist/extensions/organization-directory.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.d.ts +9 -4
- package/dist/pipeline/steps/guard-memory-capture.d.ts.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.js +10 -12
- package/dist/pipeline/steps/guard-memory-capture.js.map +1 -1
- package/dist/temporal/agentexecution/runner-result.d.ts +12 -0
- package/dist/temporal/agentexecution/runner-result.d.ts.map +1 -1
- package/dist/temporal/agentexecution/runner-result.js +15 -0
- package/dist/temporal/agentexecution/runner-result.js.map +1 -1
- package/dist/temporal/agentexecution/workflows/invoke-agent-execution.d.ts.map +1 -1
- package/dist/temporal/agentexecution/workflows/invoke-agent-execution.js +44 -22
- package/dist/temporal/agentexecution/workflows/invoke-agent-execution.js.map +1 -1
- package/package.json +6 -6
- package/src/domain/agentexecution/__tests__/agentexecution.test.ts +2 -8
- package/src/domain/agentexecution/__tests__/store-faults.test.ts +315 -0
- package/src/domain/agentexecution/__tests__/update-status.test.ts +217 -0
- package/src/domain/agentexecution/artifacts.ts +21 -12
- package/src/domain/agentexecution/constants.ts +0 -15
- package/src/domain/agentexecution/lifecycle.ts +5 -3
- package/src/domain/agentexecution/platform-rows.ts +133 -0
- package/src/domain/agentexecution/subscribe.ts +7 -3
- package/src/domain/agentexecution/update-status.ts +70 -19
- package/src/domain/agentexecution/usage.ts +16 -13
- package/src/domain/organization/controller.ts +47 -10
- package/src/domain/workflow/__tests__/version-audit-degradation.test.ts +192 -0
- package/src/domain/workflow/controller.ts +2 -1
- package/src/domain/workflow/steps.ts +30 -18
- package/src/domain/workflowexecution/__tests__/store-faults.test.ts +418 -0
- package/src/domain/workflowexecution/create-steps.ts +6 -2
- package/src/domain/workflowexecution/lifecycle.ts +5 -2
- package/src/domain/workflowexecution/send-signal.ts +11 -4
- package/src/domain/workflowexecution/submit-approval.ts +6 -2
- package/src/domain/workflowexecution/submit-file-decision.ts +6 -2
- package/src/domain/workflowexecution/submit-workflow-task-approval.ts +6 -2
- package/src/domain/workflowexecution/subscribe-events.ts +6 -2
- package/src/domain/workflowexecution/subscribe.ts +11 -4
- package/src/extensions/__tests__/extension-composition.test.ts +90 -18
- package/src/extensions/organization-directory.ts +33 -7
- package/src/index.ts +5 -1
- package/src/pipeline/__tests__/blind-not-found.test.ts +297 -0
- package/src/pipeline/steps/__tests__/guard-memory-capture.test.ts +67 -5
- package/src/pipeline/steps/guard-memory-capture.ts +24 -19
- package/src/temporal/agentexecution/__tests__/invoke-workflow.test.ts +51 -1
- package/src/temporal/agentexecution/__tests__/own-behalf-status-writes.test.ts +149 -9
- package/src/temporal/agentexecution/__tests__/replay-histories/runner-failed.json +757 -0
- package/src/temporal/agentexecution/__tests__/replay-histories/user-cancel.json +755 -0
- package/src/temporal/agentexecution/__tests__/replay.test.ts +4 -2
- package/src/temporal/agentexecution/runner-result.ts +16 -0
- package/src/temporal/agentexecution/workflows/invoke-agent-execution.ts +49 -22
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
* every Stigmer-minted non-first-party credential carries the
|
|
15
15
|
* `platform_client_id` claim (StigmerJwtIssuer stamps it on user, guest,
|
|
16
16
|
* channel, and schedule tokens alike), so its presence IS the exclusion
|
|
17
|
-
* the Java RequestCallerIdentity.isFirstPartyHumanOperator computes.
|
|
17
|
+
* the Java RequestCallerIdentity.isFirstPartyHumanOperator computes. The
|
|
18
|
+
* claim counts only on a platform token (`iss: "stigmer"`, read through
|
|
19
|
+
* decodeVerifiedPlatformTokenPayload as the PlatformClient origin guard
|
|
20
|
+
* reads it): a bearer another verifier claimed is some other issuer's,
|
|
21
|
+
* whatever claims it carries, and is never taken for a Stigmer-minted
|
|
22
|
+
* one (stigmer#1312).
|
|
18
23
|
*
|
|
19
24
|
* RUNNER credentials carry neither the machine class nor that claim —
|
|
20
25
|
* their eligibility is EDITION POLICY, so the gate consults the composed
|
|
@@ -30,16 +35,21 @@
|
|
|
30
35
|
* this gate's own logic applies). The capability throws its own
|
|
31
36
|
* byte-pinned refusal for the org-mismatch arm. With no capability
|
|
32
37
|
* composed the gate's logic is byte-identical to before the seam: OSS
|
|
33
|
-
* trusted-local callers carry no token and OIDC console logins
|
|
34
|
-
*
|
|
35
|
-
*
|
|
38
|
+
* trusted-local callers carry no token and OIDC console logins are not
|
|
39
|
+
* platform tokens — the step admits both, so the single-user posture is
|
|
40
|
+
* unchanged (proven by the rosters).
|
|
36
41
|
*/
|
|
37
42
|
import type { DescMessage } from "@bufbuild/protobuf";
|
|
38
43
|
import { Code, ConnectError } from "@connectrpc/connect";
|
|
39
44
|
|
|
45
|
+
import { USER_TOKEN_CLAIMS } from "../../domain/platformclient/constants.js";
|
|
40
46
|
import { isServerComposedRequest } from "../../extensions/identity.js";
|
|
41
47
|
import type { PipelineStep } from "../pipeline.js";
|
|
42
48
|
import type { RequestContext } from "../request-context.js";
|
|
49
|
+
import {
|
|
50
|
+
decodeVerifiedPlatformTokenPayload,
|
|
51
|
+
stringClaim,
|
|
52
|
+
} from "../../platformtoken/envelope.js";
|
|
43
53
|
import type { RunnerCredentialProvider } from "../../runnerauth/runner-credential-provider.js";
|
|
44
54
|
import { metadataOf } from "./shapes.js";
|
|
45
55
|
|
|
@@ -138,20 +148,15 @@ export function memoryCaptureCredentialOf<Desc extends DescMessage>(
|
|
|
138
148
|
return undefined;
|
|
139
149
|
}
|
|
140
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Whether the bearer is a platform token naming a PlatformClient. Anything
|
|
153
|
+
* that is not a platform token (no token, an API key, another issuer's
|
|
154
|
+
* JWT) is not one, whatever claims it carries.
|
|
155
|
+
*/
|
|
141
156
|
function carriesPlatformClientClaim(rawToken: string): boolean {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const payload = JSON.parse(
|
|
148
|
-
Buffer.from(segments[1], "base64url").toString("utf8"),
|
|
149
|
-
) as { platform_client_id?: unknown };
|
|
150
|
-
return (
|
|
151
|
-
typeof payload.platform_client_id === "string" &&
|
|
152
|
-
payload.platform_client_id !== ""
|
|
153
|
-
);
|
|
154
|
-
} catch {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
+
const payload = decodeVerifiedPlatformTokenPayload(rawToken);
|
|
158
|
+
return (
|
|
159
|
+
payload !== undefined &&
|
|
160
|
+
stringClaim(payload, USER_TOKEN_CLAIMS.platformClientId) !== undefined
|
|
161
|
+
);
|
|
157
162
|
}
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
* - bounded auto-recovery on worker-shutdown shapes (#776: IN_PROGRESS
|
|
17
17
|
* persisted, honest status copy on exhaustion);
|
|
18
18
|
* - EXECUTION_FAILED propagation with the fallback persist;
|
|
19
|
+
* - the stop and failure copy each has one writer (stigmer#980): the
|
|
20
|
+
* pause persist carries the pause row, a runner-reported failure's
|
|
21
|
+
* write carries no lines (the runner explained it), and only a broken
|
|
22
|
+
* flow gets the platform's two lines;
|
|
19
23
|
* - external cancellation cleanup (CANCELLED persisted quietly —
|
|
20
24
|
* stigmer#282 — and the EC deleted);
|
|
21
25
|
* - callback-token completion on success AND failure (the DD-001 lane,
|
|
@@ -50,9 +54,15 @@ import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/ag
|
|
|
50
54
|
import {
|
|
51
55
|
ExecutionPhase,
|
|
52
56
|
FileChangeSetStatus,
|
|
57
|
+
MessageType,
|
|
53
58
|
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
54
59
|
import { Harness } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
|
|
55
60
|
|
|
61
|
+
import {
|
|
62
|
+
CANCEL_STOP_ROW,
|
|
63
|
+
PAUSE_STOP_ROW,
|
|
64
|
+
} from "../../../domain/agentexecution/platform-rows.js";
|
|
65
|
+
|
|
56
66
|
import {
|
|
57
67
|
INVOKE_AGENT_EXECUTION_WORKFLOW_NAME,
|
|
58
68
|
MEMO_ACTIVITY_TASK_QUEUE,
|
|
@@ -565,6 +575,12 @@ describe("invoke-agent-execution workflow (TestWorkflowEnvironment)", () => {
|
|
|
565
575
|
.includes(ExecutionPhase.EXECUTION_IN_PROGRESS),
|
|
566
576
|
"resume re-asserts IN_PROGRESS after the PAUSED persist",
|
|
567
577
|
).toBe(true);
|
|
578
|
+
// The workflow is the pause row's one writer: the runner cannot tell
|
|
579
|
+
// this stop from a cancel, so it settles without a row.
|
|
580
|
+
expect(
|
|
581
|
+
script.persistedStatuses[pausedAt]!.status.messages.map((m) => [m.type, m.content]),
|
|
582
|
+
"the PAUSED persist carries the pause row",
|
|
583
|
+
).toEqual([[MessageType.MESSAGE_SYSTEM, PAUSE_STOP_ROW]]);
|
|
568
584
|
}, 60_000);
|
|
569
585
|
|
|
570
586
|
it("auto-recovers a worker-shutdown interruption and persists IN_PROGRESS (#776)", async (testCtx) => { if (!envReady) return testCtx.skip();
|
|
@@ -611,6 +627,40 @@ describe("invoke-agent-execution workflow (TestWorkflowEnvironment)", () => {
|
|
|
611
627
|
);
|
|
612
628
|
expect(failed.length).toBeGreaterThan(0);
|
|
613
629
|
expect(failed[0]!.status.error).toBe("agent blew up");
|
|
630
|
+
// The runner already explained its own failure in the transcript, so
|
|
631
|
+
// no FAILED write of the workflow adds a second explanation.
|
|
632
|
+
expect(
|
|
633
|
+
failed.flatMap((entry) => entry.status.messages),
|
|
634
|
+
"a runner-reported failure adds no platform lines",
|
|
635
|
+
).toEqual([]);
|
|
636
|
+
expectExecutionContextDeleted();
|
|
637
|
+
}, 30_000);
|
|
638
|
+
|
|
639
|
+
it("explains a flow that broke without a runner verdict with the platform's two lines", async (testCtx) => { if (!envReady) return testCtx.skip();
|
|
640
|
+
const { ApplicationFailure } = await import("@temporalio/common");
|
|
641
|
+
script.executeBehaviors = [
|
|
642
|
+
async () => {
|
|
643
|
+
// Not recoverable (not a cancellation, heartbeat timeout or worker
|
|
644
|
+
// shutdown), so the flow breaks with no FAILED result to read.
|
|
645
|
+
throw ApplicationFailure.create({ message: "runner crashed", nonRetryable: true });
|
|
646
|
+
},
|
|
647
|
+
];
|
|
648
|
+
|
|
649
|
+
const handle = await startWorkflow(workflowInput());
|
|
650
|
+
await expect(handle.result()).rejects.toThrow(/Workflow execution failed/);
|
|
651
|
+
|
|
652
|
+
const failed = script.persistedStatuses.filter(
|
|
653
|
+
(entry) => entry.status.phase === ExecutionPhase.EXECUTION_FAILED,
|
|
654
|
+
);
|
|
655
|
+
expect(failed.length).toBeGreaterThan(0);
|
|
656
|
+
const lines = failed.at(-1)!.status.messages;
|
|
657
|
+
expect(lines.map((m) => m.type)).toEqual([
|
|
658
|
+
MessageType.MESSAGE_SYSTEM,
|
|
659
|
+
MessageType.MESSAGE_SYSTEM,
|
|
660
|
+
]);
|
|
661
|
+
// The details carry the wrapped activity error (wrapActivityError's
|
|
662
|
+
// operator-actionable prefix), not a runner-authored sentence.
|
|
663
|
+
expect(lines[1]!.content).toMatch(/^Error details: activity 'ExecuteDeepAgent'/);
|
|
614
664
|
expectExecutionContextDeleted();
|
|
615
665
|
}, 30_000);
|
|
616
666
|
|
|
@@ -638,7 +688,7 @@ describe("invoke-agent-execution workflow (TestWorkflowEnvironment)", () => {
|
|
|
638
688
|
// system message is the durable marker.
|
|
639
689
|
expect(cancelled.status.error).toBe("");
|
|
640
690
|
expect(cancelled.status.messages.map((message) => message.content)).toEqual([
|
|
641
|
-
|
|
691
|
+
CANCEL_STOP_ROW,
|
|
642
692
|
]);
|
|
643
693
|
expectExecutionContextDeleted();
|
|
644
694
|
}, 60_000);
|
|
@@ -20,19 +20,24 @@
|
|
|
20
20
|
* execution status` from authorize.ts — the dark production composition's
|
|
21
21
|
* exact log line.
|
|
22
22
|
*
|
|
23
|
-
* The
|
|
23
|
+
* The payload shapes are the workflow's real ones
|
|
24
24
|
* (invoke-agent-execution.ts): updateStatusOnFailure's FAILED with two
|
|
25
|
-
* MESSAGE_SYSTEM lines
|
|
25
|
+
* MESSAGE_SYSTEM lines (a flow that broke) or none (a runner-reported
|
|
26
|
+
* failure), updateStatusOnCancellation's CANCELLED with its stop row,
|
|
26
27
|
* and the phase-only persistFinalStatus / persistInterruptedStatus /
|
|
27
|
-
* persistResumedStatus shape.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* persistResumedStatus shape.
|
|
29
|
+
*
|
|
30
|
+
* The last arms pin the transcript a stopped execution keeps over this
|
|
31
|
+
* production lane (stigmer#980): the platform's lines are appended to the
|
|
32
|
+
* runner's transcript, never substituted for it, and the cancel row stays
|
|
33
|
+
* last when the runner's settle write lands after it. The merge's full
|
|
34
|
+
* case matrix is domain/agentexecution/__tests__/update-status.test.ts.
|
|
30
35
|
*/
|
|
31
36
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
32
37
|
import { tmpdir } from "node:os";
|
|
33
38
|
import path from "node:path";
|
|
34
39
|
import { create, toJson } from "@bufbuild/protobuf";
|
|
35
|
-
import type { JsonValue } from "@bufbuild/protobuf";
|
|
40
|
+
import type { JsonValue, MessageInitShape } from "@bufbuild/protobuf";
|
|
36
41
|
import { createClient } from "@connectrpc/connect";
|
|
37
42
|
import type { Client } from "@temporalio/client";
|
|
38
43
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
@@ -46,9 +51,12 @@ import {
|
|
|
46
51
|
ExecutionPhase,
|
|
47
52
|
MessageType,
|
|
48
53
|
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
54
|
+
import { AgentExecutionUpdateStatusInputSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
55
|
+
import type { AgentMessageSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
49
56
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
50
57
|
|
|
51
58
|
import { loadConfig } from "../../../boot/config.js";
|
|
59
|
+
import { CANCEL_STOP_ROW } from "../../../domain/agentexecution/platform-rows.js";
|
|
52
60
|
import { composeServer } from "../../../boot/compose.js";
|
|
53
61
|
import type { ComposedServer } from "../../../boot/compose.js";
|
|
54
62
|
import { createLogger } from "../../../boot/logger.js";
|
|
@@ -147,7 +155,11 @@ describe("own-behalf status writes under an enforcing Authorizer", () => {
|
|
|
147
155
|
transitions.length = 0;
|
|
148
156
|
});
|
|
149
157
|
|
|
150
|
-
async function seedExecution(
|
|
158
|
+
async function seedExecution(
|
|
159
|
+
id: string,
|
|
160
|
+
phase: ExecutionPhase = ExecutionPhase.EXECUTION_IN_PROGRESS,
|
|
161
|
+
messages: MessageInitShape<typeof AgentMessageSchema>[] = [],
|
|
162
|
+
): Promise<void> {
|
|
151
163
|
// Seeded through the store: the create path is not under test, and
|
|
152
164
|
// the denying Authorizer would refuse it over the wire.
|
|
153
165
|
await server.store.saveResource(
|
|
@@ -159,7 +171,7 @@ describe("own-behalf status writes under an enforcing Authorizer", () => {
|
|
|
159
171
|
kind: "AgentExecution",
|
|
160
172
|
metadata: { id, name: "own-behalf", org: "acme" },
|
|
161
173
|
spec: { agentId: "agt_1", sessionId: "ses_1" },
|
|
162
|
-
status: { phase
|
|
174
|
+
status: { phase, messages },
|
|
163
175
|
}),
|
|
164
176
|
);
|
|
165
177
|
}
|
|
@@ -253,7 +265,7 @@ describe("own-behalf status writes under an enforcing Authorizer", () => {
|
|
|
253
265
|
messages: [
|
|
254
266
|
{
|
|
255
267
|
type: MessageType.MESSAGE_SYSTEM,
|
|
256
|
-
content:
|
|
268
|
+
content: CANCEL_STOP_ROW,
|
|
257
269
|
},
|
|
258
270
|
],
|
|
259
271
|
}),
|
|
@@ -296,6 +308,134 @@ describe("own-behalf status writes under an enforcing Authorizer", () => {
|
|
|
296
308
|
expect(authorizerCalls).toHaveLength(0);
|
|
297
309
|
});
|
|
298
310
|
|
|
311
|
+
describe("the transcript a stopped execution keeps (stigmer#980)", () => {
|
|
312
|
+
const runnerTurns = [
|
|
313
|
+
{ type: MessageType.MESSAGE_AI, content: "a1" },
|
|
314
|
+
{ type: MessageType.MESSAGE_AI, content: "a2" },
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
async function transcriptOf(id: string): Promise<string[]> {
|
|
318
|
+
return ((await persisted(id)).status?.messages ?? []).map(
|
|
319
|
+
(m) => m.content,
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
it("appends the cancel row to a transcript the Cancel RPC already stamped CANCELLED, and keeps it last across the runner's late settle write", async () => {
|
|
324
|
+
await seedExecution(
|
|
325
|
+
"aex_ownbehalf_cancel_race",
|
|
326
|
+
ExecutionPhase.EXECUTION_CANCELLED,
|
|
327
|
+
runnerTurns,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
await updateStatus(
|
|
331
|
+
"aex_ownbehalf_cancel_race",
|
|
332
|
+
toJson(
|
|
333
|
+
AgentExecutionStatusSchema,
|
|
334
|
+
create(AgentExecutionStatusSchema, {
|
|
335
|
+
phase: ExecutionPhase.EXECUTION_CANCELLED,
|
|
336
|
+
messages: [
|
|
337
|
+
{ type: MessageType.MESSAGE_SYSTEM, content: CANCEL_STOP_ROW },
|
|
338
|
+
],
|
|
339
|
+
}),
|
|
340
|
+
),
|
|
341
|
+
);
|
|
342
|
+
expect(await transcriptOf("aex_ownbehalf_cancel_race")).toEqual([
|
|
343
|
+
"a1",
|
|
344
|
+
"a2",
|
|
345
|
+
CANCEL_STOP_ROW,
|
|
346
|
+
]);
|
|
347
|
+
|
|
348
|
+
// The runner learns of the cancel on its next heartbeat and settles
|
|
349
|
+
// its transcript over the same handler, with no row and no phase.
|
|
350
|
+
await statusWriter.updateStatus(
|
|
351
|
+
create(AgentExecutionUpdateStatusInputSchema, {
|
|
352
|
+
executionId: "aex_ownbehalf_cancel_race",
|
|
353
|
+
status: {
|
|
354
|
+
messages: [
|
|
355
|
+
...runnerTurns,
|
|
356
|
+
{ type: MessageType.MESSAGE_AI, content: "a3" },
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
}),
|
|
360
|
+
);
|
|
361
|
+
expect(await transcriptOf("aex_ownbehalf_cancel_race")).toEqual([
|
|
362
|
+
"a1",
|
|
363
|
+
"a2",
|
|
364
|
+
"a3",
|
|
365
|
+
CANCEL_STOP_ROW,
|
|
366
|
+
]);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("appends a broken flow's two lines to the runner's transcript", async () => {
|
|
370
|
+
await seedExecution(
|
|
371
|
+
"aex_ownbehalf_broken_flow",
|
|
372
|
+
ExecutionPhase.EXECUTION_IN_PROGRESS,
|
|
373
|
+
runnerTurns,
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
await updateStatus(
|
|
377
|
+
"aex_ownbehalf_broken_flow",
|
|
378
|
+
toJson(
|
|
379
|
+
AgentExecutionStatusSchema,
|
|
380
|
+
create(AgentExecutionStatusSchema, {
|
|
381
|
+
phase: ExecutionPhase.EXECUTION_FAILED,
|
|
382
|
+
error: "activity crashed",
|
|
383
|
+
messages: [
|
|
384
|
+
{
|
|
385
|
+
type: MessageType.MESSAGE_SYSTEM,
|
|
386
|
+
content: "Internal system error occurred during execution.",
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
type: MessageType.MESSAGE_SYSTEM,
|
|
390
|
+
content: "Error details: activity crashed",
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
}),
|
|
394
|
+
),
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
const execution = await persisted("aex_ownbehalf_broken_flow");
|
|
398
|
+
expect(execution.status?.phase).toBe(ExecutionPhase.EXECUTION_FAILED);
|
|
399
|
+
expect(await transcriptOf("aex_ownbehalf_broken_flow")).toEqual([
|
|
400
|
+
"a1",
|
|
401
|
+
"a2",
|
|
402
|
+
"Internal system error occurred during execution.",
|
|
403
|
+
"Error details: activity crashed",
|
|
404
|
+
]);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it("leaves a runner-reported failure's transcript as the runner wrote it", async () => {
|
|
408
|
+
await seedExecution(
|
|
409
|
+
"aex_ownbehalf_runner_failed",
|
|
410
|
+
ExecutionPhase.EXECUTION_FAILED,
|
|
411
|
+
[
|
|
412
|
+
...runnerTurns,
|
|
413
|
+
{
|
|
414
|
+
type: MessageType.MESSAGE_SYSTEM,
|
|
415
|
+
content: "Execution failed: the tool refused",
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
await updateStatus(
|
|
421
|
+
"aex_ownbehalf_runner_failed",
|
|
422
|
+
toJson(
|
|
423
|
+
AgentExecutionStatusSchema,
|
|
424
|
+
create(AgentExecutionStatusSchema, {
|
|
425
|
+
phase: ExecutionPhase.EXECUTION_FAILED,
|
|
426
|
+
error: "agent execution failed: the tool refused",
|
|
427
|
+
}),
|
|
428
|
+
),
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
expect(await transcriptOf("aex_ownbehalf_runner_failed")).toEqual([
|
|
432
|
+
"a1",
|
|
433
|
+
"a2",
|
|
434
|
+
"Execution failed: the tool refused",
|
|
435
|
+
]);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
|
|
299
439
|
it("surfaces the lane's NotFound as the activity's failure for a deleted execution", async () => {
|
|
300
440
|
await expect(
|
|
301
441
|
updateStatus("aex_ownbehalf_missing", { phase: "EXECUTION_FAILED" }),
|