@nylorun/runtime 0.20.0-beta → 0.21.1-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 (109) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +19 -12
  3. package/dist/adapters/execution/restate.js +26 -1
  4. package/dist/api/components.d.ts +155 -36
  5. package/dist/api/components.js +3 -3
  6. package/dist/api/http/app.js +2 -0
  7. package/dist/api/http/routes/mcp.d.ts +9 -0
  8. package/dist/api/http/routes/mcp.js +32 -0
  9. package/dist/api/http/routes/vaults.d.ts +1 -9
  10. package/dist/api/http/routes/vaults.js +3 -103
  11. package/dist/api/openapi.js +6 -3
  12. package/dist/artifacts/media-types.d.ts +2 -0
  13. package/dist/artifacts/media-types.js +5 -0
  14. package/dist/core/flow-host.d.ts +2 -3
  15. package/dist/core/flow-host.js +10 -7
  16. package/dist/execution/types.d.ts +13 -5
  17. package/dist/execution/types.js +13 -5
  18. package/dist/gates/http-tool.d.ts +7 -0
  19. package/dist/gates/http-tool.js +34 -3
  20. package/dist/gates/mcp-handler.d.ts +4 -1
  21. package/dist/gates/mcp-handler.js +64 -8
  22. package/dist/gates/tenant-vaults.d.ts +9 -15
  23. package/dist/gates/tenant-vaults.js +9 -17
  24. package/dist/gates/tool-client.js +36 -17
  25. package/dist/gates/tool-contract.d.ts +19 -3
  26. package/dist/gates/tool-contract.js +9 -3
  27. package/dist/gates/tool-gate.d.ts +2 -2
  28. package/dist/harness/calls.d.ts +2 -0
  29. package/dist/harness/calls.js +13 -3
  30. package/dist/harness-api/record.d.ts +7 -6
  31. package/dist/harness-api/record.js +99 -17
  32. package/dist/harness-api/server.js +35 -27
  33. package/dist/harness-api/start.js +2 -1
  34. package/dist/host/config-for.d.ts +0 -2
  35. package/dist/host/config-for.js +0 -1
  36. package/dist/host/create-host.d.ts +1 -1
  37. package/dist/host/create-host.js +2 -1
  38. package/dist/host/gates.js +0 -1
  39. package/dist/host/http.d.ts +0 -1
  40. package/dist/host/http.js +0 -6
  41. package/dist/host/main.js +13 -3
  42. package/dist/host/stack-config.d.ts +7 -8
  43. package/dist/host/stack-config.js +50 -92
  44. package/dist/index.d.ts +0 -1
  45. package/dist/keys/client.js +1 -9
  46. package/dist/keys/keys.d.ts +11 -13
  47. package/dist/keys/keys.js +5 -7
  48. package/dist/management-openapi.json +315 -345
  49. package/dist/mcp/connect.d.ts +159 -13
  50. package/dist/mcp/connect.js +538 -55
  51. package/dist/mcp/pool.d.ts +11 -2
  52. package/dist/mcp/pool.js +74 -27
  53. package/dist/mcp/preview.d.ts +35 -0
  54. package/dist/mcp/preview.js +215 -0
  55. package/dist/mcp/snapshot.d.ts +79 -5
  56. package/dist/mcp/snapshot.js +145 -27
  57. package/dist/model/catalog.d.ts +14 -0
  58. package/dist/model/catalog.js +14 -1
  59. package/dist/model/pi-model.js +36 -2
  60. package/dist/openapi.json +362 -123
  61. package/dist/redact.d.ts +19 -1
  62. package/dist/redact.js +42 -13
  63. package/dist/sandbox/session-sandbox.d.ts +7 -0
  64. package/dist/sandbox/session-sandbox.js +89 -28
  65. package/dist/store/postgres/drizzle/0016_mcp_oauth_removed.sql +5 -0
  66. package/dist/store/postgres/drizzle/0017_wake_outbox.sql +9 -0
  67. package/dist/store/postgres/drizzle/meta/_journal.json +14 -0
  68. package/dist/store/postgres/schema.d.ts +105 -261
  69. package/dist/store/postgres/schema.js +18 -36
  70. package/dist/store/postgres/store.js +54 -21
  71. package/dist/store/postgres/tenant.d.ts +14 -0
  72. package/dist/store/postgres/tenant.js +22 -2
  73. package/dist/store/types.d.ts +48 -13
  74. package/dist/tenant/advance.js +1 -4
  75. package/dist/tenant/artifact-tool.d.ts +9 -1
  76. package/dist/tenant/artifact-tool.js +113 -9
  77. package/dist/tenant/commands.js +5 -5
  78. package/dist/tenant/context.d.ts +15 -13
  79. package/dist/tenant/effects.d.ts +2 -2
  80. package/dist/tenant/effects.js +20 -5
  81. package/dist/tenant/ephemeral.d.ts +0 -6
  82. package/dist/tenant/ephemeral.js +0 -1
  83. package/dist/tenant/outbound.d.ts +14 -5
  84. package/dist/tenant/outbound.js +21 -9
  85. package/dist/tenant/runtime.js +11 -15
  86. package/dist/tenant/sessions.js +20 -3
  87. package/dist/tenant/store-pg.js +9 -0
  88. package/dist/tenant/sweep.d.ts +14 -0
  89. package/dist/tenant/sweep.js +69 -10
  90. package/dist/tenant/tool-results.d.ts +57 -0
  91. package/dist/tenant/tool-results.js +298 -0
  92. package/dist/tenant/tool-search.d.ts +43 -0
  93. package/dist/tenant/tool-search.js +100 -0
  94. package/dist/tenant/transcript.d.ts +108 -0
  95. package/dist/tenant/transcript.js +13 -0
  96. package/dist/tenant/types.d.ts +0 -14
  97. package/dist/vault/crypto.d.ts +0 -7
  98. package/dist/vault/crypto.js +0 -16
  99. package/dist/vault/headers.d.ts +17 -0
  100. package/dist/vault/headers.js +61 -0
  101. package/dist/vault/service.d.ts +11 -68
  102. package/dist/vault/service.js +83 -438
  103. package/dist/vault/sources.d.ts +20 -48
  104. package/dist/vault/sources.js +25 -189
  105. package/dist/version.d.ts +1 -1
  106. package/dist/version.js +1 -1
  107. package/package.json +3 -3
  108. package/dist/vault/oauth.d.ts +0 -55
  109. package/dist/vault/oauth.js +0 -186
package/dist/redact.d.ts CHANGED
@@ -1 +1,19 @@
1
- export declare function scrub(value: unknown, secrets: readonly string[]): unknown;
1
+ export interface ScrubOptions {
2
+ /**
3
+ * Replace only `secrets` (R2b C8, Q16): no value under a secret-looking key, and no inline
4
+ * image data. A tool's answer keeps fields such as `nextToken`.
5
+ */
6
+ readonly valuesOnly?: boolean;
7
+ /** Told how many times a secret was replaced. */
8
+ readonly counted?: (replacements: number) => void;
9
+ }
10
+ /**
11
+ * `value` with each of `secrets` (those of at least 8 characters) replaced by `[redacted]`. By
12
+ * default also any value under a key such as `token` or `secret`, and inline image data.
13
+ */
14
+ export declare function scrub(value: unknown, secrets: readonly string[], options?: ScrubOptions): unknown;
15
+ /** `value` with `secrets` alone replaced (`valuesOnly`), and how many replacements were made. */
16
+ export declare function scrubValues<T>(value: T, secrets: readonly string[]): {
17
+ value: T;
18
+ redacted: number;
19
+ };
package/dist/redact.js CHANGED
@@ -1,14 +1,43 @@
1
- export function scrub(value, secrets) {
2
- if (typeof value === "string")
3
- return secrets.reduce((text, secret) => secret.length >= 8 ? text.split(secret).join("[redacted]") : text, value.replace(/data:image\/[a-z0-9.+-]+;base64,[A-Za-z0-9+/=]+/giu, "[inline image data redacted]"));
4
- if (Array.isArray(value))
5
- return value.map((item) => scrub(item, secrets));
6
- if (value && typeof value === "object")
7
- return Object.fromEntries(Object.entries(value).map(([key, item]) => [
8
- key,
9
- /^(authorization|api[_-]?key|(?:access|refresh|id|auth)[_-]?token|token|secret|password|cookie|credentials?)$/iu.test(key)
10
- ? "[redacted]"
11
- : scrub(item, secrets),
12
- ]));
13
- return value;
1
+ /**
2
+ * `value` with each of `secrets` (those of at least 8 characters) replaced by `[redacted]`. By
3
+ * default also any value under a key such as `token` or `secret`, and inline image data.
4
+ */
5
+ export function scrub(value, secrets, options = {}) {
6
+ // The longest first, so a header value goes whole before the token inside it.
7
+ const sorted = secrets.filter((secret) => secret.length >= 8).sort((a, b) => b.length - a.length);
8
+ let replaced = 0;
9
+ const walk = (item) => {
10
+ if (typeof item === "string") {
11
+ const text = options.valuesOnly
12
+ ? item
13
+ : item.replace(/data:image\/[a-z0-9.+-]+;base64,[A-Za-z0-9+/=]+/giu, "[inline image data redacted]");
14
+ return sorted.reduce((current, secret) => {
15
+ const parts = current.split(secret);
16
+ replaced += parts.length - 1;
17
+ return parts.join("[redacted]");
18
+ }, text);
19
+ }
20
+ if (Array.isArray(item))
21
+ return item.map(walk);
22
+ if (item && typeof item === "object")
23
+ return Object.fromEntries(Object.entries(item).map(([key, entry]) => [
24
+ key,
25
+ !options.valuesOnly &&
26
+ /^(authorization|api[_-]?key|(?:access|refresh|id|auth)[_-]?token|token|secret|password|cookie|credentials?)$/iu.test(key)
27
+ ? "[redacted]"
28
+ : walk(entry),
29
+ ]));
30
+ return item;
31
+ };
32
+ const result = walk(value);
33
+ options.counted?.(replaced);
34
+ return result;
35
+ }
36
+ /** `value` with `secrets` alone replaced (`valuesOnly`), and how many replacements were made. */
37
+ export function scrubValues(value, secrets) {
38
+ if (secrets.length === 0)
39
+ return { value, redacted: 0 };
40
+ let redacted = 0;
41
+ const scrubbed = scrub(value, secrets, { valuesOnly: true, counted: (count) => (redacted = count) });
42
+ return { value: redacted === 0 ? value : scrubbed, redacted };
14
43
  }
@@ -9,3 +9,10 @@ export type SandboxedManifest = {
9
9
  };
10
10
  /** The agent's manifest with the sandbox capability added, or why it cannot take one. */
11
11
  export declare function withSandboxCapability(manifest: AgentManifest, spec: SandboxManifest): SandboxedManifest;
12
+ /**
13
+ * The pinned manifest of an agent session without a sandbox: `read_artifact` for each agent of
14
+ * the tree with a remote MCP server or an HTTP tool (R2b C11), and `nylorun.tools` for each with
15
+ * a remote MCP server (R2b C10). Undefined when nothing is added, and the session pins its
16
+ * definition as it is.
17
+ */
18
+ export declare function withPlatformTools(manifest: AgentManifest): SandboxedManifest | undefined;
@@ -1,13 +1,23 @@
1
1
  /**
2
- * Gives a session the sandbox chosen when it was opened. An agent session's pinned manifest is
3
- * its definition plus the `nylorun.sandbox` capability (the six built-in tools, the sandbox
4
- * instructions and the resolved spec) and the `nylorun.artifacts` capability (`save_artifact`),
5
- * also added to each agent it uses as a tool, so the tree shares one spec. The sandbox's
6
- * instructions name the agent's skills' folders (`/skills/<name>/`). The definition in the
7
- * registry never changes.
2
+ * Gives a session the sandbox chosen when it was opened, and the platform tools its agents
3
+ * need. An agent session's pinned manifest is its definition plus:
4
+ *
5
+ * - with a sandbox, the `nylorun.sandbox` capability (the six built-in tools, the sandbox
6
+ * instructions and the resolved spec) and the `nylorun.artifacts` capability
7
+ * (`save_artifact`), also added to each agent it uses as a tool, so the tree shares one spec.
8
+ * The sandbox's instructions name the agent's skills' folders (`/skills/<name>/`);
9
+ * - for each agent of the tree with a remote MCP server or an HTTP tool, `read_artifact` in its
10
+ * `nylorun.artifacts` capability (R2b C11), with or without a sandbox: the Runtime stores a
11
+ * result of those tools too large to show as an artifact, and the model reads it in pages.
12
+ * An agent that declares a tool named `read_artifact` keeps its own, and sees previews only;
13
+ * - for each agent of the tree with a remote MCP server, the `nylorun.tools` capability (R2b
14
+ * C10), empty: when the session defers some of the agent's MCP tools, its session tools put
15
+ * `tool_search` and `tool_call` there. An agent that declares either name defers nothing.
16
+ *
17
+ * The definition in the registry never changes.
8
18
  */
9
19
  import { AgentManifestSchema } from "@nylorun/core/contracts";
10
- import { artifactsCapabilityManifest, hashManifest, sandboxCapabilityManifest, } from "@nylorun/core/define";
20
+ import { READ_ARTIFACT_TOOL, TOOL_CALL_TOOL, TOOL_SEARCH_TOOL, TOOLS_CAPABILITY_ID, artifactsCapabilityManifest, hashManifest, sandboxCapabilityManifest, toolsCapabilityManifest, } from "@nylorun/core/define";
11
21
  import { sandboxSpecOf } from "./share.js";
12
22
  function isAgentManifest(value) {
13
23
  return (typeof value === "object" &&
@@ -19,35 +29,72 @@ function isAgentManifest(value) {
19
29
  function skillNamesOf(manifest) {
20
30
  return manifest.capabilities.flatMap((capability) => Object.keys(capability.skills ?? {})).sort();
21
31
  }
22
- function addCapability(manifest, spec) {
23
- const capabilities = manifest.capabilities.map((capability) => capability.tools === undefined
24
- ? capability
25
- : {
26
- ...capability,
27
- tools: capability.tools.map((tool) => {
28
- // Agents used as tools share the session's sandbox; flow agents used as tools run
29
- // in linked sessions that inherit it.
30
- if (tool.agent === undefined || !isAgentManifest(tool.agent))
31
- return tool;
32
- if (sandboxSpecOf(tool.agent) !== undefined)
33
- return tool;
34
- return { ...tool, agent: addCapability(tool.agent, spec) };
35
- }),
32
+ /**
33
+ * True when the agent itself declares a remote MCP server or an HTTP tool, whose results the
34
+ * Runtime may store as artifacts (R2b C11), and no tool of its own is named `read_artifact`.
35
+ */
36
+ function readsStoredResults(manifest) {
37
+ const remote = manifest.capabilities.some((capability) => Object.keys(capability.mcpServers ?? {}).length > 0 ||
38
+ (capability.tools ?? []).some((tool) => tool.http !== undefined));
39
+ const taken = manifest.capabilities.some((capability) => (capability.tools ?? []).some((tool) => tool.name === READ_ARTIFACT_TOOL));
40
+ return remote && !taken;
41
+ }
42
+ /**
43
+ * True when the agent itself declares a remote MCP server, some of whose tools the session may
44
+ * defer (R2b C10), and names no tool `tool_search` or `tool_call` (it then keeps every tool in
45
+ * its list).
46
+ */
47
+ function defersTools(manifest) {
48
+ const servers = manifest.capabilities.some((capability) => Object.keys(capability.mcpServers ?? {}).length > 0);
49
+ const taken = manifest.capabilities.some((capability) => capability.id === TOOLS_CAPABILITY_ID ||
50
+ (capability.tools ?? []).some((tool) => tool.name === TOOL_SEARCH_TOOL || tool.name === TOOL_CALL_TOOL));
51
+ return servers && !taken;
52
+ }
53
+ /** `manifest` with the session's capabilities added; the same object when none are. */
54
+ function addCapabilities(manifest, spec) {
55
+ let changed = false;
56
+ const capabilities = manifest.capabilities.map((capability) => {
57
+ if (capability.tools === undefined)
58
+ return capability;
59
+ let toolsChanged = false;
60
+ const tools = capability.tools.map((tool) => {
61
+ // Agents used as tools share the session's sandbox; flow agents used as tools run in
62
+ // linked sessions that inherit it. One that declares its own sandbox keeps it.
63
+ if (tool.agent === undefined || !isAgentManifest(tool.agent))
64
+ return tool;
65
+ const agent = addCapabilities(tool.agent, sandboxSpecOf(tool.agent) === undefined ? spec : undefined);
66
+ if (agent === tool.agent)
67
+ return tool;
68
+ toolsChanged = true;
69
+ return { ...tool, agent };
36
70
  });
71
+ if (!toolsChanged)
72
+ return capability;
73
+ changed = true;
74
+ return { ...capability, tools };
75
+ });
76
+ const save = spec !== undefined;
77
+ const read = readsStoredResults(manifest);
78
+ const defer = defersTools(manifest);
79
+ if (!save && !read && !defer && !changed)
80
+ return manifest;
37
81
  return {
38
82
  ...manifest,
39
83
  // With a sandbox comes `save_artifact`, so files the agent makes reach the user (F8.1). The
40
84
  // sandbox's instructions name where the agent's skills are (R2 M4).
41
85
  capabilities: [
42
86
  ...capabilities,
43
- sandboxCapabilityManifest(spec, { skills: skillNamesOf(manifest) }),
44
- artifactsCapabilityManifest(),
87
+ ...(spec === undefined
88
+ ? []
89
+ : [sandboxCapabilityManifest(spec, { skills: skillNamesOf(manifest) })]),
90
+ ...(save || read ? [artifactsCapabilityManifest({ save, read })] : []),
91
+ // Empty: when the session defers MCP tools, its tools put tool_search and tool_call here.
92
+ ...(defer ? [toolsCapabilityManifest()] : []),
45
93
  ],
46
94
  };
47
95
  }
48
- /** The agent's manifest with the sandbox capability added, or why it cannot take one. */
49
- export function withSandboxCapability(manifest, spec) {
50
- const sandboxed = addCapability(manifest, spec);
96
+ /** `sandboxed` checked as a manifest, with its hash. */
97
+ function checked(original, sandboxed) {
51
98
  const parsed = AgentManifestSchema.safeParse(sandboxed);
52
99
  if (!parsed.success) {
53
100
  const issues = parsed.error.issues.map((issue) => issue.message);
@@ -55,9 +102,23 @@ export function withSandboxCapability(manifest, spec) {
55
102
  return {
56
103
  ok: false,
57
104
  message: clash
58
- ? `'${manifest.id}' declares a tool named like a sandbox tool (bash, read, write, edit, grep, glob, save_artifact). Rename it, or open the session with sandbox: false.`
59
- : `'${manifest.id}' cannot take this sandbox: ${[...new Set(issues)].join("; ")}`,
105
+ ? `'${original.id}' declares a tool named like a sandbox tool (bash, read, write, edit, grep, glob, save_artifact). Rename it, or open the session with sandbox: false.`
106
+ : `'${original.id}' cannot take this sandbox: ${[...new Set(issues)].join("; ")}`,
60
107
  };
61
108
  }
62
109
  return { ok: true, manifest: sandboxed, manifestHash: hashManifest(sandboxed) };
63
110
  }
111
+ /** The agent's manifest with the sandbox capability added, or why it cannot take one. */
112
+ export function withSandboxCapability(manifest, spec) {
113
+ return checked(manifest, addCapabilities(manifest, spec));
114
+ }
115
+ /**
116
+ * The pinned manifest of an agent session without a sandbox: `read_artifact` for each agent of
117
+ * the tree with a remote MCP server or an HTTP tool (R2b C11), and `nylorun.tools` for each with
118
+ * a remote MCP server (R2b C10). Undefined when nothing is added, and the session pins its
119
+ * definition as it is.
120
+ */
121
+ export function withPlatformTools(manifest) {
122
+ const pinned = addCapabilities(manifest, undefined);
123
+ return pinned === manifest ? undefined : checked(manifest, pinned);
124
+ }
@@ -0,0 +1,5 @@
1
+ DROP TABLE "nylorun"."oauth_pending" CASCADE;--> statement-breakpoint
2
+ INSERT INTO "nylorun"."vault_audit" ("id", "at", "actor", "action", "vault_id", "credential_id", "target", "outcome")
3
+ SELECT gen_random_uuid()::text, to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'), 'migration', 'delete', "vault_id", "id", "binding_json"::jsonb ->> 'url', 'deleted'
4
+ FROM "nylorun"."vault_credentials" WHERE "type" = 'oauth' ORDER BY "vault_id", "id";--> statement-breakpoint
5
+ DELETE FROM "nylorun"."vault_credentials" WHERE "type" = 'oauth';
@@ -0,0 +1,9 @@
1
+ CREATE TABLE "nylorun"."wakes" (
2
+ "id" text COLLATE "C" PRIMARY KEY NOT NULL,
3
+ "session_id" text COLLATE "C" NOT NULL,
4
+ "reason" text NOT NULL,
5
+ "dedupe_key" text,
6
+ "created_at" timestamp with time zone DEFAULT clock_timestamp() NOT NULL
7
+ );
8
+ --> statement-breakpoint
9
+ CREATE INDEX "wakes_created_at" ON "nylorun"."wakes" USING btree ("created_at");
@@ -113,6 +113,20 @@
113
113
  "when": 1791296131735,
114
114
  "tag": "0015_control_signals",
115
115
  "breakpoints": true
116
+ },
117
+ {
118
+ "idx": 16,
119
+ "version": "7",
120
+ "when": 1791395759051,
121
+ "tag": "0016_mcp_oauth_removed",
122
+ "breakpoints": true
123
+ },
124
+ {
125
+ "idx": 17,
126
+ "version": "7",
127
+ "when": 1791455573196,
128
+ "tag": "0017_wake_outbox",
129
+ "breakpoints": true
116
130
  }
117
131
  ]
118
132
  }