@nylorun/runtime 0.27.0-beta → 0.28.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/api/a2a/card.d.ts +4 -3
  3. package/dist/api/a2a/card.js +3 -3
  4. package/dist/api/a2a/routes.js +18 -7
  5. package/dist/api/ag-ui/routes.js +4 -7
  6. package/dist/api/components.d.ts +5823 -2720
  7. package/dist/api/components.js +39 -30
  8. package/dist/api/http/app.js +9 -4
  9. package/dist/api/http/body.d.ts +2 -0
  10. package/dist/api/http/body.js +20 -0
  11. package/dist/api/http/routes/agent-manifests.d.ts +10 -0
  12. package/dist/api/http/routes/agent-manifests.js +135 -0
  13. package/dist/api/http/routes/agents.d.ts +11 -0
  14. package/dist/api/http/routes/agents.js +418 -0
  15. package/dist/api/http/routes/artifacts.js +2 -2
  16. package/dist/api/http/routes/coverage.d.ts +4 -4
  17. package/dist/api/http/routes/coverage.js +12 -6
  18. package/dist/api/http/routes/files.js +1 -1
  19. package/dist/api/http/routes/mcp.js +2 -2
  20. package/dist/api/http/routes/me.d.ts +1 -1
  21. package/dist/api/http/routes/me.js +1 -2
  22. package/dist/api/http/routes/memories.d.ts +9 -0
  23. package/dist/api/http/routes/memories.js +152 -0
  24. package/dist/api/http/routes/reads.d.ts +1 -137
  25. package/dist/api/http/routes/reads.js +2 -8
  26. package/dist/api/http/routes/sessions.js +21 -50
  27. package/dist/api/http/routes/tenant.js +8 -8
  28. package/dist/api/http/routes/triggers.d.ts +10 -0
  29. package/dist/api/http/routes/triggers.js +180 -0
  30. package/dist/api/openapi.js +69 -47
  31. package/dist/artifacts/disk.d.ts +10 -10
  32. package/dist/artifacts/disk.js +27 -27
  33. package/dist/artifacts/quota.d.ts +1 -1
  34. package/dist/artifacts/quota.js +2 -2
  35. package/dist/artifacts/service.js +2 -2
  36. package/dist/core/flow-host.d.ts +0 -1
  37. package/dist/gates/egress-credentials.js +1 -1
  38. package/dist/gates/egress.d.ts +5 -5
  39. package/dist/gates/egress.js +6 -6
  40. package/dist/gates/http-tool.js +1 -1
  41. package/dist/gates/meter.js +6 -5
  42. package/dist/gates/policy.d.ts +8 -3
  43. package/dist/gates/policy.js +7 -2
  44. package/dist/harness/pod.d.ts +2 -0
  45. package/dist/harness/setup.js +17 -6
  46. package/dist/harness-api/record.js +11 -0
  47. package/dist/harness-api/server.js +4 -0
  48. package/dist/harness-api/start.d.ts +11 -7
  49. package/dist/harness-api/start.js +49 -5
  50. package/dist/harness-api/workspace.js +2 -2
  51. package/dist/host/http.d.ts +2 -2
  52. package/dist/host/http.js +5 -2
  53. package/dist/keys/keys.d.ts +15 -4
  54. package/dist/keys/keys.js +16 -0
  55. package/dist/keys/webhooks.d.ts +25 -0
  56. package/dist/keys/webhooks.js +62 -0
  57. package/dist/management-openapi.json +245 -123
  58. package/dist/mcp/preview.js +7 -7
  59. package/dist/memory/service.d.ts +97 -0
  60. package/dist/memory/service.js +335 -0
  61. package/dist/memory/tool.d.ts +36 -0
  62. package/dist/memory/tool.js +84 -0
  63. package/dist/openapi.json +12837 -8790
  64. package/dist/reads/types.d.ts +3 -3
  65. package/dist/record/agent.d.ts +27 -0
  66. package/dist/record/agent.js +46 -0
  67. package/dist/record/index.d.ts +1 -1
  68. package/dist/record/index.js +1 -1
  69. package/dist/sandbox/join.js +5 -5
  70. package/dist/sandbox/placement.d.ts +1 -1
  71. package/dist/sandbox/placement.js +3 -3
  72. package/dist/sandbox/pods/lifecycle.d.ts +6 -6
  73. package/dist/sandbox/pods/lifecycle.js +6 -6
  74. package/dist/sandbox/pods/reconcile.js +10 -2
  75. package/dist/sandbox/resolve.d.ts +3 -3
  76. package/dist/sandbox/resolve.js +7 -7
  77. package/dist/sandbox/setup.d.ts +3 -1
  78. package/dist/sandbox/setup.js +14 -4
  79. package/dist/store/postgres/drizzle/0032_agents.sql +54 -0
  80. package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
  81. package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
  82. package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
  83. package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
  84. package/dist/store/postgres/drizzle/meta/_journal.json +35 -0
  85. package/dist/store/postgres/reads.js +13 -86
  86. package/dist/store/postgres/record-writer.d.ts +4 -4
  87. package/dist/store/postgres/record-writer.js +9 -9
  88. package/dist/store/postgres/schema.d.ts +1373 -161
  89. package/dist/store/postgres/schema.js +184 -37
  90. package/dist/store/postgres/store.js +329 -79
  91. package/dist/store/postgres/tenant.js +1 -2
  92. package/dist/store/types.d.ts +155 -50
  93. package/dist/store/types.js +0 -1
  94. package/dist/tenant/advance.js +20 -8
  95. package/dist/tenant/agent-manifests.d.ts +72 -0
  96. package/dist/tenant/agent-manifests.js +359 -0
  97. package/dist/tenant/agent-versions.d.ts +126 -0
  98. package/dist/tenant/agent-versions.js +389 -0
  99. package/dist/tenant/agents.d.ts +230 -0
  100. package/dist/tenant/{environments.js → agents.js} +464 -320
  101. package/dist/tenant/auth.d.ts +1 -1
  102. package/dist/tenant/auth.js +3 -3
  103. package/dist/tenant/commands.d.ts +63 -2
  104. package/dist/tenant/commands.js +240 -83
  105. package/dist/tenant/context.d.ts +46 -19
  106. package/dist/tenant/context.js +6 -2
  107. package/dist/tenant/effects.js +7 -16
  108. package/dist/tenant/identity-file.d.ts +6 -6
  109. package/dist/tenant/identity-file.js +12 -10
  110. package/dist/tenant/issuers.js +3 -3
  111. package/dist/tenant/limits.d.ts +0 -3
  112. package/dist/tenant/limits.js +0 -3
  113. package/dist/tenant/memories.d.ts +38 -0
  114. package/dist/tenant/memories.js +119 -0
  115. package/dist/tenant/reset.js +2 -2
  116. package/dist/tenant/run-grants.d.ts +6 -0
  117. package/dist/tenant/run-grants.js +4 -0
  118. package/dist/tenant/runtime.d.ts +1 -1
  119. package/dist/tenant/runtime.js +8 -0
  120. package/dist/tenant/sessions.d.ts +28 -45
  121. package/dist/tenant/sessions.js +85 -208
  122. package/dist/tenant/status.js +6 -7
  123. package/dist/tenant/sweep.js +12 -3
  124. package/dist/tenant/transcript.d.ts +16 -16
  125. package/dist/tenant/triggers.d.ts +143 -0
  126. package/dist/tenant/triggers.js +715 -0
  127. package/dist/tenant/turn-timing.d.ts +11 -1
  128. package/dist/tenant/turn-timing.js +3 -1
  129. package/dist/tenant/worker.d.ts +2 -0
  130. package/dist/tenant/worker.js +5 -0
  131. package/dist/traces/map.d.ts +1 -1
  132. package/dist/traces/map.js +7 -1
  133. package/dist/tuning.d.ts +7 -0
  134. package/dist/tuning.js +19 -0
  135. package/dist/vault/service.d.ts +13 -8
  136. package/dist/vault/service.js +61 -17
  137. package/dist/version.d.ts +1 -1
  138. package/dist/version.js +1 -1
  139. package/package.json +13 -12
  140. package/dist/api/http/routes/environment-manifests.d.ts +0 -9
  141. package/dist/api/http/routes/environment-manifests.js +0 -118
  142. package/dist/api/http/routes/environments.d.ts +0 -11
  143. package/dist/api/http/routes/environments.js +0 -332
  144. package/dist/record/environment.d.ts +0 -27
  145. package/dist/record/environment.js +0 -46
  146. package/dist/tenant/environment-manifests.d.ts +0 -57
  147. package/dist/tenant/environment-manifests.js +0 -208
  148. package/dist/tenant/environments.d.ts +0 -172
@@ -0,0 +1,152 @@
1
+ import { z } from "zod";
2
+ import { isAgentId, PutMemoryRequestSchema } from "@nylorun/core/contracts";
3
+ import { MemoryList, MemoryVersionDetail, MemoryVersionPage, MemoryView, PutMemoryRequest } from "../../components.js";
4
+ import { assertReaches } from "../../../tenant/agents.js";
5
+ import { fail } from "../../../tenant/http.js";
6
+ import { deleteMemoryPath, listMemories, listMemoryVersions, putMemoryContent, readMemory, readMemoryVersion, } from "../../../tenant/memories.js";
7
+ import { readJson } from "../body.js";
8
+ import { tenantRoute } from "../define.js";
9
+ import { pageQuery, pageSize, parseQuery } from "./reads.js";
10
+ import { jsonResponse } from "../respond.js";
11
+ const READ = {
12
+ credentials: ["application", "subject", "token"],
13
+ scopes: ["sessions:own", "agents:read", "agents:write"],
14
+ };
15
+ const WRITE = {
16
+ credentials: ["application", "subject", "token"],
17
+ scopes: ["agents:write"],
18
+ };
19
+ const json = (schema, description) => ({
20
+ description,
21
+ content: { "application/json": { schema } },
22
+ });
23
+ const agentId = z.object({
24
+ agentId: z.string().meta({ description: "The Agent id, percent-encoded" }),
25
+ });
26
+ const memoryPath = agentId.extend({
27
+ path: z.string().meta({
28
+ description: "The memory's path under `/memories`, percent-encoded: `conventions%2Frelease.md` is `/memories/conventions/release.md`",
29
+ }),
30
+ });
31
+ function idOf(value) {
32
+ if (!isAgentId(value))
33
+ return fail(400, "An Agent id is up to 200 characters: /-separated segments of letters, digits, '.', '_' and '-'");
34
+ return value;
35
+ }
36
+ export function memoryRoutes(api) {
37
+ tenantRoute(api, READ, {
38
+ method: "get",
39
+ path: "/v1/agents/{agentId}/memories",
40
+ tags: ["Memory"],
41
+ summary: "List an Agent's memories",
42
+ description: "Its memories by path, each with its size, `content_sha256`, frontmatter `description` and version. `pathPrefix` keeps one folder's (`conventions` for `/memories/conventions/…`).",
43
+ request: {
44
+ params: agentId,
45
+ query: pageQuery.extend({
46
+ pathPrefix: z.string().optional().meta({ description: "Only the memories in this folder, relative to `/memories`" }),
47
+ }),
48
+ },
49
+ responses: { 200: json(MemoryList, "A page of memories") },
50
+ }, async (c) => {
51
+ const id = idOf(c.req.param("agentId"));
52
+ assertReaches(c.get("scope"), id);
53
+ const pathPrefix = c.req.query("pathPrefix");
54
+ const { limit, cursor } = parseQuery(pageQuery, { limit: c.req.query("limit"), cursor: c.req.query("cursor") });
55
+ return jsonResponse(200, await listMemories(c.env.tenant, id, {
56
+ ...(pathPrefix ? { pathPrefix } : {}),
57
+ ...(cursor ? { cursor } : {}),
58
+ limit: await pageSize(c.env.tenant, limit, { fallback: 100 }),
59
+ }));
60
+ });
61
+ tenantRoute(api, READ, {
62
+ method: "get",
63
+ path: "/v1/agents/{agentId}/memories/{path}",
64
+ tags: ["Memory"],
65
+ summary: "Read a memory",
66
+ description: "The memory's content, with its size, `content_sha256`, description and version.",
67
+ request: { params: memoryPath },
68
+ responses: { 200: json(MemoryView, "The memory"), 404: { description: "No memory has this path" } },
69
+ }, async (c) => {
70
+ const id = idOf(c.req.param("agentId"));
71
+ assertReaches(c.get("scope"), id);
72
+ return jsonResponse(200, await readMemory(c.env.tenant, id, c.req.param("path") ?? ""));
73
+ });
74
+ tenantRoute(api, WRITE, {
75
+ method: "put",
76
+ path: "/v1/agents/{agentId}/memories/{path}",
77
+ tags: ["Memory"],
78
+ summary: "Write a memory",
79
+ description: "Creates the memory or replaces its content, as the `memory` tool's `create` does, and records a version with the caller as its actor. This is how an app seeds an Agent's memory. With `contentSha256` the write applies only while the memory still has that content (`\"\"`: only while no memory has the path); otherwise `409 memory_conflict` with the current memory. The path ends in `.md`; the Runtime limits `memoryBytes` (`413`) and `memoriesPerAgent` (`409 limit_exceeded`) hold.",
80
+ request: {
81
+ params: memoryPath,
82
+ body: { required: true, content: { "application/json": { schema: PutMemoryRequest } } },
83
+ },
84
+ responses: {
85
+ 200: json(MemoryView, "The memory as written"),
86
+ 409: { description: "The `contentSha256` precondition no longer holds, or a folder has this path (`memory_conflict`)" },
87
+ 413: { description: "Larger than the Runtime limit `memoryBytes`" },
88
+ },
89
+ }, async (c) => {
90
+ const id = idOf(c.req.param("agentId"));
91
+ const scope = c.get("scope");
92
+ assertReaches(scope, id);
93
+ const body = PutMemoryRequestSchema.safeParse(await readJson(c));
94
+ if (!body.success)
95
+ fail(400, "The body is { content, contentSha256? }", { code: "invalid_request" });
96
+ return jsonResponse(200, await putMemoryContent(c.env.tenant, id, c.req.param("path") ?? "", body.data, scope));
97
+ });
98
+ tenantRoute(api, WRITE, {
99
+ method: "delete",
100
+ path: "/v1/agents/{agentId}/memories/{path}",
101
+ tags: ["Memory"],
102
+ summary: "Delete a memory or a folder",
103
+ description: "Deletes the memory, or every memory in the folder the path names, each with a version that records the delete. Versions stay with the Agent.",
104
+ request: { params: memoryPath },
105
+ responses: {
106
+ 200: json(z.object({ path: z.string(), deleted: z.number().int().nonnegative() }).strict(), "What was deleted"),
107
+ 404: { description: "No memory or folder has this path" },
108
+ },
109
+ }, async (c) => {
110
+ const id = idOf(c.req.param("agentId"));
111
+ const scope = c.get("scope");
112
+ assertReaches(scope, id);
113
+ return jsonResponse(200, await deleteMemoryPath(c.env.tenant, id, c.req.param("path") ?? "", scope));
114
+ });
115
+ tenantRoute(api, READ, {
116
+ method: "get",
117
+ path: "/v1/agents/{agentId}/memory_versions",
118
+ tags: ["Memory"],
119
+ summary: "List an Agent's memory versions",
120
+ description: "Every change to its memories, newest first: the operation, the path (a rename's names its old one), and who made it: `agent:<manifest>@<version>` with the session, turn and trigger run of a turn's write, or the API caller. `path` keeps one memory's.",
121
+ request: {
122
+ params: agentId,
123
+ query: pageQuery.extend({
124
+ path: z.string().optional().meta({ description: "Only this memory's versions, relative to `/memories`" }),
125
+ }),
126
+ },
127
+ responses: { 200: json(MemoryVersionPage, "A page of versions") },
128
+ }, async (c) => {
129
+ const id = idOf(c.req.param("agentId"));
130
+ assertReaches(c.get("scope"), id);
131
+ const path = c.req.query("path");
132
+ const { limit, cursor } = parseQuery(pageQuery, { limit: c.req.query("limit"), cursor: c.req.query("cursor") });
133
+ return jsonResponse(200, await listMemoryVersions(c.env.tenant, id, {
134
+ ...(path ? { path } : {}),
135
+ ...(cursor ? { cursor } : {}),
136
+ limit: await pageSize(c.env.tenant, limit, { fallback: 50 }),
137
+ }));
138
+ });
139
+ tenantRoute(api, READ, {
140
+ method: "get",
141
+ path: "/v1/agents/{agentId}/memory_versions/{versionId}",
142
+ tags: ["Memory"],
143
+ summary: "Read a memory version",
144
+ description: "One version with the content it left (`null` for a delete). To roll a memory back, write this content again.",
145
+ request: { params: agentId.extend({ versionId: z.string().meta({ description: "The version's id (`mv_…`)" }) }) },
146
+ responses: { 200: json(MemoryVersionDetail, "The version"), 404: { description: "No such version" } },
147
+ }, async (c) => {
148
+ const id = idOf(c.req.param("agentId"));
149
+ assertReaches(c.get("scope"), id);
150
+ return jsonResponse(200, await readMemoryVersion(c.env.tenant, id, c.req.param("versionId") ?? ""));
151
+ });
152
+ }
@@ -30,7 +30,7 @@ export declare const sessionPageQuery: z.ZodObject<{
30
30
  uncertain: "uncertain";
31
31
  waiting: "waiting";
32
32
  }>>;
33
- environmentId: z.ZodOptional<z.ZodString>;
33
+ manifest: z.ZodOptional<z.ZodString>;
34
34
  ownerUserId: z.ZodOptional<z.ZodString>;
35
35
  }, z.core.$strict>;
36
36
  export declare function parseQuery<T>(schema: z.ZodType<T>, query: unknown): T;
@@ -42,145 +42,9 @@ export declare function sessionPage(ctx: TenantEnv["Bindings"]["tenant"], scope:
42
42
  status: "cancelled" | "completed" | "failed" | "idle" | "paused" | "runnable" | "running" | "uncertain" | "waiting";
43
43
  activeTurnId: string | null;
44
44
  lastTurnId: string | null;
45
- environmentId: string | null;
46
45
  createdAt: string | null;
47
46
  lastEventAt: string | null;
48
47
  }[];
49
48
  nextCursor: string | null;
50
49
  }>;
51
- export declare function environmentPage(ctx: TenantEnv["Bindings"]["tenant"], scope: TenantEnv["Variables"]["scope"], query: Record<string, string>, labels: Record<string, string>): Promise<{
52
- environments: {
53
- id: string;
54
- kind: "none" | "pod" | "virtual";
55
- labels: Record<string, string>;
56
- manifest: {
57
- image?: string | undefined;
58
- resources?: {
59
- cpus: number;
60
- memory: string;
61
- } | undefined;
62
- storage?: string | undefined;
63
- network?: {
64
- allowedHosts: string[];
65
- } | undefined;
66
- harness: string;
67
- lifecycle: {
68
- idle?: string | undefined;
69
- deleteAfter?: string | undefined;
70
- };
71
- setup?: {
72
- command: string;
73
- cwd?: string | undefined;
74
- timeout?: string | undefined;
75
- }[] | undefined;
76
- files?: ({
77
- artifactId: string;
78
- version: number;
79
- path: string;
80
- } | {
81
- path: string;
82
- files: Record<string, string>;
83
- })[] | undefined;
84
- limits?: {
85
- artifacts?: {
86
- fileBytes?: number | undefined;
87
- totalBytes?: number | undefined;
88
- } | undefined;
89
- skills?: {
90
- totalBytes?: number | undefined;
91
- } | undefined;
92
- disk?: {
93
- versionBytes?: number | undefined;
94
- versionEntries?: number | undefined;
95
- } | undefined;
96
- } | undefined;
97
- secrets?: Record<string, {
98
- required?: boolean | undefined;
99
- description?: string | undefined;
100
- type: "environment_secret";
101
- allowedHosts: string[];
102
- inject?: {
103
- header: string;
104
- format: string;
105
- } | undefined;
106
- } | {
107
- required?: boolean | undefined;
108
- description?: string | undefined;
109
- type: "environment_variable";
110
- } | {
111
- via?: string | undefined;
112
- identity?: {
113
- header: string;
114
- } | undefined;
115
- required?: boolean | undefined;
116
- description?: string | undefined;
117
- type: "bearer";
118
- url: string;
119
- } | {
120
- via?: string | undefined;
121
- identity?: {
122
- header: string;
123
- } | undefined;
124
- required?: boolean | undefined;
125
- description?: string | undefined;
126
- type: "headers";
127
- url: string;
128
- }> | undefined;
129
- };
130
- manifestName?: string | undefined;
131
- manifestVersion?: number | undefined;
132
- manifestHash?: string | undefined;
133
- limits: {
134
- artifacts: {
135
- fileBytes: number;
136
- totalBytes: number;
137
- };
138
- skills: {
139
- totalBytes: number;
140
- };
141
- disk: {
142
- versionBytes: number;
143
- versionEntries: number;
144
- };
145
- };
146
- forkedFrom?: {
147
- environment: string;
148
- disk: number | null;
149
- } | undefined;
150
- slots?: {
151
- name: string;
152
- type: string;
153
- required: boolean;
154
- set: boolean;
155
- }[] | undefined;
156
- version: number;
157
- appliedVersion: number;
158
- ownerUserId: string | null;
159
- state: string;
160
- pod?: {
161
- desired: "deleted" | "running" | "suspended";
162
- observed: "creating" | "deleting" | "failed" | "lost" | "running" | "suspended";
163
- volumeGeneration: number;
164
- hostEpoch: number;
165
- reason?: string | undefined;
166
- imageDigest?: string | undefined;
167
- } | undefined;
168
- lastUsedAt: string;
169
- disk?: {
170
- latestVersion: number | null;
171
- operation?: {
172
- kind: "restore" | "seed" | "snapshot";
173
- version?: number | undefined;
174
- startedAt: string;
175
- } | undefined;
176
- } | undefined;
177
- sessions: {
178
- id: string;
179
- activeTurnId: string | null;
180
- }[];
181
- createdAt: string;
182
- updatedAt: string;
183
- }[];
184
- nextCursor: string | null;
185
- }>;
186
50
  export declare function readRoutes(api: OpenAPIHono<TenantEnv>): void;
@@ -6,7 +6,6 @@ import { readAccess, readStoreOf } from "../../../reads/access.js";
6
6
  import { requireApplication, requirePrincipal } from "../../../tenant/auth.js";
7
7
  import { fail } from "../../../tenant/http.js";
8
8
  import { readRuntimeLimits } from "../../../tenant/runtime-limits.js";
9
- import { environmentGrantsOf } from "../../../tenant/environments.js";
10
9
  import { tenantRoute } from "../define.js";
11
10
  import { jsonResponse } from "../respond.js";
12
11
  /** A page's `limit`, held to the Tenant's Runtime limit `pageItems` (`pageSize`). */
@@ -40,7 +39,8 @@ export const sessionPageQuery = pageQuery
40
39
  .extend({
41
40
  agentId: z.string().optional(),
42
41
  status: z.enum(SESSION_STATUSES).optional(),
43
- environmentId: z.string().optional(),
42
+ /** Sessions that pinned a version of this agent manifest. */
43
+ manifest: z.string().optional(),
44
44
  ownerUserId: z.string().optional(),
45
45
  })
46
46
  .strict();
@@ -56,12 +56,6 @@ export async function sessionPage(ctx, scope, query) {
56
56
  requireApplication(scope);
57
57
  return readStoreOf(ctx).sessions(filters, { limit: await pageSize(ctx, limit), cursor }, readAccess(scope));
58
58
  }
59
- export async function environmentPage(ctx, scope, query, labels) {
60
- const { limit, cursor } = parseQuery(pageQuery.extend({ label: z.string().optional() }).strict(), query);
61
- if (limit === undefined)
62
- return fail(400, "Pagination requires limit");
63
- return readStoreOf(ctx).environments(labels, { limit: await pageSize(ctx, limit), cursor }, readAccess(scope), environmentGrantsOf(scope));
64
- }
65
59
  export function readRoutes(api) {
66
60
  const sessionId = z.object({ sessionId: z.string() });
67
61
  const turnQuery = z.object({ turnId: z.string().optional() }).strict();
@@ -1,16 +1,17 @@
1
1
  import { RESPONSE_ALREADY_SENT } from "@hono/node-server/utils/response";
2
2
  import { z } from "zod";
3
- import { PutAgentRequestSchema, PutSessionRequestSchema, SessionCommandSchema, } from "@nylorun/core/contracts";
3
+ import { PROTOCOL_HEADER } from "@nylorun/core/compatibility";
4
+ import { PutSessionRequestSchema, SessionCommandSchema, } from "@nylorun/core/contracts";
4
5
  import { handleSessionSandboxTool, SandboxRouteError } from "../../../core/sandbox-routes.js";
5
6
  import { traceContextOf } from "../../../traces/ids.js";
6
- import { AcceptedResponse, ListAgentsResponse, ListPublicAgentsResponse, ListSessionsResponse, SessionPage, HistoryPage, SessionEvent, PutAgentRequest, PutAgentResponse, PutSessionRequest, SandboxToolOutcome, SessionCommand, SessionItemsResponse, SessionView, StreamClosedFrame, } from "../../components.js";
7
+ import { AcceptedResponse, QueuedResponse, ListSessionsResponse, SessionPage, HistoryPage, SessionEvent, PutSessionRequest, SandboxToolOutcome, SessionCommand, SessionItemsResponse, SessionView, StreamClosedFrame, } from "../../components.js";
7
8
  import { accessOf, requireApplication } from "../../../tenant/auth.js";
8
9
  import { command } from "../../../tenant/commands.js";
9
10
  import { loadSession, sessionOf } from "../../../tenant/context.js";
10
11
  import { fail } from "../../../tenant/http.js";
11
- import { environmentGrantsOf, sandboxRouteDeps } from "../../../tenant/environments.js";
12
+ import { agentGrantsOf, sandboxRouteDeps } from "../../../tenant/agents.js";
12
13
  import { readHistory, requestCursor, streamHolderOf, streamSessionEvents } from "../../../tenant/session-streams.js";
13
- import { listAgentsPublic, listDefinitions, listSessions, putDefinition, putSession, sessionView, } from "../../../tenant/sessions.js";
14
+ import { listSessions, putSession, sessionView } from "../../../tenant/sessions.js";
14
15
  import { readJson } from "../body.js";
15
16
  import { TraceHeaders, tenantRoute } from "../define.js";
16
17
  import { pageQuery, pageSize, sessionPage, sessionPageQuery, parseQuery } from "./reads.js";
@@ -46,61 +47,31 @@ async function sessionBelow(ctx, incoming, id, access) {
46
47
  await loadSession(ctx, id, access);
47
48
  return requestCursor(incoming, urlOf(incoming));
48
49
  }
50
+ /**
51
+ * What a message to a busy session does when it says nothing (D76): a protocol 17 client's waits
52
+ * in the queue; a protocol 16 client's is refused, as before.
53
+ */
54
+ function deliveryFor(headers) {
55
+ const protocol = Number(headers.get(PROTOCOL_HEADER)?.trim());
56
+ return Number.isInteger(protocol) && protocol >= 17 ? "queue" : "reject";
57
+ }
49
58
  export function sessionRoutes(api) {
50
59
  tenantRoute(api, OWN_SESSIONS, {
51
60
  method: "post",
52
61
  path: "/v1/sessions/{sessionId}/commands",
53
62
  tags: ["Sessions"],
54
63
  summary: "Send a command to a session",
55
- description: "A message starts a turn; the others answer what the session waits on, or cancel. Idempotent on the command's `idempotencyKey`.",
64
+ description: "A message starts a turn, or waits in the session's queue while a turn runs or waits (protocol 17; `delivery: \"reject\"` is refused with `409 session_busy` instead); the others answer what the session waits on, or cancel. Idempotent on the command's `idempotencyKey`.",
56
65
  request: { params: sessionId, body: body(SessionCommand), headers: TraceHeaders },
57
66
  responses: {
58
- 200: json(AcceptedResponse, "The command, accepted"),
59
- 409: { description: "A turn is running, or the session does not wait on this" },
67
+ 200: json(z.union([AcceptedResponse, QueuedResponse]), "The command, accepted; a message may be queued"),
68
+ 409: { description: "A turn is running (`session_busy`), or the session does not wait on this" },
69
+ 429: { description: "The session's queue is full (`queue_full`)" },
60
70
  },
61
71
  }, async (c) => {
62
72
  // A `traceparent` joins the turn to the caller's trace (R7.4); a malformed one is ignored.
63
73
  const trace = traceContextOf(c.req.raw.headers);
64
- return jsonResponse(200, await command(c.env.tenant, c.req.param("sessionId"), SessionCommandSchema.parse(await readJson(c)), c.get("scope"), trace ? { trace } : {}));
65
- });
66
- tenantRoute(api, {
67
- credentials: ["application", "subject", "token"],
68
- scopes: ["agents:read", "agents:write"],
69
- }, {
70
- method: "get",
71
- path: "/v1/agents",
72
- tags: ["Agents"],
73
- summary: "List agents",
74
- description: "With an application key, every definition, manifest included. With a trusted issuer's token, only the agents it may use, by name.",
75
- responses: {
76
- 200: json(z.union([ListAgentsResponse, ListPublicAgentsResponse]), "The agents"),
77
- },
78
- }, async (c) => {
79
- const scope = c.get("scope");
80
- const ctx = c.env.tenant;
81
- return jsonResponse(200, scope.kind === "token"
82
- ? await listAgentsPublic(ctx, scope.agents)
83
- : await listDefinitions(ctx));
84
- });
85
- tenantRoute(api, { credentials: ["application", "subject"], scopes: ["agents:write"] }, {
86
- method: "put",
87
- path: "/v1/agents/{agentId}",
88
- tags: ["Agents"],
89
- summary: "Put an agent's definition",
90
- description: "Saves the agent's definition. With `?dryRun=true` (protocol 14) every check runs and nothing is saved: the answer says what a save would record.",
91
- request: {
92
- params: z.object({ agentId: z.string() }),
93
- query: z.object({
94
- dryRun: z.enum(["true", "false"]).optional().meta({ description: "`true`: check and save nothing" }),
95
- }),
96
- body: body(PutAgentRequest),
97
- },
98
- responses: {
99
- 200: json(PutAgentResponse, "The definition, stored (or checked, with dryRun)"),
100
- 413: { description: "A skill's files hold more than the Tenant's skill limit together (`limit_exceeded`)" },
101
- },
102
- }, async (c) => {
103
- return jsonResponse(200, await putDefinition(c.env.tenant, c.req.param("agentId"), PutAgentRequestSchema.parse(await readJson(c)), { dryRun: c.req.query("dryRun") === "true" }));
74
+ return jsonResponse(200, await command(c.env.tenant, c.req.param("sessionId"), SessionCommandSchema.parse(await readJson(c)), c.get("scope"), { ...(trace ? { trace } : {}), delivery: deliveryFor(c.req.raw.headers) }));
104
75
  });
105
76
  tenantRoute(api, OWN_SESSIONS, {
106
77
  method: "get",
@@ -121,7 +92,7 @@ export function sessionRoutes(api) {
121
92
  path: "/v1/sessions/{sessionId}",
122
93
  tags: ["Sessions"],
123
94
  summary: "Open a session",
124
- description: "Creates the session, or answers the one already opened with the same agent, owner, info and Environment. Its Environment (its sandbox and vault) is chosen now and fixed for its life.",
95
+ description: "Creates the session on an Agent, or answers the one already opened with the same Agent, owner and info. Its Agent (its sandbox and vault) is fixed for its life, and it pins the manifest version the Agent runs. No session is special: an app that keeps one conversation going reuses its id.",
125
96
  request: { params: sessionId, body: body(PutSessionRequest) },
126
97
  responses: {
127
98
  200: json(SessionView, "The session"),
@@ -134,8 +105,8 @@ export function sessionRoutes(api) {
134
105
  // Agent code may trust `info`: only an app server sets it.
135
106
  if (scope.kind === "token" && request.info !== undefined)
136
107
  fail(403, "A token caller cannot set session info", { code: "scope_required" });
137
- const grants = environmentGrantsOf(scope);
138
- const session = await putSession(ctx, c.req.param("sessionId"), request, accessOf(scope), grants === undefined ? {} : { environmentGrants: grants });
108
+ const grants = agentGrantsOf(scope);
109
+ const session = await putSession(ctx, c.req.param("sessionId"), request, accessOf(scope), grants === undefined ? {} : { agentGrants: grants });
139
110
  return jsonResponse(200, await ctx.store.tx((t) => sessionView(t, session)));
140
111
  });
141
112
  tenantRoute(api, OWN_SESSIONS, {
@@ -18,7 +18,7 @@ import { tenantReset, streamsStatus, } from "../../../tenant/streams.js";
18
18
  import { readJson } from "../body.js";
19
19
  import { pathSegments, tenantRoute } from "../define.js";
20
20
  import { jsonResponse } from "../respond.js";
21
- import { rearmPodTimers } from "../../../tenant/environments.js";
21
+ import { rearmPodTimers } from "../../../tenant/agents.js";
22
22
  import { hostTuning } from "../../../tuning.js";
23
23
  import { budgetIssues, toolPolicyIssues } from "../../../tenant/limits.js";
24
24
  /** Every Management API route (`/v1/tenant/*`): a management key, acting as itself (protocol 8). */
@@ -37,7 +37,7 @@ const body = (schema) => ({
37
37
  });
38
38
  const budgetRow = (budget, updatedAt) => ({
39
39
  scope: budget.scope,
40
- scopeId: budget.scope === "agent" ? budget.id : "*",
40
+ scopeId: budget.scope === "manifest" ? budget.id : "*",
41
41
  period: budget.period ?? null,
42
42
  limitUsd: budget.limitUsd ?? null,
43
43
  limitTokens: budget.limitTokens ?? null,
@@ -45,7 +45,7 @@ const budgetRow = (budget, updatedAt) => ({
45
45
  });
46
46
  const budgetView = (row) => ({
47
47
  scope: row.scope,
48
- ...(row.scope === "agent" ? { id: row.scopeId } : {}),
48
+ ...(row.scope === "manifest" ? { id: row.scopeId } : {}),
49
49
  ...(row.period ? { period: row.period } : {}),
50
50
  ...(row.limitUsd !== null ? { limitUsd: row.limitUsd } : {}),
51
51
  ...(row.limitTokens !== null ? { limitTokens: row.limitTokens } : {}),
@@ -114,7 +114,7 @@ export function tenantRoutes(api) {
114
114
  path: "/v1/tenant/reset",
115
115
  tags: ["Tenant"],
116
116
  summary: "Reset the Tenant",
117
- description: "Deletes its sessions, its Environments (with their vaults), or both, after draining or cancelling the turns in progress. The Tenant stays open.",
117
+ description: "Deletes its sessions, its Agents (with their vaults), or both, after draining or cancelling the turns in progress. The Tenant stays open.",
118
118
  request: { body: body(ResetTenantRequest) },
119
119
  responses: { 200: json(ResetTenantResponse, "Reset") },
120
120
  }, async (c) => {
@@ -123,7 +123,7 @@ export function tenantRoutes(api) {
123
123
  await drain(ctx, request.activeWork, hostTuning().tenant.drainTimeoutMs);
124
124
  // A sessions reset moves the Tenant to a new basin generation, so a session created
125
125
  // again with the same id, during or after the reset, starts in an empty basin at 0.
126
- const sessionsReset = request.scope !== "environments";
126
+ const sessionsReset = request.scope !== "agents";
127
127
  await resetTenant({
128
128
  store: ctx.store,
129
129
  sandbox: ctx.sandbox,
@@ -221,15 +221,15 @@ export function tenantRoutes(api) {
221
221
  method: "get",
222
222
  path: "/v1/tenant/sandbox",
223
223
  tags: ["Tenant"],
224
- summary: "Get the Tenant's sandbox backend and Environment settings",
224
+ summary: "Get the Tenant's sandbox backend and Agent compute settings",
225
225
  responses: { 200: json(TenantSandboxView, "The backend in use and the configuration in force") },
226
226
  }, async (c) => jsonResponse(200, await sandboxView(c.env.tenant)));
227
227
  tenantRoute(api, MANAGEMENT, {
228
228
  method: "put",
229
229
  path: "/v1/tenant/sandbox",
230
230
  tags: ["Tenant"],
231
- summary: "Set the Tenant's Environment settings",
232
- description: "The limits every Environment's recipe must fit, the pods' stop grace and where harnesses may run (Tenant setting `environments.config`).",
231
+ summary: "Set the Tenant's Agent compute settings",
232
+ description: "The limits every Agent's runtime must fit, the pods' stop grace and where harnesses may run (Tenant setting `environments.config`).",
233
233
  request: { body: body(PutTenantEnvironmentsRequest) },
234
234
  responses: { 200: json(TenantSandboxView, "The configuration now in force") },
235
235
  }, async (c) => {
@@ -0,0 +1,10 @@
1
+ /**
2
+ * An Agent's triggers and their runs (track T4, D74): read them, pause and resume one, run one now
3
+ * (keyed by `Idempotency-Key`), and page the runs; make a webhook trigger's signing secret, and
4
+ * take a sender's delivery at its endpoint (D75), an anonymous, unversioned route. What a trigger is lives in the Agent's manifest
5
+ * (`runtime.triggers`); these routes change only what the Agent owns, its status.
6
+ */
7
+ import type { OpenAPIHono } from "@hono/zod-openapi";
8
+ import type { TenantEnv } from "../app.js";
9
+ export declare function triggerRoutes(api: OpenAPIHono<TenantEnv>): void;
10
+ export declare function webhookRoutes(api: OpenAPIHono<TenantEnv>): void;