@nylorun/runtime 0.27.0-beta → 0.29.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 +67 -0
- package/README.md +1 -1
- package/dist/api/a2a/card.d.ts +4 -3
- package/dist/api/a2a/card.js +3 -3
- package/dist/api/a2a/routes.js +18 -7
- package/dist/api/ag-ui/routes.js +4 -7
- package/dist/api/components.d.ts +5854 -2718
- package/dist/api/components.js +39 -30
- package/dist/api/http/app.js +9 -4
- package/dist/api/http/body.d.ts +2 -0
- package/dist/api/http/body.js +20 -0
- package/dist/api/http/define.d.ts +4 -0
- package/dist/api/http/define.js +20 -9
- 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 +2 -2
- package/dist/api/http/routes/coverage.d.ts +4 -4
- package/dist/api/http/routes/coverage.js +12 -6
- package/dist/api/http/routes/files.js +1 -1
- package/dist/api/http/routes/mcp.js +2 -2
- 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 +1 -137
- package/dist/api/http/routes/reads.js +31 -12
- package/dist/api/http/routes/sessions.js +21 -50
- package/dist/api/http/routes/tenant.js +13 -10
- package/dist/api/http/routes/triggers.d.ts +10 -0
- package/dist/api/http/routes/triggers.js +183 -0
- package/dist/api/openapi.js +69 -47
- package/dist/artifacts/disk.d.ts +10 -10
- package/dist/artifacts/disk.js +27 -27
- package/dist/artifacts/quota.d.ts +1 -1
- package/dist/artifacts/quota.js +2 -2
- package/dist/artifacts/service.js +2 -2
- package/dist/core/flow-host.d.ts +0 -1
- package/dist/gates/egress-credentials.js +1 -1
- package/dist/gates/egress.d.ts +5 -5
- package/dist/gates/egress.js +6 -6
- package/dist/gates/http-tool.js +2 -2
- package/dist/gates/meter.js +22 -5
- package/dist/gates/policy.d.ts +8 -3
- package/dist/gates/policy.js +7 -2
- package/dist/gates/tenant-vaults.d.ts +1 -1
- package/dist/harness/pod.d.ts +2 -0
- package/dist/harness/setup.js +17 -6
- package/dist/harness-api/record.js +11 -0
- package/dist/harness-api/server.js +4 -0
- package/dist/harness-api/start.d.ts +11 -7
- package/dist/harness-api/start.js +49 -5
- package/dist/harness-api/workspace.js +2 -2
- package/dist/host/config-for.d.ts +2 -0
- package/dist/host/config-for.js +1 -0
- package/dist/host/http.d.ts +2 -2
- package/dist/host/http.js +5 -2
- package/dist/host/main.js +1 -0
- package/dist/keys/keys.d.ts +15 -4
- package/dist/keys/keys.js +16 -0
- package/dist/keys/webhooks.d.ts +25 -0
- package/dist/keys/webhooks.js +62 -0
- package/dist/management-openapi.json +313 -133
- package/dist/mcp/connect.d.ts +2 -2
- package/dist/mcp/connect.js +3 -3
- package/dist/mcp/preview.js +7 -7
- 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/pi-model.js +7 -0
- package/dist/openapi.json +13120 -9034
- 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/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/sandbox/join.js +5 -5
- package/dist/sandbox/placement.d.ts +1 -1
- package/dist/sandbox/placement.js +3 -3
- package/dist/sandbox/pods/lifecycle.d.ts +6 -6
- package/dist/sandbox/pods/lifecycle.js +6 -6
- package/dist/sandbox/pods/reconcile.js +10 -2
- package/dist/sandbox/resolve.d.ts +3 -3
- package/dist/sandbox/resolve.js +7 -7
- package/dist/sandbox/setup.d.ts +3 -1
- package/dist/sandbox/setup.js +14 -4
- 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/0037_model_usage_cost_split.sql +4 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +42 -0
- package/dist/store/postgres/reads.js +44 -87
- 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 +1408 -123
- package/dist/store/postgres/schema.js +194 -38
- package/dist/store/postgres/store.js +329 -79
- package/dist/store/postgres/tenant.js +1 -2
- package/dist/store/types.d.ts +155 -50
- package/dist/store/types.js +0 -1
- package/dist/tenant/advance.js +20 -8
- 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} +464 -320
- 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 -83
- package/dist/tenant/context.d.ts +46 -19
- package/dist/tenant/context.js +6 -2
- package/dist/tenant/effects.js +7 -16
- package/dist/tenant/identity-file.d.ts +6 -6
- package/dist/tenant/identity-file.js +12 -10
- package/dist/tenant/issuers.js +3 -3
- package/dist/tenant/limits.d.ts +0 -3
- package/dist/tenant/limits.js +0 -3
- package/dist/tenant/memories.d.ts +38 -0
- package/dist/tenant/memories.js +119 -0
- package/dist/tenant/reset.js +2 -2
- package/dist/tenant/run-grants.d.ts +6 -0
- package/dist/tenant/run-grants.js +4 -0
- package/dist/tenant/runtime.d.ts +1 -1
- package/dist/tenant/runtime.js +8 -0
- package/dist/tenant/sessions.d.ts +28 -45
- package/dist/tenant/sessions.js +85 -208
- package/dist/tenant/status.js +6 -7
- package/dist/tenant/sweep.js +12 -3
- package/dist/tenant/transcript.d.ts +16 -16
- 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 +5 -0
- package/dist/tenant/worker.d.ts +2 -0
- package/dist/tenant/worker.js +5 -0
- package/dist/traces/map.d.ts +1 -1
- package/dist/traces/map.js +7 -1
- package/dist/tuning.d.ts +7 -0
- package/dist/tuning.js +19 -0
- 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/record/environment.d.ts +0 -27
- package/dist/record/environment.js +0 -46
- package/dist/tenant/environment-manifests.d.ts +0 -57
- package/dist/tenant/environment-manifests.js +0 -208
- package/dist/tenant/environments.d.ts +0 -172
package/dist/core/flow-host.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Nothing here knows a tool: the header name and its format are the credential's. A release the
|
|
13
13
|
* vault refuses (two secrets for one host, an unreadable value) answers 502 and is logged, never
|
|
14
14
|
* as an event; a value never is. Upgrades (WebSocket) and HTTP/2 are not served on this path.
|
|
15
|
-
* Each connection keeps a tally for its `
|
|
15
|
+
* Each connection keeps a tally for its `agent.egress` event (R7.3, D67): how many requests it
|
|
16
16
|
* read and which credentials it set, by id.
|
|
17
17
|
*/
|
|
18
18
|
import { createServer } from "node:http";
|
package/dist/gates/egress.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Server } from "node:http";
|
|
2
2
|
import { type Socket } from "node:net";
|
|
3
|
-
import type { EgressRunningTurn,
|
|
3
|
+
import type { EgressRunningTurn, AgentEventPayload } from "@nylorun/core/contracts";
|
|
4
4
|
import type { EgressTokenVerdict } from "../sandbox/egress-token.js";
|
|
5
5
|
import type { SessionStore } from "../store/types.js";
|
|
6
6
|
import type { Logger } from "../tenant/types.js";
|
|
@@ -16,10 +16,10 @@ export interface EgressSandboxes {
|
|
|
16
16
|
readonly allow: readonly string[];
|
|
17
17
|
} | undefined>;
|
|
18
18
|
}
|
|
19
|
-
/** One connection's record (R7.3, D67): `
|
|
20
|
-
export type EgressConnection =
|
|
19
|
+
/** One connection's record (R7.3, D67): `agent.egress`'s payload. */
|
|
20
|
+
export type EgressConnection = AgentEventPayload<"agent.egress">;
|
|
21
21
|
/**
|
|
22
|
-
* How many running turns one connection names, at most: fixed by the `
|
|
22
|
+
* How many running turns one connection names, at most: fixed by the `agent.egress` event
|
|
23
23
|
* contract (`runningTurns` holds at most 16 in `/core/contracts`), which every reader
|
|
24
24
|
* validates against, so not a setting here.
|
|
25
25
|
*/
|
|
@@ -90,7 +90,7 @@ export declare function storeEgressSandboxes(store: SessionStore): EgressSandbox
|
|
|
90
90
|
/**
|
|
91
91
|
* `EgressRecord` over the Tenant's record (R7.3, D67). The running turns are the sessions on the
|
|
92
92
|
* Environment whose body has an `activeTurnId`, in id order, at most `EGRESS_RUNNING_TURNS_MAX`.
|
|
93
|
-
* The write is `
|
|
93
|
+
* The write is `agent.egress` on the Environment's stream, under its row's lock; a deleted
|
|
94
94
|
* Environment gets nothing.
|
|
95
95
|
*/
|
|
96
96
|
export declare function storeEgressRecord(store: SessionStore): EgressRecord;
|
package/dist/gates/egress.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* never is. Tunnels close after `idleMs` without traffic in either direction.
|
|
26
26
|
*
|
|
27
27
|
* Every connection to a named host on a live token is recorded once (R7.3, D67), when it closes
|
|
28
|
-
* or at once when the gate refuses it, as `
|
|
28
|
+
* or at once when the gate refuses it, as `agent.egress` on the Environment's own stream:
|
|
29
29
|
* the connection is the Environment's, since its token names the pod and turns run in parallel
|
|
30
30
|
* (D66). `runningTurns` lists the turns active on the Environment as it opened, as context and
|
|
31
31
|
* never as an owner: one read, started as the name resolves and never waited for, so a turn that
|
|
@@ -44,7 +44,7 @@ import { bindListener } from "../host/http.js";
|
|
|
44
44
|
import { hostTuning } from "../tuning.js";
|
|
45
45
|
import { CONNECT_ESTABLISHED, credentialTerminator, } from "./egress-credentials.js";
|
|
46
46
|
/**
|
|
47
|
-
* How many running turns one connection names, at most: fixed by the `
|
|
47
|
+
* How many running turns one connection names, at most: fixed by the `agent.egress` event
|
|
48
48
|
* contract (`runningTurns` holds at most 16 in `/core/contracts`), which every reader
|
|
49
49
|
* validates against, so not a setting here.
|
|
50
50
|
*/
|
|
@@ -156,7 +156,7 @@ function connectionRecord(sink, logger, client, at) {
|
|
|
156
156
|
const allowed = fields.outcome === "allowed";
|
|
157
157
|
const durationMs = Math.max(0, Date.now() - at.startedAt.getTime());
|
|
158
158
|
const connection = (turns) => ({
|
|
159
|
-
|
|
159
|
+
agentId: at.environmentId,
|
|
160
160
|
host: at.host,
|
|
161
161
|
port: at.port,
|
|
162
162
|
...fields,
|
|
@@ -414,7 +414,7 @@ export function storeEgressSandboxes(store) {
|
|
|
414
414
|
/**
|
|
415
415
|
* `EgressRecord` over the Tenant's record (R7.3, D67). The running turns are the sessions on the
|
|
416
416
|
* Environment whose body has an `activeTurnId`, in id order, at most `EGRESS_RUNNING_TURNS_MAX`.
|
|
417
|
-
* The write is `
|
|
417
|
+
* The write is `agent.egress` on the Environment's stream, under its row's lock; a deleted
|
|
418
418
|
* Environment gets nothing.
|
|
419
419
|
*/
|
|
420
420
|
export function storeEgressRecord(store) {
|
|
@@ -429,9 +429,9 @@ export function storeEgressRecord(store) {
|
|
|
429
429
|
},
|
|
430
430
|
async write(connection) {
|
|
431
431
|
await store.tx(async (t) => {
|
|
432
|
-
if (!(await t.environment(connection.
|
|
432
|
+
if (!(await t.environment(connection.agentId, { lock: true })))
|
|
433
433
|
return;
|
|
434
|
-
await t.
|
|
434
|
+
await t.agentEvent(connection.agentId, "agent.egress", connection);
|
|
435
435
|
});
|
|
436
436
|
},
|
|
437
437
|
};
|
package/dist/gates/http-tool.js
CHANGED
|
@@ -198,7 +198,7 @@ export async function runHttpTool(tenant, call, options) {
|
|
|
198
198
|
if (authorized.status === "refused")
|
|
199
199
|
return failed("http.credential", `The credential '${http.credential}' was refused: ${authorized.reason}`);
|
|
200
200
|
if (authorized.status === "unauthenticated")
|
|
201
|
-
return failed("http.credential", `The session's
|
|
201
|
+
return failed("http.credential", `The session's Agent holds no credential '${http.credential}' for ${authorized.url}`);
|
|
202
202
|
credential = authorized.headers;
|
|
203
203
|
identity = authorized.identity;
|
|
204
204
|
// A credential with `via` sends the call there, a gateway (R2b C2, Q9): to its one URL, so
|
|
@@ -229,7 +229,7 @@ export async function runHttpTool(tenant, call, options) {
|
|
|
229
229
|
return outcome;
|
|
230
230
|
return {
|
|
231
231
|
...outcome,
|
|
232
|
-
message: `The service answered HTTP 401: it rejected the credential '${http.credential}' from the session's
|
|
232
|
+
message: `The service answered HTTP 401: it rejected the credential '${http.credential}' from the session's Agent`,
|
|
233
233
|
server: http.credential,
|
|
234
234
|
};
|
|
235
235
|
}
|
package/dist/gates/meter.js
CHANGED
|
@@ -37,7 +37,7 @@ export function createMeter(options) {
|
|
|
37
37
|
session: request.sessionId,
|
|
38
38
|
effect: request.effectId,
|
|
39
39
|
scope: budget.scope,
|
|
40
|
-
...(budget.scope === "
|
|
40
|
+
...(budget.scope === "manifest" ? { manifest: budget.scopeId } : {}),
|
|
41
41
|
});
|
|
42
42
|
return { keys, refusal: failure("budget_exhausted", reached, false) };
|
|
43
43
|
}
|
|
@@ -124,7 +124,8 @@ export function usageRow(request, outcome, at, timing) {
|
|
|
124
124
|
: null,
|
|
125
125
|
sessionId: request.sessionId,
|
|
126
126
|
turnId: request.turnId,
|
|
127
|
-
|
|
127
|
+
// The calling agent's loop manifest id: a manifest version's name (protocol 16).
|
|
128
|
+
manifestName: request.agentId,
|
|
128
129
|
provider: producer.provider ?? null,
|
|
129
130
|
model: producer.model ?? outcome.evidence?.resolvedModel ?? request.call.model?.id ?? null,
|
|
130
131
|
inputTokens,
|
|
@@ -141,6 +142,22 @@ export function usageRow(request, outcome, at, timing) {
|
|
|
141
142
|
...(typeof firstTokenMs === "number" && Number.isFinite(firstTokenMs) && firstTokenMs >= 0
|
|
142
143
|
? { firstTokenMs: Math.round(firstTokenMs) }
|
|
143
144
|
: {}),
|
|
145
|
+
...costSplitColumns(outcome.evidence?.extras?.costSplit),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** The ledger's cost-by-kind columns from the adapter's `costSplit`; none unless all four are costs. */
|
|
149
|
+
function costSplitColumns(split) {
|
|
150
|
+
if (!split || typeof split !== "object" || Array.isArray(split))
|
|
151
|
+
return {};
|
|
152
|
+
const { input, cacheRead, cacheWrite, output } = split;
|
|
153
|
+
const parts = [input, cacheRead, cacheWrite, output];
|
|
154
|
+
if (!parts.every((part) => typeof part === "number" && Number.isFinite(part) && part >= 0))
|
|
155
|
+
return {};
|
|
156
|
+
return {
|
|
157
|
+
costInputUsd: input,
|
|
158
|
+
costCacheReadUsd: cacheRead,
|
|
159
|
+
costCacheWriteUsd: cacheWrite,
|
|
160
|
+
costOutputUsd: output,
|
|
144
161
|
};
|
|
145
162
|
}
|
|
146
163
|
/** The ledger rows a budget counts for `request`; undefined when it doesn't apply. */
|
|
@@ -148,8 +165,8 @@ function scopeOf(budget, request, at) {
|
|
|
148
165
|
const since = budget.period ? { since: periodStart(budget.period, at) } : {};
|
|
149
166
|
if (budget.scope === "turn")
|
|
150
167
|
return { scope: "turn", id: request.turnId };
|
|
151
|
-
if (budget.scope === "
|
|
152
|
-
return budget.scopeId === request.agentId ? { scope: "
|
|
168
|
+
if (budget.scope === "manifest")
|
|
169
|
+
return budget.scopeId === request.agentId ? { scope: "manifest", id: request.agentId, ...since } : undefined;
|
|
153
170
|
return { scope: "tenant", ...since };
|
|
154
171
|
}
|
|
155
172
|
/**
|
|
@@ -160,7 +177,7 @@ export function capReached(budget, spent, inflight) {
|
|
|
160
177
|
const projected = (total) => total + (spent.calls > 0 ? (inflight * total) / spent.calls : 0);
|
|
161
178
|
const name = budget.scope === "turn"
|
|
162
179
|
? "The turn's cap"
|
|
163
|
-
: `${budget.scope === "
|
|
180
|
+
: `${budget.scope === "manifest" ? `Agent manifest ${budget.scopeId}'s` : "The Tenant's"} ${budget.period === "day" ? "daily" : "monthly"} cap`;
|
|
164
181
|
const raise = "raise it with PUT /v1/tenant/budgets";
|
|
165
182
|
if (budget.limitTokens !== null && projected(spent.tokens) >= budget.limitTokens)
|
|
166
183
|
return `${name} of ${budget.limitTokens} tokens is reached (${spent.tokens} used); ${raise}`;
|
package/dist/gates/policy.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export interface PolicyCall {
|
|
|
35
35
|
readonly tool: string;
|
|
36
36
|
/** The MCP server's name. */
|
|
37
37
|
readonly server?: string;
|
|
38
|
-
/** The agent that calls it. */
|
|
38
|
+
/** The agent that calls it: its manifest's id (an agent manifest's name), never an Agent's id. */
|
|
39
39
|
readonly agent: string;
|
|
40
40
|
readonly input: unknown;
|
|
41
41
|
/** The name the model called it by, for display only: no rule matches it. */
|
|
@@ -61,8 +61,13 @@ export declare function readToolPolicy(t: Pick<Tx, "getSetting">): Promise<ToolP
|
|
|
61
61
|
* pinned manifest's `approval`. A policy that cannot be read refuses the call.
|
|
62
62
|
*/
|
|
63
63
|
export declare function toolCallDecision(t: Pick<Tx, "getSetting">, s: Session, request: HostEffect, kind: "mcp" | "http"): Promise<PolicyDecision>;
|
|
64
|
-
/**
|
|
65
|
-
|
|
64
|
+
/**
|
|
65
|
+
* `request` as the policy reads it: by its target's identity (protocol 15), and by the agent that
|
|
66
|
+
* calls it: an agent used as a tool by its id, the session's root agent by its loop manifest's id
|
|
67
|
+
* (protocol 16: the agent manifest's name). Never the session's Agent id (`s.agentId`), which
|
|
68
|
+
* protocol 16 made the Agent the session runs on (`users/ada/desk` for the manifest `desk`).
|
|
69
|
+
*/
|
|
70
|
+
export declare function policyCallOf(s: Pick<Session, "manifest">, request: HostEffect, kind: "mcp" | "http"): PolicyCall;
|
|
66
71
|
/** Decides `call` by `rules`; `pinnedAsk` adds the ask of a manifest saved before R5 step 2. */
|
|
67
72
|
export declare function decide(rules: readonly ToolRule[], call: PolicyCall, pinnedAsk?: boolean): PolicyDecision;
|
|
68
73
|
/**
|
package/dist/gates/policy.js
CHANGED
|
@@ -68,7 +68,12 @@ export async function toolCallDecision(t, s, request, kind) {
|
|
|
68
68
|
}
|
|
69
69
|
return decide(rules, policyCallOf(s, request, kind), pinnedApproval(turnManifestOf(s), request));
|
|
70
70
|
}
|
|
71
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* `request` as the policy reads it: by its target's identity (protocol 15), and by the agent that
|
|
73
|
+
* calls it: an agent used as a tool by its id, the session's root agent by its loop manifest's id
|
|
74
|
+
* (protocol 16: the agent manifest's name). Never the session's Agent id (`s.agentId`), which
|
|
75
|
+
* protocol 16 made the Agent the session runs on (`users/ada/desk` for the manifest `desk`).
|
|
76
|
+
*/
|
|
72
77
|
export function policyCallOf(s, request, kind) {
|
|
73
78
|
const target = request.target;
|
|
74
79
|
return {
|
|
@@ -77,7 +82,7 @@ export function policyCallOf(s, request, kind) {
|
|
|
77
82
|
? target.tool
|
|
78
83
|
: (request.key ?? request.effectId),
|
|
79
84
|
...(target?.kind === "mcp" ? { server: target.server } : {}),
|
|
80
|
-
agent: request.agent?.id ?? s.
|
|
85
|
+
agent: request.agent?.id ?? s.manifest.id,
|
|
81
86
|
input: request.input,
|
|
82
87
|
...(request.toolName === undefined ? {} : { name: request.toolName }),
|
|
83
88
|
};
|
|
@@ -29,7 +29,7 @@ export interface TenantVault {
|
|
|
29
29
|
session(sessionId: string): Promise<Session | undefined>;
|
|
30
30
|
/**
|
|
31
31
|
* The authorization of one request to a session's remote MCP server or HTTP tool: its
|
|
32
|
-
* credential from the vault of the session's
|
|
32
|
+
* credential from the vault of the session's Agent.
|
|
33
33
|
*/
|
|
34
34
|
authorizeMcp(sessionId: string, request: McpCredentialRequest): Promise<AuthorizeResult>;
|
|
35
35
|
/** Vault writes and token signing with the Tenant's vault key (the keys service, F4.2). */
|
package/dist/harness/pod.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { PodHostConfig } from "../host/stack-config.js";
|
|
|
2
2
|
/** What to run and copy on a new disk (`sandbox/setup.ts` in core), from the join answer. */
|
|
3
3
|
export interface DiskSetup {
|
|
4
4
|
readonly recipeVersion: number;
|
|
5
|
+
/** The sha256 of the commands and files (track T3): a marker with another runs the setup again. */
|
|
6
|
+
readonly setupHash?: string;
|
|
5
7
|
readonly commands: readonly {
|
|
6
8
|
readonly command: string;
|
|
7
9
|
readonly cwd: string;
|
package/dist/harness/setup.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The disk's setup in a pod Environment's engine (R2d step 2): after it joins and before it takes
|
|
3
3
|
* any turn, the engine copies the recipe's input files and runs its setup commands, once per
|
|
4
|
-
* volume. A marker on the volume (`/harness/setup.json`, `{ volumeId, recipeVersion,
|
|
5
|
-
* says it was done: a resume finds it and skips, a reset (a new volume, a new volume
|
|
6
|
-
* again
|
|
4
|
+
* volume. A marker on the volume (`/harness/setup.json`, `{ volumeId, recipeVersion, setupHash,
|
|
5
|
+
* status }`) says it was done: a resume finds it and skips, a reset (a new volume, a new volume
|
|
6
|
+
* id) runs it again, and so does a start on a new version whose setup differs (`setupHash`,
|
|
7
|
+
* track T3). A marker without a hash (written before T3) counts as this setup's. Commands run as the sandbox tools' do (`localExec`): `/bin/sh -c` in `cwd`, with the
|
|
7
8
|
* Environment's variables and secrets (as the sentinel) and egress through egress-gate.
|
|
8
9
|
*
|
|
9
10
|
* A failure is reported to core, which fails the pod and the turn waiting for it; the engine then
|
|
@@ -17,6 +18,15 @@ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
|
17
18
|
import { randomUUID } from "node:crypto";
|
|
18
19
|
import { dirname, posix } from "node:path";
|
|
19
20
|
import { hostTuning } from "../tuning.js";
|
|
21
|
+
/** The marker of `setup` done on volume `volumeId`. */
|
|
22
|
+
function markerOf(volumeId, setup) {
|
|
23
|
+
return {
|
|
24
|
+
volumeId,
|
|
25
|
+
recipeVersion: setup?.recipeVersion ?? 0,
|
|
26
|
+
...(setup?.setupHash === undefined ? {} : { setupHash: setup.setupHash }),
|
|
27
|
+
status: "ok",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
20
30
|
async function readText(path) {
|
|
21
31
|
try {
|
|
22
32
|
return (await readFile(path, "utf8")).trim();
|
|
@@ -111,7 +121,7 @@ export async function prepareDisk(options) {
|
|
|
111
121
|
else if (options.host.seeded) {
|
|
112
122
|
// A volume seeded from a disk version: the version holds what setup made, so mark it done.
|
|
113
123
|
if (volumeId) {
|
|
114
|
-
const done =
|
|
124
|
+
const done = markerOf(volumeId, setup);
|
|
115
125
|
await writeAtomic(options.markerFile, new TextEncoder().encode(`${JSON.stringify(done)}\n`));
|
|
116
126
|
}
|
|
117
127
|
options.logger.info("environment_setup_skipped", { reason: "seeded" });
|
|
@@ -123,7 +133,8 @@ export async function prepareDisk(options) {
|
|
|
123
133
|
if (marker && volumeId) {
|
|
124
134
|
try {
|
|
125
135
|
const done = JSON.parse(marker);
|
|
126
|
-
|
|
136
|
+
const same = done.setupHash === undefined || setup.setupHash === undefined || done.setupHash === setup.setupHash;
|
|
137
|
+
if (done.volumeId === volumeId && done.status === "ok" && same)
|
|
127
138
|
return;
|
|
128
139
|
}
|
|
129
140
|
catch {
|
|
@@ -137,7 +148,7 @@ export async function prepareDisk(options) {
|
|
|
137
148
|
const report = await runOnce(options, setup);
|
|
138
149
|
if (report.status === "ok") {
|
|
139
150
|
if (volumeId) {
|
|
140
|
-
const done =
|
|
151
|
+
const done = markerOf(volumeId, setup);
|
|
141
152
|
await writeAtomic(options.markerFile, new TextEncoder().encode(`${JSON.stringify(done)}\n`));
|
|
142
153
|
}
|
|
143
154
|
await options.host.reportSetup(report).catch((error) => options.logger.warn("environment_setup_report_failed", {
|
|
@@ -38,6 +38,8 @@ import { abortKind } from "../tenant/worker.js";
|
|
|
38
38
|
import { redactTokens } from "./redact.js";
|
|
39
39
|
import { toolCallDecision } from "../gates/policy.js";
|
|
40
40
|
import { traceKey } from "../traces/key.js";
|
|
41
|
+
import { serveRuntimeTool } from "../tenant/triggers.js";
|
|
42
|
+
import { serveMemoryTool } from "../memory/tool.js";
|
|
41
43
|
import { effectSpanOf, traceparentOf, turnTraceIdOf } from "../traces/ids.js";
|
|
42
44
|
import { firstSend, interrupted, settleToolRow, toolKindOf, } from "./tool-journal.js";
|
|
43
45
|
const TURN_CANCELLED = () => new HarnessApiError("turn_cancelled", "Turn cancelled");
|
|
@@ -138,6 +140,14 @@ export async function recordIntent(scope, effect, requestHash) {
|
|
|
138
140
|
const agentManifest = manifestFor(s.manifest, request.agent);
|
|
139
141
|
if (!agentManifest)
|
|
140
142
|
throw new Error(`Agent '${request.agent?.id ?? ""}' is not used as a tool`);
|
|
143
|
+
// An operation the Runtime serves itself (track T4): settled here, in the call's own
|
|
144
|
+
// transaction, keyed by its effect id, so a replay finds it completed.
|
|
145
|
+
if (request.kind === "tool" && request.target?.kind === "runtime") {
|
|
146
|
+
const row = { request, requestHash, status: "invoking", ...firstSend(toolKindOf(s, agentManifest, request)) };
|
|
147
|
+
const served = request.target.operation === "memory" ? await serveMemoryTool(t, s, request) : await serveRuntimeTool(t, s, request);
|
|
148
|
+
const outcome = await settleToolRow(t, s, row, served, { writer: coreIn(scope) });
|
|
149
|
+
return { status: "completed", outcome };
|
|
150
|
+
}
|
|
141
151
|
const refused = request.kind === "tool" ? refusal(s, request) : undefined;
|
|
142
152
|
if (refused) {
|
|
143
153
|
// A call with no target (an author's code tool, refused at save, so a backstop), or one
|
|
@@ -188,6 +198,7 @@ export async function recordIntent(scope, effect, requestHash) {
|
|
|
188
198
|
* - `http`: an HTTP tool its agent declares under that name (R2 M3), which crosses the Tool Gate;
|
|
189
199
|
* - `sandbox`: one of the sandbox operations, in a session with a sandbox;
|
|
190
200
|
* - `harness`: a tool the harness runs in its own process; its egress gate is its only control.
|
|
201
|
+
* - `runtime`: served by `recordIntent` itself, before this (`schedule_wake`, track T4).
|
|
191
202
|
*
|
|
192
203
|
* No target, and anything else, runs nothing (`tool.unavailable`).
|
|
193
204
|
*/
|
|
@@ -45,6 +45,7 @@ import { RecordEntryTooLargeError } from "../record/index.js";
|
|
|
45
45
|
import { redactTokens } from "./redact.js";
|
|
46
46
|
import { beat, renewEveryMs, tokenOf } from "./renew.js";
|
|
47
47
|
import { hostTuning } from "../tuning.js";
|
|
48
|
+
import { passStartCheck } from "../tenant/agent-versions.js";
|
|
48
49
|
/** No attached harness serves workspaces (or, with `pod`, that pod sandbox's). */
|
|
49
50
|
export class NoWorkspaceHarness extends Error {
|
|
50
51
|
pod;
|
|
@@ -211,6 +212,9 @@ export function createHarnessApiServer(ctx, options = {}) {
|
|
|
211
212
|
const expected = environment?.applied.harness ?? DEFAULT_HARNESS;
|
|
212
213
|
if (expected !== name)
|
|
213
214
|
throw new HarnessApiError("invalid", `Environment ${connection.host.sandboxId} runs the ${expected} harness, and this pod says ${name}`);
|
|
215
|
+
// Its pod started on the version it switched to, setup done (track T3): the start check passed.
|
|
216
|
+
if (environment?.startCheck)
|
|
217
|
+
await passStartCheck(ctx, connection.host.sandboxId, connection.host.epoch);
|
|
214
218
|
}
|
|
215
219
|
return name;
|
|
216
220
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelDescriptor, RecordedOutcome, RuntimeLimits,
|
|
1
|
+
import type { ModelDescriptor, RecordedOutcome, RuntimeLimits, TurnAgent, TurnMemory, TurnStart } from "@nylorun/core/harness-api";
|
|
2
2
|
import { type SandboxManifest } from "@nylorun/core/define";
|
|
3
3
|
import type { SandboxPodState } from "../store/types.js";
|
|
4
4
|
import { type Lease, type Session, type TenantContext } from "../tenant/context.js";
|
|
@@ -14,7 +14,7 @@ export interface SegmentStart {
|
|
|
14
14
|
/** The record position of `transcript` (`foldTranscriptWithCursor`). */
|
|
15
15
|
readonly cursor: number;
|
|
16
16
|
readonly outcomes: RecordedOutcome[];
|
|
17
|
-
/** The session's sandbox workspace: the session, and the
|
|
17
|
+
/** The session's sandbox workspace: the session, and the Agent whose workspace it is. */
|
|
18
18
|
readonly sandbox: {
|
|
19
19
|
readonly ownerId: string;
|
|
20
20
|
readonly environmentId?: string;
|
|
@@ -24,18 +24,20 @@ export interface SegmentStart {
|
|
|
24
24
|
readonly spec?: SandboxManifest;
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
-
* The
|
|
28
|
-
*
|
|
27
|
+
* The Agent the session runs on, as `TurnStart.agent` states it (Harness API 5): its id and
|
|
28
|
+
* kind, and its sandbox's workspace when the session has a sandbox.
|
|
29
29
|
*/
|
|
30
|
-
readonly
|
|
30
|
+
readonly agent?: TurnAgent;
|
|
31
31
|
/**
|
|
32
|
-
* The
|
|
33
|
-
*
|
|
32
|
+
* The Agent is a pod (F7.2): its engine runs the segment. Its lifecycle state, and whether its
|
|
33
|
+
* new volume is still being seeded from a disk version (R2d step 3).
|
|
34
34
|
*/
|
|
35
35
|
readonly pod?: {
|
|
36
36
|
readonly id: string;
|
|
37
37
|
readonly pod: SandboxPodState;
|
|
38
38
|
readonly seeding?: boolean;
|
|
39
|
+
/** It starts on a new version (track T3) and has not joined yet: a failed start goes back. */
|
|
40
|
+
readonly starting?: boolean;
|
|
39
41
|
};
|
|
40
42
|
/** The turn's span context (R7.4): what `TurnStart.trace` hands the harness. */
|
|
41
43
|
readonly trace?: TraceContext;
|
|
@@ -44,6 +46,8 @@ export interface SegmentStart {
|
|
|
44
46
|
* hands an agent's harness. Absent for a flow, and when the Tenant has no model.
|
|
45
47
|
*/
|
|
46
48
|
readonly model?: ModelDescriptor;
|
|
49
|
+
/** The Agent's memory (track T5): its access, instructions and the index the harness pins. */
|
|
50
|
+
readonly memory?: TurnMemory;
|
|
47
51
|
}
|
|
48
52
|
/** The segment's starting transaction and fold. */
|
|
49
53
|
export declare function startSegment(ctx: TenantContext, lease: Lease,
|
|
@@ -11,6 +11,7 @@ import { isWorkflowManifest } from "../core/flow-host.js";
|
|
|
11
11
|
import { sandboxWorkspaceOf } from "../sandbox/share.js";
|
|
12
12
|
import { ownedTx } from "../store/ownership.js";
|
|
13
13
|
import { harnessOf } from "../tenant/context.js";
|
|
14
|
+
import { repinTurn } from "../tenant/agent-versions.js";
|
|
14
15
|
import { HARNESS_ID } from "../sandbox/placement.js";
|
|
15
16
|
import { checkParity, foldTranscriptWithCursor, leanState, transcriptOf, transcriptUpdates, withTranscript, } from "../tenant/history.js";
|
|
16
17
|
import { usesFixtureModel } from "../tenant/model-setting.js";
|
|
@@ -20,6 +21,8 @@ import { flowLimitsOf } from "../core/limits.js";
|
|
|
20
21
|
import { turnManifestOf } from "../tenant/session.js";
|
|
21
22
|
import { mcpFactsOf } from "../mcp/snapshot.js";
|
|
22
23
|
import { bulkOutcomes } from "./record.js";
|
|
24
|
+
import { renderMemoryIndex } from "../memory/service.js";
|
|
25
|
+
import { memorySettingsOf } from "../memory/tool.js";
|
|
23
26
|
import { traceKey } from "../traces/key.js";
|
|
24
27
|
import { hostTuning } from "../tuning.js";
|
|
25
28
|
import { traceparentOf, turnSpanOf, turnTraceIdOf, } from "../traces/ids.js";
|
|
@@ -31,6 +34,9 @@ options = {}) {
|
|
|
31
34
|
// Read before the transaction: the first read of the Tenant may create the key.
|
|
32
35
|
const key = options.harness ? await traceKey(ctx.store) : undefined;
|
|
33
36
|
const opened = await ownedTx(ctx.store, id, lease.epoch, async (t, current) => {
|
|
37
|
+
// A turn's first segment runs the version its Agent runs now (T3, D73).
|
|
38
|
+
if (options.harness && (await repinTurn(t, current)))
|
|
39
|
+
await t.put("sessions", id, current);
|
|
34
40
|
// A transcript left on our engine's state by an older Runtime is recorded once; another
|
|
35
41
|
// harness's state is its own (R4.2).
|
|
36
42
|
const limits = await readRuntimeLimits(t);
|
|
@@ -52,16 +58,25 @@ options = {}) {
|
|
|
52
58
|
...(current.sandbox === undefined ? {} : { spec: current.sandbox }),
|
|
53
59
|
};
|
|
54
60
|
const turnId = current.activeTurnId;
|
|
61
|
+
const memory = isWorkflowManifest(current.manifest) ? undefined : await turnMemoryOf(t, current, limits);
|
|
55
62
|
return {
|
|
56
63
|
current,
|
|
57
64
|
fixtureModel,
|
|
58
65
|
limits,
|
|
66
|
+
...(memory ? { memory } : {}),
|
|
59
67
|
outcomes: current.checkpoint ? await bulkOutcomes(t, current, current.checkpoint) : [],
|
|
60
68
|
sandbox,
|
|
61
|
-
...(resource ? {
|
|
69
|
+
...(resource ? { agent: agentOf(resource, current.sandbox, await selfWakeOf(t, current)) } : {}),
|
|
62
70
|
...(key && turnId ? { trace: turnContextOf(key, ctx.store.tenantId, current, turnId) } : {}),
|
|
63
71
|
...(resource?.pod
|
|
64
|
-
? {
|
|
72
|
+
? {
|
|
73
|
+
pod: {
|
|
74
|
+
id: resource.id,
|
|
75
|
+
pod: resource.pod,
|
|
76
|
+
...(resource.diskSeed ? { seeding: true } : {}),
|
|
77
|
+
...(resource.startCheck ? { starting: true } : {}),
|
|
78
|
+
},
|
|
79
|
+
}
|
|
65
80
|
: {}),
|
|
66
81
|
};
|
|
67
82
|
});
|
|
@@ -116,20 +131,49 @@ export function buildTurnStart(ctx, segment) {
|
|
|
116
131
|
...(segment.trace ? { trace: segment.trace } : {}),
|
|
117
132
|
limits: harnessLimitsOf(segment.limits),
|
|
118
133
|
...(segment.model && !flow ? { model: segment.model } : {}),
|
|
119
|
-
...(segment.
|
|
134
|
+
...(segment.agent && !flow ? { agent: segment.agent } : {}),
|
|
135
|
+
// The agent manifest version the session pinned, and its harness's own settings (protocol 16).
|
|
136
|
+
...(current.definition === undefined ? {} : { definition: current.definition }),
|
|
137
|
+
...(current.loop === undefined || flow ? {} : { loop: current.loop }),
|
|
138
|
+
...(segment.memory && !flow ? { memory: segment.memory } : {}),
|
|
120
139
|
};
|
|
121
140
|
}
|
|
122
141
|
/**
|
|
123
|
-
* The
|
|
142
|
+
* The Agent's memory as the segment starts (track T5, D72), when the version the session runs sets
|
|
143
|
+
* `harness.memory`: its access (the same in every turn), the agent's instructions, and the index of
|
|
144
|
+
* its memories, newest first, within the Tenant's index limits.
|
|
145
|
+
*/
|
|
146
|
+
async function turnMemoryOf(t, s, limits) {
|
|
147
|
+
const settings = await memorySettingsOf(t, s);
|
|
148
|
+
if (!settings)
|
|
149
|
+
return undefined;
|
|
150
|
+
const { index, truncated } = await renderMemoryIndex(t, s.agentId, limits);
|
|
151
|
+
return {
|
|
152
|
+
access: settings.access ?? "read_write",
|
|
153
|
+
...(settings.instructions === undefined ? {} : { instructions: settings.instructions }),
|
|
154
|
+
index,
|
|
155
|
+
truncated,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The facts of the Agent a session runs on (Harness API 5): its id and kind, and, with the
|
|
124
160
|
* session's sandbox, the workspace its commands start in. The harness words them.
|
|
125
161
|
*/
|
|
126
|
-
function
|
|
162
|
+
function agentOf(resource, spec, selfWake) {
|
|
127
163
|
return {
|
|
128
164
|
id: resource.id,
|
|
129
165
|
kind: resource.kind,
|
|
130
166
|
...(spec === undefined ? {} : { sandbox: { workspace: SANDBOX_WORKSPACE } }),
|
|
167
|
+
...(selfWake ? { selfWake: true } : {}),
|
|
131
168
|
};
|
|
132
169
|
}
|
|
170
|
+
/** Whether the manifest version the session runs sets `runtime.selfWake` (track T4, D77). */
|
|
171
|
+
async function selfWakeOf(t, s) {
|
|
172
|
+
if (!s.definition)
|
|
173
|
+
return false;
|
|
174
|
+
const version = await t.agentManifest(s.definition.name, s.definition.version);
|
|
175
|
+
return version?.body.runtime?.selfWake === true;
|
|
176
|
+
}
|
|
133
177
|
/**
|
|
134
178
|
* The context of turn `turnId`'s span (R7.4): its trace (the caller's when its message carried a
|
|
135
179
|
* `traceparent`) and its span, as the trace projector derives them, with the caller's
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - **Pod sandboxes** (F7.2, `withPodWorkspaces`): a session attached to a pod sandbox has its
|
|
16
16
|
* workspace in the pod, served by the sandbox's engine (its host connection). Its tool calls
|
|
17
17
|
* and disk operations go there whatever serves the other workspaces; with the pod stopped or
|
|
18
|
-
* starting they are `409
|
|
18
|
+
* starting they are `409 agent_unavailable`.
|
|
19
19
|
*/
|
|
20
20
|
import { HarnessApiError } from "@nylorun/core/harness-api";
|
|
21
21
|
import { fromWireAnswer, toWireOp } from "../sandbox/disk-fs.js";
|
|
@@ -57,7 +57,7 @@ export function remoteWorkspace(options) {
|
|
|
57
57
|
: { pod: options.pod, ...(options.minEpoch === undefined ? {} : { minEpoch: options.minEpoch }) };
|
|
58
58
|
const unavailable = (error) => error.pod === undefined
|
|
59
59
|
? fail(503, error.message, { code: "request_rejected" })
|
|
60
|
-
: fail(409, error.message, { code: "
|
|
60
|
+
: fail(409, error.message, { code: "agent_unavailable" });
|
|
61
61
|
/** The harness's answer, or `undefined` when none serves workspaces. */
|
|
62
62
|
const ask = async (method, params, signal) => {
|
|
63
63
|
try {
|
|
@@ -14,4 +14,6 @@ export declare function configForFactory(options: {
|
|
|
14
14
|
delivery?: TenantConfig["delivery"];
|
|
15
15
|
/** The trusted issuers of the identity file (`NYLORUN_IDENTITY_FILE`). */
|
|
16
16
|
issuers?: TenantConfig["issuers"];
|
|
17
|
+
/** The URL clients reach the Runtime API at (`NYLORUN_PUBLIC_URL`). */
|
|
18
|
+
publicUrl?: string;
|
|
17
19
|
}): (id: string) => TenantConfig;
|
package/dist/host/config-for.js
CHANGED
|
@@ -16,6 +16,7 @@ export function configForFactory(options) {
|
|
|
16
16
|
model: options.model ?? { kind: "vault" },
|
|
17
17
|
...(options.delivery ? { delivery: options.delivery } : {}),
|
|
18
18
|
...(options.issuers ? { issuers: options.issuers } : {}),
|
|
19
|
+
...(options.publicUrl ? { publicUrl: options.publicUrl } : {}),
|
|
19
20
|
logger: {
|
|
20
21
|
info: (message, fields) => options.logger.info(message, { tenantId: id, ...fields }),
|
|
21
22
|
warn: (message, fields) => options.logger.warn(message, { tenantId: id, ...fields }),
|
package/dist/host/http.d.ts
CHANGED
|
@@ -44,8 +44,8 @@ export declare function requestHasBody(request: IncomingMessage): boolean;
|
|
|
44
44
|
/** `/health` and `/ready` are polled; the request log leaves them out. */
|
|
45
45
|
export declare function pathnameIsLogged(rawUrl: string | undefined): boolean;
|
|
46
46
|
/**
|
|
47
|
-
* Redact path parameters after `/v1/sessions/` for Host logs,
|
|
48
|
-
*
|
|
47
|
+
* Redact path parameters after `/v1/sessions/` for Host logs, a capability link's token (a
|
|
48
|
+
* credential) after `/v1/artifact-links/`, and a webhook endpoint's id after `/v1/webhooks/`.
|
|
49
49
|
*/
|
|
50
50
|
export declare function redactRoutePath(pathname: string): string;
|
|
51
51
|
/** Constant-time comparison of SHA-256 digests of a presented and a stored secret. */
|
package/dist/host/http.js
CHANGED
|
@@ -136,8 +136,8 @@ export function pathnameIsLogged(rawUrl) {
|
|
|
136
136
|
return pathname !== "/health" && pathname !== "/ready";
|
|
137
137
|
}
|
|
138
138
|
/**
|
|
139
|
-
* Redact path parameters after `/v1/sessions/` for Host logs,
|
|
140
|
-
*
|
|
139
|
+
* Redact path parameters after `/v1/sessions/` for Host logs, a capability link's token (a
|
|
140
|
+
* credential) after `/v1/artifact-links/`, and a webhook endpoint's id after `/v1/webhooks/`.
|
|
141
141
|
*/
|
|
142
142
|
export function redactRoutePath(pathname) {
|
|
143
143
|
const parts = pathname.split("/");
|
|
@@ -147,6 +147,9 @@ export function redactRoutePath(pathname) {
|
|
|
147
147
|
}
|
|
148
148
|
if (parts[1] === "v1" && parts[2] === "artifact-links" && parts.length > 3)
|
|
149
149
|
return "/v1/artifact-links/:token";
|
|
150
|
+
// A webhook endpoint's id is its address: whoever has it may try to deliver (T4).
|
|
151
|
+
if (parts[1] === "v1" && parts[2] === "webhooks" && parts.length > 3)
|
|
152
|
+
return "/v1/webhooks/:endpointId";
|
|
150
153
|
return pathname;
|
|
151
154
|
}
|
|
152
155
|
/** Constant-time comparison of SHA-256 digests of a presented and a stored secret. */
|
package/dist/host/main.js
CHANGED
|
@@ -279,6 +279,7 @@ export async function main() {
|
|
|
279
279
|
logger,
|
|
280
280
|
...(stack.delivery ? { delivery: stack.delivery } : {}),
|
|
281
281
|
...(issuers ? { issuers } : {}),
|
|
282
|
+
...(stack.publicUrl ? { publicUrl: stack.publicUrl } : {}),
|
|
282
283
|
});
|
|
283
284
|
// The process's Durable Session Execution: Restate when its endpoints are set, else the
|
|
284
285
|
// in-process memory execution. An invocation that arrives while the Tenant opens waits for it.
|