@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,4 +1,4 @@
1
- import type { ArtifactKind, ArtifactPage, ModelCallExportPage, ModelCallsPage, EnvironmentPage, ToolCallsPage, SessionManifestView, SessionPage, SessionListItem, SessionUsageTotals, LiveEvent } from "@nylorun/core/contracts";
1
+ import type { ArtifactKind, ArtifactPage, ModelCallExportPage, ModelCallsPage, ToolCallsPage, SessionManifestView, SessionPage, SessionListItem, SessionUsageTotals, LiveEvent } from "@nylorun/core/contracts";
2
2
  export type ReadAccess = {
3
3
  owner?: string;
4
4
  agents?: readonly string[];
@@ -6,7 +6,8 @@ export type ReadAccess = {
6
6
  export type SessionFilters = {
7
7
  agentId?: string;
8
8
  status?: SessionListItem["status"];
9
- environmentId?: string;
9
+ /** Sessions that pinned a version of this agent manifest. */
10
+ manifest?: string;
10
11
  ownerUserId?: string;
11
12
  };
12
13
  export type PageOptions = {
@@ -33,6 +34,5 @@ export interface ReadStore {
33
34
  * for a turn the session never had.
34
35
  */
35
36
  turnEvents(id: string, turnId: string, access: ReadAccess): Promise<LiveEvent[]>;
36
- environments(labels: Record<string, string>, page: PageOptions, access: ReadAccess, grants?: readonly string[]): Promise<EnvironmentPage>;
37
37
  close(): Promise<void>;
38
38
  }
@@ -0,0 +1,27 @@
1
+ import { type AgentEvent, type AgentEventPayload, type AgentEventType } from "@nylorun/core/contracts";
2
+ /** The Agent stream's two statements, in the caller's transaction. */
3
+ export interface AgentRecordWriter {
4
+ /** The seq the Agent's next event takes: one past its last, or 0. */
5
+ nextSeq(agentId: string): Promise<number>;
6
+ insert(row: {
7
+ agentId: string;
8
+ seq: number;
9
+ type: string;
10
+ body: AgentEvent;
11
+ }): Promise<void>;
12
+ }
13
+ export interface AgentAppendInput<T extends AgentEventType> {
14
+ tenantId: string;
15
+ agentId: string;
16
+ time: Date;
17
+ type: T;
18
+ payload: AgentEventPayload<T>;
19
+ /** The Tenant's Runtime limit `recordEntryBytes`, as for a session's events. */
20
+ maxEntryBytes: number;
21
+ }
22
+ /** Builds the event and checks it against the Agent event catalog. */
23
+ export declare function buildAgentEvent<T extends AgentEventType>(input: AgentAppendInput<T> & {
24
+ seq: number;
25
+ }): AgentEvent;
26
+ /** Appends one event to the Agent's stream through `writer`. */
27
+ export declare function appendAgentEvent<T extends AgentEventType>(writer: AgentRecordWriter, input: AgentAppendInput<T>): Promise<AgentEvent>;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * An Agent's lifecycle stream (blueprint D39, F7.1; an Environment's before protocol 16):
3
+ * `agent.created`, `agent.attached`, `agent.detached` and `agent.deleted` (and the rest of
4
+ * `AGENT_EVENT_TYPES`, egress-gate's `agent.egress` for every connection among them, D67), on
5
+ * the `nylorun.agent-event/1` envelope, numbered from 0 per Agent id in
6
+ * `nylorun_streams.agent_events`. The record module is its only writer, as for session events;
7
+ * the store runs the two statements through `AgentRecordWriter` in the caller's transaction, under
8
+ * the Agent row's lock (or, for an Agent being created, its new row).
9
+ *
10
+ * The stream lives in the record only: it is not published to the stream relay, so no S2 stream
11
+ * follows it yet. Clients read it with `GET /v1/agents/{id}/events`.
12
+ */
13
+ import { randomUUID } from "node:crypto";
14
+ import { z } from "zod";
15
+ import { AGENT_EVENT_PAYLOADS, AGENT_EVENT_SCHEMA, AgentEventSchema, } from "@nylorun/core/contracts";
16
+ import { InvalidEventError } from "./envelope.js";
17
+ import { checkEntry } from "./append.js";
18
+ /** Builds the event and checks it against the Agent event catalog. */
19
+ export function buildAgentEvent(input) {
20
+ const payload = AGENT_EVENT_PAYLOADS[input.type];
21
+ const checkedPayload = payload.safeParse(input.payload);
22
+ if (!checkedPayload.success)
23
+ throw new InvalidEventError(input.type, z.prettifyError(checkedPayload.error));
24
+ const event = JSON.parse(JSON.stringify({
25
+ schema: AGENT_EVENT_SCHEMA,
26
+ eventId: randomUUID(),
27
+ tenantId: input.tenantId,
28
+ agentId: input.agentId,
29
+ seq: input.seq,
30
+ time: input.time.toISOString(),
31
+ type: input.type,
32
+ payload: input.payload,
33
+ }));
34
+ const checked = AgentEventSchema.safeParse(event);
35
+ if (!checked.success)
36
+ throw new InvalidEventError(input.type, z.prettifyError(checked.error));
37
+ return event;
38
+ }
39
+ /** Appends one event to the Agent's stream through `writer`. */
40
+ export async function appendAgentEvent(writer, input) {
41
+ checkEntry(`The ${input.type} event's payload`, input.payload, input.maxEntryBytes);
42
+ const seq = await writer.nextSeq(input.agentId);
43
+ const event = buildAgentEvent({ ...input, seq });
44
+ await writer.insert({ agentId: input.agentId, seq, type: input.type, body: event });
45
+ return event;
46
+ }
@@ -9,4 +9,4 @@
9
9
  export { buildEvent, InvalidEventError, type EventInput } from "./envelope.js";
10
10
  export { decodeCursor, encodeCursor } from "./cursor.js";
11
11
  export { RecordEntryTooLargeError, appendEvent, checkEntry, entryBytes, type AppendInput, type RecordWriter, } from "./append.js";
12
- export { appendEnvironmentEvent, buildEnvironmentEvent, type EnvironmentAppendInput, type EnvironmentRecordWriter, } from "./environment.js";
12
+ export { appendAgentEvent, buildAgentEvent, type AgentAppendInput, type AgentRecordWriter, } from "./agent.js";
@@ -9,4 +9,4 @@
9
9
  export { buildEvent, InvalidEventError } from "./envelope.js";
10
10
  export { decodeCursor, encodeCursor } from "./cursor.js";
11
11
  export { RecordEntryTooLargeError, appendEvent, checkEntry, entryBytes, } from "./append.js";
12
- export { appendEnvironmentEvent, buildEnvironmentEvent, } from "./environment.js";
12
+ export { appendAgentEvent, buildAgentEvent, } from "./agent.js";
@@ -32,7 +32,7 @@
32
32
  * which check failed.
33
33
  */
34
34
  import { createHash, timingSafeEqual } from "node:crypto";
35
- import { isEnvironmentId } from "@nylorun/core/contracts";
35
+ import { isAgentId } from "@nylorun/core/contracts";
36
36
  import { recoverLostVolume, runPendingSeed } from "../artifacts/disk.js";
37
37
  import { recordSetup, setupFile, setupPlanOf, setupState, } from "./setup.js";
38
38
  import { mintHostTokens, staleHost, verifyHostToken, } from "../tenant/host-token.js";
@@ -87,7 +87,7 @@ export function hostAuthority(ctx) {
87
87
  return {
88
88
  async join(request) {
89
89
  const { sandboxId, podUid, joinToken, volumeId } = request;
90
- if (!isEnvironmentId(sandboxId) ||
90
+ if (!isAgentId(sandboxId) ||
91
91
  typeof podUid !== "string" ||
92
92
  !POD_UID.test(podUid) ||
93
93
  typeof joinToken !== "string" ||
@@ -136,7 +136,7 @@ export function hostAuthority(ctx) {
136
136
  // A new, empty volume under the same Sandbox: the sandbox's files are gone.
137
137
  const reason = "The sandbox's volume was replaced: its files are gone";
138
138
  await t.updateEnvironmentPod(sandboxId, { observed: "lost", reason, hostEpoch: epoch }, now);
139
- await t.environmentEvent(sandboxId, "environment.lost", { reason });
139
+ await t.agentEvent(sandboxId, "agent.lost", { reason });
140
140
  // Its Sandbox is deleted; only a reset brings the sandbox back.
141
141
  await t.signal({ type: "host.revoked", sandboxId, epoch });
142
142
  await t.signalSandbox(sandboxId, { kind: "reconcile" });
@@ -150,9 +150,9 @@ export function hostAuthority(ctx) {
150
150
  }, now);
151
151
  if (pod.podUid !== podUid) {
152
152
  if (pod.podUid !== undefined)
153
- await t.environmentEvent(sandboxId, "environment.relaunched", { hostEpoch: epoch });
153
+ await t.agentEvent(sandboxId, "agent.relaunched", { hostEpoch: epoch });
154
154
  else
155
- await t.environmentEvent(sandboxId, "environment.running", { hostEpoch: epoch });
155
+ await t.agentEvent(sandboxId, "agent.running", { hostEpoch: epoch });
156
156
  }
157
157
  // The previous host's connection (an older epoch) serves nothing more, on any process.
158
158
  await t.signal({ type: "host.revoked", sandboxId, epoch });
@@ -8,7 +8,7 @@
8
8
  * A session on a pod Environment runs on `sandbox`; every other session (a virtual or `none`
9
9
  * Environment, or none) on `harness-container` (D52). A host the Tenant does not allow is
10
10
  * `409 placement_refused`. A pod Environment needs sandbox pods: without a cluster (`nylorun
11
- * sandbox enable`), or with the sandboxes service not ready, it is `409 environment_unavailable`.
11
+ * sandbox enable`), or with the sandboxes service not ready, it is `409 agent_unavailable`.
12
12
  */
13
13
  import { type EnvironmentKind, type PlacementHost as SandboxPlacementHost } from "@nylorun/core/contracts";
14
14
  import type { TenantSandboxConfig } from "./tenant-config.js";
@@ -8,7 +8,7 @@
8
8
  * A session on a pod Environment runs on `sandbox`; every other session (a virtual or `none`
9
9
  * Environment, or none) on `harness-container` (D52). A host the Tenant does not allow is
10
10
  * `409 placement_refused`. A pod Environment needs sandbox pods: without a cluster (`nylorun
11
- * sandbox enable`), or with the sandboxes service not ready, it is `409 environment_unavailable`.
11
+ * sandbox enable`), or with the sandboxes service not ready, it is `409 agent_unavailable`.
12
12
  */
13
13
  import { DEFAULT_HARNESS, } from "@nylorun/core/contracts";
14
14
  import { fail, failRetryLater } from "../tenant/http.js";
@@ -50,9 +50,9 @@ export async function podsReady(pods) {
50
50
  export async function requirePods(pods, what) {
51
51
  if (!pods)
52
52
  return fail(409, `${what} needs sandbox pods, and this Runtime has no cluster: ${ENABLE_PODS}.`, {
53
- code: "environment_unavailable",
53
+ code: "agent_unavailable",
54
54
  });
55
55
  if (!(await podsReady(pods)))
56
- failRetryLater(`${what} needs sandbox pods, and the sandboxes service is not ready. Check \`nylorun local sandbox status\`.`, "environment_unavailable");
56
+ failRetryLater(`${what} needs sandbox pods, and the sandboxes service is not ready. Check \`nylorun local sandbox status\`.`, "agent_unavailable");
57
57
  return pods;
58
58
  }
@@ -13,14 +13,14 @@
13
13
  * <name>.r<rev>`, so a repeated apply is a no-op at the service, and a Sandbox whose `opId`
14
14
  * is not the row's is applied again.
15
15
  * - **Idle** (`trigger: idle`): no turn on a session that names it, and none ended within its
16
- * `lifecycle.idle` (at most the Tenant's `limits.idle`) → `Suspended` (`environment.suspended`,
16
+ * `lifecycle.idle` (at most the Tenant's `limits.idle`) → `Suspended` (`agent.suspended`,
17
17
  * reason `idle`). The volume is kept; the next turn start asks for `Running` again.
18
18
  * - **Lost**: an incarnation that has run (a pod joined) whose Sandbox is gone or whose volume
19
- * is missing is `lost` (`environment.lost`): its host epoch moves, its Sandbox is deleted, and
19
+ * is missing is `lost` (`agent.lost`): its host epoch moves, its Sandbox is deleted, and
20
20
  * only a reset brings the Environment back. agent-sandbox recreates a deleted claim empty, so
21
21
  * the engine's join also checks the id it keeps on the volume (`join.ts`): a new volume is
22
22
  * lost too. A pod replaced on the same volume is not lost: the new pod's join bumps the epoch
23
- * (`environment.relaunched`).
23
+ * (`agent.relaunched`).
24
24
  * - **Delete** (`desired: deleted`): the Sandbox is deleted, and the row once it is gone.
25
25
  * - **Reset** (`tenant/environments.ts`) bumps the volume generation: a new name, so a new
26
26
  * Sandbox and volume; the old name is `retiring` and deleted here.
@@ -35,7 +35,7 @@
35
35
  *
36
36
  * An unused Environment is deleted by the Tenant sweep (`lifecycle.deleteAfter`), not here.
37
37
  */
38
- import type { EnvironmentEventPayload, EnvironmentEventType } from "@nylorun/core/contracts";
38
+ import type { AgentEventPayload, AgentEventType } from "@nylorun/core/contracts";
39
39
  import type { Environment, SandboxPodPatch, SandboxPodState } from "../../store/types.js";
40
40
  import type { PodStatus } from "./client.js";
41
41
  /**
@@ -79,9 +79,9 @@ export interface PodFacts {
79
79
  readonly now: number;
80
80
  readonly trigger: PodTrigger;
81
81
  }
82
- export interface PodEvent<T extends EnvironmentEventType = EnvironmentEventType> {
82
+ export interface PodEvent<T extends AgentEventType = AgentEventType> {
83
83
  readonly type: T;
84
- readonly payload: EnvironmentEventPayload<T>;
84
+ readonly payload: AgentEventPayload<T>;
85
85
  }
86
86
  export interface PodDecision {
87
87
  /** Apply the row's spec (after `patch`) in this mode; `rotateJoin` mints a new join token. */
@@ -113,7 +113,7 @@ export function decide(facts) {
113
113
  patch.desired = desired;
114
114
  patch.rev = rev;
115
115
  patch.hostEpoch = pod.hostEpoch + 1;
116
- events.push(event("environment.suspended", { reason: "idle" }));
116
+ events.push(event("agent.suspended", { reason: "idle" }));
117
117
  }
118
118
  else
119
119
  arm.push({ timer: "idle", at: due });
@@ -121,14 +121,14 @@ export function decide(facts) {
121
121
  // An incarnation a pod has joined, whose Sandbox or volume is gone: lost.
122
122
  if (pod.podUid !== undefined && (!status.exists || status.deleting || status.volume === "missing")) {
123
123
  const reason = !status.exists
124
- ? "The Environment's pod and volume were deleted outside the Runtime"
124
+ ? "The Agent's pod and volume were deleted outside the Runtime"
125
125
  : status.deleting
126
- ? "The Environment's pod is being deleted outside the Runtime"
127
- : "The Environment's volume is gone";
126
+ ? "The Agent's pod is being deleted outside the Runtime"
127
+ : "The Agent's volume is gone";
128
128
  patch.observed = "lost";
129
129
  patch.reason = reason;
130
130
  patch.hostEpoch = (patch.hostEpoch ?? pod.hostEpoch) + 1;
131
- events.push(event("environment.lost", { reason }));
131
+ events.push(event("agent.lost", { reason }));
132
132
  if (status.exists && !status.deleting)
133
133
  deletes.push(pod.k8sName);
134
134
  if (status.exists)
@@ -184,7 +184,7 @@ export function decide(facts) {
184
184
  `The pod was not ready within ${Math.round(readyDeadlineMs / 1000)} s`;
185
185
  patch.observed = "failed";
186
186
  patch.reason = reason;
187
- events.push(event("environment.failed", { reason }));
187
+ events.push(event("agent.failed", { reason }));
188
188
  return done();
189
189
  }
190
190
  if (pod.observed !== "creating")
@@ -23,6 +23,7 @@ import { recoverLostVolume } from "../../artifacts/disk.js";
23
23
  import { decide } from "./lifecycle.js";
24
24
  import { podLifecycleConfig, podSpecOf } from "./spec.js";
25
25
  import { hostTuning } from "../../tuning.js";
26
+ import { idleMsOf, revertStart } from "../../tenant/agent-versions.js";
26
27
  export async function reconcileSandbox(ctx, sandboxId, trigger) {
27
28
  const pods = ctx.pods;
28
29
  if (!pods || ctx.closed)
@@ -51,7 +52,8 @@ export async function reconcileSandbox(ctx, sandboxId, trigger) {
51
52
  return undefined;
52
53
  }
53
54
  };
54
- const lifecycle = podLifecycleConfig(config, row.recipe);
55
+ // While a start would apply a new version (track T3), it idles at most `tenant.upgradeIdleMs`.
56
+ const lifecycle = { ...podLifecycleConfig(config, row.recipe), idleMs: idleMsOf(config, row) };
55
57
  const decision = decide({
56
58
  row,
57
59
  image: row.applied.image ?? DEFAULT_SANDBOX_IMAGE,
@@ -140,9 +142,15 @@ export async function reconcileSandbox(ctx, sandboxId, trigger) {
140
142
  if (Object.keys(finalPatch).length > 0)
141
143
  await t.updateEnvironmentPod(sandboxId, finalPatch, new Date().toISOString());
142
144
  for (const item of decision.events)
143
- await t.environmentEvent(sandboxId, item.type, item.payload);
145
+ await t.agentEvent(sandboxId, item.type, item.payload);
144
146
  if (epochMoved)
145
147
  await t.signal({ type: "host.revoked", sandboxId, epoch: finalPatch.hostEpoch });
148
+ // A pod that failed to start on a new version goes back to the version it ran (track T3).
149
+ if (finalPatch.observed === "failed" && current.startCheck) {
150
+ const failed = (await t.environment(sandboxId, { lock: true }));
151
+ await revertStart(t, failed, finalPatch.reason ?? "The pod failed", new Date().toISOString());
152
+ return { kind: "changed" };
153
+ }
146
154
  return { kind: "written" };
147
155
  });
148
156
  if (written.kind === "changed")
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Resolves an Environment's recipe (`PUT /v1/environments/{id}`) against its Tenant's limits,
2
+ * Resolves an Agent's recipe (`PUT /v1/agents/{id}`) against its Tenant's limits,
3
3
  * and the sandbox spec a recipe gives a session (`Session.sandbox`) and the pod. Pure: the
4
4
  * caller supplies the configuration. Every problem is reported, not only the first, so one
5
5
  * response lists all of them.
@@ -38,11 +38,11 @@ export type RecipeResolution = {
38
38
  readonly kind: "error";
39
39
  readonly errors: readonly string[];
40
40
  };
41
- /** The recipe `request` asks for an Environment of `kind`, or every reason it cannot have it. */
41
+ /** The recipe `request` asks for an Agent of `kind`, or every reason it cannot have it. */
42
42
  export declare function resolveRecipe(kind: EnvironmentKind, request: RecipeRequest, config: EffectiveSandboxConfig): RecipeResolution;
43
43
  /**
44
44
  * The sandbox spec of a recipe: what a session pins when it opens and the virtual
45
- * workspace runs. Idle is the Environment's `lifecycle.idle`, else the Tenant's.
45
+ * workspace runs. Idle is the Agent's `lifecycle.idle`, else the Tenant's.
46
46
  */
47
47
  export declare function sandboxSpecOf(recipe: EnvironmentRecipe, tenantIdle: string): SandboxManifest;
48
48
  /** The pod's spec of a recipe: its sandbox spec and its volume's size. */
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_HARNESS } from "@nylorun/core/contracts";
2
2
  import { parseSandboxDuration } from "@nylorun/core/define";
3
3
  import { memoryMiB } from "./tenant-config.js";
4
- /** The recipe `request` asks for an Environment of `kind`, or every reason it cannot have it. */
4
+ /** The recipe `request` asks for an Agent of `kind`, or every reason it cannot have it. */
5
5
  export function resolveRecipe(kind, request, config) {
6
6
  const { limits } = config;
7
7
  const errors = [];
@@ -9,7 +9,7 @@ export function resolveRecipe(kind, request, config) {
9
9
  // Any harness id (R4.1): placement decides where it may run, when a session opens.
10
10
  const harness = request.harness ?? DEFAULT_HARNESS;
11
11
  if (pod && harness !== DEFAULT_HARNESS && request.image === undefined)
12
- errors.push(`harness ${harness} needs image: a pod Environment runs its image as the harness.`);
12
+ errors.push(`harness ${harness} needs image: a pod Agent runs its image as the harness.`);
13
13
  const lifecycle = request.lifecycle ?? {};
14
14
  const idle = lifecycle.idle === undefined ? undefined : parseSandboxDuration(lifecycle.idle);
15
15
  if (idle !== undefined && idle > (parseSandboxDuration(limits.idle) ?? Infinity))
@@ -17,7 +17,7 @@ export function resolveRecipe(kind, request, config) {
17
17
  if (kind === "none") {
18
18
  for (const field of ["image", "resources", "storage", "network", "setup", "files"])
19
19
  if (request[field] !== undefined)
20
- errors.push(`${field} is for an Environment with a sandbox: kind none has none.`);
20
+ errors.push(`${field} is for an Agent with a sandbox: kind none has none.`);
21
21
  if (errors.length > 0)
22
22
  return { kind: "error", errors };
23
23
  return {
@@ -26,9 +26,9 @@ export function resolveRecipe(kind, request, config) {
26
26
  };
27
27
  }
28
28
  if (request.image !== undefined && !pod)
29
- errors.push("image is for kind pod: a virtual Environment runs no images.");
29
+ errors.push("image is for kind pod: a virtual Agent runs no images.");
30
30
  if (request.storage !== undefined && !pod)
31
- errors.push("storage is for kind pod: a virtual Environment has no volume.");
31
+ errors.push("storage is for kind pod: a virtual Agent has no volume.");
32
32
  for (const field of ["setup", "files"])
33
33
  if (request[field] !== undefined && request[field].length > 0 && !pod)
34
34
  errors.push(`${field} is for kind pod: it runs when a pod's disk is made or reset.`);
@@ -37,7 +37,7 @@ export function resolveRecipe(kind, request, config) {
37
37
  if (!withinCeiling(host, limits.network))
38
38
  errors.push(`network.allowedHosts includes ${host}, which this Tenant does not allow.`);
39
39
  else if (host.startsWith("*.") && !pod)
40
- errors.push(`network.allowedHosts includes ${host}; a virtual Environment allows exact host names only, such as ${host.slice(2)}.`);
40
+ errors.push(`network.allowedHosts includes ${host}; a virtual Agent allows exact host names only, such as ${host.slice(2)}.`);
41
41
  }
42
42
  const cpus = request.resources?.cpus ?? limits.defaultResources.cpus;
43
43
  const memory = request.resources?.memory === undefined
@@ -79,7 +79,7 @@ function slotsOf(request) {
79
79
  }
80
80
  /**
81
81
  * The sandbox spec of a recipe: what a session pins when it opens and the virtual
82
- * workspace runs. Idle is the Environment's `lifecycle.idle`, else the Tenant's.
82
+ * workspace runs. Idle is the Agent's `lifecycle.idle`, else the Tenant's.
83
83
  */
84
84
  export function sandboxSpecOf(recipe, tenantIdle) {
85
85
  const allow = recipe.network?.allowedHosts ?? [];
@@ -6,6 +6,8 @@ export { SETUP_FAILED };
6
6
  export interface SetupPlan {
7
7
  /** The applied recipe's version: written into the volume's marker. */
8
8
  readonly recipeVersion: number;
9
+ /** The sha256 of the commands and files: a marker with another runs the setup again (T3). */
10
+ readonly setupHash: string;
9
11
  readonly commands: readonly {
10
12
  readonly command: string;
11
13
  readonly cwd: string;
@@ -33,7 +35,7 @@ export interface SetupReport {
33
35
  export declare function parseSetupReport(value: unknown): SetupReport | undefined;
34
36
  /**
35
37
  * Records a failed setup on Environment `id` held at host epoch `epoch` (the reporting engine's):
36
- * the pod is `failed` with a reason that starts `setup:`, and `environment.failed` says which
38
+ * the pod is `failed` with a reason that starts `setup:`, and `agent.failed` says which
37
39
  * command failed and how, with the end of its output (the Tenant's `setupOutputChars`; the reason
38
40
  * quotes `setupReasonChars` of it and `setupCommandExcerptChars` of the command). A report
39
41
  * from an older host, or a success, changes nothing.
@@ -10,19 +10,26 @@
10
10
  * a pod reads the artifact versions and the definition files by hash (Q6) its own recipe names,
11
11
  * and nothing else of the Object store.
12
12
  *
13
+ * The plan's `setupHash` names its commands and files: the engine runs the setup again on a volume
14
+ * whose marker names another (a new version's setup, track T3).
15
+ *
13
16
  * A failed command fails the pod (`observed: failed`, a reason that starts `setup:`, and
14
- * `environment.failed` with the command, its exit code and the end of its output): the turn that
17
+ * `agent.failed` with the command, its exit code and the end of its output), or, on a start on
18
+ * a new version, takes the Agent back to the version it ran (`revertStart`, T3): the turn that
15
19
  * waits for it fails with that reason (`podUnusable`, `tenant/advance.ts`), the failure stays
16
20
  * until a turn start asks the pod to run again (`wantRunning`), and the engine, polling, runs the
17
21
  * setup again then.
18
22
  */
19
23
  import { ENVIRONMENT_SETUP_TIMEOUT_DEFAULT, } from "@nylorun/core/contracts";
24
+ import { createHash } from "node:crypto";
20
25
  import { parseSandboxDuration } from "@nylorun/core/define";
26
+ import { canonical } from "../store/canonical.js";
21
27
  import { contentKey, readManifest } from "../artifacts/folders.js";
22
28
  import { contentTypeFor } from "@nylorun/core/contracts";
23
29
  import { definitionFileKey } from "../tenant/definition-files.js";
24
30
  import { SETUP_FAILED } from "./pods/lifecycle.js";
25
31
  import { readRuntimeLimits } from "../tenant/runtime-limits.js";
32
+ import { revertStart } from "../tenant/agent-versions.js";
26
33
  export { SETUP_FAILED };
27
34
  /** The setup of `recipe`, or undefined when it has neither commands nor files. */
28
35
  export async function setupPlanOf(ctx, t, environment) {
@@ -34,6 +41,7 @@ export async function setupPlanOf(ctx, t, environment) {
34
41
  const timeoutOf = (timeout) => Math.min(parseSandboxDuration(timeout ?? ENVIRONMENT_SETUP_TIMEOUT_DEFAULT), setupCommandMs);
35
42
  return {
36
43
  recipeVersion: environment.appliedVersion,
44
+ setupHash: createHash("sha256").update(canonical({ setup: recipe.setup ?? [], files: recipe.files ?? [] })).digest("hex"),
37
45
  commands: (recipe.setup ?? []).map((command) => ({
38
46
  command: command.command,
39
47
  cwd: command.cwd ?? "/workspace",
@@ -61,7 +69,7 @@ export function parseSetupReport(value) {
61
69
  }
62
70
  /**
63
71
  * Records a failed setup on Environment `id` held at host epoch `epoch` (the reporting engine's):
64
- * the pod is `failed` with a reason that starts `setup:`, and `environment.failed` says which
72
+ * the pod is `failed` with a reason that starts `setup:`, and `agent.failed` says which
65
73
  * command failed and how, with the end of its output (the Tenant's `setupOutputChars`; the reason
66
74
  * quotes `setupReasonChars` of it and `setupCommandExcerptChars` of the command). A report
67
75
  * from an older host, or a success, changes nothing.
@@ -84,8 +92,7 @@ export async function recordSetup(ctx, id, epoch, report) {
84
92
  const what = command === undefined ? "copying the input files failed" : `command ${report.index + 1} (${command.command.slice(0, setupCommandExcerptChars)}) exited ${report.exitCode ?? "abnormally"}`;
85
93
  const tail = output.trim().slice(-setupReasonChars);
86
94
  const reason = `${SETUP_FAILED} ${what}${tail ? `: ${tail}` : ""}`;
87
- await t.updateEnvironmentPod(id, { observed: "failed", reason }, new Date().toISOString());
88
- await t.environmentEvent(id, "environment.failed", {
95
+ await t.agentEvent(id, "agent.failed", {
89
96
  reason,
90
97
  setup: {
91
98
  ...(report.index === undefined ? {} : { index: report.index }),
@@ -93,6 +100,9 @@ export async function recordSetup(ctx, id, epoch, report) {
93
100
  output,
94
101
  },
95
102
  });
103
+ // On a start on a new version (track T3), back to the version it ran; else failed.
104
+ if (!(await revertStart(t, environment, reason, new Date().toISOString())))
105
+ await t.updateEnvironmentPod(id, { observed: "failed", reason }, new Date().toISOString());
96
106
  });
97
107
  if (report.status === "failed")
98
108
  ctx.config.logger.warn("environment setup failed", { environmentId: id, index: report.index, exitCode: report.exitCode });
@@ -0,0 +1,54 @@
1
+ -- Protocol 16: Agents from one manifest (track T2), a fresh start. Agent definitions and
2
+ -- environment manifests cannot become agent manifests (v8) on their own: a definition has no
3
+ -- runtime section, and an Environment held sessions of many agents. So both go, and so do the
4
+ -- Environments with their pods, disks and vaults, as protocol 11 dropped sandboxes and vaults.
5
+ -- Every open session is closed (`protocol_16`): its history stays readable. `environments` is
6
+ -- renamed `agents` (made from a saved agent manifest only) and its lifecycle stream
7
+ -- `agent_events`; model budget scope `agent` is `manifest`, keyed by the manifest's name, so a
8
+ -- budget keeps its meaning, and the usage ledger names the manifest.
9
+ UPDATE "nylorun"."sessions" SET "body" = ("body"::jsonb || '{"closedReason":"protocol_16"}'::jsonb)::json
10
+ WHERE nylorun.doc("body") ->> 'closedReason' IS NULL;--> statement-breakpoint
11
+ DELETE FROM "nylorun"."vaults" WHERE "id" <> 'host';--> statement-breakpoint
12
+ DELETE FROM "nylorun"."vault_idempotency" WHERE "id" NOT LIKE 'host-model%';--> statement-breakpoint
13
+ DELETE FROM "nylorun"."sandboxes";--> statement-breakpoint
14
+ DELETE FROM "nylorun"."sandbox_signals";--> statement-breakpoint
15
+ DELETE FROM "nylorun"."artifacts" WHERE "environment_id" IS NOT NULL;--> statement-breakpoint
16
+ DELETE FROM "nylorun"."environments";--> statement-breakpoint
17
+ DELETE FROM "nylorun"."definition_file_uses";--> statement-breakpoint
18
+ DROP TABLE "nylorun"."definitions";--> statement-breakpoint
19
+ DROP TABLE "nylorun"."environment_manifests";--> statement-breakpoint
20
+ DROP TABLE "nylorun_streams"."environment_events";--> statement-breakpoint
21
+ CREATE TABLE "nylorun_streams"."agent_events" (
22
+ "agent_id" text COLLATE "C" NOT NULL,
23
+ "seq" bigint NOT NULL,
24
+ "type" text NOT NULL,
25
+ "body" json NOT NULL,
26
+ "committed_at" timestamp with time zone DEFAULT now() NOT NULL,
27
+ CONSTRAINT "agent_events_pkey" PRIMARY KEY("agent_id","seq")
28
+ );
29
+ --> statement-breakpoint
30
+ CREATE TABLE "nylorun"."agent_manifests" (
31
+ "name" text COLLATE "C" NOT NULL,
32
+ "version" integer NOT NULL,
33
+ "body" json NOT NULL,
34
+ "hash" text COLLATE "C" NOT NULL,
35
+ "saved_by" json NOT NULL,
36
+ "created_at" text COLLATE "C" NOT NULL,
37
+ CONSTRAINT "agent_manifests_pkey" PRIMARY KEY("name","version")
38
+ );
39
+ --> statement-breakpoint
40
+ ALTER TABLE "nylorun"."environments" RENAME TO "agents";--> statement-breakpoint
41
+ ALTER TABLE "nylorun"."agents" RENAME CONSTRAINT "environments_pkey" TO "agents_pkey";--> statement-breakpoint
42
+ ALTER TABLE "nylorun"."agents" DROP CONSTRAINT "environments_kind_check";--> statement-breakpoint
43
+ ALTER TABLE "nylorun"."agents" ADD CONSTRAINT "agents_kind_check" CHECK ("nylorun"."agents"."kind" IN ('virtual', 'pod', 'none'));--> statement-breakpoint
44
+ ALTER INDEX "nylorun"."environments_manifest" RENAME TO "agents_manifest";--> statement-breakpoint
45
+ ALTER TABLE "nylorun"."agents" ALTER COLUMN "manifest_name" SET NOT NULL;--> statement-breakpoint
46
+ ALTER TABLE "nylorun"."agents" ALTER COLUMN "manifest_version" SET NOT NULL;--> statement-breakpoint
47
+ ALTER TABLE "nylorun"."agents" ALTER COLUMN "manifest_hash" SET NOT NULL;--> statement-breakpoint
48
+ ALTER TABLE "nylorun"."agents" ADD COLUMN "manifest_ref" text NOT NULL;--> statement-breakpoint
49
+ ALTER TABLE "nylorun"."agents" ADD COLUMN "desired_state" text DEFAULT 'on' NOT NULL;--> statement-breakpoint
50
+ ALTER TABLE "nylorun"."model_usage" RENAME COLUMN "agent_id" TO "manifest_name";--> statement-breakpoint
51
+ ALTER INDEX "nylorun"."model_usage_agent" RENAME TO "model_usage_manifest";--> statement-breakpoint
52
+ UPDATE "nylorun"."model_budgets" SET "scope" = 'manifest' WHERE "scope" = 'agent';--> statement-breakpoint
53
+ ALTER TABLE "nylorun"."sessions" ADD COLUMN "manifest_name" text GENERATED ALWAYS AS (nylorun.doc(body)->'definition'->>'name') STORED;--> statement-breakpoint
54
+ CREATE INDEX "sessions_manifest" ON "nylorun"."sessions" USING btree ("manifest_name");
@@ -0,0 +1,7 @@
1
+ -- Track T3, Agents that evolve (D73): an Agent moves between versions of its manifest. While it
2
+ -- is outdated it names the version it moves to (`target_version`); a version that failed to start
3
+ -- is kept so a tracking Agent does not move to it again (`failed_version`); and while a pod starts
4
+ -- on a new version, what it ran before (`start_check`), which a failed start reverts to.
5
+ ALTER TABLE "nylorun"."agents" ADD COLUMN "target_version" integer;--> statement-breakpoint
6
+ ALTER TABLE "nylorun"."agents" ADD COLUMN "failed_version" integer;--> statement-breakpoint
7
+ ALTER TABLE "nylorun"."agents" ADD COLUMN "start_check" json;
@@ -0,0 +1,13 @@
1
+ CREATE TABLE "nylorun"."session_queue" (
2
+ "id" text COLLATE "C" PRIMARY KEY NOT NULL,
3
+ "session_id" text COLLATE "C" NOT NULL,
4
+ "seq" bigint NOT NULL,
5
+ "command" json NOT NULL,
6
+ "input" json NOT NULL,
7
+ "file_artifacts" json NOT NULL,
8
+ "writer" json NOT NULL,
9
+ "trace" json,
10
+ "created_at" text COLLATE "C" NOT NULL
11
+ );
12
+ --> statement-breakpoint
13
+ CREATE UNIQUE INDEX "session_queue_order" ON "nylorun"."session_queue" USING btree ("session_id","seq");
@@ -0,0 +1,41 @@
1
+ CREATE TABLE "nylorun"."agent_triggers" (
2
+ "agent_id" text COLLATE "C" NOT NULL,
3
+ "trigger_id" text COLLATE "C" NOT NULL,
4
+ "kind" text NOT NULL,
5
+ "status" text NOT NULL,
6
+ "paused_reason" text,
7
+ "generation" integer DEFAULT 0 NOT NULL,
8
+ "next_fire_at" text COLLATE "C",
9
+ "last_fire_at" text COLLATE "C",
10
+ "endpoint_id" text COLLATE "C",
11
+ "sealed_secret" json,
12
+ "session_id" text COLLATE "C",
13
+ "note" text,
14
+ "effect_id" text,
15
+ "created_at" text COLLATE "C" NOT NULL,
16
+ "updated_at" text COLLATE "C" NOT NULL,
17
+ CONSTRAINT "agent_triggers_pkey" PRIMARY KEY("agent_id","trigger_id")
18
+ );
19
+ --> statement-breakpoint
20
+ CREATE TABLE "nylorun"."trigger_runs" (
21
+ "id" text COLLATE "C" PRIMARY KEY NOT NULL,
22
+ "agent_id" text COLLATE "C" NOT NULL,
23
+ "trigger_id" text COLLATE "C" NOT NULL,
24
+ "kind" text NOT NULL,
25
+ "run_key" text COLLATE "C" NOT NULL,
26
+ "due_at" text COLLATE "C" NOT NULL,
27
+ "fired_at" text COLLATE "C" NOT NULL,
28
+ "late" boolean DEFAULT false NOT NULL,
29
+ "status" text NOT NULL,
30
+ "reason" text,
31
+ "session_id" text COLLATE "C",
32
+ "turn_id" text COLLATE "C",
33
+ "queued_message_id" text COLLATE "C"
34
+ );
35
+ --> statement-breakpoint
36
+ ALTER TABLE "nylorun"."session_queue" ADD COLUMN "cause" json;--> statement-breakpoint
37
+ CREATE INDEX "agent_triggers_due" ON "nylorun"."agent_triggers" USING btree ("status","next_fire_at");--> statement-breakpoint
38
+ CREATE UNIQUE INDEX "agent_triggers_endpoint" ON "nylorun"."agent_triggers" USING btree ("endpoint_id") WHERE endpoint_id IS NOT NULL;--> statement-breakpoint
39
+ CREATE UNIQUE INDEX "trigger_runs_key" ON "nylorun"."trigger_runs" USING btree ("agent_id","run_key");--> statement-breakpoint
40
+ CREATE INDEX "trigger_runs_agent" ON "nylorun"."trigger_runs" USING btree ("agent_id","id");--> statement-breakpoint
41
+ CREATE INDEX "trigger_runs_fired" ON "nylorun"."trigger_runs" USING btree ("fired_at");
@@ -0,0 +1,30 @@
1
+ CREATE TABLE "nylorun"."memories" (
2
+ "agent_id" text COLLATE "C" NOT NULL,
3
+ "path" text COLLATE "C" NOT NULL,
4
+ "content" text NOT NULL,
5
+ "content_sha256" text NOT NULL,
6
+ "bytes" integer NOT NULL,
7
+ "description" text,
8
+ "version" integer NOT NULL,
9
+ "updated_at" text COLLATE "C" NOT NULL,
10
+ CONSTRAINT "memories_pkey" PRIMARY KEY("agent_id","path")
11
+ );
12
+ --> statement-breakpoint
13
+ CREATE TABLE "nylorun"."memory_versions" (
14
+ "id" text COLLATE "C" PRIMARY KEY NOT NULL,
15
+ "agent_id" text COLLATE "C" NOT NULL,
16
+ "path" text COLLATE "C" NOT NULL,
17
+ "from_path" text COLLATE "C",
18
+ "operation" text NOT NULL,
19
+ "content" text,
20
+ "content_sha256" text,
21
+ "bytes" integer,
22
+ "actor" text NOT NULL,
23
+ "session_id" text COLLATE "C",
24
+ "turn_id" text COLLATE "C",
25
+ "trigger_run_id" text COLLATE "C",
26
+ "created_at" text COLLATE "C" NOT NULL
27
+ );
28
+ --> statement-breakpoint
29
+ CREATE INDEX "memory_versions_agent" ON "nylorun"."memory_versions" USING btree ("agent_id","id");--> statement-breakpoint
30
+ CREATE INDEX "memory_versions_path" ON "nylorun"."memory_versions" USING btree ("agent_id","path","id");
@@ -0,0 +1,4 @@
1
+ ALTER TABLE "nylorun"."model_usage" ADD COLUMN "cost_input_usd" double precision;--> statement-breakpoint
2
+ ALTER TABLE "nylorun"."model_usage" ADD COLUMN "cost_cache_read_usd" double precision;--> statement-breakpoint
3
+ ALTER TABLE "nylorun"."model_usage" ADD COLUMN "cost_cache_write_usd" double precision;--> statement-breakpoint
4
+ ALTER TABLE "nylorun"."model_usage" ADD COLUMN "cost_output_usd" double precision;