@agent-native/core 0.227.0 → 0.228.0-nightly-20261111004142

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 (190) hide show
  1. package/dist/action.d.ts +6 -2
  2. package/dist/agent/engine/builder-engine.js +6 -1
  3. package/dist/agent/engine/tool-call-journal-seed.d.ts +2 -0
  4. package/dist/agent/engine/tool-call-journal-seed.js +14 -0
  5. package/dist/agent/harness/store.js +14 -0
  6. package/dist/agent/production-agent.js +257 -17
  7. package/dist/agent/reaped-run-thread.js +6 -0
  8. package/dist/agent/run-manager.js +6 -0
  9. package/dist/agent/tool-call-journal.d.ts +4 -0
  10. package/dist/agent/tool-call-journal.js +12 -0
  11. package/dist/agent/types.d.ts +7 -1
  12. package/dist/app-config/agent.d.ts +3 -0
  13. package/dist/app-config/agent.js +7 -0
  14. package/dist/app-config/schema.d.ts +3 -0
  15. package/dist/application-state/handlers.d.ts +3 -3
  16. package/dist/application-state/handlers.js +6 -1
  17. package/dist/application-state/index.d.ts +3 -2
  18. package/dist/application-state/index.js +3 -2
  19. package/dist/application-state/last-used-tab.d.ts +11 -0
  20. package/dist/application-state/last-used-tab.js +43 -0
  21. package/dist/application-state/script-helpers.d.ts +26 -0
  22. package/dist/application-state/script-helpers.js +83 -15
  23. package/dist/application-state/store.d.ts +8 -0
  24. package/dist/application-state/store.js +16 -0
  25. package/dist/cli/agent.js +7 -3
  26. package/dist/cli/code-agent-executor.d.ts +1 -0
  27. package/dist/cli/code-agent-executor.js +37 -2
  28. package/dist/cli/code.js +3 -1
  29. package/dist/client/agent-sidebar-state.d.ts +2 -0
  30. package/dist/client/browser-tab-use.d.ts +9 -0
  31. package/dist/client/browser-tab-use.js +112 -0
  32. package/dist/client/chat/runtime.js +3 -1
  33. package/dist/client/error-capture.js +33 -1
  34. package/dist/client/navigation/index.d.ts +1 -0
  35. package/dist/client/navigation/index.js +1 -0
  36. package/dist/client/resources/mcp-integration-logos.js +16 -0
  37. package/dist/client/review/use-review.d.ts +28 -0
  38. package/dist/client/review/use-review.js +41 -12
  39. package/dist/client/route-state.js +2 -0
  40. package/dist/client/sse-event-processor.d.ts +1 -0
  41. package/dist/client/sse-event-processor.js +7 -1
  42. package/dist/client/use-db-sync.d.ts +8 -1
  43. package/dist/client/use-db-sync.js +6 -5
  44. package/dist/coding-tools/run-code.js +12 -2
  45. package/dist/coding-tools/sandbox/executions-store.d.ts +1 -1
  46. package/dist/coding-tools/sandbox/executions-store.js +9 -3
  47. package/dist/collab/routes.d.ts +1 -1
  48. package/dist/data-programs/execute.js +1 -1
  49. package/dist/db/schema-snapshot.generated.js +17 -3
  50. package/dist/extensions/url-safety.d.ts +1 -3
  51. package/dist/extensions/url-safety.js +23 -23
  52. package/dist/file-upload/actions/upload-image.d.ts +5 -5
  53. package/dist/file-upload/index.d.ts +1 -1
  54. package/dist/file-upload/index.js +1 -1
  55. package/dist/file-upload/s3-client.d.ts +1 -0
  56. package/dist/file-upload/s3-client.js +70 -13
  57. package/dist/file-upload/s3.d.ts +9 -0
  58. package/dist/file-upload/s3.js +14 -1
  59. package/dist/framework-package-versions.json +8 -8
  60. package/dist/jobs/run-history.js +10 -3
  61. package/dist/jobs/scheduler.js +5 -36
  62. package/dist/localization/core-messages/supplemental/ar-SA.js +35 -0
  63. package/dist/localization/core-messages/supplemental/de-DE.js +35 -0
  64. package/dist/localization/core-messages/supplemental/en-US.js +35 -0
  65. package/dist/localization/core-messages/supplemental/es-ES.js +35 -0
  66. package/dist/localization/core-messages/supplemental/fr-FR.js +35 -0
  67. package/dist/localization/core-messages/supplemental/hi-IN.js +35 -0
  68. package/dist/localization/core-messages/supplemental/ja-JP.js +35 -0
  69. package/dist/localization/core-messages/supplemental/ko-KR.js +35 -0
  70. package/dist/localization/core-messages/supplemental/pt-BR.js +35 -0
  71. package/dist/localization/core-messages/supplemental/zh-CN.js +35 -0
  72. package/dist/localization/core-messages/supplemental/zh-TW.js +35 -0
  73. package/dist/localization/default-messages.d.ts +35 -0
  74. package/dist/localization/default-messages.js +35 -0
  75. package/dist/localization/mcp-settings-messages.d.ts +35 -0
  76. package/dist/mcp/actions/list-mcp-client-connections.d.ts +8 -0
  77. package/dist/mcp/actions/list-mcp-client-connections.js +39 -0
  78. package/dist/mcp/browser-tab-target.d.ts +16 -0
  79. package/dist/mcp/browser-tab-target.js +58 -0
  80. package/dist/mcp/build-server.d.ts +1 -0
  81. package/dist/mcp/build-server.js +38 -7
  82. package/dist/mcp/call-attribution.d.ts +4 -1
  83. package/dist/mcp/call-attribution.js +3 -1
  84. package/dist/mcp/client-connections.d.ts +6 -0
  85. package/dist/mcp/client-connections.js +91 -0
  86. package/dist/mcp/connect-route.js +12 -2
  87. package/dist/mcp/connect-store.d.ts +3 -0
  88. package/dist/mcp/connect-store.js +10 -1
  89. package/dist/mcp/direct-action-call.js +1 -0
  90. package/dist/mcp/oauth-store.d.ts +6 -0
  91. package/dist/mcp/oauth-store.js +23 -0
  92. package/dist/mcp/oauth-token.d.ts +1 -0
  93. package/dist/mcp/oauth-token.js +1 -0
  94. package/dist/mcp/server.js +9 -20
  95. package/dist/mcp-client/config.d.ts +2 -0
  96. package/dist/mcp-client/manager.js +23 -0
  97. package/dist/mcp-client/workspace-servers.js +42 -10
  98. package/dist/notifications/routes.d.ts +3 -3
  99. package/dist/org/handlers.d.ts +1 -1
  100. package/dist/org/handlers.js +21 -17
  101. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  102. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  103. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  104. package/dist/provider-api/index.js +17 -36
  105. package/dist/review/actions/create-review-comment.d.ts +7 -0
  106. package/dist/review/actions/create-review-comment.js +29 -3
  107. package/dist/review/actions/delete-review-comment.js +2 -2
  108. package/dist/review/actions/list-review-comments.d.ts +3 -0
  109. package/dist/review/actions/list-review-comments.js +41 -17
  110. package/dist/review/actions/react-to-review-comment.js +2 -2
  111. package/dist/review/actions/reply-review-comment.d.ts +4 -0
  112. package/dist/review/actions/reply-review-comment.js +15 -6
  113. package/dist/review/actions/update-review-comment-anchor.d.ts +6 -0
  114. package/dist/review/actions/update-review-comment-anchor.js +39 -10
  115. package/dist/review/actions/update-review-comment.d.ts +3 -0
  116. package/dist/review/actions/update-review-comment.js +28 -3
  117. package/dist/review/errors.d.ts +33 -0
  118. package/dist/review/errors.js +51 -0
  119. package/dist/review/index.d.ts +5 -4
  120. package/dist/review/index.js +3 -2
  121. package/dist/review/registry.d.ts +51 -1
  122. package/dist/review/registry.js +117 -0
  123. package/dist/review/store.d.ts +59 -14
  124. package/dist/review/store.js +622 -101
  125. package/dist/review/suggestions/actions.js +65 -10
  126. package/dist/review/suggestions/actor-kind.d.ts +5 -0
  127. package/dist/review/suggestions/actor-kind.js +20 -0
  128. package/dist/review/suggestions/store.js +16 -2
  129. package/dist/review/suggestions/types.d.ts +13 -0
  130. package/dist/review/types.d.ts +89 -0
  131. package/dist/scripts/db/dev-query-proxy.d.ts +2 -0
  132. package/dist/scripts/db/dev-query-proxy.js +4 -1
  133. package/dist/scripts/db/query.js +3 -0
  134. package/dist/scripts/dev/shell.js +4 -0
  135. package/dist/scripts/dev-session.d.ts +31 -0
  136. package/dist/scripts/dev-session.js +58 -0
  137. package/dist/scripts/resources/list.js +53 -4
  138. package/dist/scripts/runner.js +22 -4
  139. package/dist/search/query.d.ts +11 -5
  140. package/dist/search/query.js +52 -15
  141. package/dist/search/tokenize.d.ts +1 -0
  142. package/dist/search/tokenize.js +3 -0
  143. package/dist/secrets/api-keys.d.ts +4 -0
  144. package/dist/secrets/api-keys.js +5 -0
  145. package/dist/secrets/register.d.ts +2 -0
  146. package/dist/secrets/routes.d.ts +2 -2
  147. package/dist/secrets/routes.js +85 -6
  148. package/dist/secrets/storage.d.ts +2 -0
  149. package/dist/secrets/storage.js +34 -9
  150. package/dist/secrets/substitution.d.ts +3 -2
  151. package/dist/secrets/substitution.js +56 -7
  152. package/dist/server/action-discovery.js +5 -0
  153. package/dist/server/action-routes.js +8 -6
  154. package/dist/server/agent-chat/prompt-resources.js +117 -79
  155. package/dist/server/agent-chat/script-entries.js +15 -8
  156. package/dist/server/agent-chat-plugin.js +9 -4
  157. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  158. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  159. package/dist/server/agents-bundle.d.ts +3 -1
  160. package/dist/server/agents-bundle.js +4 -4
  161. package/dist/server/csrf.js +1 -0
  162. package/dist/server/dev-action-bridge.d.ts +2 -0
  163. package/dist/server/dev-action-bridge.js +34 -5
  164. package/dist/server/first-touch-route.d.ts +1 -1
  165. package/dist/server/http-response-telemetry.js +4 -0
  166. package/dist/server/loopback.js +3 -1
  167. package/dist/server/request-context.d.ts +26 -0
  168. package/dist/shared/app-state-routes.d.ts +7 -0
  169. package/dist/shared/app-state-routes.js +7 -0
  170. package/dist/shared/mcp-client-connections.d.ts +41 -0
  171. package/dist/shared/mcp-client-connections.js +18 -0
  172. package/dist/shared/mcp-connect-content.d.ts +18 -0
  173. package/dist/shared/mcp-connect-content.js +200 -172
  174. package/dist/shared/runtime-config.js +5 -1
  175. package/dist/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
  176. package/dist/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
  177. package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
  178. package/dist/tracking/providers.d.ts +7 -0
  179. package/dist/tracking/providers.js +39 -17
  180. package/dist/triggers/condition-evaluator.d.ts +2 -0
  181. package/dist/triggers/condition-evaluator.js +93 -3
  182. package/dist/triggers/dispatcher.js +24 -6
  183. package/docs/AGENTS.md +16 -16
  184. package/docs/content/create-with-an-agent.mdx +10 -4
  185. package/docs/content/durable-resume.mdx +11 -2
  186. package/docs/content/toolkit-comments-review.mdx +54 -1
  187. package/package.json +5 -4
  188. package/src/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
  189. package/src/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
  190. package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
@@ -26,7 +26,9 @@
26
26
  */
27
27
  import { resolveCredentialForScope } from "../credentials/index.js";
28
28
  import { orderCredentialScopes } from "../server/credential-read-order.js";
29
+ import { isLoopbackHost } from "../server/origin-allowlist.js";
29
30
  import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
31
+ import { getRequiredSecret } from "./register.js";
30
32
  import { readAppSecret, readAppSecretMeta } from "./storage.js";
31
33
  const KEY_REFERENCE_REGEX = /\$\{keys\.([A-Za-z0-9_-]+)\}/g;
32
34
  function isWorkspaceFallbackEnabled() {
@@ -153,24 +155,71 @@ function requestSecretCandidates(userScopeId) {
153
155
  { scope: "workspace", scopeId: `solo:${email}` },
154
156
  ];
155
157
  }
158
+ const DEFAULT_PROVIDER_ORIGINS = {
159
+ OPENAI_API_KEY: ["https://api.openai.com"],
160
+ ANTHROPIC_API_KEY: ["https://api.anthropic.com"],
161
+ GITHUB_TOKEN: ["https://api.github.com"],
162
+ STRIPE_SECRET_KEY: ["https://api.stripe.com"],
163
+ SLACK_BOT_TOKEN: ["https://slack.com"],
164
+ NOTION_API_KEY: ["https://api.notion.com"],
165
+ BUILDER_PUBLIC_KEY: ["https://cdn.builder.io"],
166
+ HUBSPOT_PRIVATE_APP_TOKEN: ["https://api.hubapi.com"],
167
+ HUBSPOT_ACCESS_TOKEN: ["https://api.hubapi.com"],
168
+ CLAY_PUBLIC_API_KEY: ["https://api.clay.com"],
169
+ GONG_ACCESS_KEY: ["https://api.gong.io"],
170
+ GONG_ACCESS_SECRET: ["https://api.gong.io"],
171
+ SENTRY_AUTH_TOKEN: ["https://sentry.io"],
172
+ TWITTER_BEARER_TOKEN: ["https://api.x.com", "https://api.twitter.com"],
173
+ };
174
+ export function registeredUrlAllowlist(name) {
175
+ const configured = getRequiredSecret(name)?.urlAllowlist;
176
+ const origins = configured ??
177
+ (Object.hasOwn(DEFAULT_PROVIDER_ORIGINS, name)
178
+ ? DEFAULT_PROVIDER_ORIGINS[name]
179
+ : undefined);
180
+ return origins ? [...origins] : null;
181
+ }
156
182
  export function validateUrlAllowlist(url, allowlist) {
157
183
  if (!allowlist || allowlist.length === 0)
158
- return true;
184
+ return false;
159
185
  let origin;
160
186
  try {
161
- origin = new URL(url).origin;
187
+ const target = new URL(url);
188
+ if (!usesSecureSecretTransport(target))
189
+ return false;
190
+ origin = target.origin;
162
191
  }
163
192
  catch {
164
193
  return false;
165
194
  }
166
- return allowlist.some((entry) => {
195
+ let matched = false;
196
+ for (const entry of allowlist) {
167
197
  try {
168
- return new URL(entry).origin === origin;
198
+ const policyUrl = new URL(entry);
199
+ if (!usesSecureSecretTransport(policyUrl) ||
200
+ policyUrl.username ||
201
+ policyUrl.password) {
202
+ return false;
203
+ }
204
+ matched ||= policyUrl.origin === origin;
169
205
  }
170
206
  catch {
171
207
  return false;
172
208
  }
173
- });
209
+ }
210
+ return matched;
211
+ }
212
+ function usesSecureSecretTransport(url) {
213
+ return (url.protocol === "https:" ||
214
+ (url.protocol === "http:" && isLoopbackHost(url.host)));
215
+ }
216
+ function effectiveUrlAllowlist(name, stored) {
217
+ const registered = registeredUrlAllowlist(name);
218
+ if (!registered)
219
+ return stored ?? null;
220
+ if (!stored)
221
+ return registered;
222
+ return stored.filter((origin) => validateUrlAllowlist(origin, registered));
174
223
  }
175
224
  /**
176
225
  * Convenience helper: look up a key's allowlist by name+scope. Returns null
@@ -191,7 +240,7 @@ export async function getKeyAllowlist(name, scope, scopeId) {
191
240
  scopeId: getWorkspaceScopeId(scopeId),
192
241
  });
193
242
  }
194
- return meta?.urlAllowlist ?? null;
243
+ return effectiveUrlAllowlist(name, meta?.urlAllowlist);
195
244
  }
196
245
  export async function getResolvedKeyAllowlist(ref) {
197
246
  const meta = await readAppSecretMeta({
@@ -199,7 +248,7 @@ export async function getResolvedKeyAllowlist(ref) {
199
248
  scope: ref.scope,
200
249
  scopeId: ref.scopeId,
201
250
  });
202
- return meta?.urlAllowlist ?? null;
251
+ return effectiveUrlAllowlist(ref.name, meta?.urlAllowlist);
203
252
  }
204
253
  function getWorkspaceScopeId(userScopeId) {
205
254
  const orgId = getRequestOrgId();
@@ -305,6 +305,7 @@ export const ALWAYS_ON_CORE_ACTIONS = new Set([
305
305
  "call-mcp-tool",
306
306
  // Connection setup UI needs the workspace prerequisite even in minimal apps.
307
307
  "get-managed-mcp-oauth-readiness",
308
+ "list-mcp-client-connections",
308
309
  // Any app calling a provider API needs to see which scopes its connection holds.
309
310
  "list-workspace-connection-scopes",
310
311
  "get-agentkit-capabilities",
@@ -861,6 +862,10 @@ export function coreActionLoaderEntries() {
861
862
  "create-org-service-token",
862
863
  () => import("../mcp/actions/create-org-service-token.js"),
863
864
  ],
865
+ [
866
+ "list-mcp-client-connections",
867
+ () => import("../mcp/actions/list-mcp-client-connections.js"),
868
+ ],
864
869
  [
865
870
  "list-org-service-tokens",
866
871
  () => import("../mcp/actions/list-org-service-tokens.js"),
@@ -9,6 +9,7 @@ import { assertActionAccess } from "../authorization/check-action.js";
9
9
  import { isTransientDatabaseError } from "../db/client.js";
10
10
  import { declaresFeatureFlagDelegation } from "../feature-flags/a2a-action-route.js";
11
11
  import { isFeatureFlagAdminEmail } from "../feature-flags/permissions.js";
12
+ import { mcpCallAttribution } from "../mcp/call-attribution.js";
12
13
  import { hasForbiddenOrgScope, hasOrgScopeResolutionUnavailable, isFederationMembershipValidatedForEvent, resolveOrgIdForEmail, resolveOrgScopeForEmail, } from "../org/context.js";
13
14
  import { isWorkspaceAppAccessUnavailable, workspaceAppAccessUnavailableBody, } from "../org/workspace-app-access-outcome.js";
14
15
  import { checkWorkspaceAppAccessForRequest } from "../org/workspace-app-access-request.js";
@@ -1162,12 +1163,13 @@ function mountActionRoutesInternal(nitroApp, actions, options) {
1162
1163
  : {}),
1163
1164
  ...(event.req?.signal ? { signal: event.req.signal } : {}),
1164
1165
  actionName: name,
1165
- ...(caller === "mcp" && mcpCredential?.oauthClientId
1166
- ? {
1167
- mcpClient: {
1168
- oauthClientId: mcpCredential.oauthClientId,
1169
- },
1170
- }
1166
+ ...(caller === "mcp"
1167
+ ? mcpCallAttribution({
1168
+ request: undefined,
1169
+ envelope: undefined,
1170
+ oauthClientId: mcpCredential?.oauthClientId,
1171
+ userAgent: getHeader(event, "user-agent"),
1172
+ })
1171
1173
  : {}),
1172
1174
  ...(resolvedCaller?.delegationJti
1173
1175
  ? {
@@ -1,6 +1,7 @@
1
1
  import { JEV_TIMEOUT_MS, rankJevCandidatesWithStatus, } from "../../agent/jev-tool-prefetch.js";
2
+ import { getAppConfig } from "../../app-config/index.js";
2
3
  import { frameworkGroupEnabled, } from "../../framework-tools.js";
3
- import { getFrontmatterValue, getSkillNameFromPath, parseFrontmatter, } from "../../resources/metadata.js";
4
+ import { getFrontmatterValue, getSkillNameFromPath, isSkillPath, parseFrontmatter, } from "../../resources/metadata.js";
4
5
  import { ensurePersonalDefaults, isWorkspaceResourceOwner, organizationIdFromResourceOwner, resourceGet, resourceGetByPath, resourceList, resourceListAccessible, SHARED_OWNER, sharedResourceOwner, WORKSPACE_OWNER, workspaceResourceOwner, } from "../../resources/store.js";
5
6
  import { discoverAgentContextAgentsWithDirectoryStatus } from "../agent-context-agents.js";
6
7
  import { getRequestOrgId, getRequestContext, getRequestRunContext, getRequestUserEmail, } from "../request-context.js";
@@ -70,7 +71,9 @@ export function compactPromptLine(value, maxChars) {
70
71
  }
71
72
  const SHARED_RESOURCE_INDEX_LIMIT = 40;
72
73
  const PROMPT_SKILL_SUMMARY_LIMIT = 40;
73
- const PROMPT_SKILL_METADATA_READ_LIMIT = 80;
74
+ const PROMPT_SKILL_READ_BATCH_SIZE = 40;
75
+ const PROMPT_SKILL_MAX_READ_BATCHES = 5;
76
+ const PROMPT_SKILL_READ_BUDGET_MS = 2_000;
74
77
  const PROMPT_INSTRUCTION_SUMMARY_LIMIT = 20;
75
78
  const PROMPT_SUMMARY_DESCRIPTION_MAX_CHARS = 180;
76
79
  const JEV_CONTEXT_ITEM_MAX_CHARS = 10_000;
@@ -503,7 +506,7 @@ class RequiredSkillLabsReadError extends Error {
503
506
  this.name = "RequiredSkillLabsReadError";
504
507
  }
505
508
  }
506
- async function loadResourceSkillPromptEntries(owner, orgId) {
509
+ async function loadResourceSkillPromptEntries(owner, orgId, includeSupportingFiles = true) {
507
510
  try {
508
511
  const resources = owner === SHARED_OWNER
509
512
  ? [
@@ -511,59 +514,79 @@ async function loadResourceSkillPromptEntries(owner, orgId) {
511
514
  ...(await resourceList(WORKSPACE_OWNER, "skills/", { orgId })),
512
515
  ]
513
516
  : await resourceListAccessible(owner, "skills/", { orgId });
514
- const sorted = sortResourceSkills(resources, { owner, orgId });
515
- const skillCandidates = sorted.slice(0, PROMPT_SKILL_METADATA_READ_LIMIT);
516
- const loaded = await Promise.all(skillCandidates.map(async (resource) => ({
517
- resource,
518
- // coercion-ok: an unreadable optional skill is absent from Jev's catalog, not a required prompt failure.
519
- full: await resourceGet(resource.id, { orgId }).catch(() => null),
520
- })));
521
- const parsed = loaded.map(({ resource, full }) => ({
522
- resource,
523
- full,
524
- meta: full?.content ? parseSkillFrontmatter(full.content) : null,
525
- }));
526
- const requiredLabs = parsed.flatMap(({ meta }) => meta?.requiresLab ? [meta.requiresLab] : []);
527
- const enabledLabs = requiredLabs.length
528
- ? await import("../agents-bundle.js")
529
- .then(({ getEnabledSkillLabsForUser }) => getEnabledSkillLabsForUser(requiredLabs, owner === SHARED_OWNER
530
- ? (getRequestUserEmail() ?? getRequestRunContext()?.owner)
531
- : owner))
532
- .catch((error) => {
533
- throw new RequiredSkillLabsReadError(error);
534
- })
535
- : new Set();
517
+ const sorted = sortResourceSkills(includeSupportingFiles
518
+ ? resources
519
+ : resources.filter((resource) => isSkillPath(resource.path)), { owner, ...(orgId !== undefined ? { orgId } : {}) });
536
520
  const seen = new Set();
537
521
  const entries = [];
538
- for (const { resource, full, meta } of parsed) {
539
- if (!full?.content || !meta)
540
- continue;
541
- if (meta.userInvocable === false)
542
- continue;
543
- if (!isRuntimeVisibleScope(meta.scope))
544
- continue;
545
- if (meta.requiresLab && !enabledLabs.has(meta.requiresLab))
546
- continue;
547
- const name = meta.name || getSkillNameFromPath(resource.path);
548
- if (!name || seen.has(name))
549
- continue;
550
- seen.add(name);
551
- const scope = resourceScopeForOwner(resource.owner, owner);
552
- const description = compactPromptLine(meta.description || "(no description)", PROMPT_SUMMARY_DESCRIPTION_MAX_CHARS);
553
- entries.push({ resource, full, name, description, scope });
522
+ let metadataRead = 0;
523
+ let hasOmittedSkills = false;
524
+ const deadlineAt = Date.now() + PROMPT_SKILL_READ_BUDGET_MS;
525
+ for (let batch = 0; batch < PROMPT_SKILL_MAX_READ_BATCHES &&
526
+ metadataRead < sorted.length &&
527
+ entries.length < PROMPT_SKILL_SUMMARY_LIMIT &&
528
+ (batch === 0 || Date.now() < deadlineAt); batch++) {
529
+ const skillCandidates = sorted.slice(metadataRead, metadataRead + PROMPT_SKILL_READ_BATCH_SIZE);
530
+ metadataRead += skillCandidates.length;
531
+ const loaded = await Promise.all(skillCandidates.map(async (resource) => ({
532
+ resource,
533
+ full: await resourceGet(resource.id, {
534
+ ...(orgId !== undefined ? { orgId } : {}),
535
+ }).catch(() => null), // coercion-ok: an unreadable optional skill is absent from Jev's catalog, not a required prompt failure.
536
+ })));
537
+ const parsed = loaded.map(({ resource, full }) => ({
538
+ resource,
539
+ full,
540
+ meta: full?.content ? parseSkillFrontmatter(full.content) : null,
541
+ }));
542
+ const requiredLabs = parsed.flatMap(({ meta }) => meta?.requiresLab ? [meta.requiresLab] : []);
543
+ const enabledLabs = requiredLabs.length
544
+ ? await import("../agents-bundle.js")
545
+ .then(({ getEnabledSkillLabsForUser }) => getEnabledSkillLabsForUser(requiredLabs, owner === SHARED_OWNER
546
+ ? (getRequestUserEmail() ?? getRequestRunContext()?.owner)
547
+ : owner))
548
+ .catch((error) => {
549
+ throw new RequiredSkillLabsReadError(error);
550
+ })
551
+ : new Set();
552
+ for (const { resource, full, meta } of parsed) {
553
+ if (!full?.content || !meta)
554
+ continue;
555
+ if (meta.userInvocable === false)
556
+ continue;
557
+ if (!isRuntimeVisibleScope(meta.scope))
558
+ continue;
559
+ if (meta.requiresLab && !enabledLabs.has(meta.requiresLab))
560
+ continue;
561
+ const name = meta.name || getSkillNameFromPath(resource.path);
562
+ if (!name || seen.has(name))
563
+ continue;
564
+ if (entries.length >= PROMPT_SKILL_SUMMARY_LIMIT) {
565
+ hasOmittedSkills = true;
566
+ continue;
567
+ }
568
+ seen.add(name);
569
+ const scope = resourceScopeForOwner(resource.owner, owner);
570
+ const description = compactPromptLine(meta.description || "(no description)", PROMPT_SUMMARY_DESCRIPTION_MAX_CHARS);
571
+ entries.push({ resource, full, name, description, scope });
572
+ }
554
573
  }
555
- return { entries, total: sorted.length, metadataRead: loaded.length };
574
+ return { entries, total: sorted.length, metadataRead, hasOmittedSkills };
556
575
  }
557
576
  catch (error) {
558
577
  if (error instanceof RequiredSkillLabsReadError)
559
578
  throw error;
560
- return { entries: [], total: 0, metadataRead: 0 };
579
+ return { entries: [], total: 0, metadataRead: 0, hasOmittedSkills: false };
561
580
  }
562
581
  }
563
- async function loadResourceSkillsPromptBlock(owner, orgId, runtimeToolHints = true) {
564
- const { entries, total, metadataRead } = await loadResourceSkillPromptEntries(owner, orgId);
582
+ async function loadResourceSkillsPromptBlock(owner, orgId, runtimeToolHints = true, includeSupportingFiles = true) {
583
+ const { entries, total, metadataRead, hasOmittedSkills } = await loadResourceSkillPromptEntries(owner, orgId, includeSupportingFiles);
584
+ const inspectionLimited = total > metadataRead;
585
+ const inspectionNotice = inspectionLimited
586
+ ? `Skill inspection is incomplete after ${metadataRead} candidate read attempts; uninspected candidates remain.`
587
+ : null;
565
588
  if (!runtimeToolHints) {
566
- return (entries
589
+ const blocks = entries
567
590
  .slice(0, PROMPT_SKILL_SUMMARY_LIMIT)
568
591
  .map(({ resource, full, scope }) => promptResourceBlock({
569
592
  name: resource.path,
@@ -573,18 +596,32 @@ async function loadResourceSkillsPromptBlock(owner, orgId, runtimeToolHints = tr
573
596
  maxChars: COMPACT_PROMPT_RESOURCE_MAX_CHARS,
574
597
  readHint: null,
575
598
  }))
576
- .filter(Boolean)
577
- .join("\n\n") || null);
599
+ .filter(Boolean);
600
+ const notices = [
601
+ hasOmittedSkills
602
+ ? "...more skills omitted from the startup summary."
603
+ : null,
604
+ inspectionNotice,
605
+ ].filter(Boolean);
606
+ if (notices.length > 0)
607
+ blocks.push(`<resource-skills>\n${notices.join("\n")}\n</resource-skills>`);
608
+ return blocks.join("\n\n") || null;
578
609
  }
579
610
  const lines = entries
580
611
  .slice(0, PROMPT_SKILL_SUMMARY_LIMIT)
581
612
  .map(({ resource, name, description, scope }) => `- \`${name}\` at resource \`${resource.path}\` (${scope}) - ${ensureSentence(description)} ${resourceToolHint("read", `\`path: "${resource.path}"\` and \`scope: "${scope}"\` before starting a task it applies to`)}`);
582
- if (lines.length === 0)
613
+ if (lines.length === 0 && !inspectionNotice)
583
614
  return null;
584
- if (total > metadataRead || metadataRead > PROMPT_SKILL_SUMMARY_LIMIT) {
615
+ if (inspectionNotice) {
616
+ lines.push(`- ${inspectionNotice} ${resourceToolHint("list", '`prefix: "skills/"` to inspect the full catalog')}`);
617
+ }
618
+ if (hasOmittedSkills) {
585
619
  lines.push(`- ...more skills omitted from the startup summary. ${resourceToolHint("list", '`prefix: "skills/"` to inspect the full catalog')}`);
586
620
  }
587
- return `<resource-skills>\nThe following workspace skills are available in addition to codebase skills. They may come from SQL resources, Dispatch workspace resources, or local file mode. Read a matching skill before starting a task it applies to.\n\n${lines.join("\n")}\n</resource-skills>`;
621
+ const introduction = entries.length > 0
622
+ ? "The following workspace skills are available in addition to codebase skills. They may come from SQL resources, Dispatch workspace resources, or local file mode. Read a matching skill before starting a task it applies to.\n\n"
623
+ : "";
624
+ return `<resource-skills>\n${introduction}${lines.join("\n")}\n</resource-skills>`;
588
625
  }
589
626
  async function loadResourceIndexForPrompt(owner, scope, orgId) {
590
627
  const resources = (await resourceList(owner, undefined, { orgId }))
@@ -1348,6 +1385,7 @@ export async function preloadJevContextWithStatus(options) {
1348
1385
  * the bundle in dev so changes land instantly.
1349
1386
  */
1350
1387
  export async function loadResourcesForPrompt(owner, compact = false, selfAppId, orgId = getRequestOrgId() ?? null, opts) {
1388
+ const { includeSupportingFiles } = getAppConfig().agent.promptSkills;
1351
1389
  const promptPreloadDeadlineAt = opts?.promptPreloadDeadlineAt ?? Date.now() + JEV_PRELOAD_BUDGET_MS;
1352
1390
  await ensurePersonalDefaults(owner);
1353
1391
  const skillReadTool = opts?.skillReadTool === undefined ? "docs-search" : opts.skillReadTool;
@@ -1398,7 +1436,9 @@ export async function loadResourcesForPrompt(owner, compact = false, selfAppId,
1398
1436
  ? (getRequestUserEmail() ?? getRequestRunContext()?.owner)
1399
1437
  : owner);
1400
1438
  if (!compact && (runtimeToolHints || skillReadTool)) {
1401
- const skillsBlock = generateSkillsPromptBlock(bundle, runtimeSkills);
1439
+ const skillsBlock = generateSkillsPromptBlock(bundle, runtimeSkills, {
1440
+ includeSupportingFiles,
1441
+ });
1402
1442
  addSection(skillsBlock);
1403
1443
  }
1404
1444
  else if (skillReadTool) {
@@ -1454,7 +1494,29 @@ export async function loadResourcesForPrompt(owner, compact = false, selfAppId,
1454
1494
  }), "required");
1455
1495
  }
1456
1496
  }
1457
- const resourceSkillsBlock = await loadResourceSkillsPromptBlock(owner, orgId, runtimeToolHints);
1497
+ const appDiscoveryPromise = frameworkGroupEnabled(opts?.disabledFrameworkGroups, "workspaceApps")
1498
+ ? (async () => {
1499
+ try {
1500
+ const appRead = await withinPromptBudget(() => discoverAgentContextAgentsWithDirectoryStatus(selfAppId, {
1501
+ includePersonalAgents: owner !== SHARED_OWNER &&
1502
+ getRequestContext()?.userEmail === owner,
1503
+ }), promptPreloadDeadlineAt);
1504
+ if (appRead.status === "completed")
1505
+ return appRead.value;
1506
+ return {
1507
+ agents: [],
1508
+ directory: { status: "unavailable", reason: "discovery-timed-out" },
1509
+ };
1510
+ }
1511
+ catch {
1512
+ return {
1513
+ agents: [],
1514
+ directory: { status: "unavailable", reason: "discovery-failed" },
1515
+ };
1516
+ }
1517
+ })()
1518
+ : undefined;
1519
+ const resourceSkillsBlock = await loadResourceSkillsPromptBlock(owner, orgId, runtimeToolHints, includeSupportingFiles);
1458
1520
  addSection(resourceSkillsBlock);
1459
1521
  let sharedLearnings = null;
1460
1522
  try {
@@ -1530,32 +1592,8 @@ export async function loadResourcesForPrompt(owner, compact = false, selfAppId,
1530
1592
  selfAppId,
1531
1593
  orgId,
1532
1594
  }));
1533
- if (frameworkGroupEnabled(opts?.disabledFrameworkGroups, "workspaceApps")) {
1534
- let discovery = {
1535
- agents: [],
1536
- directory: { status: "unavailable", reason: "discovery-timed-out" },
1537
- };
1538
- try {
1539
- const appRead = await withinPromptBudget(() => discoverAgentContextAgentsWithDirectoryStatus(selfAppId, {
1540
- includePersonalAgents: owner !== SHARED_OWNER &&
1541
- getRequestContext()?.userEmail === owner,
1542
- }), promptPreloadDeadlineAt);
1543
- if (appRead.status === "completed") {
1544
- discovery = appRead.value;
1545
- }
1546
- else {
1547
- discovery = {
1548
- agents: [],
1549
- directory: { status: "unavailable", reason: "discovery-timed-out" },
1550
- };
1551
- }
1552
- }
1553
- catch {
1554
- discovery = {
1555
- agents: [],
1556
- directory: { status: "unavailable", reason: "discovery-failed" },
1557
- };
1558
- }
1595
+ if (appDiscoveryPromise) {
1596
+ const discovery = await appDiscoveryPromise;
1559
1597
  try {
1560
1598
  const selection = selectWorkspaceAppCandidates(discovery.agents, opts?.requestText ?? getRequestRunContext()?.appDiscoveryQuery ?? "", 30);
1561
1599
  const agents = selection.agents;
@@ -319,7 +319,7 @@ export async function createResourceScriptEntries() {
319
319
  },
320
320
  path: {
321
321
  type: "string",
322
- description: "Resource path (e.g. 'LEARNINGS.md', 'notes/ideas.md'). Required for read/write/delete.",
322
+ description: "Resource path (e.g. 'notes/ideas.md'). Required for read/write/delete.",
323
323
  },
324
324
  content: {
325
325
  type: "string",
@@ -327,7 +327,7 @@ export async function createResourceScriptEntries() {
327
327
  },
328
328
  offset: {
329
329
  type: "string",
330
- description: "read: start character (default 0); use the partial header's next offset.",
330
+ description: "read: start character; list: start entry. Use the next offset given.",
331
331
  },
332
332
  length: {
333
333
  type: "string",
@@ -340,17 +340,21 @@ export async function createResourceScriptEntries() {
340
340
  },
341
341
  prefix: {
342
342
  type: "string",
343
- description: "Filter by path prefix when listing (e.g. 'notes/')",
343
+ description: "list: path prefix filter (e.g. 'notes/')",
344
344
  },
345
345
  mime: {
346
346
  type: "string",
347
- description: "MIME type for write (default: inferred from extension)",
347
+ description: "write: MIME type (default: from extension)",
348
348
  },
349
349
  format: {
350
350
  type: "string",
351
- description: 'Output format for list: "json" or "text" (default: text)',
351
+ description: "list output format (default: text)",
352
352
  enum: ["json", "text"],
353
353
  },
354
+ limit: {
355
+ type: "number",
356
+ description: "list: page size, 1-100.",
357
+ },
354
358
  visibility: {
355
359
  type: "string",
356
360
  description: 'Visibility for write: "agent_scratch" for internal working files, "workspace" for user-requested files. Defaults to agent_scratch except durable instruction/skill/job/memory paths.',
@@ -358,7 +362,7 @@ export async function createResourceScriptEntries() {
358
362
  },
359
363
  includeAgentScratch: {
360
364
  type: "boolean",
361
- description: "Include hidden agent scratch files when listing.",
365
+ description: "list: include hidden agent scratch files.",
362
366
  },
363
367
  },
364
368
  required: ["action"],
@@ -377,8 +381,11 @@ export async function createResourceScriptEntries() {
377
381
  allowPersistentApproval: false,
378
382
  run: async (args, context) => {
379
383
  const { action: a, ...rest } = args;
380
- if (a === "list")
381
- return listEntry.run(rest);
384
+ if (a === "list") {
385
+ // The CLI keeps unpaged json as a bare array; the agent always pages.
386
+ const noPageArgs = !rest.limit && !rest.offset;
387
+ return listEntry.run(noPageArgs ? { ...rest, offset: "0" } : rest);
388
+ }
382
389
  if (a === "read") {
383
390
  if (!rest.path)
384
391
  return "Error: path is required for read";
@@ -1439,7 +1439,7 @@ export function createAgentChatPlugin(options) {
1439
1439
  validateUrl: async (url, usedKeys, resolvedKeys) => {
1440
1440
  for (const keyName of usedKeys) {
1441
1441
  const allowlist = await resolveFetchToolKeyAllowlist(keyName, resolvedKeys, requireCurrentRunOwner("validate URL allowlist"), { getKeyAllowlist, getResolvedKeyAllowlist });
1442
- if (allowlist && !validateUrlAllowlist(url, allowlist)) {
1442
+ if (!validateUrlAllowlist(url, allowlist)) {
1443
1443
  return false;
1444
1444
  }
1445
1445
  }
@@ -5682,9 +5682,14 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5682
5682
  if (backgroundEventsMatch && method === "GET") {
5683
5683
  const runId = decodeURIComponent(backgroundEventsMatch[1]);
5684
5684
  const { getAgentTeamBackgroundRun, listAgentTeamBackgroundTranscriptEvents, } = await import("./agent-teams.js");
5685
- const run = await runWithRequestContext({ userEmail: owner }, () => getAgentTeamBackgroundRun(runId));
5686
- if (run) {
5687
- const events = await runWithRequestContext({ userEmail: owner }, () => listAgentTeamBackgroundTranscriptEvents(runId));
5685
+ const scope = { ownerEmail: owner, orgId: orgId ?? null };
5686
+ const requestScope = {
5687
+ userEmail: owner,
5688
+ ...(orgId !== undefined ? { orgId } : {}),
5689
+ };
5690
+ const run = await runWithRequestContext(requestScope, () => getAgentTeamBackgroundRun(runId, scope));
5691
+ if (run && (await canViewThread(run.sourceRecord.threadId))) {
5692
+ const events = await runWithRequestContext(requestScope, () => listAgentTeamBackgroundTranscriptEvents(runId, scope));
5688
5693
  return { status: "ok", runId, events };
5689
5694
  }
5690
5695
  const { getAgentHarnessBackgroundRun, listAgentHarnessBackgroundTranscriptEvents, } = await import("../agent/harness/background.js");
@@ -66,8 +66,8 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
66
66
  error: string;
67
67
  code: ProviderKeyCheckCode;
68
68
  } | {
69
- errorCode?: undefined;
70
69
  error?: undefined;
70
+ errorCode?: undefined;
71
71
  code?: undefined;
72
72
  ok: boolean;
73
73
  key: string;
@@ -1,6 +1,6 @@
1
1
  export declare function createAgentEngineOllamaModelsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
- error: string;
3
2
  ok?: undefined;
3
+ error: string;
4
4
  models?: undefined;
5
5
  } | {
6
6
  error?: undefined;
@@ -44,6 +44,8 @@ export declare function getEnabledSkillLabsForUser(requiredLabs: Iterable<string
44
44
  export declare function getDevelopmentSkills(bundle: AgentsBundle): Skill[];
45
45
  export declare function skillDocsSlug(name: string): string;
46
46
  export declare function skillSubfileDocsSlug(skillName: string, relPath: string): string;
47
- export declare function generateSkillsPromptBlock(bundle: AgentsBundle, runtimeSkills?: Skill[]): string;
47
+ export declare function generateSkillsPromptBlock(bundle: AgentsBundle, runtimeSkills?: Skill[], options?: {
48
+ includeSupportingFiles?: boolean;
49
+ }): string;
48
50
  export declare function generateDevelopmentSkillsPromptBlock(bundle: AgentsBundle): string;
49
51
  export declare function __resetAgentsBundleCache(): void;
@@ -324,12 +324,12 @@ function subfileSlugSuffix(relPath) {
324
324
  export function skillSubfileDocsSlug(skillName, relPath) {
325
325
  return `${skillDocsSlug(skillName)}--${subfileSlugSuffix(relPath)}`;
326
326
  }
327
- function generateSkillsPromptBlockForEntries(entries, mode) {
327
+ function generateSkillsPromptBlockForEntries(entries, mode, includeSupportingFiles = true) {
328
328
  if (entries.length === 0)
329
329
  return "";
330
330
  const lines = entries.map((s) => {
331
331
  let extras = "";
332
- if (s.extraFiles.length > 0) {
332
+ if (includeSupportingFiles && s.extraFiles.length > 0) {
333
333
  if (mode === "runtime") {
334
334
  const readable = s.extraFiles.filter((f) => f in s.files);
335
335
  const unreadable = s.extraFiles.filter((f) => !(f in s.files));
@@ -367,8 +367,8 @@ Available skills:
367
367
  ${lines.join("\n")}
368
368
  </skills>`;
369
369
  }
370
- export function generateSkillsPromptBlock(bundle, runtimeSkills = getRuntimeSkills(bundle)) {
371
- return generateSkillsPromptBlockForEntries(runtimeSkills, "runtime");
370
+ export function generateSkillsPromptBlock(bundle, runtimeSkills = getRuntimeSkills(bundle), options) {
371
+ return generateSkillsPromptBlockForEntries(runtimeSkills, "runtime", options?.includeSupportingFiles);
372
372
  }
373
373
  export function generateDevelopmentSkillsPromptBlock(bundle) {
374
374
  return generateSkillsPromptBlockForEntries(getDevelopmentSkills(bundle), "development");
@@ -65,6 +65,7 @@ import { getConfiguredAppBasePath } from "./app-base-path.js";
65
65
  * and cookie-authenticated control routes share one subtree.
66
66
  */
67
67
  const CSRF_PROTECTED_PREFIXES = [
68
+ "/auth/logout-all",
68
69
  // Remote-device relay. The device's own routes (poll/result/heartbeat)
69
70
  // authenticate with a bearer token and send no cookies, so they never reach
70
71
  // the check; the sibling routes (register, enqueue, computer/approvals,
@@ -27,6 +27,8 @@ export declare const DEV_DB_MIGRATE_ROUTE = "/_agent-native/dev/db-migrate";
27
27
  export declare const DEV_ACTION_TOKEN_HEADER = "x-agent-native-dev-token";
28
28
  export declare const DEV_ACTION_USER_HEADER = "x-agent-native-dev-user";
29
29
  export declare const DEV_ACTION_ORG_HEADER = "x-agent-native-dev-org";
30
+ /** "personal" when the CLI caller acts in no org; see AGENT_ORG_SCOPE_ENV. */
31
+ export declare const DEV_ACTION_ORG_SCOPE_HEADER = "x-agent-native-dev-org-scope";
30
32
  export { readDevActionDiscoveryFile } from "./dev-action-discovery.js";
31
33
  export declare function hashDatabaseKey(databaseUrl: string): string;
32
34
  export declare function isLoopbackDevActionOrigin(origin: string): boolean;