@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.
Files changed (160) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +1 -1
  3. package/dist/api/a2a/card.d.ts +4 -3
  4. package/dist/api/a2a/card.js +3 -3
  5. package/dist/api/a2a/routes.js +18 -7
  6. package/dist/api/ag-ui/routes.js +4 -7
  7. package/dist/api/components.d.ts +5854 -2718
  8. package/dist/api/components.js +39 -30
  9. package/dist/api/http/app.js +9 -4
  10. package/dist/api/http/body.d.ts +2 -0
  11. package/dist/api/http/body.js +20 -0
  12. package/dist/api/http/define.d.ts +4 -0
  13. package/dist/api/http/define.js +20 -9
  14. package/dist/api/http/routes/agent-manifests.d.ts +10 -0
  15. package/dist/api/http/routes/agent-manifests.js +135 -0
  16. package/dist/api/http/routes/agents.d.ts +11 -0
  17. package/dist/api/http/routes/agents.js +418 -0
  18. package/dist/api/http/routes/artifacts.js +2 -2
  19. package/dist/api/http/routes/coverage.d.ts +4 -4
  20. package/dist/api/http/routes/coverage.js +12 -6
  21. package/dist/api/http/routes/files.js +1 -1
  22. package/dist/api/http/routes/mcp.js +2 -2
  23. package/dist/api/http/routes/me.d.ts +1 -1
  24. package/dist/api/http/routes/me.js +1 -2
  25. package/dist/api/http/routes/memories.d.ts +9 -0
  26. package/dist/api/http/routes/memories.js +152 -0
  27. package/dist/api/http/routes/reads.d.ts +1 -137
  28. package/dist/api/http/routes/reads.js +31 -12
  29. package/dist/api/http/routes/sessions.js +21 -50
  30. package/dist/api/http/routes/tenant.js +13 -10
  31. package/dist/api/http/routes/triggers.d.ts +10 -0
  32. package/dist/api/http/routes/triggers.js +183 -0
  33. package/dist/api/openapi.js +69 -47
  34. package/dist/artifacts/disk.d.ts +10 -10
  35. package/dist/artifacts/disk.js +27 -27
  36. package/dist/artifacts/quota.d.ts +1 -1
  37. package/dist/artifacts/quota.js +2 -2
  38. package/dist/artifacts/service.js +2 -2
  39. package/dist/core/flow-host.d.ts +0 -1
  40. package/dist/gates/egress-credentials.js +1 -1
  41. package/dist/gates/egress.d.ts +5 -5
  42. package/dist/gates/egress.js +6 -6
  43. package/dist/gates/http-tool.js +2 -2
  44. package/dist/gates/meter.js +22 -5
  45. package/dist/gates/policy.d.ts +8 -3
  46. package/dist/gates/policy.js +7 -2
  47. package/dist/gates/tenant-vaults.d.ts +1 -1
  48. package/dist/harness/pod.d.ts +2 -0
  49. package/dist/harness/setup.js +17 -6
  50. package/dist/harness-api/record.js +11 -0
  51. package/dist/harness-api/server.js +4 -0
  52. package/dist/harness-api/start.d.ts +11 -7
  53. package/dist/harness-api/start.js +49 -5
  54. package/dist/harness-api/workspace.js +2 -2
  55. package/dist/host/config-for.d.ts +2 -0
  56. package/dist/host/config-for.js +1 -0
  57. package/dist/host/http.d.ts +2 -2
  58. package/dist/host/http.js +5 -2
  59. package/dist/host/main.js +1 -0
  60. package/dist/keys/keys.d.ts +15 -4
  61. package/dist/keys/keys.js +16 -0
  62. package/dist/keys/webhooks.d.ts +25 -0
  63. package/dist/keys/webhooks.js +62 -0
  64. package/dist/management-openapi.json +313 -133
  65. package/dist/mcp/connect.d.ts +2 -2
  66. package/dist/mcp/connect.js +3 -3
  67. package/dist/mcp/preview.js +7 -7
  68. package/dist/memory/service.d.ts +97 -0
  69. package/dist/memory/service.js +335 -0
  70. package/dist/memory/tool.d.ts +36 -0
  71. package/dist/memory/tool.js +84 -0
  72. package/dist/model/pi-model.js +7 -0
  73. package/dist/openapi.json +13120 -9034
  74. package/dist/reads/types.d.ts +3 -3
  75. package/dist/record/agent.d.ts +27 -0
  76. package/dist/record/agent.js +46 -0
  77. package/dist/record/index.d.ts +1 -1
  78. package/dist/record/index.js +1 -1
  79. package/dist/sandbox/join.js +5 -5
  80. package/dist/sandbox/placement.d.ts +1 -1
  81. package/dist/sandbox/placement.js +3 -3
  82. package/dist/sandbox/pods/lifecycle.d.ts +6 -6
  83. package/dist/sandbox/pods/lifecycle.js +6 -6
  84. package/dist/sandbox/pods/reconcile.js +10 -2
  85. package/dist/sandbox/resolve.d.ts +3 -3
  86. package/dist/sandbox/resolve.js +7 -7
  87. package/dist/sandbox/setup.d.ts +3 -1
  88. package/dist/sandbox/setup.js +14 -4
  89. package/dist/store/postgres/drizzle/0032_agents.sql +54 -0
  90. package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
  91. package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
  92. package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
  93. package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
  94. package/dist/store/postgres/drizzle/0037_model_usage_cost_split.sql +4 -0
  95. package/dist/store/postgres/drizzle/meta/_journal.json +42 -0
  96. package/dist/store/postgres/reads.js +44 -87
  97. package/dist/store/postgres/record-writer.d.ts +4 -4
  98. package/dist/store/postgres/record-writer.js +9 -9
  99. package/dist/store/postgres/schema.d.ts +1408 -123
  100. package/dist/store/postgres/schema.js +194 -38
  101. package/dist/store/postgres/store.js +329 -79
  102. package/dist/store/postgres/tenant.js +1 -2
  103. package/dist/store/types.d.ts +155 -50
  104. package/dist/store/types.js +0 -1
  105. package/dist/tenant/advance.js +20 -8
  106. package/dist/tenant/agent-manifests.d.ts +72 -0
  107. package/dist/tenant/agent-manifests.js +359 -0
  108. package/dist/tenant/agent-versions.d.ts +126 -0
  109. package/dist/tenant/agent-versions.js +389 -0
  110. package/dist/tenant/agents.d.ts +230 -0
  111. package/dist/tenant/{environments.js → agents.js} +464 -320
  112. package/dist/tenant/auth.d.ts +1 -1
  113. package/dist/tenant/auth.js +3 -3
  114. package/dist/tenant/commands.d.ts +63 -2
  115. package/dist/tenant/commands.js +240 -83
  116. package/dist/tenant/context.d.ts +46 -19
  117. package/dist/tenant/context.js +6 -2
  118. package/dist/tenant/effects.js +7 -16
  119. package/dist/tenant/identity-file.d.ts +6 -6
  120. package/dist/tenant/identity-file.js +12 -10
  121. package/dist/tenant/issuers.js +3 -3
  122. package/dist/tenant/limits.d.ts +0 -3
  123. package/dist/tenant/limits.js +0 -3
  124. package/dist/tenant/memories.d.ts +38 -0
  125. package/dist/tenant/memories.js +119 -0
  126. package/dist/tenant/reset.js +2 -2
  127. package/dist/tenant/run-grants.d.ts +6 -0
  128. package/dist/tenant/run-grants.js +4 -0
  129. package/dist/tenant/runtime.d.ts +1 -1
  130. package/dist/tenant/runtime.js +8 -0
  131. package/dist/tenant/sessions.d.ts +28 -45
  132. package/dist/tenant/sessions.js +85 -208
  133. package/dist/tenant/status.js +6 -7
  134. package/dist/tenant/sweep.js +12 -3
  135. package/dist/tenant/transcript.d.ts +16 -16
  136. package/dist/tenant/triggers.d.ts +143 -0
  137. package/dist/tenant/triggers.js +715 -0
  138. package/dist/tenant/turn-timing.d.ts +11 -1
  139. package/dist/tenant/turn-timing.js +3 -1
  140. package/dist/tenant/types.d.ts +5 -0
  141. package/dist/tenant/worker.d.ts +2 -0
  142. package/dist/tenant/worker.js +5 -0
  143. package/dist/traces/map.d.ts +1 -1
  144. package/dist/traces/map.js +7 -1
  145. package/dist/tuning.d.ts +7 -0
  146. package/dist/tuning.js +19 -0
  147. package/dist/vault/service.d.ts +13 -8
  148. package/dist/vault/service.js +61 -17
  149. package/dist/version.d.ts +1 -1
  150. package/dist/version.js +1 -1
  151. package/package.json +13 -12
  152. package/dist/api/http/routes/environment-manifests.d.ts +0 -9
  153. package/dist/api/http/routes/environment-manifests.js +0 -118
  154. package/dist/api/http/routes/environments.d.ts +0 -11
  155. package/dist/api/http/routes/environments.js +0 -332
  156. package/dist/record/environment.d.ts +0 -27
  157. package/dist/record/environment.js +0 -46
  158. package/dist/tenant/environment-manifests.d.ts +0 -57
  159. package/dist/tenant/environment-manifests.js +0 -208
  160. package/dist/tenant/environments.d.ts +0 -172
@@ -1,118 +1,38 @@
1
1
  /**
2
- * Session and definition resources: `PUT/GET /v1/agents`, `PUT /v1/sessions/:id` (create, or
3
- * find an identical one), the session list and the session view. Reads use the store's typed
4
- * queries; `PUT /v1/sessions/:id` runs in one transaction with the Environment's checks.
2
+ * Session resources: `PUT /v1/sessions/:id` (create, or find an identical one), the session list
3
+ * and the session view. A session runs on one Agent (protocol 16) and pins the manifest version
4
+ * the Agent runs when it opens; each turn takes the version the Agent runs then
5
+ * (`agent-versions.ts`). Reads use the store's typed queries; `PUT /v1/sessions/:id` runs in
6
+ * one transaction with the Agent's checks.
5
7
  */
6
- import { DefinitionDocumentSchema, definitionFilesOf, } from "@nylorun/core/contracts";
7
- import { hashManifest } from "@nylorun/core/compatibility";
8
- import { approvalRefusal, codeToolRefusal } from "@nylorun/core/define";
8
+ import {} from "@nylorun/core/contracts";
9
9
  import { aggregateWaits, isWorkflowManifest } from "../core/flow-host.js";
10
10
  import { canonical } from "../store/canonical.js";
11
- import { sandboxSpecOf } from "../sandbox/resolve.js";
12
- import { declaredSandboxes } from "../sandbox/share.js";
13
- import { effectiveSandboxConfig, readSandboxConfig } from "../sandbox/tenant-config.js";
14
- import { HARNESS_ID, checkPlacement, requirePods } from "../sandbox/placement.js";
15
- import { openEnvironment, recordAttachment } from "./environments.js";
16
- import { harnessOf, } from "./context.js";
11
+ import { readSandboxConfig } from "../sandbox/tenant-config.js";
12
+ import { checkPlacement, requirePods } from "../sandbox/placement.js";
13
+ import { pinOf } from "./agent-versions.js";
14
+ import { openAgent, recordAttachment } from "./agents.js";
15
+ import { harnessOf, reachesAgent, } from "./context.js";
17
16
  import { fail } from "./http.js";
18
17
  import { definitionFileIssues } from "./definition-files.js";
19
- import { limitsInForce, readSkillLimits, readTenantLimits } from "./limits.js";
20
- import { readRuntimeLimits } from "./runtime-limits.js";
21
- import { httpToolTimeoutIssues } from "../gates/http-tool.js";
22
- import { requiresIssues } from "./limits.js";
18
+ import { limitsInForce, readTenantLimits } from "./limits.js";
23
19
  /** A session's creation identity: everything but the request id. */
24
20
  const sessionIdentity = (value) => {
25
21
  const { requestId: _requestId, ...body } = value ?? {};
26
22
  return canonical(body);
27
23
  };
28
- export async function listDefinitions(ctx) {
29
- const definitions = await ctx.store.tx((t) => t.listDefinitions());
30
- return {
31
- agents: definitions.map((d) => ({
32
- agentId: d.manifest.id,
33
- manifest: d.manifest,
34
- manifestHash: d.manifestHash,
35
- implementationVersion: d.implementationVersion,
36
- })),
37
- };
38
- }
39
- /**
40
- * The agents a token caller may see: id, name and description only, never
41
- * instructions, tools or MCP servers, and only the agents allowed.
42
- */
43
- export async function listAgentsPublic(ctx, agents) {
44
- const definitions = await ctx.store.tx((t) => t.listDefinitions());
45
- return {
46
- agents: definitions
47
- .filter((d) => agents === "*" || agents.has(d.manifest.id))
48
- .map((d) => ({
49
- agentId: d.manifest.id,
50
- ...(typeof d.manifest.name === "string" ? { name: d.manifest.name } : {}),
51
- ...(typeof d.manifest.description === "string"
52
- ? { description: d.manifest.description }
53
- : {}),
54
- })),
55
- };
56
- }
57
- export async function putDefinition(ctx, agentId, body, options = {}) {
58
- if (body.manifest.id !== agentId)
59
- fail(400, "Agent id mismatch");
60
- DefinitionDocumentSchema.parse(body.manifest);
61
- // The Runtime runs agents from their manifests alone (track R2): no tool may need it.
62
- const code = codeToolRefusal(body.manifest);
63
- if (code)
64
- fail(400, code);
65
- // Tool approvals are the Tenant's tool policy, decided at the Tool Gate (R5 step 2).
66
- const approval = approvalRefusal(body.manifest);
67
- if (approval)
68
- fail(400, approval);
69
- const declared = declaredSandboxes(body.manifest);
70
- if (declared.length > 0)
71
- fail(400, `${declared.join(", ")} declares a sandbox. Agents no longer declare one: remove .sandbox(), create an Environment with the sandbox (PUT /v1/environments/{id}) and open the session on it, createSession({ environment }). See MIGRATION.md.`);
72
- const definition = {
73
- ...body,
74
- manifestHash: hashManifest(body.manifest),
75
- };
76
- const files = [...definitionFilesOf(body.manifest)];
77
- await ctx.store.tx(async (t) => {
78
- const limits = await readRuntimeLimits(t);
79
- const issues = [
80
- ...httpToolTimeoutIssues(body.manifest, limits.httpToolTimeoutMs),
81
- ...requiresIssues(body.manifest.requires, limits),
82
- ];
83
- if (issues.length > 0)
84
- fail(400, issues.join(" "));
85
- const { totalBytes } = await readSkillLimits(t);
86
- const { missing, oversized } = await definitionFileIssues(t, body.manifest, totalBytes);
87
- if (missing.length > 0)
88
- fail(400, `The definition names ${missing.length} file(s) the Runtime does not hold; upload each with PUT /v1/files/<sha256> first: ${missing.join(", ")}`, { code: "definition_files_missing", details: { missing } });
89
- if (oversized.length > 0)
90
- fail(413, `This Tenant allows a skill's files at most ${totalBytes} bytes together: ${oversized.map((skill) => `'${skill.name}' holds ${skill.bytes}`).join(", ")}`, { code: "limit_exceeded", details: { limitBytes: totalBytes, skills: oversized } });
91
- if (options.dryRun)
92
- return;
93
- await t.put("definitions", agentId, definition);
94
- await t.insertDefinitionFileUses(agentId, definition.manifestHash, files);
95
- });
96
- return {
97
- agentId,
98
- manifestHash: definition.manifestHash,
99
- implementationVersion: body.implementationVersion,
100
- ...(options.dryRun ? { dryRun: true } : {}),
101
- };
102
- }
103
24
  /**
104
- * `access`, when the request acts for a person, limits the list to that person's sessions of
105
- * the agents they may use.
25
+ * `access`, when the request acts for a person, limits the list to that person's sessions on
26
+ * the Agents they may use.
106
27
  */
107
28
  export async function listSessions(ctx, agentId, access) {
108
29
  const sessions = await ctx.store.tx((t) => t.listSessions({
109
30
  ...(agentId === null ? {} : { agentId }),
110
31
  ...(access === undefined ? {} : { ownerUserId: access.owner }),
111
32
  }));
112
- const allowed = access?.agents;
113
33
  return {
114
34
  sessions: sessions
115
- .filter((s) => allowed === undefined || allowed.has(s.agentId))
35
+ .filter((s) => reachesAgent(access, s.agentId))
116
36
  .map((s) => ({
117
37
  id: s.id,
118
38
  agentId: s.agentId,
@@ -123,130 +43,90 @@ export async function listSessions(ctx, agentId, access) {
123
43
  };
124
44
  }
125
45
  /**
126
- * Create a session from its definition, or find an identical one. `access`, when the request
127
- * acts for a subject, must be the session's owner: another owner's session id is the 404 of a
128
- * missing one (not the 409 of a mismatch), and so is an Environment whose owner guard names
129
- * another owner.
46
+ * Create a session on an Agent, or find an identical one. `access`, when the request acts for a
47
+ * subject, must be the session's owner: another owner's session id is the 404 of a missing one
48
+ * (not the 409 of a mismatch), and so is an Agent whose owner guard names another owner.
130
49
  *
131
- * The session's Environment is fixed for its life: its sandbox (kinds `virtual` and `pod`) is
132
- * the session's, with the spec of the Environment's applied recipe pinned now, and its vault
133
- * holds the session's credentials. Without one the session has no sandbox and no credentials.
50
+ * The session runs on its Agent for its life: the Agent's sandbox (kinds `virtual` and `pod`) is
51
+ * the session's, with the spec of what the Agent runs pinned now, and the Agent's vault holds the
52
+ * session's credentials. It pins the manifest version the Agent runs (`definition`), as the loop
53
+ * manifest that version's harness section makes, and the harness that section names (`pinOf`);
54
+ * a turn's first segment pins the version the Agent runs then (`repinTurn`).
134
55
  */
135
56
  export async function putSession(ctx, id, body, access, options = {}) {
136
57
  const opaque = access !== undefined;
137
58
  if (opaque && body.ownerUserId !== access.owner)
138
59
  fail(403, "ownerUserId must be the subject");
139
- // An agent the subject may not use is the 404 of a missing definition.
140
- if (access?.agents !== undefined && !access.agents.has(body.agentId))
141
- fail(404, "Definition not found");
142
- return ctx.store.tx(async (t) => {
143
- const prior = await t.lockSession(id);
144
- if (opaque &&
145
- prior &&
146
- (prior.ownerUserId !== access.owner ||
147
- (access.agents !== undefined && !access.agents.has(prior.agentId))))
148
- fail(404, "Session not found");
149
- if (prior && options.createOnly) {
150
- if (prior.ownerUserId !== body.ownerUserId ||
151
- prior.agentId !== body.agentId)
152
- fail(404, "Session not found");
153
- return prior;
154
- }
155
- if (prior) {
156
- if (sessionIdentity(prior.creation) !== sessionIdentity(body))
157
- fail(409, "Session already exists with different creation parameters");
158
- return prior;
159
- }
160
- const definition = (await t.get("definitions", body.agentId)) ??
161
- fail(404, "Definition not found");
162
- const environment = body.environment === undefined
163
- ? undefined
164
- : await openEnvironment(t, body.environment, {
165
- ...(options.environmentGrants === undefined ? {} : { grants: options.environmentGrants }),
166
- ownerUserId: body.ownerUserId,
167
- opaque,
168
- });
169
- if (!isWorkflowManifest(definition.manifest))
170
- await checkRequirements(t, definition.manifest, environment);
171
- if (environment !== undefined)
172
- await checkSkillLimit(t, definition.manifest, environment);
173
- const config = await readSandboxConfig(t);
174
- // The harness the session runs for its life (R4.1): its Environment's, else ours. A flow
175
- // runs in core and our engine, whatever its Environment names.
176
- const harness = isWorkflowManifest(definition.manifest) || environment === undefined
177
- ? HARNESS_ID
178
- : environment.applied.harness;
179
- // Placement (D38, D52): where this session's harness may run, decided now and kept.
180
- checkPlacement(config, environment?.kind, harness);
181
- if (environment?.kind === "pod")
182
- await requirePods(ctx.pods, `Environment ${environment.id}`);
183
- const spec = environment === undefined || environment.kind === "none"
184
- ? undefined
185
- : sandboxSpecOf(environment.applied, effectiveSandboxConfig(config).limits.idle);
186
- // The session pins its definition as registered (protocol 15): the sandbox's spec stays on
187
- // the session, and the harness words the facts and defines the tools.
188
- const created = {
189
- id,
190
- agentId: body.agentId,
191
- ownerUserId: body.ownerUserId,
192
- manifest: definition.manifest,
193
- manifestHash: definition.manifestHash,
194
- definitionHash: definition.manifestHash,
195
- implementationVersion: definition.implementationVersion,
196
- info: body.info,
197
- status: "idle",
198
- activeTurnId: null,
199
- creation: body,
200
- harness,
201
- ...(environment === undefined
202
- ? {}
203
- : { environmentId: environment.id, environmentVersion: environment.version }),
204
- ...(spec === undefined ? {} : { sandbox: spec }),
205
- };
206
- await t.put("sessions", id, created);
207
- if (environment !== undefined)
208
- await recordAttachment(t, id, environment.id);
209
- return created;
210
- });
60
+ // An Agent the subject may not use is the 404 of a missing one.
61
+ if (!reachesAgent(access, body.agent))
62
+ fail(404, "Agent not found");
63
+ return ctx.store.tx((t) => openSessionIn(ctx, t, id, body, access, options));
211
64
  }
212
65
  /**
213
- * Refuses a session whose agent requires what its Environment lacks (protocol 14, D65): a
214
- * sandbox (`any`: kind `virtual` or `pod`), a pod, or a secret its vault does not hold. Every
215
- * gap is named in one `409 environment_unsuitable`.
66
+ * `putSession`'s work in transaction `t`: also how a trigger's first run opens the session it
67
+ * names (track T4), in the run's transaction.
216
68
  */
217
- async function checkRequirements(t, manifest, environment) {
218
- const requires = manifest.requires;
219
- if (requires === undefined)
220
- return;
221
- const missing = [];
222
- const kind = environment?.kind;
223
- if (requires.sandbox === "any" && kind !== "virtual" && kind !== "pod")
224
- missing.push("sandbox");
225
- if (requires.sandbox === "pod" && kind !== "pod")
226
- missing.push("pod");
227
- if (requires.secrets?.length) {
228
- const vault = environment === undefined ? undefined : await t.vaultOfEnvironment(environment.id);
229
- const held = new Set(vault ? (await t.credentialsForVault(vault.id)).map((row) => row.name) : []);
230
- for (const name of requires.secrets)
231
- if (!held.has(name))
232
- missing.push(`secret:${name}`);
69
+ export async function openSessionIn(ctx, t, id, body, access, options = {}) {
70
+ const opaque = access !== undefined;
71
+ const prior = await t.lockSession(id);
72
+ if (opaque && prior && (prior.ownerUserId !== access.owner || !reachesAgent(access, prior.agentId)))
73
+ fail(404, "Session not found");
74
+ if (prior && options.createOnly) {
75
+ if (prior.ownerUserId !== body.ownerUserId || prior.agentId !== body.agent)
76
+ fail(404, "Session not found");
77
+ return prior;
233
78
  }
234
- if (missing.length === 0)
235
- return;
236
- const where = environment === undefined ? "the session names no Environment" : `Environment ${environment.id} lacks it`;
237
- fail(409, `Agent ${manifest.id} requires ${missing.map((gap) => (gap.startsWith("secret:") ? `the secret ${gap.slice(7)}` : gap === "pod" ? "a pod Environment" : "a sandbox")).join(", ")}, and ${where}.`, { code: "environment_unsuitable", details: { missing } });
79
+ if (prior) {
80
+ if (sessionIdentity(prior.creation) !== sessionIdentity(body))
81
+ fail(409, "Session already exists with different creation parameters");
82
+ return prior;
83
+ }
84
+ const agent = await openAgent(t, body.agent, {
85
+ ...(options.agentGrants === undefined ? {} : { grants: options.agentGrants }),
86
+ ownerUserId: body.ownerUserId,
87
+ opaque,
88
+ });
89
+ // The version the Agent runs, both sections (T3): its loop manifest, harness and sandbox spec.
90
+ const { pin } = await pinOf(t, agent);
91
+ await checkSkillLimit(t, pin.manifest, agent);
92
+ const config = await readSandboxConfig(t);
93
+ // Placement (D38, D52): where this session's harness may run, decided now and kept.
94
+ checkPlacement(config, agent.kind, pin.harness);
95
+ if (agent.kind === "pod")
96
+ await requirePods(ctx.pods, `Agent ${agent.id}`);
97
+ const created = {
98
+ id,
99
+ agentId: agent.id,
100
+ ownerUserId: body.ownerUserId,
101
+ manifest: pin.manifest,
102
+ manifestHash: pin.manifestHash,
103
+ definition: { ...pin.definition },
104
+ ...(pin.loop === undefined ? {} : { loop: pin.loop }),
105
+ info: body.info,
106
+ status: "idle",
107
+ activeTurnId: null,
108
+ creation: body,
109
+ harness: pin.harness,
110
+ environmentId: agent.id,
111
+ environmentVersion: agent.version,
112
+ ...(pin.sandbox === undefined ? {} : { sandbox: pin.sandbox }),
113
+ };
114
+ await t.put("sessions", id, created);
115
+ await recordAttachment(t, id, agent.id);
116
+ return created;
238
117
  }
239
118
  /**
240
- * Refuses a session whose agent carries a skill larger than its Environment allows: its
241
- * manifest's `limits.skills.totalBytes`, else the Tenant's, never above the Tenant's.
242
- * `409 environment_unsuitable`, with each skill and its size in `details.skills`.
119
+ * Refuses a session whose Agent's manifest carries a skill larger than the Agent allows now: its
120
+ * manifest's `limits.skills.totalBytes`, else the Tenant's, never above the Tenant's, which may
121
+ * have been lowered since the manifest was saved. `413 limit_exceeded`, with each skill and its
122
+ * size in `details.skills`.
243
123
  */
244
- async function checkSkillLimit(t, manifest, environment) {
245
- const limitBytes = limitsInForce(await readTenantLimits(t), environment.applied.limits).skills.totalBytes;
124
+ async function checkSkillLimit(t, manifest, agent) {
125
+ const limitBytes = limitsInForce(await readTenantLimits(t), agent.applied.limits).skills.totalBytes;
246
126
  const { oversized } = await definitionFileIssues(t, manifest, limitBytes);
247
127
  if (oversized.length === 0)
248
128
  return;
249
- fail(409, `Environment ${environment.id} allows a skill's files at most ${limitBytes} bytes together, and agent ${manifest.id} carries ${oversized.map((skill) => `'${skill.name}' (${skill.bytes})`).join(", ")}.`, { code: "environment_unsuitable", details: { limitBytes, skills: oversized } });
129
+ fail(413, `Agent ${agent.id} allows a skill's files at most ${limitBytes} bytes together, and its manifest carries ${oversized.map((skill) => `'${skill.name}' (${skill.bytes})`).join(", ")}.`, { code: "limit_exceeded", details: { limitBytes, skills: oversized } });
250
130
  }
251
131
  /** The session resource returned by `GET`/`PUT /v1/sessions/:id`. */
252
132
  export async function sessionView(t, s) {
@@ -270,14 +150,11 @@ export async function sessionView(t, s) {
270
150
  id: s.id,
271
151
  agentId: s.agentId,
272
152
  ownerUserId: s.ownerUserId,
153
+ manifest: s.definition ?? null,
273
154
  manifestHash: s.manifestHash,
274
- ...(s.definitionHash === undefined ? {} : { definitionHash: s.definitionHash }),
275
- implementationVersion: s.implementationVersion,
276
155
  status: s.status,
277
156
  activeTurnId: s.activeTurnId,
278
- environment: s.environmentId === undefined
279
- ? null
280
- : { id: s.environmentId, version: s.environmentVersion ?? 1 },
157
+ queuedMessages: await t.countQueuedMessages(s.id),
281
158
  harness: harnessOf(s),
282
159
  ...(s.closedReason === undefined ? {} : { closedReason: s.closedReason }),
283
160
  mcpSnapshot: s.mcpSnapshot ?? null,
@@ -47,9 +47,9 @@ async function executionStatus(stuckInvocations) {
47
47
  /** Build `TenantStatusSchema` with secrets redacted (A14). */
48
48
  export async function buildTenantStatus(ctx) {
49
49
  const health = await ctx.store.health();
50
- const { counts, definitions } = await ctx.store.tx(async (t) => ({
50
+ const { counts, rows } = await ctx.store.tx(async (t) => ({
51
51
  counts: await t.counts(),
52
- definitions: await t.listDefinitions(),
52
+ rows: await t.listEnvironments(),
53
53
  }));
54
54
  const [modelView, sandboxReport, execution, streams] = await Promise.all([
55
55
  ctx.vault.getHostModel(),
@@ -57,11 +57,10 @@ export async function buildTenantStatus(ctx) {
57
57
  ctx.stuckInvocations ? executionStatus(ctx.stuckInvocations) : undefined,
58
58
  ctx.streamsStatus?.(),
59
59
  ]);
60
- const agents = [
61
- ...new Set(definitions
62
- .map((d) => d.manifest?.id)
63
- .filter((id) => typeof id === "string")),
64
- ]
60
+ // The Tenant's Agents; a pod Agent being deleted is none.
61
+ const agents = rows
62
+ .filter((row) => row.pod?.desired !== "deleted")
63
+ .map((row) => row.id)
65
64
  .sort()
66
65
  .map((agentId) => ({ agentId }));
67
66
  return {
@@ -33,7 +33,9 @@
33
33
  * workspaces). Idle MCP connections of the in-process harness are closed. As often as the
34
34
  * orphan scan, pod Environments whose change stalled get a reconcile (`reconcileStalePods`).
35
35
  * Environments unused for their `lifecycle.deleteAfter` are deleted
36
- * (`sweepUnusedEnvironments`).
36
+ * (`sweepUnusedAgents`).
37
+ * Triggers (track T4, `triggers.ts`): on this process's first pass every active trigger's
38
+ * timer is armed, later overdue ones only, and runs past `triggerRunRetentionMs` are deleted.
37
39
  * 5. **Control signals.** Signals on the control bus older than `sweep.signalRetentionMs` are
38
40
  * deleted: followers read back only the last two minutes (`store/postgres/control.ts`).
39
41
  * 6. **Hooks.** Callbacks registered with `ctx.onSweep`.
@@ -43,10 +45,12 @@
43
45
  * step does not stop the others; the first error is rethrown at the end so the execution
44
46
  * reports it.
45
47
  */
46
- import { sweepUnusedEnvironments } from "./environments.js";
48
+ import { sweepUnusedAgents } from "./agents.js";
49
+ import { reconcileTriggers } from "./triggers.js";
47
50
  import { sweepPendingSeeds } from "../artifacts/disk.js";
48
51
  import { pendingAgentEffects, reconcilePendingAgentEffect, } from "../core/flow-host.js";
49
52
  import { hostTuning } from "../tuning.js";
53
+ import { sweepStartChecks } from "./agent-versions.js";
50
54
  /*
51
55
  * The sweep's timings are the Host tuning's `sweep` group (`tuning.ts`):
52
56
  * - `outboxGraceMs`: how old an outbox row is before the sweep delivers it, unless its delivery
@@ -113,7 +117,12 @@ export async function sweep(ctx) {
113
117
  ]
114
118
  : []),
115
119
  // Environments unused for their `lifecycle.deleteAfter`: one query when none is due.
116
- ["environments", () => sweepUnusedEnvironments(ctx, now.getTime())],
120
+ ["agents", () => sweepUnusedAgents(ctx, now.getTime())],
121
+ // Pods slower than `tenant.upgradeStartMs` to start on a new version go back (track T3).
122
+ ["starts", () => sweepStartChecks(ctx, now.getTime())],
123
+ // Triggers (track T4): every active one armed on this process's first pass, overdue ones after;
124
+ // runs past their retention deleted.
125
+ ["triggers", () => reconcileTriggers(ctx, first)],
117
126
  // Seeds of new volumes whose engine is connected and no process copies (R2d step 3).
118
127
  ["seeds", () => sweepPendingSeeds(ctx)],
119
128
  // A harness elsewhere closes its own idle MCP connections.
@@ -192,7 +192,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
192
192
  redacted: number;
193
193
  toolName: string;
194
194
  status?: "cancelled" | "completed" | "failed" | undefined;
195
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
195
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
196
196
  server?: string | undefined;
197
197
  tool?: string | undefined;
198
198
  skill?: string | undefined;
@@ -214,7 +214,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
214
214
  redacted: number;
215
215
  toolName: string;
216
216
  status?: "cancelled" | "completed" | "failed" | undefined;
217
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
217
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
218
218
  server?: string | undefined;
219
219
  tool?: string | undefined;
220
220
  skill?: string | undefined;
@@ -236,7 +236,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
236
236
  redacted?: undefined;
237
237
  toolName: string;
238
238
  status?: "cancelled" | "completed" | "failed" | undefined;
239
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
239
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
240
240
  server?: string | undefined;
241
241
  tool?: string | undefined;
242
242
  skill?: string | undefined;
@@ -258,7 +258,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
258
258
  redacted?: undefined;
259
259
  toolName: string;
260
260
  status?: "cancelled" | "completed" | "failed" | undefined;
261
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
261
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
262
262
  server?: string | undefined;
263
263
  tool?: string | undefined;
264
264
  skill?: string | undefined;
@@ -275,7 +275,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
275
275
  redacted: number;
276
276
  toolName: string;
277
277
  status?: "cancelled" | "completed" | "failed" | undefined;
278
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
278
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
279
279
  server?: string | undefined;
280
280
  tool?: string | undefined;
281
281
  skill?: string | undefined;
@@ -292,7 +292,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
292
292
  redacted: number;
293
293
  toolName: string;
294
294
  status?: "cancelled" | "completed" | "failed" | undefined;
295
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
295
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
296
296
  server?: string | undefined;
297
297
  tool?: string | undefined;
298
298
  skill?: string | undefined;
@@ -309,7 +309,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
309
309
  redacted?: undefined;
310
310
  toolName: string;
311
311
  status?: "cancelled" | "completed" | "failed" | undefined;
312
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
312
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
313
313
  server?: string | undefined;
314
314
  tool?: string | undefined;
315
315
  skill?: string | undefined;
@@ -326,7 +326,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
326
326
  redacted?: undefined;
327
327
  toolName: string;
328
328
  status?: "cancelled" | "completed" | "failed" | undefined;
329
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
329
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
330
330
  server?: string | undefined;
331
331
  tool?: string | undefined;
332
332
  skill?: string | undefined;
@@ -350,7 +350,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
350
350
  redacted: number;
351
351
  toolName: string;
352
352
  status?: "cancelled" | "completed" | "failed" | undefined;
353
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
353
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
354
354
  server?: string | undefined;
355
355
  tool?: string | undefined;
356
356
  skill?: string | undefined;
@@ -374,7 +374,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
374
374
  redacted: number;
375
375
  toolName: string;
376
376
  status?: "cancelled" | "completed" | "failed" | undefined;
377
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
377
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
378
378
  server?: string | undefined;
379
379
  tool?: string | undefined;
380
380
  skill?: string | undefined;
@@ -398,7 +398,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
398
398
  redacted?: undefined;
399
399
  toolName: string;
400
400
  status?: "cancelled" | "completed" | "failed" | undefined;
401
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
401
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
402
402
  server?: string | undefined;
403
403
  tool?: string | undefined;
404
404
  skill?: string | undefined;
@@ -422,7 +422,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
422
422
  redacted?: undefined;
423
423
  toolName: string;
424
424
  status?: "cancelled" | "completed" | "failed" | undefined;
425
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
425
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
426
426
  server?: string | undefined;
427
427
  tool?: string | undefined;
428
428
  skill?: string | undefined;
@@ -441,7 +441,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
441
441
  redacted: number;
442
442
  toolName: string;
443
443
  status?: "cancelled" | "completed" | "failed" | undefined;
444
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
444
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
445
445
  server?: string | undefined;
446
446
  tool?: string | undefined;
447
447
  skill?: string | undefined;
@@ -460,7 +460,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
460
460
  redacted: number;
461
461
  toolName: string;
462
462
  status?: "cancelled" | "completed" | "failed" | undefined;
463
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
463
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
464
464
  server?: string | undefined;
465
465
  tool?: string | undefined;
466
466
  skill?: string | undefined;
@@ -479,7 +479,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
479
479
  redacted?: undefined;
480
480
  toolName: string;
481
481
  status?: "cancelled" | "completed" | "failed" | undefined;
482
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
482
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
483
483
  server?: string | undefined;
484
484
  tool?: string | undefined;
485
485
  skill?: string | undefined;
@@ -498,7 +498,7 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
498
498
  redacted?: undefined;
499
499
  toolName: string;
500
500
  status?: "cancelled" | "completed" | "failed" | undefined;
501
- kind?: "harness" | "http" | "mcp" | "sandbox" | undefined;
501
+ kind?: "harness" | "http" | "mcp" | "runtime" | "sandbox" | undefined;
502
502
  server?: string | undefined;
503
503
  tool?: string | undefined;
504
504
  skill?: string | undefined;