@nylorun/runtime 0.26.0-beta → 0.28.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 +171 -0
- package/README.md +18 -14
- package/dist/adapters/execution/restate.d.ts +15 -18
- package/dist/adapters/execution/restate.js +31 -27
- package/dist/adapters/replication/pgoutput.d.ts +1 -1
- package/dist/adapters/replication/pgoutput.js +3 -2
- package/dist/adapters/sandbox/local.js +8 -5
- package/dist/adapters/sandbox/virtual.js +2 -2
- package/dist/adapters/streams/s2.d.ts +1 -1
- package/dist/adapters/streams/s2.js +18 -14
- package/dist/api/a2a/card.d.ts +4 -3
- package/dist/api/a2a/card.js +4 -4
- package/dist/api/a2a/endpoint.js +3 -4
- package/dist/api/a2a/protocol.js +4 -0
- package/dist/api/a2a/routes.d.ts +2 -6
- package/dist/api/a2a/routes.js +30 -25
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +8 -11
- package/dist/api/components.d.ts +6789 -3405
- package/dist/api/components.js +43 -30
- package/dist/api/gate/keys.js +7 -4
- package/dist/api/gate/routes.d.ts +6 -2
- package/dist/api/gate/routes.js +15 -11
- package/dist/api/http/app.js +9 -4
- package/dist/api/http/body.d.ts +17 -3
- package/dist/api/http/body.js +35 -7
- package/dist/api/http/respond.js +6 -2
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/agent-manifests.d.ts +10 -0
- package/dist/api/http/routes/agent-manifests.js +135 -0
- package/dist/api/http/routes/agents.d.ts +11 -0
- package/dist/api/http/routes/agents.js +418 -0
- package/dist/api/http/routes/artifacts.js +21 -9
- package/dist/api/http/routes/coverage.d.ts +4 -4
- package/dist/api/http/routes/coverage.js +13 -7
- package/dist/api/http/routes/files.d.ts +2 -1
- package/dist/api/http/routes/files.js +31 -5
- package/dist/api/http/routes/mcp.js +3 -3
- package/dist/api/http/routes/me.d.ts +1 -1
- package/dist/api/http/routes/me.js +1 -2
- package/dist/api/http/routes/memories.d.ts +9 -0
- package/dist/api/http/routes/memories.js +152 -0
- package/dist/api/http/routes/reads.d.ts +10 -111
- package/dist/api/http/routes/reads.js +51 -29
- package/dist/api/http/routes/sessions.js +26 -54
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +77 -24
- package/dist/api/http/routes/triggers.d.ts +10 -0
- package/dist/api/http/routes/triggers.js +180 -0
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +73 -47
- package/dist/artifacts/disk.d.ts +10 -14
- package/dist/artifacts/disk.js +99 -95
- package/dist/artifacts/files.d.ts +13 -4
- package/dist/artifacts/files.js +27 -9
- package/dist/artifacts/folder-upload.d.ts +75 -0
- package/dist/artifacts/folder-upload.js +263 -0
- package/dist/artifacts/folders.d.ts +14 -8
- package/dist/artifacts/folders.js +19 -11
- package/dist/artifacts/links.js +7 -6
- package/dist/artifacts/parts.d.ts +9 -14
- package/dist/artifacts/parts.js +11 -9
- package/dist/artifacts/pipe.d.ts +15 -0
- package/dist/artifacts/pipe.js +71 -0
- package/dist/artifacts/quota.d.ts +36 -0
- package/dist/artifacts/quota.js +44 -0
- package/dist/artifacts/service.d.ts +25 -1
- package/dist/artifacts/service.js +72 -27
- package/dist/blob/fs.js +2 -1
- package/dist/blob/s3.d.ts +6 -2
- package/dist/blob/s3.js +9 -7
- package/dist/blob/types.d.ts +1 -1
- package/dist/blob/types.js +1 -1
- package/dist/core/flow-host.d.ts +0 -1
- package/dist/core/limits.d.ts +11 -35
- package/dist/core/limits.js +5 -73
- package/dist/core/sandbox-routes.js +0 -1
- package/dist/core/turn-manifest.d.ts +1 -2
- package/dist/core/turn-manifest.js +4 -28
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +8 -6
- package/dist/execution/types.d.ts +1 -6
- package/dist/execution/types.js +0 -5
- package/dist/gates/contract.d.ts +3 -2
- package/dist/gates/contract.js +1 -1
- package/dist/gates/egress-credentials.js +5 -4
- package/dist/gates/egress.d.ts +13 -13
- package/dist/gates/egress.js +19 -21
- package/dist/gates/fixture-model.js +13 -2
- package/dist/gates/handler.d.ts +1 -3
- package/dist/gates/handler.js +4 -3
- package/dist/gates/http-client.d.ts +4 -3
- package/dist/gates/http-client.js +5 -7
- package/dist/gates/http-tool.d.ts +24 -4
- package/dist/gates/http-tool.js +41 -13
- package/dist/gates/in-process.d.ts +2 -1
- package/dist/gates/in-process.js +17 -9
- package/dist/gates/inflight.d.ts +2 -2
- package/dist/gates/inflight.js +3 -2
- package/dist/gates/journal.js +5 -7
- package/dist/gates/mcp-handler.d.ts +4 -3
- package/dist/gates/mcp-handler.js +15 -5
- package/dist/gates/meter.js +6 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +34 -6
- package/dist/gates/policy.js +66 -18
- package/dist/gates/provider-gate.d.ts +22 -0
- package/dist/gates/provider-gate.js +30 -0
- package/dist/gates/tool-calls.d.ts +1 -3
- package/dist/gates/tool-calls.js +2 -3
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +5 -6
- package/dist/gates/tool-contract.d.ts +8 -5
- package/dist/gates/tool-contract.js +1 -1
- package/dist/gates/tool-gate.d.ts +18 -7
- package/dist/gates/tool-gate.js +49 -7
- package/dist/harness/calls.d.ts +27 -25
- package/dist/harness/calls.js +40 -46
- package/dist/harness/executors.d.ts +10 -3
- package/dist/harness/executors.js +53 -23
- package/dist/harness/main.js +2 -2
- package/dist/harness/pod.d.ts +2 -2
- package/dist/harness/pod.js +7 -6
- package/dist/harness/run-tokens.d.ts +4 -1
- package/dist/harness/run-tokens.js +6 -2
- package/dist/harness/service.d.ts +17 -14
- package/dist/harness/service.js +21 -35
- package/dist/harness/setup.d.ts +0 -2
- package/dist/harness/setup.js +21 -15
- package/dist/harness/ws-client.d.ts +2 -2
- package/dist/harness/ws-client.js +7 -5
- package/dist/harness/ws-port.d.ts +3 -4
- package/dist/harness/ws-port.js +3 -4
- package/dist/harness-api/artifacts.d.ts +20 -0
- package/dist/harness-api/artifacts.js +478 -0
- package/dist/harness-api/in-process.d.ts +2 -2
- package/dist/harness-api/in-process.js +18 -16
- package/dist/harness-api/record.d.ts +10 -9
- package/dist/harness-api/record.js +113 -157
- package/dist/harness-api/renew.d.ts +1 -1
- package/dist/harness-api/renew.js +2 -2
- package/dist/harness-api/server.d.ts +1 -16
- package/dist/harness-api/server.js +86 -73
- package/dist/harness-api/start.d.ts +27 -5
- package/dist/harness-api/start.js +102 -19
- package/dist/harness-api/tool-journal.d.ts +15 -10
- package/dist/harness-api/tool-journal.js +26 -24
- package/dist/harness-api/workspace.d.ts +0 -4
- package/dist/harness-api/workspace.js +4 -47
- package/dist/harness-api/ws-server.js +10 -10
- package/dist/host/create-host.js +2 -3
- package/dist/host/execution.js +2 -3
- package/dist/host/gates.d.ts +2 -9
- package/dist/host/gates.js +15 -12
- package/dist/host/http.d.ts +2 -4
- package/dist/host/http.js +5 -29
- package/dist/host/main.js +10 -3
- package/dist/host/operate.js +12 -3
- package/dist/host/stack-config.d.ts +11 -0
- package/dist/host/stack-config.js +21 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/index.js +2 -1
- package/dist/infra/readiness.d.ts +0 -2
- package/dist/infra/readiness.js +4 -3
- package/dist/keys/client.d.ts +1 -0
- package/dist/keys/client.js +2 -3
- package/dist/keys/contract.d.ts +0 -2
- package/dist/keys/contract.js +0 -2
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.d.ts +15 -4
- package/dist/keys/keys.js +22 -1
- package/dist/keys/webhooks.d.ts +25 -0
- package/dist/keys/webhooks.js +62 -0
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +3043 -1954
- package/dist/mcp/connect.d.ts +35 -26
- package/dist/mcp/connect.js +112 -141
- package/dist/mcp/pool.d.ts +27 -22
- package/dist/mcp/pool.js +34 -48
- package/dist/mcp/preview.d.ts +12 -5
- package/dist/mcp/preview.js +43 -26
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/memory/service.d.ts +97 -0
- package/dist/memory/service.js +335 -0
- package/dist/memory/tool.d.ts +36 -0
- package/dist/memory/tool.js +84 -0
- package/dist/model/auth-store.js +5 -3
- package/dist/model/capabilities.d.ts +1 -1
- package/dist/model/catalog.d.ts +9 -36
- package/dist/model/catalog.js +6 -33
- package/dist/model/descriptor.d.ts +51 -0
- package/dist/model/descriptor.js +83 -0
- package/dist/model/models.d.ts +0 -3
- package/dist/model/models.js +3 -5
- package/dist/model/native-blocks.js +1 -1
- package/dist/model/pi-model.d.ts +16 -5
- package/dist/model/pi-model.js +86 -105
- package/dist/openapi.json +13653 -9986
- package/dist/reads/cursor.js +3 -1
- package/dist/reads/types.d.ts +3 -3
- package/dist/record/agent.d.ts +27 -0
- package/dist/record/agent.js +46 -0
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/index.d.ts +2 -2
- package/dist/record/index.js +2 -2
- package/dist/redact.d.ts +1 -1
- package/dist/redact.js +3 -2
- package/dist/sandbox/egress-token.d.ts +3 -3
- package/dist/sandbox/egress-token.js +3 -3
- package/dist/sandbox/join.js +8 -7
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +2 -2
- package/dist/sandbox/placement.js +7 -10
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +25 -17
- package/dist/sandbox/pods/lifecycle.js +51 -29
- package/dist/sandbox/pods/network-gate.d.ts +4 -5
- package/dist/sandbox/pods/network-gate.js +7 -7
- package/dist/sandbox/pods/reconcile.js +14 -3
- package/dist/sandbox/pods/spec.js +8 -5
- package/dist/sandbox/policy.d.ts +7 -1
- package/dist/sandbox/policy.js +10 -3
- package/dist/sandbox/resolve.d.ts +11 -6
- package/dist/sandbox/resolve.js +19 -9
- package/dist/sandbox/setup.d.ts +6 -2
- package/dist/sandbox/setup.js +47 -14
- package/dist/sandbox/share.d.ts +5 -14
- package/dist/sandbox/share.js +5 -34
- package/dist/sandbox/skills.d.ts +7 -13
- package/dist/sandbox/skills.js +16 -31
- package/dist/sandbox/tenant-config.d.ts +2 -2
- package/dist/sandbox/tenant-config.js +4 -3
- package/dist/sandbox/tools.d.ts +7 -1
- package/dist/sandbox/tools.js +26 -26
- package/dist/sandbox/types.d.ts +10 -3
- package/dist/sandbox/types.js +9 -0
- package/dist/store/postgres/connect.d.ts +6 -5
- package/dist/store/postgres/connect.js +14 -11
- package/dist/store/postgres/control.js +13 -13
- package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
- package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
- package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
- package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
- package/dist/store/postgres/drizzle/0032_agents.sql +54 -0
- package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
- package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
- package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +63 -0
- package/dist/store/postgres/reads.js +17 -82
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/schema.d.ts +1382 -151
- package/dist/store/postgres/schema.js +191 -37
- package/dist/store/postgres/store.js +384 -105
- package/dist/store/postgres/tenant.js +1 -2
- package/dist/store/types.d.ts +172 -57
- package/dist/store/types.js +0 -1
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +41 -39
- package/dist/tenant/agent-manifests.d.ts +72 -0
- package/dist/tenant/agent-manifests.js +359 -0
- package/dist/tenant/agent-versions.d.ts +126 -0
- package/dist/tenant/agent-versions.js +389 -0
- package/dist/tenant/agents.d.ts +230 -0
- package/dist/tenant/{environments.js → agents.js} +493 -328
- package/dist/tenant/auth.d.ts +1 -1
- package/dist/tenant/auth.js +3 -3
- package/dist/tenant/commands.d.ts +63 -2
- package/dist/tenant/commands.js +240 -90
- package/dist/tenant/context.d.ts +57 -33
- package/dist/tenant/context.js +6 -2
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +15 -51
- package/dist/tenant/ephemeral.js +2 -1
- package/dist/tenant/history.d.ts +1 -1
- package/dist/tenant/history.js +1 -1
- package/dist/tenant/host-token.d.ts +3 -3
- package/dist/tenant/host-token.js +11 -10
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +29 -6
- package/dist/tenant/identity-file.js +45 -15
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +34 -26
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +87 -0
- package/dist/tenant/limits.js +172 -0
- package/dist/tenant/memories.d.ts +38 -0
- package/dist/tenant/memories.js +119 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +11 -14
- package/dist/tenant/outbound.js +10 -15
- package/dist/tenant/reset.js +2 -2
- package/dist/tenant/run-grants.d.ts +6 -0
- package/dist/tenant/run-grants.js +7 -2
- package/dist/tenant/run-token.d.ts +0 -4
- package/dist/tenant/run-token.js +6 -11
- package/dist/tenant/runtime-limits.d.ts +24 -0
- package/dist/tenant/runtime-limits.js +27 -0
- package/dist/tenant/runtime.d.ts +1 -1
- package/dist/tenant/runtime.js +38 -43
- package/dist/tenant/session-streams.js +7 -6
- package/dist/tenant/session.d.ts +1 -1
- package/dist/tenant/session.js +1 -1
- package/dist/tenant/sessions.d.ts +28 -45
- package/dist/tenant/sessions.js +89 -209
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +10 -10
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +51 -51
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/triggers.d.ts +143 -0
- package/dist/tenant/triggers.js +715 -0
- package/dist/tenant/turn-timing.d.ts +11 -1
- package/dist/tenant/turn-timing.js +3 -1
- package/dist/tenant/types.d.ts +9 -14
- package/dist/tenant/worker.d.ts +4 -10
- package/dist/tenant/worker.js +10 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +10 -7
- package/dist/traces/map.js +16 -12
- package/dist/traces/otlp.d.ts +3 -3
- package/dist/traces/otlp.js +5 -3
- package/dist/traces/projector.d.ts +9 -4
- package/dist/traces/projector.js +13 -7
- package/dist/traces/read.js +6 -1
- package/dist/tuning.d.ts +390 -0
- package/dist/tuning.js +677 -0
- package/dist/vault/headers.js +4 -2
- package/dist/vault/service.d.ts +13 -8
- package/dist/vault/service.js +61 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -12
- package/dist/api/http/routes/environment-manifests.d.ts +0 -9
- package/dist/api/http/routes/environment-manifests.js +0 -118
- package/dist/api/http/routes/environments.d.ts +0 -11
- package/dist/api/http/routes/environments.js +0 -332
- package/dist/artifacts/export.d.ts +0 -8
- package/dist/artifacts/export.js +0 -204
- package/dist/artifacts/media-types.d.ts +0 -30
- package/dist/artifacts/media-types.js +0 -137
- package/dist/artifacts/workspace.d.ts +0 -60
- package/dist/artifacts/workspace.js +0 -45
- package/dist/media.d.ts +0 -10
- package/dist/media.js +0 -58
- package/dist/record/environment.d.ts +0 -25
- package/dist/record/environment.js +0 -44
- package/dist/sandbox/runtime-tools.d.ts +0 -9
- package/dist/sandbox/runtime-tools.js +0 -16
- package/dist/sandbox/session-sandbox.d.ts +0 -16
- package/dist/sandbox/session-sandbox.js +0 -77
- package/dist/tenant/artifact-tool.d.ts +0 -13
- package/dist/tenant/artifact-tool.js +0 -243
- package/dist/tenant/environment-manifests.d.ts +0 -57
- package/dist/tenant/environment-manifests.js +0 -200
- package/dist/tenant/environments.d.ts +0 -174
- package/dist/tenant/skill-tool.d.ts +0 -20
- package/dist/tenant/skill-tool.js +0 -67
- package/dist/tenant/tool-results.d.ts +0 -57
- package/dist/tenant/tool-results.js +0 -298
- package/dist/tenant/tool-search.d.ts +0 -39
- package/dist/tenant/tool-search.js +0 -103
package/dist/tenant/auth.d.ts
CHANGED
|
@@ -48,6 +48,6 @@ export declare function accessOf(scope: AuthScope): SessionAccess | undefined;
|
|
|
48
48
|
export declare function ownerOf(scope: AuthScope): string | undefined;
|
|
49
49
|
/** The application principal behind the request, acting for a subject or not. */
|
|
50
50
|
export declare function requirePrincipal(scope: AuthScope): string;
|
|
51
|
-
/** True when a token caller may reach `agentId` (its
|
|
51
|
+
/** True when a token caller may reach Agent `agentId` (its grants). Other callers always may. */
|
|
52
52
|
export declare function mayUseAgent(scope: AuthScope, agentId: string): boolean;
|
|
53
53
|
export declare function requireApplication(scope: AuthScope): string;
|
package/dist/tenant/auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SCOPES_HEADER, SUBJECT_HEADER, } from "@nylorun/core/compatibility";
|
|
2
|
-
import { parseSubjectHeaders, TOKEN_SCOPES } from "@nylorun/core/contracts";
|
|
2
|
+
import { agentGranted, parseSubjectHeaders, TOKEN_SCOPES } from "@nylorun/core/contracts";
|
|
3
3
|
import { hashToken } from "../core/bearer.js";
|
|
4
4
|
import { fail } from "./http.js";
|
|
5
5
|
import { verifyIssuerToken } from "./issuers.js";
|
|
@@ -168,9 +168,9 @@ export function requirePrincipal(scope) {
|
|
|
168
168
|
return scope.principalId;
|
|
169
169
|
return fail(403, "Application credential required");
|
|
170
170
|
}
|
|
171
|
-
/** True when a token caller may reach `agentId` (its
|
|
171
|
+
/** True when a token caller may reach Agent `agentId` (its grants). Other callers always may. */
|
|
172
172
|
export function mayUseAgent(scope, agentId) {
|
|
173
|
-
return scope.kind !== "token" || scope.agents === "*" || scope.agents
|
|
173
|
+
return scope.kind !== "token" || scope.agents === "*" || agentGranted(scope.agents, agentId);
|
|
174
174
|
}
|
|
175
175
|
export function requireApplication(scope) {
|
|
176
176
|
if (scope.kind === "application")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { EventWriter, SessionCommand } from "@nylorun/core/contracts";
|
|
2
|
-
import
|
|
1
|
+
import type { EventWriter, MessageDelivery, MessageEventBody, SessionCommand } from "@nylorun/core/contracts";
|
|
2
|
+
import type { Tx } from "../store/types.js";
|
|
3
|
+
import { type AuthScope, type Session, type TenantContext } from "./context.js";
|
|
3
4
|
import type { TraceContext } from "../traces/ids.js";
|
|
4
5
|
export declare function command(ctx: TenantContext, id: string, input: SessionCommand, scope: AuthScope,
|
|
5
6
|
/**
|
|
@@ -8,7 +9,67 @@ export declare function command(ctx: TenantContext, id: string, input: SessionCo
|
|
|
8
9
|
*/
|
|
9
10
|
options?: {
|
|
10
11
|
trace?: TraceContext;
|
|
12
|
+
/**
|
|
13
|
+
* What a message to a busy session does when it says nothing (`delivery`): the route's
|
|
14
|
+
* answer to the caller's protocol (`queue` from 17). Core's own messages and the AG-UI and
|
|
15
|
+
* A2A adapters, which stream the run they start, take `reject`.
|
|
16
|
+
*/
|
|
17
|
+
delivery?: MessageDelivery;
|
|
11
18
|
}): Promise<unknown>;
|
|
19
|
+
export declare const takesMessages: (s: Session) => boolean;
|
|
20
|
+
/** A message ready to start a turn: as sent, its engine input and who sent it. */
|
|
21
|
+
export interface TurnMessage {
|
|
22
|
+
command: MessageEventBody;
|
|
23
|
+
/** An agent's message input, or a workflow's input value. */
|
|
24
|
+
input: unknown;
|
|
25
|
+
/** The artifacts its file parts named. */
|
|
26
|
+
fileArtifacts: string[];
|
|
27
|
+
writer: EventWriter;
|
|
28
|
+
/** Set for a message that waited in the session's queue. */
|
|
29
|
+
queuedMessageId?: string;
|
|
30
|
+
trace?: TraceContext;
|
|
31
|
+
/** What sent it when not a client (track T4): a trigger's run, or the session's own wake. */
|
|
32
|
+
cause?: MessageCause;
|
|
33
|
+
}
|
|
34
|
+
/** What sent a message when not a client (`command.message`'s `cause`). */
|
|
35
|
+
export interface MessageCause {
|
|
36
|
+
kind: "trigger" | "self";
|
|
37
|
+
triggerId: string;
|
|
38
|
+
runId: string;
|
|
39
|
+
}
|
|
40
|
+
/** How a message was taken: it started a turn, or waits in the session's queue. */
|
|
41
|
+
export type MessageAnswer = {
|
|
42
|
+
status: "accepted";
|
|
43
|
+
turnId: string;
|
|
44
|
+
cursor: string;
|
|
45
|
+
} | {
|
|
46
|
+
status: "queued";
|
|
47
|
+
queuedMessageId: string;
|
|
48
|
+
position: number;
|
|
49
|
+
cursor: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Takes a resolved message on session `s` (locked) in transaction `t`: it starts a turn when the
|
|
53
|
+
* session takes messages and none wait, else it waits in the queue behind them (protocol 17), and
|
|
54
|
+
* an idle session whose queue was left waiting (its Agent was unavailable when the last turn
|
|
55
|
+
* ended) starts a turn with all of them, this one last. Past `sessionQueuedMessages` it is
|
|
56
|
+
* `429 queue_full`. `waiting` is the queue's length, read under the session's lock.
|
|
57
|
+
*/
|
|
58
|
+
export declare function acceptMessage(ctx: TenantContext, t: Tx, s: Session, id: string, message: TurnMessage, waiting?: number): Promise<MessageAnswer>;
|
|
59
|
+
/** Who checks a queued message's turn start: core, since the sender's grants were checked when it was queued. */
|
|
60
|
+
export declare const QUEUE_SCOPE: AuthScope;
|
|
61
|
+
/**
|
|
62
|
+
* Starts the next turn of session `s` (locked) from its queue, when it takes messages and messages
|
|
63
|
+
* wait (protocol 17, D76): in the transaction that ended the last turn, or a cancel's. Our engine
|
|
64
|
+
* takes every queued message that carries the same turn manifest as the first, a workflow one.
|
|
65
|
+
* Writes `queue.taken`. Returns nothing when nothing started: an empty queue, or an Agent that
|
|
66
|
+
* cannot take a turn now, whose messages wait for the next message or turn end.
|
|
67
|
+
*/
|
|
68
|
+
export declare function startQueuedTurn(ctx: TenantContext, t: Tx, s: Session, id: string): Promise<{
|
|
69
|
+
turnId: string;
|
|
70
|
+
taken: string[];
|
|
71
|
+
cursor: string;
|
|
72
|
+
} | undefined>;
|
|
12
73
|
/**
|
|
13
74
|
* Who sent a command (R4.9), from its credential and never its body: a client over the Runtime
|
|
14
75
|
* API (an application, or a person through it or a token), or core itself.
|
package/dist/tenant/commands.js
CHANGED
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
* same transaction; the engine host sees it before its next effect and before settlement on
|
|
15
15
|
* any Worker. It then aborts an advance running on this process (`ctx.abortLocal`); every
|
|
16
16
|
* other process aborts its own on the signal (`control.ts`).
|
|
17
|
+
*
|
|
18
|
+
* A message to a session whose turn runs or waits (protocol 17, D76) is queued
|
|
19
|
+
* (`session_queue`, `message.queued`) unless it asks `delivery: "reject"`, which is
|
|
20
|
+
* `409 session_busy`. A turn that ends takes every queued message in the same transaction
|
|
21
|
+
* (`startQueuedTurn`): client messages and queued ones start a turn through one door,
|
|
22
|
+
* `beginMessageTurn`, which writes each one's `command.message`.
|
|
17
23
|
*/
|
|
18
24
|
import { randomUUID } from "node:crypto";
|
|
19
25
|
import { createDurableCheckpoint, createFlowCheckpoint, resumeFlowCheckpoint, } from "@nylorun/harness/run";
|
|
@@ -21,14 +27,14 @@ import { commandKey, cancelQueuedEffects, flowInteractionOf, foreignInteractionC
|
|
|
21
27
|
import { resolveMessageManifest } from "../core/turn-manifest.js";
|
|
22
28
|
import { canonical } from "../store/canonical.js";
|
|
23
29
|
import { lockedSession, } from "./context.js";
|
|
24
|
-
import { fail } from "./http.js";
|
|
30
|
+
import { fail, HttpError } from "./http.js";
|
|
31
|
+
import { readRuntimeLimits } from "./runtime-limits.js";
|
|
25
32
|
import { settleToolRow } from "../harness-api/tool-journal.js";
|
|
26
33
|
import { accessOf } from "./auth.js";
|
|
27
|
-
import {
|
|
34
|
+
import { checkAgentTurn } from "./agents.js";
|
|
28
35
|
import { turnManifestOf, variantStore } from "./session.js";
|
|
29
36
|
import { slimModelEffects } from "./slim.js";
|
|
30
37
|
import { resolveMessageParts } from "../artifacts/parts.js";
|
|
31
|
-
import { currentModelInputs } from "../model/catalog.js";
|
|
32
38
|
import { endTurn, resumeTurn, startTurn } from "./turn-timing.js";
|
|
33
39
|
/** A command's identity for idempotency: everything but the request id. */
|
|
34
40
|
const semantic = (value) => {
|
|
@@ -43,11 +49,7 @@ export async function command(ctx, id, input, scope,
|
|
|
43
49
|
options = {}) {
|
|
44
50
|
const { store } = ctx;
|
|
45
51
|
const cascadeCancelIds = [];
|
|
46
|
-
|
|
47
|
-
// the session's transaction. Undefined without a model in the vault: nothing is refused.
|
|
48
|
-
const inputs = input.type === "message" && "parts" in input && input.parts.some((part) => part.type === "file")
|
|
49
|
-
? await currentModelInputs(ctx)
|
|
50
|
-
: undefined;
|
|
52
|
+
let cancelledTurn = null;
|
|
51
53
|
const response = await store.tx(async (t) => {
|
|
52
54
|
const s = await lockedSession(t, id, accessOf(scope));
|
|
53
55
|
const command = input;
|
|
@@ -70,8 +72,11 @@ options = {}) {
|
|
|
70
72
|
let held;
|
|
71
73
|
// The turn a `cancel` ended, if one was active.
|
|
72
74
|
let cancelledTurnId = null;
|
|
75
|
+
/** A message's answer: accepted with its turn, or queued (protocol 17). */
|
|
76
|
+
let answer;
|
|
73
77
|
if (command.type === "cancel") {
|
|
74
78
|
cancelledTurnId = s.activeTurnId;
|
|
79
|
+
cancelledTurn = cancelledTurnId;
|
|
75
80
|
const workflowCancel = isWorkflowManifest(s.manifest);
|
|
76
81
|
// Linked sessions this one started: a workflow's agents, or flow agents an agent
|
|
77
82
|
// uses as tools.
|
|
@@ -134,85 +139,25 @@ options = {}) {
|
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
141
|
else {
|
|
137
|
-
// A session opened before protocol 11 (migration 0020)
|
|
138
|
-
// history but takes no turn.
|
|
142
|
+
// A session opened before protocol 11 (migration 0020), 13 (migration 0024), 15
|
|
143
|
+
// (migrations 0029, 0030) or 16 (migration 0032) keeps its history but takes no turn.
|
|
139
144
|
if (s.closedReason !== undefined)
|
|
140
|
-
fail(409, s.closedReason === "
|
|
141
|
-
? "This session was opened before protocol
|
|
142
|
-
:
|
|
145
|
+
fail(409, s.closedReason === "protocol_16"
|
|
146
|
+
? "This session was opened before protocol 16, from an agent definition: save the agent as an agent manifest (nylorun migrate v8, then nylorun apply), make an Agent from it and open a new session on it."
|
|
147
|
+
: s.closedReason === "protocol_11"
|
|
148
|
+
? "This session was opened before protocol 11: open a new session on an Agent."
|
|
149
|
+
: s.closedReason === "protocol_15"
|
|
150
|
+
? "This session was opened before protocol 15, and its pinned manifest holds a runtime block: our harness now writes the model's instructions and defines its tools itself. Open a new session."
|
|
151
|
+
: "This session was opened before protocol 13, on a manifest the Runtime no longer runs: register the agent again (manifest 7) and open a new session.", { code: "session_closed" });
|
|
143
152
|
if (command.type === "message") {
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
const delivery = command.delivery ?? options.delivery ?? "reject";
|
|
154
|
+
const waiting = await t.countQueuedMessages(id);
|
|
155
|
+
if (!takesMessages(s) && delivery === "reject")
|
|
156
|
+
fail(409, "Session has active or unresolved work", { code: "session_busy" });
|
|
146
157
|
// Every turn start on a sandbox: it exists, the token's grants reach it, and no other
|
|
147
|
-
// session's turn holds it.
|
|
148
|
-
await
|
|
149
|
-
|
|
150
|
-
// A new turn folds from the latest snapshot: a cancel or failure only reverts this turn.
|
|
151
|
-
if (s.history?.snapshot !== undefined)
|
|
152
|
-
s.history = { ...s.history, from: s.history.snapshot };
|
|
153
|
-
s.activeTurnId = randomUUID();
|
|
154
|
-
startTurn(s);
|
|
155
|
-
if (options.trace)
|
|
156
|
-
s.turnTrace = options.trace;
|
|
157
|
-
else
|
|
158
|
-
delete s.turnTrace;
|
|
159
|
-
// File parts name artifacts the caller may read; each is pinned to a version now. An
|
|
160
|
-
// agent's model must read each one (a workflow passes them on as data).
|
|
161
|
-
const parts = "parts" in command
|
|
162
|
-
? await resolveMessageParts(t, command.parts, id, accessOf(scope), isWorkflowManifest(s.manifest) ? undefined : inputs)
|
|
163
|
-
: undefined;
|
|
164
|
-
if (parts && parts.refused.length > 0)
|
|
165
|
-
fail(422, `The model does not read ${parts.refused
|
|
166
|
-
.map((part) => `${part.name} (${part.reason})`)
|
|
167
|
-
.join(", ")}.`, { code: "attachment_unsupported", details: { parts: parts.refused } });
|
|
168
|
-
// The files the session's messages named: `read_artifact` reads them again (protocol 12).
|
|
169
|
-
if (parts) {
|
|
170
|
-
const named = parts.pinned.flatMap((part) => (part.type === "file" ? [part.artifactId] : []));
|
|
171
|
-
if (named.length > 0)
|
|
172
|
-
s.fileArtifacts = [...new Set([...(s.fileArtifacts ?? []), ...named])];
|
|
173
|
-
}
|
|
174
|
-
const data = "data" in command ? command.data : undefined;
|
|
175
|
-
if (isWorkflowManifest(s.manifest)) {
|
|
176
|
-
const input = "content" in command
|
|
177
|
-
? command.content
|
|
178
|
-
: parts
|
|
179
|
-
? { parts: parts.pinned }
|
|
180
|
-
: data;
|
|
181
|
-
s.checkpoint = createFlowCheckpoint({
|
|
182
|
-
manifest: s.manifest,
|
|
183
|
-
sessionId: id,
|
|
184
|
-
turnId: s.activeTurnId,
|
|
185
|
-
input,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
// Optional message.manifest: validate as variant, pin by hash, apply this turn only.
|
|
190
|
-
const resolved = resolveMessageManifest({
|
|
191
|
-
pinned: s.manifest,
|
|
192
|
-
pinnedHash: s.manifestHash,
|
|
193
|
-
messageManifest: command.manifest,
|
|
194
|
-
store: variantStore(s),
|
|
195
|
-
});
|
|
196
|
-
if (!resolved.ok) {
|
|
197
|
-
fail(400, resolved.message);
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
s.checkpoint = createDurableCheckpoint({
|
|
201
|
-
manifest: resolved.manifest,
|
|
202
|
-
sessionId: id,
|
|
203
|
-
turnId: s.activeTurnId,
|
|
204
|
-
input: "content" in command
|
|
205
|
-
? command.content
|
|
206
|
-
: parts
|
|
207
|
-
? { content: parts.engine }
|
|
208
|
-
: typeof data === "string"
|
|
209
|
-
? data
|
|
210
|
-
: JSON.stringify(data),
|
|
211
|
-
state: s.state,
|
|
212
|
-
info: s.info,
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
158
|
+
// session's turn holds it. A queued message is checked when it is queued, too.
|
|
159
|
+
await checkAgentTurn(t, s, scope, ctx);
|
|
160
|
+
answer = await acceptMessage(ctx, t, s, id, await turnMessage(t, s, id, command, scope, options.trace), waiting);
|
|
216
161
|
}
|
|
217
162
|
else if (command.type === "approve" &&
|
|
218
163
|
(held = await heldCall(t, s, command.interactionId)) !== undefined) {
|
|
@@ -285,13 +230,15 @@ options = {}) {
|
|
|
285
230
|
segment: (s.checkpoint?.segment ?? 0) + 1,
|
|
286
231
|
});
|
|
287
232
|
}
|
|
233
|
+
}
|
|
234
|
+
if (command.type !== "cancel" && command.type !== "message") {
|
|
288
235
|
// A paused turn goes on: its wait for a person is not its running time (R7.1).
|
|
289
236
|
resumeTurn(s);
|
|
290
237
|
s.status = "runnable";
|
|
291
238
|
s.waits = undefined;
|
|
292
239
|
// A flow answers each interaction in the same segment, as a held tool call's approval is
|
|
293
240
|
// answered (R5 step 2): the interaction names the cause.
|
|
294
|
-
const cause =
|
|
241
|
+
const cause = isWorkflowManifest(s.manifest) || held !== undefined
|
|
295
242
|
? command.interactionId
|
|
296
243
|
: s.checkpoint.segment;
|
|
297
244
|
const commandWake = {
|
|
@@ -299,8 +246,11 @@ options = {}) {
|
|
|
299
246
|
dedupeKey: `${command.type}:${s.activeTurnId}:${cause}`,
|
|
300
247
|
};
|
|
301
248
|
await t.wake(id, commandWake);
|
|
302
|
-
event = await t.event(id, s.activeTurnId, `command.${command.type}`, command
|
|
249
|
+
event = await t.event(id, s.activeTurnId, `command.${command.type}`, command, commandWriter(scope));
|
|
303
250
|
}
|
|
251
|
+
// A cancel ends the turn, not the queue: the messages waiting start the next one.
|
|
252
|
+
if (command.type === "cancel")
|
|
253
|
+
await startQueuedTurn(ctx, t, s, id);
|
|
304
254
|
await t.put("sessions", id, s);
|
|
305
255
|
// Direct cancel of a linked agent fails that node with agent.cancelled (WF-R54). The agent
|
|
306
256
|
// session is locked above, before wakeLinkedWorkflow locks its workflow.
|
|
@@ -313,16 +263,18 @@ options = {}) {
|
|
|
313
263
|
error: "Agent turn was cancelled",
|
|
314
264
|
});
|
|
315
265
|
const response = {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
266
|
+
...(answer ?? {
|
|
267
|
+
status: "accepted",
|
|
268
|
+
turnId: command.type === "cancel" ? null : s.activeTurnId,
|
|
269
|
+
cursor: event.cursor,
|
|
270
|
+
}),
|
|
319
271
|
requestId: command.requestId,
|
|
320
272
|
};
|
|
321
273
|
await t.put("commands", key, { command, response });
|
|
322
274
|
return response;
|
|
323
275
|
});
|
|
324
276
|
if (input.type === "cancel")
|
|
325
|
-
ctx.abortLocal(id);
|
|
277
|
+
ctx.abortLocal(id, cancelledTurn ?? undefined);
|
|
326
278
|
// Cascade cancel linked agents deepest-first under existing fencing (WF-R53).
|
|
327
279
|
for (const agentId of cascadeCancelIds) {
|
|
328
280
|
try {
|
|
@@ -339,6 +291,204 @@ options = {}) {
|
|
|
339
291
|
}
|
|
340
292
|
return response;
|
|
341
293
|
}
|
|
294
|
+
/** Statuses in which a session takes a message as a new turn; in the others a turn runs or waits. */
|
|
295
|
+
const TAKES_MESSAGES = ["idle", "completed", "failed", "cancelled"];
|
|
296
|
+
export const takesMessages = (s) => TAKES_MESSAGES.includes(s.status);
|
|
297
|
+
/**
|
|
298
|
+
* Takes a resolved message on session `s` (locked) in transaction `t`: it starts a turn when the
|
|
299
|
+
* session takes messages and none wait, else it waits in the queue behind them (protocol 17), and
|
|
300
|
+
* an idle session whose queue was left waiting (its Agent was unavailable when the last turn
|
|
301
|
+
* ended) starts a turn with all of them, this one last. Past `sessionQueuedMessages` it is
|
|
302
|
+
* `429 queue_full`. `waiting` is the queue's length, read under the session's lock.
|
|
303
|
+
*/
|
|
304
|
+
export async function acceptMessage(ctx, t, s, id, message, waiting) {
|
|
305
|
+
const queued = waiting ?? (await t.countQueuedMessages(id));
|
|
306
|
+
const takes = takesMessages(s);
|
|
307
|
+
if (takes && queued === 0) {
|
|
308
|
+
const event = await beginMessageTurn(t, s, id, [message]);
|
|
309
|
+
return { status: "accepted", turnId: s.activeTurnId, cursor: event.cursor };
|
|
310
|
+
}
|
|
311
|
+
// The turn runs or waits, or earlier messages still wait: this one waits behind them.
|
|
312
|
+
const { sessionQueuedMessages } = await readRuntimeLimits(t);
|
|
313
|
+
if (queued >= sessionQueuedMessages)
|
|
314
|
+
fail(429, `Session ${id} already holds ${queued} queued messages, the Runtime limit sessionQueuedMessages (PUT /v1/tenant/limits); they start a turn when the running one ends.`, { code: "queue_full" });
|
|
315
|
+
const queuedMessageId = randomUUID();
|
|
316
|
+
await t.enqueueMessage({
|
|
317
|
+
id: queuedMessageId,
|
|
318
|
+
sessionId: id,
|
|
319
|
+
command: message.command,
|
|
320
|
+
input: message.input,
|
|
321
|
+
fileArtifacts: message.fileArtifacts,
|
|
322
|
+
writer: message.writer,
|
|
323
|
+
trace: message.trace ?? null,
|
|
324
|
+
cause: message.cause ?? null,
|
|
325
|
+
createdAt: new Date().toISOString(),
|
|
326
|
+
});
|
|
327
|
+
const position = queued + 1;
|
|
328
|
+
const event = await t.event(id, s.activeTurnId, "message.queued", { queuedMessageId, position, requestId: message.command.requestId }, message.writer);
|
|
329
|
+
const started = takes ? await startQueuedTurn(ctx, t, s, id) : undefined;
|
|
330
|
+
if (started?.taken.includes(queuedMessageId))
|
|
331
|
+
return { status: "accepted", turnId: started.turnId, cursor: started.cursor };
|
|
332
|
+
return { status: "queued", queuedMessageId, position, cursor: event.cursor };
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Resolves a message for a turn, now or when it leaves the queue: file parts name artifacts the
|
|
336
|
+
* caller may read, each pinned to a version (any file goes since protocol 15: the harness decides
|
|
337
|
+
* how the model sees it, and a workflow passes it on as data), and a turn manifest must be a
|
|
338
|
+
* variant of the pinned agent.
|
|
339
|
+
*/
|
|
340
|
+
async function turnMessage(t, s, id, command, scope, trace) {
|
|
341
|
+
const parts = "parts" in command ? await resolveMessageParts(t, command.parts, id, accessOf(scope)) : undefined;
|
|
342
|
+
const fileArtifacts = (parts?.pinned ?? []).flatMap((part) => (part.type === "file" ? [part.artifactId] : []));
|
|
343
|
+
const data = "data" in command ? command.data : undefined;
|
|
344
|
+
let input;
|
|
345
|
+
if (isWorkflowManifest(s.manifest))
|
|
346
|
+
input =
|
|
347
|
+
"content" in command
|
|
348
|
+
? command.content
|
|
349
|
+
: parts
|
|
350
|
+
? { parts: parts.pinned }
|
|
351
|
+
: data;
|
|
352
|
+
else {
|
|
353
|
+
const resolved = resolveMessageManifest({
|
|
354
|
+
pinned: s.manifest,
|
|
355
|
+
pinnedHash: s.manifestHash,
|
|
356
|
+
messageManifest: command.manifest,
|
|
357
|
+
store: variantStore(s),
|
|
358
|
+
});
|
|
359
|
+
if (!resolved.ok)
|
|
360
|
+
fail(400, resolved.message);
|
|
361
|
+
input =
|
|
362
|
+
"content" in command
|
|
363
|
+
? command.content
|
|
364
|
+
: parts
|
|
365
|
+
? { content: parts.engine }
|
|
366
|
+
: typeof data === "string"
|
|
367
|
+
? data
|
|
368
|
+
: JSON.stringify(data);
|
|
369
|
+
}
|
|
370
|
+
return { command, input, fileArtifacts, writer: commandWriter(scope), ...(trace ? { trace } : {}) };
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Starts a turn on session `s` (locked, taking messages) with `messages`, in order: the one door
|
|
374
|
+
* for a client's message and queued ones (protocol 17). Our engine reads more than one as
|
|
375
|
+
* `{kind: "messages", messages}`, each its own user message; a workflow takes one. The checkpoint
|
|
376
|
+
* is made before the session changes, so a manifest that no longer resolves changes nothing.
|
|
377
|
+
* Writes each message's `command.message` and returns the last.
|
|
378
|
+
*/
|
|
379
|
+
async function beginMessageTurn(t, s, id, messages) {
|
|
380
|
+
const turnId = randomUUID();
|
|
381
|
+
const first = messages[0];
|
|
382
|
+
let checkpoint;
|
|
383
|
+
if (isWorkflowManifest(s.manifest))
|
|
384
|
+
checkpoint = createFlowCheckpoint({
|
|
385
|
+
manifest: s.manifest,
|
|
386
|
+
sessionId: id,
|
|
387
|
+
turnId,
|
|
388
|
+
input: first.input,
|
|
389
|
+
});
|
|
390
|
+
else {
|
|
391
|
+
const resolved = resolveMessageManifest({
|
|
392
|
+
pinned: s.manifest,
|
|
393
|
+
pinnedHash: s.manifestHash,
|
|
394
|
+
messageManifest: first.command.manifest,
|
|
395
|
+
store: variantStore(s),
|
|
396
|
+
});
|
|
397
|
+
if (!resolved.ok)
|
|
398
|
+
return fail(400, resolved.message);
|
|
399
|
+
checkpoint = createDurableCheckpoint({
|
|
400
|
+
manifest: resolved.manifest,
|
|
401
|
+
sessionId: id,
|
|
402
|
+
turnId,
|
|
403
|
+
input: (messages.length === 1
|
|
404
|
+
? first.input
|
|
405
|
+
: { kind: "messages", messages: messages.map((message) => message.input) }),
|
|
406
|
+
state: s.state,
|
|
407
|
+
info: s.info,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
s.turnStartState = s.state;
|
|
411
|
+
// A new turn folds from the latest snapshot: a cancel or failure only reverts this turn.
|
|
412
|
+
if (s.history?.snapshot !== undefined)
|
|
413
|
+
s.history = { ...s.history, from: s.history.snapshot };
|
|
414
|
+
s.activeTurnId = turnId;
|
|
415
|
+
startTurn(s);
|
|
416
|
+
if (first.trace)
|
|
417
|
+
s.turnTrace = first.trace;
|
|
418
|
+
else
|
|
419
|
+
delete s.turnTrace;
|
|
420
|
+
// A trigger's run or a booked wake among the messages started it (track T4).
|
|
421
|
+
const cause = messages.find((message) => message.cause)?.cause;
|
|
422
|
+
if (cause)
|
|
423
|
+
s.turnCause = { kind: cause.kind, triggerId: cause.triggerId, runId: cause.runId };
|
|
424
|
+
else
|
|
425
|
+
delete s.turnCause;
|
|
426
|
+
// The files the session's messages named: `read_artifact` reads them again (protocol 12).
|
|
427
|
+
const named = messages.flatMap((message) => message.fileArtifacts);
|
|
428
|
+
if (named.length > 0)
|
|
429
|
+
s.fileArtifacts = [...new Set([...(s.fileArtifacts ?? []), ...named])];
|
|
430
|
+
s.checkpoint = checkpoint;
|
|
431
|
+
s.status = "runnable";
|
|
432
|
+
s.waits = undefined;
|
|
433
|
+
await t.wake(id, { reason: "message", dedupeKey: `message:${turnId}:${checkpoint.segment}` });
|
|
434
|
+
let event;
|
|
435
|
+
for (const message of messages)
|
|
436
|
+
event = await t.event(id, turnId, "command.message", {
|
|
437
|
+
...message.command,
|
|
438
|
+
...(message.queuedMessageId === undefined ? {} : { queuedMessageId: message.queuedMessageId }),
|
|
439
|
+
...(message.trace ? { trace: message.trace } : {}),
|
|
440
|
+
...(message.cause ? { cause: message.cause } : {}),
|
|
441
|
+
}, message.writer);
|
|
442
|
+
return event;
|
|
443
|
+
}
|
|
444
|
+
/** Who checks a queued message's turn start: core, since the sender's grants were checked when it was queued. */
|
|
445
|
+
export const QUEUE_SCOPE = { kind: "application", principalId: "queue" };
|
|
446
|
+
/**
|
|
447
|
+
* Starts the next turn of session `s` (locked) from its queue, when it takes messages and messages
|
|
448
|
+
* wait (protocol 17, D76): in the transaction that ended the last turn, or a cancel's. Our engine
|
|
449
|
+
* takes every queued message that carries the same turn manifest as the first, a workflow one.
|
|
450
|
+
* Writes `queue.taken`. Returns nothing when nothing started: an empty queue, or an Agent that
|
|
451
|
+
* cannot take a turn now, whose messages wait for the next message or turn end.
|
|
452
|
+
*/
|
|
453
|
+
export async function startQueuedTurn(ctx, t, s, id) {
|
|
454
|
+
if (s.closedReason !== undefined || !takesMessages(s) || s.activeTurnId !== null)
|
|
455
|
+
return undefined;
|
|
456
|
+
const queued = await t.queuedMessages(id);
|
|
457
|
+
if (queued.length === 0)
|
|
458
|
+
return undefined;
|
|
459
|
+
const manifestOf = (row) => canonical(row.command.manifest ?? null);
|
|
460
|
+
const batch = isWorkflowManifest(s.manifest) ? [queued[0]] : [];
|
|
461
|
+
if (batch.length === 0)
|
|
462
|
+
for (const row of queued) {
|
|
463
|
+
if (manifestOf(row) !== manifestOf(queued[0]))
|
|
464
|
+
break;
|
|
465
|
+
batch.push(row);
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
await checkAgentTurn(t, s, QUEUE_SCOPE, ctx);
|
|
469
|
+
await beginMessageTurn(t, s, id, batch.map((row) => ({
|
|
470
|
+
command: row.command,
|
|
471
|
+
input: row.input,
|
|
472
|
+
fileArtifacts: row.fileArtifacts,
|
|
473
|
+
writer: row.writer,
|
|
474
|
+
queuedMessageId: row.id,
|
|
475
|
+
...(row.trace ? { trace: row.trace } : {}),
|
|
476
|
+
...(row.cause ? { cause: row.cause } : {}),
|
|
477
|
+
})));
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
if (error instanceof HttpError)
|
|
481
|
+
return undefined;
|
|
482
|
+
throw error;
|
|
483
|
+
}
|
|
484
|
+
const taken = batch.map((row) => row.id);
|
|
485
|
+
await t.deleteQueuedMessages(taken);
|
|
486
|
+
const event = await t.event(id, s.activeTurnId, "queue.taken", {
|
|
487
|
+
turnId: s.activeTurnId,
|
|
488
|
+
queuedMessageIds: taken,
|
|
489
|
+
});
|
|
490
|
+
return { turnId: s.activeTurnId, taken, cursor: event.cursor };
|
|
491
|
+
}
|
|
342
492
|
/** Core's own commands (R4.9): the flow host's, and a drain's cancels. */
|
|
343
493
|
const CORE_PRINCIPALS = { "flow-host": "flow-host", drain: "core" };
|
|
344
494
|
/**
|