@nylorun/runtime 0.16.0-beta → 0.19.0-beta
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/CHANGELOG.md +207 -0
- package/README.md +105 -63
- package/dist/adapters/execution/restate.d.ts +1 -3
- package/dist/adapters/execution/restate.js +4 -31
- package/dist/adapters/replication/pgoutput.js +1 -1
- package/dist/adapters/sandbox/local.js +1 -1
- package/dist/adapters/sandbox/virtual.js +1 -1
- package/dist/api/ag-ui/endpoint.js +1 -2
- package/dist/api/ag-ui/routes.d.ts +1 -1
- package/dist/api/ag-ui/routes.js +11 -13
- package/dist/api/ag-ui/translate.d.ts +1 -3
- package/dist/api/ag-ui/translate.js +2 -17
- package/dist/api/components.d.ts +4783 -2588
- package/dist/api/components.js +19 -21
- package/dist/api/gate/routes.d.ts +7 -8
- package/dist/api/gate/routes.js +58 -52
- package/dist/api/http/app.d.ts +6 -0
- package/dist/api/http/app.js +20 -14
- package/dist/api/http/define.d.ts +22 -13
- package/dist/api/http/define.js +43 -39
- package/dist/api/http/routes/access.d.ts +3 -19
- package/dist/api/http/routes/access.js +25 -184
- package/dist/api/http/routes/artifacts.js +3 -7
- package/dist/api/http/routes/files.d.ts +8 -0
- package/dist/api/http/routes/files.js +77 -0
- package/dist/api/http/routes/keys.d.ts +10 -0
- package/dist/api/http/routes/keys.js +64 -0
- package/dist/api/http/routes/me.d.ts +13 -0
- package/dist/api/http/routes/me.js +48 -0
- package/dist/api/http/routes/reads.d.ts +66 -0
- package/dist/api/http/routes/reads.js +127 -0
- package/dist/api/http/routes/sandboxes.d.ts +2 -2
- package/dist/api/http/routes/sandboxes.js +8 -7
- package/dist/api/http/routes/sessions.js +31 -20
- package/dist/api/http/routes/tenant.js +15 -18
- package/dist/api/http/routes/vaults.d.ts +13 -3
- package/dist/api/http/routes/vaults.js +136 -64
- package/dist/api/openapi.d.ts +10 -11
- package/dist/api/openapi.js +292 -68
- package/dist/artifacts/links.js +6 -6
- package/dist/artifacts/service.js +1 -1
- package/dist/core/flow-host.d.ts +44 -23
- package/dist/core/flow-host.js +88 -106
- package/dist/core/provider.d.ts +4 -1
- package/dist/core/provider.js +6 -1
- package/dist/core/sandbox-routes.d.ts +0 -9
- package/dist/core/sandbox-routes.js +2 -21
- package/dist/execution/memory.d.ts +4 -5
- package/dist/execution/memory.js +11 -25
- package/dist/execution/types.d.ts +3 -33
- package/dist/execution/types.js +2 -12
- package/dist/gates/http-tool.d.ts +86 -0
- package/dist/gates/http-tool.js +197 -0
- package/dist/gates/mcp-handler.d.ts +3 -0
- package/dist/gates/mcp-handler.js +6 -4
- package/dist/gates/meter.d.ts +2 -2
- package/dist/gates/meter.js +6 -0
- package/dist/gates/tenant-vaults.d.ts +19 -14
- package/dist/gates/tenant-vaults.js +17 -12
- package/dist/gates/tool-calls.d.ts +7 -15
- package/dist/gates/tool-calls.js +4 -4
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +23 -47
- package/dist/gates/tool-contract.d.ts +27 -39
- package/dist/gates/tool-contract.js +30 -21
- package/dist/gates/tool-gate.d.ts +25 -24
- package/dist/gates/tool-gate.js +11 -7
- package/dist/harness/calls.d.ts +10 -4
- package/dist/harness/calls.js +15 -6
- package/dist/harness/executors.d.ts +1 -1
- package/dist/harness/executors.js +17 -12
- package/dist/harness/main.d.ts +2 -2
- package/dist/harness/main.js +5 -13
- package/dist/harness/service.d.ts +4 -5
- package/dist/harness/service.js +9 -2
- package/dist/harness-api/in-process.js +2 -2
- package/dist/harness-api/record.d.ts +6 -3
- package/dist/harness-api/record.js +65 -72
- package/dist/harness-api/server.d.ts +1 -9
- package/dist/harness-api/server.js +19 -17
- package/dist/harness-api/start.js +1 -5
- package/dist/harness-api/workspace.js +2 -3
- package/dist/harness-api/ws-server.js +1 -1
- package/dist/host/app.d.ts +2 -10
- package/dist/host/app.js +24 -44
- package/dist/host/config-for.d.ts +5 -5
- package/dist/host/config-for.js +2 -3
- package/dist/host/config.d.ts +0 -11
- package/dist/host/create-host.d.ts +3 -40
- package/dist/host/create-host.js +18 -91
- package/dist/host/environment.d.ts +0 -3
- package/dist/host/environment.js +0 -9
- package/dist/host/gates.d.ts +4 -1
- package/dist/host/gates.js +9 -3
- package/dist/host/http.d.ts +1 -2
- package/dist/host/http.js +1 -7
- package/dist/host/index.d.ts +2 -2
- package/dist/host/index.js +2 -2
- package/dist/host/main.js +55 -40
- package/dist/host/operate.d.ts +29 -0
- package/dist/host/operate.js +223 -0
- package/dist/host/stack-config.d.ts +23 -20
- package/dist/host/stack-config.js +28 -44
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/dist/infra/workers.d.ts +1 -1
- package/dist/keys/client.js +9 -1
- package/dist/keys/keys.d.ts +12 -2
- package/dist/keys/keys.js +10 -2
- package/dist/management-openapi.json +6491 -0
- package/dist/mcp/connect.d.ts +8 -4
- package/dist/mcp/connect.js +8 -27
- package/dist/mcp/pool.d.ts +6 -9
- package/dist/mcp/pool.js +8 -18
- package/dist/mcp/snapshot.d.ts +8 -2
- package/dist/mcp/snapshot.js +29 -2
- package/dist/media.d.ts +1 -1
- package/dist/media.js +1 -1
- package/dist/model/pi-model.js +3 -0
- package/dist/openapi.json +6228 -15615
- package/dist/reads/access.d.ts +4 -0
- package/dist/reads/access.js +12 -0
- package/dist/reads/cursor.d.ts +11 -0
- package/dist/reads/cursor.js +80 -0
- package/dist/reads/types.d.ts +25 -0
- package/dist/reads/types.js +1 -0
- package/dist/sandbox/manager.d.ts +16 -0
- package/dist/sandbox/manager.js +40 -0
- package/dist/sandbox/session-sandbox.js +13 -3
- package/dist/sandbox/skills.d.ts +13 -0
- package/dist/sandbox/skills.js +46 -0
- package/dist/sandbox/types.d.ts +2 -2
- package/dist/store/ownership.d.ts +9 -0
- package/dist/store/ownership.js +13 -0
- package/dist/store/postgres/connect.d.ts +2 -0
- package/dist/store/postgres/connect.js +29 -0
- package/dist/store/postgres/drizzle/0008_installation_vaults.sql +2 -0
- package/dist/store/postgres/drizzle/0009_oauth_pending.sql +19 -0
- package/dist/store/postgres/drizzle/0010_oss_auth_removals.sql +3 -0
- package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
- package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
- package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
- package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +49 -0
- package/dist/store/postgres/reads.d.ts +3 -0
- package/dist/store/postgres/reads.js +272 -0
- package/dist/store/postgres/schema.d.ts +423 -594
- package/dist/store/postgres/schema.js +84 -101
- package/dist/store/postgres/store.js +96 -248
- package/dist/store/postgres/tenant.d.ts +34 -2
- package/dist/store/postgres/tenant.js +16 -32
- package/dist/store/types.d.ts +44 -119
- package/dist/store/types.js +0 -1
- package/dist/streams/control.d.ts +0 -7
- package/dist/streams/control.js +0 -13
- package/dist/streams/types.d.ts +2 -20
- package/dist/streams/types.js +1 -1
- package/dist/tenant/advance.d.ts +3 -3
- package/dist/tenant/advance.js +10 -7
- package/dist/tenant/auth.d.ts +24 -15
- package/dist/tenant/auth.js +44 -55
- package/dist/tenant/cause.d.ts +3 -3
- package/dist/tenant/cause.js +1 -1
- package/dist/tenant/commands.d.ts +2 -18
- package/dist/tenant/commands.js +38 -179
- package/dist/tenant/context.d.ts +33 -45
- package/dist/tenant/definition-files.d.ts +24 -0
- package/dist/tenant/definition-files.js +103 -0
- package/dist/tenant/effects.d.ts +20 -18
- package/dist/tenant/effects.js +59 -133
- package/dist/tenant/ephemeral.d.ts +21 -8
- package/dist/tenant/ephemeral.js +11 -12
- package/dist/tenant/host-token.js +5 -5
- package/dist/tenant/identity-file.d.ts +75 -0
- package/dist/tenant/identity-file.js +202 -0
- package/dist/tenant/issuers.d.ts +48 -0
- package/dist/tenant/issuers.js +311 -0
- package/dist/tenant/jwt.js +3 -3
- package/dist/tenant/module.d.ts +1 -4
- package/dist/tenant/module.js +3 -12
- package/dist/tenant/operator-keys.d.ts +44 -0
- package/dist/tenant/operator-keys.js +87 -0
- package/dist/tenant/outbound.d.ts +40 -1
- package/dist/tenant/outbound.js +167 -18
- package/dist/tenant/paths.d.ts +1 -1
- package/dist/tenant/paths.js +1 -3
- package/dist/tenant/principals.d.ts +13 -13
- package/dist/tenant/principals.js +35 -36
- package/dist/tenant/run-token.js +6 -6
- package/dist/tenant/runtime.d.ts +5 -3
- package/dist/tenant/runtime.js +29 -15
- package/dist/tenant/sandboxes.d.ts +9 -6
- package/dist/tenant/sandboxes.js +16 -5
- package/dist/tenant/scheduler.d.ts +1 -5
- package/dist/tenant/scheduler.js +2 -5
- package/dist/tenant/session-streams.d.ts +8 -11
- package/dist/tenant/session-streams.js +48 -32
- package/dist/tenant/session.d.ts +8 -41
- package/dist/tenant/session.js +0 -37
- package/dist/tenant/sessions.d.ts +2 -2
- package/dist/tenant/sessions.js +17 -9
- package/dist/tenant/signing-keys.d.ts +3 -2
- package/dist/tenant/signing-keys.js +5 -5
- package/dist/tenant/skill-tool.d.ts +20 -0
- package/dist/tenant/skill-tool.js +72 -0
- package/dist/tenant/slim.d.ts +1 -1
- package/dist/tenant/slim.js +1 -1
- package/dist/tenant/status.js +7 -12
- package/dist/tenant/store-pg.d.ts +15 -0
- package/dist/tenant/store-pg.js +7 -3
- package/dist/tenant/streams.d.ts +3 -11
- package/dist/tenant/streams.js +2 -29
- package/dist/tenant/sweep.js +0 -5
- package/dist/tenant/transcript.d.ts +2 -2
- package/dist/tenant/transcript.js +2 -2
- package/dist/tenant/types.d.ts +33 -19
- package/dist/tenant/worker.d.ts +1 -3
- package/dist/tenant/worker.js +3 -8
- package/dist/vault/crypto.d.ts +7 -0
- package/dist/vault/crypto.js +16 -0
- package/dist/vault/oauth.d.ts +55 -0
- package/dist/vault/oauth.js +186 -0
- package/dist/vault/service.d.ts +54 -7
- package/dist/vault/service.js +261 -17
- package/dist/vault/sources.d.ts +60 -0
- package/dist/vault/sources.js +196 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -4
- package/dist/admin-openapi.json +0 -604
- package/dist/api/http/routes/actions.d.ts +0 -8
- package/dist/api/http/routes/actions.js +0 -76
- package/dist/api/http/routes/endpoints.d.ts +0 -7
- package/dist/api/http/routes/endpoints.js +0 -100
- package/dist/host/admin-api.d.ts +0 -23
- package/dist/host/admin-api.js +0 -74
- package/dist/host/cors.d.ts +0 -27
- package/dist/host/cors.js +0 -64
- package/dist/mcp/stdio.d.ts +0 -17
- package/dist/mcp/stdio.js +0 -82
- package/dist/tenant/access-policy.d.ts +0 -30
- package/dist/tenant/access-policy.js +0 -52
- package/dist/tenant/actions.d.ts +0 -14
- package/dist/tenant/actions.js +0 -29
- package/dist/tenant/browser.d.ts +0 -15
- package/dist/tenant/browser.js +0 -39
- package/dist/tenant/delivery-token.d.ts +0 -41
- package/dist/tenant/delivery-token.js +0 -104
- package/dist/tenant/delivery.d.ts +0 -62
- package/dist/tenant/delivery.js +0 -481
- package/dist/tenant/endpoints.d.ts +0 -67
- package/dist/tenant/endpoints.js +0 -90
- package/dist/tenant/subject-limits.d.ts +0 -16
- package/dist/tenant/subject-limits.js +0 -45
- package/dist/tenant/tokens.d.ts +0 -14
- package/dist/tenant/tokens.js +0 -187
package/dist/core/flow-host.js
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* - `deriveAgentEffectSessionId(workflowSessionId, path, request): string`
|
|
15
15
|
* - `isWorkflowManifest(manifest): manifest is WorkflowManifest`
|
|
16
16
|
* - `isFlowToolEffect(request): boolean`, `isFlowEffect(request): boolean`
|
|
17
|
+
* - `linkedMessageInput(body): JsonValue`
|
|
18
|
+
* - `settleAgentEffect({ t, effect, outcome }): Promise<void>`
|
|
19
|
+
* - `recordVerdict(t, request, value): Promise<void>`
|
|
17
20
|
* - `pathDepth(path): number`
|
|
18
21
|
* - `countActiveFlowWork(t, workflowSessionId, turnId): Promise<number>`
|
|
19
22
|
* - `commandKey(sessionId, idempotencyKey): string`
|
|
@@ -25,8 +28,9 @@
|
|
|
25
28
|
* - `reconcilePendingAgentEffect({ t, effectId, schedule }): Promise<boolean>`
|
|
26
29
|
* - `planCancelCascade({ t, workflowSessionId, turnId }): Promise<CascadeCancelPlan>`
|
|
27
30
|
* - `cancelSiblingWork({ t, workflowSessionId, turnId, siblingPaths?, cancelEffectIds? }): Promise<CancelSiblingResult>`
|
|
28
|
-
* - `
|
|
31
|
+
* - `cancelQueuedEffects({ t, workflowSessionId, turnId }): Promise<string[]>`
|
|
29
32
|
* - `aggregateWaits({ t, workflowSessionId }): Promise<FlowWait[]>`
|
|
33
|
+
* - `flowInteractionOf(waits, workflowSessionId, interactionId): { id; kind } | undefined`
|
|
30
34
|
* - `findInteractionOwner({ t, workflowSessionId, interactionId }): Promise<{ sessionId; path } | undefined>`
|
|
31
35
|
* - `foreignInteractionConflict({ t, workflowSessionId, interactionId }): Promise<{ status: 409; message; ownerSessionId } | undefined>`
|
|
32
36
|
* - `wakeForQueuedEffects({ t, workflowSessionId, turnId, limits, schedule }): Promise<boolean>`
|
|
@@ -40,10 +44,11 @@
|
|
|
40
44
|
* commits. The effect is bound to its turn through the message's idempotency key
|
|
41
45
|
* (`linkedMessageKey`): the `commands` document of that key names the turn it opened.
|
|
42
46
|
*
|
|
43
|
-
* The Tenant sweep (`tenant/sweep.ts`) calls the reconcile
|
|
44
|
-
*
|
|
47
|
+
* The Tenant sweep (`tenant/sweep.ts`) calls the reconcile functions, one transaction per
|
|
48
|
+
* effect.
|
|
45
49
|
*/
|
|
46
50
|
import { createHash } from "node:crypto";
|
|
51
|
+
import { isVerdict } from "@nylorun/core/define";
|
|
47
52
|
import { mayDispatchMore } from "./limits.js";
|
|
48
53
|
/** Deterministic agent session id: derive(workflowSessionId, path, …parts). */
|
|
49
54
|
export function deriveSessionId(workflowSessionId, path, ...parts) {
|
|
@@ -84,23 +89,61 @@ export function isFlowToolEffect(request) {
|
|
|
84
89
|
request.capabilityId === undefined);
|
|
85
90
|
}
|
|
86
91
|
export function isFlowEffect(request) {
|
|
87
|
-
return
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
return request.kind === "agent" || isFlowToolEffect(request);
|
|
93
|
+
}
|
|
94
|
+
/** Pretty JSON for a message, or a string as it is. */
|
|
95
|
+
function text(value) {
|
|
96
|
+
return typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* What a flow `agent` effect sends its linked session (D12): the stage's input, and, when the
|
|
100
|
+
* engine sent the flow's own input beside it, that input first as the original request. Pure,
|
|
101
|
+
* so a replayed effect sends the same message.
|
|
102
|
+
*/
|
|
103
|
+
export function linkedMessageInput(body) {
|
|
104
|
+
if (body.flowInput === undefined)
|
|
105
|
+
return body.input;
|
|
106
|
+
return `Original request:\n${text(body.flowInput)}\n\n${text(body.input)}`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Settle a flow `agent` effect with its linked turn's outcome, in the caller's transaction,
|
|
110
|
+
* which holds the workflow session's lock. A verifier agent's verdict is recorded as
|
|
111
|
+
* `loop.verified`; an outcome that is not a verdict fails the Loop in the engine instead.
|
|
112
|
+
*/
|
|
113
|
+
export async function settleAgentEffect(input) {
|
|
114
|
+
const { t, effect, outcome } = input;
|
|
115
|
+
await t.put("effects", effect.request.effectId, { ...effect, status: "completed", outcome });
|
|
116
|
+
if (effect.request.context?.role === "verify-agent")
|
|
117
|
+
await recordVerdict(t, effect.request, outcome.value);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Record a Loop verifier's verdict as `loop.verified`: a verifier agent's output, or an HTTP
|
|
121
|
+
* verifier's answer (`role: "verify-http"`, a completed HTTP outcome). Anything that is not a
|
|
122
|
+
* verdict records nothing; the engine fails the Loop on it.
|
|
123
|
+
*/
|
|
124
|
+
export async function recordVerdict(t, request, value) {
|
|
125
|
+
const answer = value;
|
|
126
|
+
const verdict = request.context?.role !== "verify-http"
|
|
127
|
+
? value
|
|
128
|
+
: answer?.kind === "completed"
|
|
129
|
+
? answer.output
|
|
130
|
+
: undefined;
|
|
131
|
+
if (!isVerdict(verdict))
|
|
132
|
+
return;
|
|
133
|
+
await t.event(request.sessionId, request.turnId, "loop.verified", {
|
|
134
|
+
path: String(request.context.loopPath ?? request.path ?? ""),
|
|
135
|
+
n: Number(request.context.n ?? 1),
|
|
136
|
+
pass: verdict.pass,
|
|
137
|
+
...(verdict.pass ? {} : { feedback: verdict.feedback }),
|
|
138
|
+
...(verdict.data !== undefined ? { data: verdict.data } : {}),
|
|
139
|
+
});
|
|
91
140
|
}
|
|
92
|
-
/** Actions still to be settled: waiting to be delivered, or being delivered. */
|
|
93
|
-
const OPEN_ACTION = ["pending", "delivering"];
|
|
94
141
|
function scheduleAfterCommit(t, schedule, id, wake) {
|
|
95
142
|
t.afterCommit(() => schedule(id, wake));
|
|
96
143
|
}
|
|
97
|
-
/** Active agent turns +
|
|
144
|
+
/** Active agent turns + tool nodes in flight for one workflow turn. */
|
|
98
145
|
export async function countActiveFlowWork(t, workflowSessionId, turnId) {
|
|
99
146
|
const effects = await t.effectsForTurn(workflowSessionId, turnId);
|
|
100
|
-
const openActions = new Set((await t.actionsForSession(workflowSessionId, {
|
|
101
|
-
turnId,
|
|
102
|
-
statuses: OPEN_ACTION,
|
|
103
|
-
})).map((action) => action.actionId));
|
|
104
147
|
let n = 0;
|
|
105
148
|
for (const effect of effects) {
|
|
106
149
|
if (effect.status === "queued")
|
|
@@ -111,10 +154,7 @@ export async function countActiveFlowWork(t, workflowSessionId, turnId) {
|
|
|
111
154
|
n += 1;
|
|
112
155
|
continue;
|
|
113
156
|
}
|
|
114
|
-
if ((effect.request
|
|
115
|
-
effect.request?.kind === "verify" ||
|
|
116
|
-
isFlowToolEffect(effect.request)) &&
|
|
117
|
-
openActions.has(effect.request.effectId))
|
|
157
|
+
if (isFlowToolEffect(effect.request) && effect.status === "invoking")
|
|
118
158
|
n += 1;
|
|
119
159
|
}
|
|
120
160
|
return n;
|
|
@@ -199,11 +239,7 @@ export async function wakeLinkedWorkflow(input) {
|
|
|
199
239
|
},
|
|
200
240
|
}
|
|
201
241
|
: { value: input.output ?? null };
|
|
202
|
-
await t
|
|
203
|
-
...effect,
|
|
204
|
-
status: "completed",
|
|
205
|
-
outcome,
|
|
206
|
-
});
|
|
242
|
+
await settleAgentEffect({ t, effect, outcome });
|
|
207
243
|
workflow.status = "runnable";
|
|
208
244
|
await t.put("sessions", workflow.id, workflow);
|
|
209
245
|
scheduleAfterCommit(t, input.schedule, workflow.id, {
|
|
@@ -259,45 +295,22 @@ export function pathDepth(path) {
|
|
|
259
295
|
return 0;
|
|
260
296
|
return path.split("/").filter(Boolean).length;
|
|
261
297
|
}
|
|
262
|
-
/**
|
|
263
|
-
* Plan a cancel cascade for a workflow session: linked agents deepest first,
|
|
264
|
-
* then pending → cancelled and delivering → uncertain actions (SD-P11 / WF-R53).
|
|
265
|
-
*/
|
|
298
|
+
/** Plan a cancel cascade for a workflow session: linked agents deepest first (SD-P11 / WF-R53). */
|
|
266
299
|
export async function planCancelCascade(input) {
|
|
267
300
|
const { t } = input;
|
|
268
301
|
const agentEntries = (await t.linkedSessions(input.workflowSessionId)).map((linked) => ({ sessionId: linked.agentSessionId, path: linked.link.path }));
|
|
269
302
|
agentEntries.sort((a, b) => pathDepth(b.path) - pathDepth(a.path));
|
|
270
|
-
|
|
271
|
-
const deliveringActionIds = [];
|
|
272
|
-
const actions = await t.actionsForSession(input.workflowSessionId, {
|
|
273
|
-
...(input.turnId !== null ? { turnId: input.turnId } : {}),
|
|
274
|
-
statuses: OPEN_ACTION,
|
|
275
|
-
});
|
|
276
|
-
for (const action of actions) {
|
|
277
|
-
if (action.status === "pending")
|
|
278
|
-
pendingActionIds.push(action.actionId);
|
|
279
|
-
// Being delivered: the code may already be running.
|
|
280
|
-
else
|
|
281
|
-
deliveringActionIds.push(action.actionId);
|
|
282
|
-
}
|
|
283
|
-
return {
|
|
284
|
-
agentSessionIds: agentEntries.map((e) => e.sessionId),
|
|
285
|
-
pendingActionIds,
|
|
286
|
-
deliveringActionIds,
|
|
287
|
-
};
|
|
303
|
+
return { agentSessionIds: agentEntries.map((e) => e.sessionId) };
|
|
288
304
|
}
|
|
289
305
|
/**
|
|
290
306
|
* Cancel sibling work under a Parallel/Map parent when one branch fails (PAR-R6).
|
|
291
|
-
* Agent turns listed in `agentSessionIds` are returned for the caller to cancel
|
|
292
|
-
* pending actions → cancelled, delivering tool actions → uncertain.
|
|
307
|
+
* Agent turns listed in `agentSessionIds` are returned for the caller to cancel.
|
|
293
308
|
* When `cancelEffectIds` is provided, those effects are marked cancelled and their
|
|
294
309
|
* paths are included in the sibling path set.
|
|
295
310
|
*/
|
|
296
311
|
export async function cancelSiblingWork(input) {
|
|
297
312
|
const { t } = input;
|
|
298
313
|
await t.lockSession(input.workflowSessionId);
|
|
299
|
-
const cancelledActions = [];
|
|
300
|
-
const uncertainActions = [];
|
|
301
314
|
const agentSessionIds = [];
|
|
302
315
|
const siblingPaths = new Set(input.siblingPaths ?? []);
|
|
303
316
|
for (const effectId of input.cancelEffectIds ?? []) {
|
|
@@ -328,66 +341,21 @@ export async function cancelSiblingWork(input) {
|
|
|
328
341
|
["running", "runnable", "paused", "waiting"].includes(session.status))
|
|
329
342
|
agentSessionIds.push(agentSessionId);
|
|
330
343
|
}
|
|
331
|
-
|
|
332
|
-
turnId: input.turnId,
|
|
333
|
-
statuses: OPEN_ACTION,
|
|
334
|
-
});
|
|
335
|
-
for (const action of actions) {
|
|
336
|
-
if (!matchesSibling(action.path))
|
|
337
|
-
continue;
|
|
338
|
-
// Delivered work may already have had an external effect.
|
|
339
|
-
const next = action.status === "pending" ? "cancelled" : "uncertain";
|
|
340
|
-
action.status = next;
|
|
341
|
-
await t.put("actions", action.actionId, action);
|
|
342
|
-
const effect = await t.get("effects", action.actionId);
|
|
343
|
-
if (effect) {
|
|
344
|
-
effect.status = next;
|
|
345
|
-
await t.put("effects", action.actionId, effect);
|
|
346
|
-
}
|
|
347
|
-
if (next === "cancelled")
|
|
348
|
-
cancelledActions.push(action.actionId);
|
|
349
|
-
else
|
|
350
|
-
uncertainActions.push(action.actionId);
|
|
351
|
-
}
|
|
352
|
-
return { cancelledActions, uncertainActions, agentSessionIds };
|
|
344
|
+
return { agentSessionIds };
|
|
353
345
|
}
|
|
354
|
-
/**
|
|
355
|
-
|
|
356
|
-
* delivering→uncertain).
|
|
357
|
-
*/
|
|
358
|
-
export async function fenceWorkflowActions(input) {
|
|
346
|
+
/** Cancel a cancelled workflow turn's queued effects, which never started. */
|
|
347
|
+
export async function cancelQueuedEffects(input) {
|
|
359
348
|
const { t } = input;
|
|
360
349
|
await t.lockSession(input.workflowSessionId);
|
|
361
350
|
const turn = input.turnId !== null ? { turnId: input.turnId } : {};
|
|
362
351
|
const cancelled = [];
|
|
363
|
-
const uncertain = [];
|
|
364
|
-
const actions = await t.actionsForSession(input.workflowSessionId, {
|
|
365
|
-
...turn,
|
|
366
|
-
statuses: OPEN_ACTION,
|
|
367
|
-
});
|
|
368
|
-
for (const action of actions) {
|
|
369
|
-
// Delivered work may already have had an external effect.
|
|
370
|
-
const next = action.status === "pending" ? "cancelled" : "uncertain";
|
|
371
|
-
action.status = next;
|
|
372
|
-
await t.put("actions", action.actionId, action);
|
|
373
|
-
const effect = await t.get("effects", action.actionId);
|
|
374
|
-
if (effect) {
|
|
375
|
-
effect.status = next;
|
|
376
|
-
await t.put("effects", action.actionId, effect);
|
|
377
|
-
}
|
|
378
|
-
if (next === "cancelled")
|
|
379
|
-
cancelled.push(action.actionId);
|
|
380
|
-
else
|
|
381
|
-
uncertain.push(action.actionId);
|
|
382
|
-
}
|
|
383
|
-
// Drop queued effects that never started.
|
|
384
352
|
const queued = await t.effectsForSession(input.workflowSessionId, { ...turn, statuses: ["queued"] });
|
|
385
353
|
for (const effect of queued) {
|
|
386
354
|
effect.status = "cancelled";
|
|
387
355
|
await t.put("effects", effect.request.effectId, effect);
|
|
388
356
|
cancelled.push(effect.request.effectId);
|
|
389
357
|
}
|
|
390
|
-
return
|
|
358
|
+
return cancelled;
|
|
391
359
|
}
|
|
392
360
|
function waitsFromSession(sessionId, path, raw) {
|
|
393
361
|
if (!Array.isArray(raw))
|
|
@@ -400,9 +368,10 @@ function waitsFromSession(sessionId, path, raw) {
|
|
|
400
368
|
continue;
|
|
401
369
|
waits.push({
|
|
402
370
|
sessionId,
|
|
403
|
-
path,
|
|
371
|
+
path: typeof call.path === "string" ? call.path : path,
|
|
404
372
|
interactionId,
|
|
405
373
|
kind: String(interaction?.kind ?? call.kind ?? "approval"),
|
|
374
|
+
...(call.interaction !== undefined ? { interaction: call.interaction } : {}),
|
|
406
375
|
...(call.invocationId !== undefined
|
|
407
376
|
? { invocationId: String(call.invocationId) }
|
|
408
377
|
: {}),
|
|
@@ -421,13 +390,9 @@ export async function aggregateWaits(input) {
|
|
|
421
390
|
if (!workflow)
|
|
422
391
|
return [];
|
|
423
392
|
const waits = [];
|
|
424
|
-
// Workflow-owned interactions (tool-node
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
else if (workflow.waits &&
|
|
428
|
-
typeof workflow.waits === "object" &&
|
|
429
|
-
Array.isArray(workflow.waits.interactions))
|
|
430
|
-
waits.push(...waitsFromSession(workflow.id, "", workflow.waits.interactions));
|
|
393
|
+
// Workflow-owned interactions (tool-node approvals) live on the workflow session. Waits it
|
|
394
|
+
// copied from linked sessions are read from those sessions below, never from this copy.
|
|
395
|
+
waits.push(...waitsFromSession(workflow.id, "", ownWaits(workflow.waits, workflow.id)));
|
|
431
396
|
for (const { agentSessionId, link, session } of await t.linkedSessions(input.workflowSessionId)) {
|
|
432
397
|
if (session.status !== "paused")
|
|
433
398
|
continue;
|
|
@@ -435,6 +400,23 @@ export async function aggregateWaits(input) {
|
|
|
435
400
|
}
|
|
436
401
|
return waits;
|
|
437
402
|
}
|
|
403
|
+
/** The entries of a workflow's `waits` that the workflow itself owns. */
|
|
404
|
+
function ownWaits(raw, workflowSessionId) {
|
|
405
|
+
const list = Array.isArray(raw)
|
|
406
|
+
? raw
|
|
407
|
+
: raw && typeof raw === "object" && Array.isArray(raw.interactions)
|
|
408
|
+
? raw.interactions
|
|
409
|
+
: [];
|
|
410
|
+
return list.filter((call) => call?.sessionId === undefined || call.sessionId === workflowSessionId);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* The workflow-owned interaction `interactionId` among a workflow's `waits` (a tool node that
|
|
414
|
+
* asked), or undefined.
|
|
415
|
+
*/
|
|
416
|
+
export function flowInteractionOf(raw, workflowSessionId, interactionId) {
|
|
417
|
+
const hit = waitsFromSession(workflowSessionId, "", ownWaits(raw, workflowSessionId)).find((wait) => wait.interactionId === interactionId);
|
|
418
|
+
return hit && { id: hit.interactionId, kind: hit.kind };
|
|
419
|
+
}
|
|
438
420
|
/**
|
|
439
421
|
* Look up which session owns an interaction id. Used for 409 foreign approvals (WF-R52).
|
|
440
422
|
*/
|
package/dist/core/provider.d.ts
CHANGED
|
@@ -8,5 +8,8 @@ export declare function gatewayModel(options: {
|
|
|
8
8
|
token: string;
|
|
9
9
|
model: string;
|
|
10
10
|
}): ModelProvider;
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Deterministic release fixture; still uses the real engine. With a `lookup_order` tool it looks
|
|
13
|
+
* up the demo order through it once; without one it answers in text.
|
|
14
|
+
*/
|
|
12
15
|
export declare function toolFixtureModel(): ModelProvider;
|
package/dist/core/provider.js
CHANGED
|
@@ -22,7 +22,10 @@ export function gatewayModel(options) {
|
|
|
22
22
|
return response.json();
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Deterministic release fixture; still uses the real engine. With a `lookup_order` tool it looks
|
|
27
|
+
* up the demo order through it once; without one it answers in text.
|
|
28
|
+
*/
|
|
26
29
|
export function toolFixtureModel() {
|
|
27
30
|
return async (effect) => {
|
|
28
31
|
const call = effect.input;
|
|
@@ -46,6 +49,8 @@ export function toolFixtureModel() {
|
|
|
46
49
|
},
|
|
47
50
|
],
|
|
48
51
|
};
|
|
52
|
+
if (!call.tools.some((tool) => tool.name === "lookup_order"))
|
|
53
|
+
return { output: [{ type: "text", text: "Hello from the fixture model." }] };
|
|
49
54
|
return {
|
|
50
55
|
output: [
|
|
51
56
|
{
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Action } from "@nylorun/core/contracts";
|
|
2
1
|
import type { WorkspacePort } from "../harness-api/workspace.js";
|
|
3
2
|
import type { SandboxToolOutcome } from "../sandbox/tools.js";
|
|
4
3
|
import { type SessionSandboxRef } from "../sandbox/share.js";
|
|
@@ -19,20 +18,12 @@ export type SandboxRouteDeps = {
|
|
|
19
18
|
readonly sandbox: Pick<WorkspacePort, "run">;
|
|
20
19
|
/** The session (rejects when missing) and its owner chain, read in one transaction. */
|
|
21
20
|
readonly session: (id: string) => Promise<SandboxRouteRead>;
|
|
22
|
-
readonly getAction: (id: string) => Promise<Action | undefined>;
|
|
23
21
|
};
|
|
24
22
|
/**
|
|
25
23
|
* `POST /v1/sessions/:id/sandbox/:tool` — application principal.
|
|
26
24
|
* Refused while the session has an active agent turn.
|
|
27
25
|
*/
|
|
28
26
|
export declare function handleSessionSandboxTool(deps: SandboxRouteDeps, sessionId: string, toolName: string, body: unknown, signal: AbortSignal): Promise<SandboxToolOutcome>;
|
|
29
|
-
/**
|
|
30
|
-
* `POST /v1/actions/:id/sandbox/:tool` — the Action endpoint, authorized by the delivery token
|
|
31
|
-
* of the Action's current delivery (`delivery.generation`). The body is the tool's input.
|
|
32
|
-
*/
|
|
33
|
-
export declare function handleActionSandboxTool(deps: SandboxRouteDeps, actionId: string, toolName: string, body: unknown, signal: AbortSignal, delivery: {
|
|
34
|
-
generation: number;
|
|
35
|
-
}): Promise<SandboxToolOutcome>;
|
|
36
27
|
/**
|
|
37
28
|
* Validate PutSession.sandbox attach: same owner, and a sandbox to share. A session whose
|
|
38
29
|
* definition declares a sandbox must declare the owner's spec; one whose definition declares
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTTP handlers for session-scoped
|
|
3
|
-
*
|
|
2
|
+
* HTTP handlers for the session-scoped sandbox tool route (`POST /v1/sessions/:id/sandbox/:tool`)
|
|
3
|
+
* and the sandbox attach check of `PUT /v1/sessions/:id`.
|
|
4
4
|
*/
|
|
5
5
|
import { isSandboxToolName, } from "@nylorun/core/define";
|
|
6
6
|
import { capabilityForSandbox, owningSandboxSessionId, sandboxSpecOf, sandboxWorkspaceOf, sandboxSpecsEqual, sessionSandboxSpec, } from "../sandbox/share.js";
|
|
@@ -56,25 +56,6 @@ export async function handleSessionSandboxTool(deps, sessionId, toolName, body,
|
|
|
56
56
|
const tool = parseTool(toolName);
|
|
57
57
|
return runTool(deps, read, tool, toolInput(body), signal);
|
|
58
58
|
}
|
|
59
|
-
/**
|
|
60
|
-
* `POST /v1/actions/:id/sandbox/:tool` — the Action endpoint, authorized by the delivery token
|
|
61
|
-
* of the Action's current delivery (`delivery.generation`). The body is the tool's input.
|
|
62
|
-
*/
|
|
63
|
-
export async function handleActionSandboxTool(deps, actionId, toolName, body, signal, delivery) {
|
|
64
|
-
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
65
|
-
throw new SandboxRouteError(400, "Sandbox tool body must be an object");
|
|
66
|
-
const action = await deps.getAction(actionId);
|
|
67
|
-
if (!action)
|
|
68
|
-
throw new SandboxRouteError(404, "Action not found");
|
|
69
|
-
if (action.status !== "delivering" ||
|
|
70
|
-
action.generation !== delivery.generation ||
|
|
71
|
-
Date.parse(action.deadlineAt ?? "") <= Date.now())
|
|
72
|
-
throw new SandboxRouteError(409, "The delivery was cancelled, lost or delivered again");
|
|
73
|
-
const read = await deps.session(action.sessionId);
|
|
74
|
-
if (read.session.activeTurnId !== action.turnId)
|
|
75
|
-
throw new SandboxRouteError(409, "Action unavailable");
|
|
76
|
-
return runTool(deps, read, parseTool(toolName), toolInput(body), signal);
|
|
77
|
-
}
|
|
78
59
|
/**
|
|
79
60
|
* Validate PutSession.sandbox attach: same owner, and a sandbox to share. A session whose
|
|
80
61
|
* definition declares a sandbox must declare the owner's spec; one whose definition declares
|
|
@@ -2,7 +2,7 @@ import { type DurableExecution, type SandboxSignal, type Wake, type WorkerHandle
|
|
|
2
2
|
export interface MemoryExecutionOptions {
|
|
3
3
|
/** Delay between sweep passes of one Tenant. Default 5000. */
|
|
4
4
|
sweepIntervalMs?: number;
|
|
5
|
-
/** First retry delay after `advance`
|
|
5
|
+
/** First retry delay after `advance` throws; doubles per attempt. Default 50. */
|
|
6
6
|
retryDelayMs?: number;
|
|
7
7
|
/** Attempts before giving up on a throwing handler and reporting it. Default 5. */
|
|
8
8
|
maxAttempts?: number;
|
|
@@ -39,7 +39,6 @@ export declare class MemoryExecution implements DurableExecution {
|
|
|
39
39
|
private readonly onError;
|
|
40
40
|
constructor(options?: MemoryExecutionOptions);
|
|
41
41
|
wake(tenantId: string, sessionId: string, wake: Wake): Promise<void>;
|
|
42
|
-
deliver(tenantId: string, actionId: string): Promise<void>;
|
|
43
42
|
sandbox(tenantId: string, sandboxId: string, signal: SandboxSignal): Promise<void>;
|
|
44
43
|
private armSandbox;
|
|
45
44
|
private runSandbox;
|
|
@@ -49,8 +48,8 @@ export declare class MemoryExecution implements DurableExecution {
|
|
|
49
48
|
start(handlers: WorkerHandlers): Promise<void>;
|
|
50
49
|
stop(): Promise<void>;
|
|
51
50
|
/**
|
|
52
|
-
* Resolves once no advance
|
|
53
|
-
*
|
|
51
|
+
* Resolves once no advance is running or queued and no busy or retry re-run is
|
|
52
|
+
* pending. Timers and sweeps are not waited for. Test helper.
|
|
54
53
|
*/
|
|
55
54
|
idle(): Promise<void>;
|
|
56
55
|
private enqueue;
|
|
@@ -58,7 +57,7 @@ export declare class MemoryExecution implements DurableExecution {
|
|
|
58
57
|
/** Drops dedupe keys older than the retention window. */
|
|
59
58
|
private forgetDedupeKeys;
|
|
60
59
|
private runOnce;
|
|
61
|
-
/** Runs the
|
|
60
|
+
/** Runs the session's advance; resolves to a delay when it must run again. */
|
|
62
61
|
private call;
|
|
63
62
|
private armTimer;
|
|
64
63
|
private scheduleSweep;
|
package/dist/execution/memory.js
CHANGED
|
@@ -44,10 +44,7 @@ export class MemoryExecution {
|
|
|
44
44
|
return;
|
|
45
45
|
this.seen.set(dedupe, now);
|
|
46
46
|
}
|
|
47
|
-
this.enqueue(
|
|
48
|
-
}
|
|
49
|
-
async deliver(tenantId, actionId) {
|
|
50
|
-
this.enqueue("deliver", tenantId, actionId);
|
|
47
|
+
this.enqueue(tenantId, sessionId);
|
|
51
48
|
}
|
|
52
49
|
async sandbox(tenantId, sandboxId, signal) {
|
|
53
50
|
const key = `${tenantId}\u0000${sandboxId}`;
|
|
@@ -173,8 +170,8 @@ export class MemoryExecution {
|
|
|
173
170
|
await settleWithin(this.inflight, this.stopGraceMs);
|
|
174
171
|
}
|
|
175
172
|
/**
|
|
176
|
-
* Resolves once no advance
|
|
177
|
-
*
|
|
173
|
+
* Resolves once no advance is running or queued and no busy or retry re-run is
|
|
174
|
+
* pending. Timers and sweeps are not waited for. Test helper.
|
|
178
175
|
*/
|
|
179
176
|
async idle() {
|
|
180
177
|
while (this.delayed.size > 0 ||
|
|
@@ -182,14 +179,13 @@ export class MemoryExecution {
|
|
|
182
179
|
[...this.keys.values()].some((s) => s.running || (s.queued && this.handlers)))
|
|
183
180
|
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
184
181
|
}
|
|
185
|
-
enqueue(
|
|
186
|
-
const key =
|
|
182
|
+
enqueue(tenantId, sessionId) {
|
|
183
|
+
const key = sessionKey(tenantId, sessionId);
|
|
187
184
|
let state = this.keys.get(key);
|
|
188
185
|
if (!state)
|
|
189
186
|
this.keys.set(key, (state = {
|
|
190
|
-
kind,
|
|
191
187
|
tenantId,
|
|
192
|
-
|
|
188
|
+
sessionId,
|
|
193
189
|
running: false,
|
|
194
190
|
queued: false,
|
|
195
191
|
attempts: 0,
|
|
@@ -211,7 +207,7 @@ export class MemoryExecution {
|
|
|
211
207
|
state.running = false;
|
|
212
208
|
// Idle keys hold nothing (a retry keeps its attempt count); forget them so
|
|
213
209
|
// long-lived processes stay small.
|
|
214
|
-
const key =
|
|
210
|
+
const key = sessionKey(state.tenantId, state.sessionId);
|
|
215
211
|
if (!state.queued && state.attempts === 0 && this.keys.get(key) === state)
|
|
216
212
|
this.keys.delete(key);
|
|
217
213
|
}
|
|
@@ -228,7 +224,7 @@ export class MemoryExecution {
|
|
|
228
224
|
async runOnce(state, handlers) {
|
|
229
225
|
const controller = new AbortController();
|
|
230
226
|
state.controller = controller;
|
|
231
|
-
const again = () => this.enqueue(state.
|
|
227
|
+
const again = () => this.enqueue(state.tenantId, state.sessionId);
|
|
232
228
|
try {
|
|
233
229
|
const retryAfterMs = await this.call(state, handlers, controller.signal);
|
|
234
230
|
state.attempts = 0;
|
|
@@ -248,16 +244,10 @@ export class MemoryExecution {
|
|
|
248
244
|
state.controller = undefined;
|
|
249
245
|
}
|
|
250
246
|
}
|
|
251
|
-
/** Runs the
|
|
247
|
+
/** Runs the session's advance; resolves to a delay when it must run again. */
|
|
252
248
|
async call(state, handlers, signal) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
return result.status === "busy" ? result.retryAfterMs : undefined;
|
|
256
|
-
}
|
|
257
|
-
if (!handlers.deliver)
|
|
258
|
-
throw new Error("WorkerHandlers.deliver is required for deliveries");
|
|
259
|
-
const result = await handlers.deliver(state.tenantId, state.id, signal);
|
|
260
|
-
return result.status === "retry" ? result.retryAfterMs : undefined;
|
|
249
|
+
const result = await handlers.advance(state.tenantId, state.sessionId, signal);
|
|
250
|
+
return result.status === "busy" ? result.retryAfterMs : undefined;
|
|
261
251
|
}
|
|
262
252
|
armTimer(id, timer) {
|
|
263
253
|
timer.handle = setTimeout(() => {
|
|
@@ -305,7 +295,3 @@ export class MemoryExecution {
|
|
|
305
295
|
void promise.finally(() => this.inflight.delete(promise));
|
|
306
296
|
}
|
|
307
297
|
}
|
|
308
|
-
/** Advances and deliveries are queued apart: a session id and an Action id may be equal. */
|
|
309
|
-
function stateKey(kind, tenantId, id) {
|
|
310
|
-
return `${kind}\u0000${sessionKey(tenantId, id)}`;
|
|
311
|
-
}
|
|
@@ -27,16 +27,7 @@
|
|
|
27
27
|
* becomes `uncertain` is settled in the Session Store and `advance` returns
|
|
28
28
|
* `done`.
|
|
29
29
|
* - **Sweeps.** `armSweep` arms one self-re-arming sweep per Tenant; arming an
|
|
30
|
-
* armed Tenant is a no-op. The sweep
|
|
31
|
-
* orphaned `runnable` sessions.
|
|
32
|
-
* - **One delivery per Action at a time.** `deliver` runs
|
|
33
|
-
* `WorkerHandlers.deliver` for the key `<tenantId>:<actionId>` at least once
|
|
34
|
-
* after it resolves. Two deliveries for one key never overlap; repeated
|
|
35
|
-
* `deliver` calls may each run it, so the handler reads the Action's state
|
|
36
|
-
* and does nothing when there is nothing to deliver.
|
|
37
|
-
* - **Retry after `retry`.** A delivery that returns `retry` is run again for
|
|
38
|
-
* the same key after `retryAfterMs`. This is how an Action endpoint that is
|
|
39
|
-
* down or busy is retried; the handler throws only on infrastructure errors.
|
|
30
|
+
* armed Tenant is a no-op. The sweep re-wakes orphaned `runnable` sessions.
|
|
40
31
|
* - **One reconcile per sandbox at a time** (F7.2). `sandbox` runs
|
|
41
32
|
* `WorkerHandlers.sandbox` for the key `<tenantId>:<sandboxId>` at least once
|
|
42
33
|
* after a `reconcile` resolves, never overlapping another run for the key. A
|
|
@@ -49,8 +40,6 @@
|
|
|
49
40
|
export type WakeReason =
|
|
50
41
|
/** Session commands. */
|
|
51
42
|
"message" | "approve" | "respond"
|
|
52
|
-
/** An Action's outcome was recorded (a delivery's answer or a background result). */
|
|
53
|
-
| "action_result"
|
|
54
43
|
/** A linked agent turn completed, failed or was cancelled. */
|
|
55
44
|
| "linked"
|
|
56
45
|
/** Queued flow effects, pending agent effects reconciled. */
|
|
@@ -98,14 +87,6 @@ export interface SandboxResult {
|
|
|
98
87
|
at: number;
|
|
99
88
|
}[];
|
|
100
89
|
}
|
|
101
|
-
export type DeliverResult = {
|
|
102
|
-
status: "done";
|
|
103
|
-
}
|
|
104
|
-
/** The Action is still to be delivered; run again for this key after `retryAfterMs`. */
|
|
105
|
-
| {
|
|
106
|
-
status: "retry";
|
|
107
|
-
retryAfterMs: number;
|
|
108
|
-
};
|
|
109
90
|
/** What a Worker runs when Durable Session Execution calls it. */
|
|
110
91
|
export interface WorkerHandlers {
|
|
111
92
|
/**
|
|
@@ -118,11 +99,6 @@ export interface WorkerHandlers {
|
|
|
118
99
|
sweep(tenantId: string): Promise<void>;
|
|
119
100
|
/** A durable timer set with `timer` fired. Required when `timer` is used. */
|
|
120
101
|
fire?(tenantId: string, key: string): Promise<void>;
|
|
121
|
-
/**
|
|
122
|
-
* Delivers one Action to its Action endpoint. `signal` aborts when the Worker
|
|
123
|
-
* stops. Throw only on infrastructure errors. Required when `deliver` is used.
|
|
124
|
-
*/
|
|
125
|
-
deliver?(tenantId: string, actionId: string, signal: AbortSignal): Promise<DeliverResult>;
|
|
126
102
|
/**
|
|
127
103
|
* Reconciles one pod sandbox (F7.2), serialized per sandbox. Throw only on infrastructure
|
|
128
104
|
* errors. Required when `sandbox` is used.
|
|
@@ -138,12 +114,6 @@ export interface DurableExecution {
|
|
|
138
114
|
* implementation can; `fire` must be idempotent either way.
|
|
139
115
|
*/
|
|
140
116
|
timer(tenantId: string, key: string, at: Date): Promise<void>;
|
|
141
|
-
/**
|
|
142
|
-
* At-least-once: `WorkerHandlers.deliver(tenantId, actionId)` runs after this
|
|
143
|
-
* resolves, never overlapping another delivery of the same Action. Send it
|
|
144
|
-
* after the transaction that made the Action pending commits.
|
|
145
|
-
*/
|
|
146
|
-
deliver(tenantId: string, actionId: string): Promise<void>;
|
|
147
117
|
/**
|
|
148
118
|
* A pod sandbox's reconcile, or one of its timers (F7.2): see "One reconcile per sandbox
|
|
149
119
|
* at a time". Absent where pods are not supported.
|
|
@@ -183,7 +153,7 @@ export interface StuckInvocation {
|
|
|
183
153
|
handler: string;
|
|
184
154
|
/**
|
|
185
155
|
* Object key: `<tenantId>:<sessionId>`, `<tenantId>`, `<tenantId>:<timer key>` or
|
|
186
|
-
* `<tenantId>:<
|
|
156
|
+
* `<tenantId>:<sandboxId>`.
|
|
187
157
|
*/
|
|
188
158
|
key: string;
|
|
189
159
|
tenantId?: string;
|
|
@@ -191,7 +161,7 @@ export interface StuckInvocation {
|
|
|
191
161
|
lastFailure?: string;
|
|
192
162
|
modifiedAt?: string;
|
|
193
163
|
}
|
|
194
|
-
/** The key one advance (or one
|
|
164
|
+
/** The key one advance (or one sandbox reconcile, with a sandbox id) at a time is serialized on. */
|
|
195
165
|
export declare function sessionKey(tenantId: string, sessionId: string): string;
|
|
196
166
|
/** Splits a key made by `sessionKey`. Tenant ids never contain `:`. */
|
|
197
167
|
export declare function parseSessionKey(key: string): {
|
package/dist/execution/types.js
CHANGED
|
@@ -27,16 +27,7 @@
|
|
|
27
27
|
* becomes `uncertain` is settled in the Session Store and `advance` returns
|
|
28
28
|
* `done`.
|
|
29
29
|
* - **Sweeps.** `armSweep` arms one self-re-arming sweep per Tenant; arming an
|
|
30
|
-
* armed Tenant is a no-op. The sweep
|
|
31
|
-
* orphaned `runnable` sessions.
|
|
32
|
-
* - **One delivery per Action at a time.** `deliver` runs
|
|
33
|
-
* `WorkerHandlers.deliver` for the key `<tenantId>:<actionId>` at least once
|
|
34
|
-
* after it resolves. Two deliveries for one key never overlap; repeated
|
|
35
|
-
* `deliver` calls may each run it, so the handler reads the Action's state
|
|
36
|
-
* and does nothing when there is nothing to deliver.
|
|
37
|
-
* - **Retry after `retry`.** A delivery that returns `retry` is run again for
|
|
38
|
-
* the same key after `retryAfterMs`. This is how an Action endpoint that is
|
|
39
|
-
* down or busy is retried; the handler throws only on infrastructure errors.
|
|
30
|
+
* armed Tenant is a no-op. The sweep re-wakes orphaned `runnable` sessions.
|
|
40
31
|
* - **One reconcile per sandbox at a time** (F7.2). `sandbox` runs
|
|
41
32
|
* `WorkerHandlers.sandbox` for the key `<tenantId>:<sandboxId>` at least once
|
|
42
33
|
* after a `reconcile` resolves, never overlapping another run for the key. A
|
|
@@ -49,13 +40,12 @@ export const WAKE_REASONS = [
|
|
|
49
40
|
"message",
|
|
50
41
|
"approve",
|
|
51
42
|
"respond",
|
|
52
|
-
"action_result",
|
|
53
43
|
"linked",
|
|
54
44
|
"flow",
|
|
55
45
|
"recover",
|
|
56
46
|
"rollover",
|
|
57
47
|
];
|
|
58
|
-
/** The key one advance (or one
|
|
48
|
+
/** The key one advance (or one sandbox reconcile, with a sandbox id) at a time is serialized on. */
|
|
59
49
|
export function sessionKey(tenantId, sessionId) {
|
|
60
50
|
return `${tenantId}:${sessionId}`;
|
|
61
51
|
}
|