@agent-native/core 0.157.6 → 0.157.8

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 (93) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/agent-native.config.ts +5 -0
  3. package/corpus/templates/analytics/netlify.toml +1 -0
  4. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +23 -5
  5. package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
  6. package/corpus/templates/assets/netlify.toml +3 -0
  7. package/corpus/templates/brain/netlify.toml +3 -0
  8. package/corpus/templates/calendar/agent-native.config.ts +5 -0
  9. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +4 -1
  10. package/corpus/templates/calendar/netlify.toml +3 -0
  11. package/corpus/templates/chat/agent-native.config.ts +5 -0
  12. package/corpus/templates/chat/netlify.toml +3 -0
  13. package/corpus/templates/clips/actions/lib/meeting-content.ts +28 -0
  14. package/corpus/templates/clips/actions/list-meetings.ts +33 -11
  15. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  16. package/corpus/templates/clips/app/lib/meeting-history-query.ts +11 -0
  17. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +47 -21
  18. package/corpus/templates/clips/netlify.toml +4 -0
  19. package/corpus/templates/content/netlify.toml +3 -0
  20. package/corpus/templates/design/app/i18n-data.ts +2 -0
  21. package/corpus/templates/design/netlify.toml +3 -0
  22. package/corpus/templates/dispatch/netlify.toml +3 -0
  23. package/corpus/templates/factory/netlify.toml +3 -0
  24. package/corpus/templates/forms/netlify.toml +3 -0
  25. package/corpus/templates/macros/netlify.toml +3 -0
  26. package/corpus/templates/mail/agent-native.config.ts +5 -0
  27. package/corpus/templates/mail/netlify.toml +3 -0
  28. package/corpus/templates/plan/netlify.toml +3 -0
  29. package/corpus/templates/slides/netlify.toml +3 -0
  30. package/dist/agent/harness/acp-adapter.js +17 -2
  31. package/dist/agent/harness/ai-sdk-adapter.js +14 -3
  32. package/dist/agent/harness/background.js +10 -5
  33. package/dist/agent/harness/hosted.d.ts +20 -0
  34. package/dist/agent/harness/hosted.js +100 -0
  35. package/dist/agent/harness/runner.js +100 -77
  36. package/dist/agent/observational-memory/observer.js +12 -0
  37. package/dist/agent/production-agent.d.ts +3 -0
  38. package/dist/agent/production-agent.js +74 -4
  39. package/dist/agent/thread-data-builder.d.ts +12 -0
  40. package/dist/agent/thread-data-builder.js +23 -0
  41. package/dist/agent/types.d.ts +6 -0
  42. package/dist/client/AgentPanel.d.ts +5 -1
  43. package/dist/client/AgentPanel.js +113 -17
  44. package/dist/client/AgentTaskCard.d.ts +2 -2
  45. package/dist/client/AgentTaskCard.js +8 -22
  46. package/dist/client/AssistantChat.d.ts +8 -0
  47. package/dist/client/AssistantChat.js +106 -59
  48. package/dist/client/MultiTabAssistantChat.js +50 -6
  49. package/dist/client/NewWorkspaceAppFlow.js +7 -1
  50. package/dist/client/agent-chat-adapter.d.ts +6 -0
  51. package/dist/client/agent-chat-adapter.js +36 -8
  52. package/dist/client/app-chat-sidebar.d.ts +5 -0
  53. package/dist/client/app-chat-sidebar.js +53 -9
  54. package/dist/client/chat/composer-draft.d.ts +4 -0
  55. package/dist/client/chat/composer-draft.js +63 -0
  56. package/dist/client/chat-first/apps-rail.js +10 -1
  57. package/dist/client/chat-first/primary-nav.js +10 -5
  58. package/dist/client/frame-protocol.d.ts +1 -0
  59. package/dist/client/hooks/index.d.ts +1 -1
  60. package/dist/client/hooks/index.js +1 -1
  61. package/dist/client/i18n.js +5 -2
  62. package/dist/client/progress/RunsTray.js +129 -16
  63. package/dist/client/settings/SettingsRow.js +1 -1
  64. package/dist/client/use-chat-threads.js +11 -6
  65. package/dist/config.d.ts +11 -0
  66. package/dist/config.js +52 -0
  67. package/dist/extensions/actions.js +22 -1
  68. package/dist/hosted-harness/actions/get-hosted-harness-config.d.ts +2 -0
  69. package/dist/hosted-harness/actions/get-hosted-harness-config.js +13 -0
  70. package/dist/hosted-harness/actions/set-hosted-harness-enabled.d.ts +4 -0
  71. package/dist/hosted-harness/actions/set-hosted-harness-enabled.js +32 -0
  72. package/dist/localization/default-messages.d.ts +4 -1
  73. package/dist/localization/default-messages.js +5 -2
  74. package/dist/notifications/routes.d.ts +1 -1
  75. package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
  76. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  77. package/dist/server/action-discovery.js +12 -0
  78. package/dist/server/agent-chat-plugin.js +24 -4
  79. package/dist/server/agent-teams.d.ts +20 -12
  80. package/dist/server/agent-teams.js +69 -35
  81. package/dist/server/hosted-harness-policy.d.ts +17 -0
  82. package/dist/server/hosted-harness-policy.js +38 -0
  83. package/dist/server/poll.d.ts +4 -4
  84. package/dist/server/poll.js +45 -13
  85. package/dist/server/realtime-token.d.ts +1 -1
  86. package/dist/server/request-context.d.ts +4 -0
  87. package/dist/styles/agent-native.css +39 -0
  88. package/dist/templates/chat/agent-native.config.ts +5 -0
  89. package/dist/templates/chat/netlify.toml +3 -0
  90. package/dist/vite/action-types-plugin.js +8 -0
  91. package/package.json +5 -3
  92. package/src/templates/chat/agent-native.config.ts +5 -0
  93. package/src/templates/chat/netlify.toml +3 -0
@@ -1242,6 +1242,27 @@ async function summarizeExtensionForAgentRead(row, hiddenIds, includeContent, fo
1242
1242
  const fingerprint = contentFingerprint(row.content);
1243
1243
  if (contentQuery) {
1244
1244
  const summary = await summarizeExtension(row, hiddenIds, false);
1245
+ // The whole-body read below is deduped per run, but this branch returned
1246
+ // above it, so a repeated excerpt request re-sent bytes already in context.
1247
+ // One production turn spent 48 of 110 reads re-fetching the same spans.
1248
+ const excerptCtx = getRequestRunContext();
1249
+ const excerpts = excerptCtx
1250
+ ? (excerptCtx.extensionExcerptReads ??= {})
1251
+ : undefined;
1252
+ const excerptKey = `${row.id}:${fingerprint}:${contentContextChars}:${contentQuery}`;
1253
+ if (excerpts?.[excerptKey]) {
1254
+ return {
1255
+ ...summary,
1256
+ contentOmitted: {
1257
+ reason: "identical-excerpt-already-returned-this-run",
1258
+ contentHash: fingerprint,
1259
+ contentLength: row.content.length,
1260
+ next: 'These exact excerpts were already returned earlier in this run and the body has not changed. Use them to call update-extension with operation="edit"; use a different contentQuery only for a source area you have not read yet.',
1261
+ },
1262
+ };
1263
+ }
1264
+ if (excerpts)
1265
+ excerpts[excerptKey] = true;
1245
1266
  return {
1246
1267
  ...summary,
1247
1268
  contentMatches: findExtensionContentMatches(row.content, contentQuery, contentContextChars),
@@ -1266,7 +1287,7 @@ async function summarizeExtensionForAgentRead(row, hiddenIds, includeContent, fo
1266
1287
  contentHash: fingerprint,
1267
1288
  contentLength: row.content.length,
1268
1289
  inlineContentLimit: LARGE_EXTENSION_INLINE_CONTENT_MAX_CHARS,
1269
- next: "Call get-extension again with contentQuery set to the relevant function, variable, section marker, or literal text. Use forceContent=true only for a broad rewrite that truly needs the entire body.",
1290
+ next: `Call get-extension again with contentQuery set to the relevant function, variable, section marker, or literal text. If you expect to read more than about three separate areas of this ${row.content.length}-char body, call get-extension once with forceContent=true instead — repeated excerpt reads of the same file cost more context than one whole read.`,
1270
1291
  },
1271
1292
  };
1272
1293
  }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../../action.js").ActionDefinition<Record<string, never>, import("../../server/hosted-harness-policy.js").HostedHarnessPolicy>;
2
+ export default _default;
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import { defineAction } from "../../action.js";
3
+ import { hostedHarnessStatusForClient, resolveHostedHarnessPolicy, } from "../../server/hosted-harness-policy.js";
4
+ export default defineAction({
5
+ description: "Return whether this app's hosted tools-only harness is enabled for the current organization and which runtimes are available.",
6
+ schema: z.object({}),
7
+ http: { method: "GET" },
8
+ agentTool: false,
9
+ run: async (_args, context) => hostedHarnessStatusForClient(await resolveHostedHarnessPolicy({
10
+ orgId: context?.orgId,
11
+ userEmail: context?.userEmail,
12
+ })),
13
+ });
@@ -0,0 +1,4 @@
1
+ declare const _default: import("../../action.js").ActionDefinition<{
2
+ enabled: boolean;
3
+ }, import("../../server/hosted-harness-policy.js").HostedHarnessPolicy>;
4
+ export default _default;
@@ -0,0 +1,32 @@
1
+ import { z } from "zod";
2
+ import { defineAction } from "../../action.js";
3
+ import { HOSTED_HARNESS_ORG_SETTING_KEY } from "../../agent/harness/hosted.js";
4
+ import { getOrgRoleForEmail } from "../../mcp/actions/service-token-access.js";
5
+ import { canManageOrg } from "../../org/permissions.js";
6
+ import { hostedHarnessStatusForClient, resolveHostedHarnessPolicy, } from "../../server/hosted-harness-policy.js";
7
+ import { putOrgSetting } from "../../settings/org-settings.js";
8
+ export default defineAction({
9
+ description: "Enable or disable the hosted tools-only harness for the current organization. Organization owners and admins only.",
10
+ schema: z.object({ enabled: z.boolean() }),
11
+ http: { method: "POST" },
12
+ agentTool: false,
13
+ run: async (args, context) => {
14
+ const email = context?.userEmail?.trim().toLowerCase();
15
+ const orgId = context?.orgId?.trim();
16
+ if (!email)
17
+ throw new Error("Sign in to manage hosted harnesses.");
18
+ if (!orgId)
19
+ throw new Error("Select an organization to manage harnesses.");
20
+ const role = await getOrgRoleForEmail(orgId, email);
21
+ if (!canManageOrg(role)) {
22
+ throw new Error("Only organization owners and admins can manage hosted harnesses.");
23
+ }
24
+ await putOrgSetting(orgId, HOSTED_HARNESS_ORG_SETTING_KEY, {
25
+ enabled: args.enabled,
26
+ });
27
+ return hostedHarnessStatusForClient(await resolveHostedHarnessPolicy({
28
+ orgId,
29
+ userEmail: email,
30
+ }));
31
+ },
32
+ });
@@ -733,6 +733,9 @@ declare const messages: {
733
733
  statusDone: string;
734
734
  statusFailed: string;
735
735
  statusStopped: string;
736
+ statusNeedsApproval: string;
737
+ statusNeedsInput: string;
738
+ statusPaused: string;
736
739
  updatedJustNow: string;
737
740
  finishedJustNow: string;
738
741
  updatedMinutes: string;
@@ -743,7 +746,7 @@ declare const messages: {
743
746
  finishedDate: string;
744
747
  };
745
748
  agentTask: {
746
- backgroundTask: string;
749
+ spawnedAgent: string;
747
750
  stop: string;
748
751
  openThread: string;
749
752
  };
@@ -739,6 +739,9 @@ const messages = {
739
739
  statusDone: "Done",
740
740
  statusFailed: "Failed",
741
741
  statusStopped: "Stopped",
742
+ statusNeedsApproval: "Needs approval",
743
+ statusNeedsInput: "Needs input",
744
+ statusPaused: "Paused",
742
745
  updatedJustNow: "Updated just now",
743
746
  finishedJustNow: "Finished just now",
744
747
  updatedMinutes: "Updated {{count}}m ago",
@@ -749,8 +752,8 @@ const messages = {
749
752
  finishedDate: "Finished {{date}}",
750
753
  },
751
754
  agentTask: {
752
- backgroundTask: "Background task",
753
- stop: "Stop background task",
755
+ spawnedAgent: "Spawned agent",
756
+ stop: "Stop spawned agent",
754
757
  openThread: "Open task thread",
755
758
  },
756
759
  codeRequired: {
@@ -28,6 +28,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
28
28
  } | {
29
29
  count?: undefined;
30
30
  updated?: undefined;
31
- ok: boolean;
32
31
  error?: undefined;
32
+ ok: boolean;
33
33
  }>>;
@@ -75,8 +75,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- id?: undefined;
79
78
  message?: undefined;
79
+ id?: undefined;
80
+ provider?: undefined;
80
81
  deleted?: undefined;
81
82
  providers: {
82
83
  id: string;
@@ -88,14 +89,13 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
88
89
  updatedAt: number;
89
90
  }[];
90
91
  count: number;
91
- provider?: undefined;
92
92
  found?: undefined;
93
93
  registered?: undefined;
94
94
  label?: undefined;
95
95
  } | {
96
- id?: undefined;
97
96
  message?: undefined;
98
97
  count?: undefined;
98
+ id?: undefined;
99
99
  deleted?: undefined;
100
100
  providers?: undefined;
101
101
  found: boolean;
@@ -105,9 +105,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
105
105
  } | {
106
106
  message?: undefined;
107
107
  count?: undefined;
108
+ provider?: undefined;
108
109
  deleted?: undefined;
109
110
  providers?: undefined;
110
- provider?: undefined;
111
111
  found: boolean;
112
112
  id: string;
113
113
  registered?: undefined;
@@ -115,8 +115,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
115
115
  } | {
116
116
  message?: undefined;
117
117
  count?: undefined;
118
- providers?: undefined;
119
118
  provider?: undefined;
119
+ providers?: undefined;
120
120
  found?: undefined;
121
121
  deleted: boolean;
122
122
  id: string;
@@ -124,9 +124,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
124
124
  label?: undefined;
125
125
  } | {
126
126
  count?: undefined;
127
+ provider?: undefined;
127
128
  deleted?: undefined;
128
129
  providers?: undefined;
129
- provider?: undefined;
130
130
  found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
@@ -313,7 +313,6 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
313
313
  }, {
314
314
  deleted?: undefined;
315
315
  id?: undefined;
316
- provider?: undefined;
317
316
  providers: {
318
317
  id: string;
319
318
  label: string;
@@ -324,6 +323,7 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
324
323
  updatedAt: number;
325
324
  }[];
326
325
  count: number;
326
+ provider?: undefined;
327
327
  found?: undefined;
328
328
  registered?: undefined;
329
329
  label?: undefined;
@@ -340,18 +340,18 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
340
340
  message?: undefined;
341
341
  } | {
342
342
  deleted?: undefined;
343
- provider?: undefined;
344
343
  providers?: undefined;
345
344
  count?: undefined;
345
+ provider?: undefined;
346
346
  found: boolean;
347
347
  id: string;
348
348
  registered?: undefined;
349
349
  label?: undefined;
350
350
  message?: undefined;
351
351
  } | {
352
- provider?: undefined;
353
352
  providers?: undefined;
354
353
  count?: undefined;
354
+ provider?: undefined;
355
355
  found?: undefined;
356
356
  deleted: boolean;
357
357
  id: string;
@@ -360,9 +360,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
360
360
  message?: undefined;
361
361
  } | {
362
362
  deleted?: undefined;
363
- provider?: undefined;
364
363
  providers?: undefined;
365
364
  count?: undefined;
365
+ provider?: undefined;
366
366
  found?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
@@ -529,6 +529,10 @@ export const ALWAYS_ON_CORE_ACTIONS = new Set([
529
529
  "revoke-org-service-token",
530
530
  "list-mcp-tools",
531
531
  "call-mcp-tool",
532
+ // Hosted harness capability/policy routes are UI-facing and deliberately
533
+ // never enter the model's action surface (`agentTool: false`).
534
+ "get-hosted-harness-config",
535
+ "set-hosted-harness-enabled",
532
536
  ]);
533
537
  export async function mergeCoreSharingActions(registry) {
534
538
  const entries = [
@@ -592,6 +596,14 @@ export async function mergeCoreSharingActions(registry) {
592
596
  "get-feature-flags",
593
597
  () => import("../feature-flags/actions/get-feature-flags.js"),
594
598
  ],
599
+ [
600
+ "get-hosted-harness-config",
601
+ () => import("../hosted-harness/actions/get-hosted-harness-config.js"),
602
+ ],
603
+ [
604
+ "set-hosted-harness-enabled",
605
+ () => import("../hosted-harness/actions/set-hosted-harness-enabled.js"),
606
+ ],
595
607
  [
596
608
  "list-feature-flags",
597
609
  () => import("../feature-flags/actions/list-feature-flags.js"),
@@ -12,6 +12,7 @@ import { AGENT_CHAT_BACKGROUND_RUN_FIELD, AGENT_CHAT_PROCESS_RUN_PATH, backgroun
12
12
  import { resolveEngine, createAnthropicEngine, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, getAgentEngineEntry, isAgentEnginePackageInstalled, isStoredEngineUsableForRequest, listAgentEngines, registerBuiltinEngines, } from "../agent/engine/index.js";
13
13
  import { SYSTEM_PROMPT_CACHE_SPLIT } from "../agent/engine/prompt-cache.js";
14
14
  import { PROVIDER_TO_ENV } from "../agent/engine/provider-env-vars.js";
15
+ import { hostedHarnessSystemPrompt } from "../agent/harness/hosted.js";
15
16
  import { createProductionAgentHandler, actionsToEngineTools, executeAgentToolCall, filterActionsByAllowedNames, readPersistedActionSurface, toolCallCacheKey, getActiveRunForThreadAsync, abortRunDurably, abortTurnDurably, subscribeToRun, } from "../agent/production-agent.js";
16
17
  import { callerHasRunAccess, callerHasThreadAccess, } from "../agent/run-ownership.js";
17
18
  import { markTurnAborted, readBackgroundRunClaim } from "../agent/run-store.js";
@@ -44,6 +45,7 @@ import { captureError } from "./capture-error.js";
44
45
  import { getH3App, markDefaultPluginProvided, trackPluginInit, } from "./framework-request-handler.js";
45
46
  import { getOrigin } from "./google-oauth.js";
46
47
  import { readBody } from "./h3-helpers.js";
48
+ import { loadHostedHarnessConfig } from "./hosted-harness-policy.js";
47
49
  import { startIntervalJob } from "./interval-job.js";
48
50
  import { getModelFamilyOverlay } from "./prompts/index.js";
49
51
  import { mountRealtimeVoiceRoutes } from "./realtime-voice.js";
@@ -309,6 +311,7 @@ export function createAgentChatPlugin(options) {
309
311
  // `/runs/active` reaps the requested run on demand, and the periodic
310
312
  // recovery sweep below handles abandoned runs with no connected client.
311
313
  const env = process.env.NODE_ENV;
314
+ const hostedHarnessConfig = await loadHostedHarnessConfig();
312
315
  // AGENT_MODE=production forces production agent constraints even in dev
313
316
  const canToggle = (env === "development" || env === "test") &&
314
317
  process.env.AGENT_MODE !== "production";
@@ -2694,6 +2697,13 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
2694
2697
  const codeEditingSurfaceRestriction = shouldBlockInProductCodeEditing(event)
2695
2698
  ? APP_RENDERED_CHAT_NO_DIRECT_CODE_PROMPT
2696
2699
  : "";
2700
+ const hostedHarnessRuntime = getRequestRunContext()?.hostedHarnessRuntime;
2701
+ const hostedHarnessPromptNote = hostedHarnessRuntime
2702
+ ? hostedHarnessSystemPrompt(hostedHarnessRuntime)
2703
+ : "";
2704
+ const requestProdCodeExecPromptNote = hostedHarnessRuntime
2705
+ ? ""
2706
+ : prodCodeExecPromptNote;
2697
2707
  // Per-model overlay: nudge GPT/Gemini engines toward our behavioral norms.
2698
2708
  const modelOverlay = resolveModelOverlay();
2699
2709
  // Stable-first ordering: base prompt / schema / extra come before
@@ -2705,7 +2715,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
2705
2715
  // `SYSTEM_PROMPT_CACHE_SPLIT` marks where the cacheable prefix ends,
2706
2716
  // so everything after it can change without a cache write.
2707
2717
  if (leanPrompt) {
2708
- const leanRunPolicyPrompt = buildLeanRunPolicyPrompt(codeEditingSurfaceRestriction, prodCodeExecPromptNote);
2718
+ const leanRunPolicyPrompt = buildLeanRunPolicyPrompt(codeEditingSurfaceRestriction, `${requestProdCodeExecPromptNote}${hostedHarnessPromptNote ? `\n\n${hostedHarnessPromptNote}` : ""}`);
2709
2719
  const resources = await loadResourcesForPrompt(owner, true, options?.appId, undefined, { disabledFrameworkGroups });
2710
2720
  await emitContextXraySystemSections(event, {
2711
2721
  frameworkPrompt: requestLeanPrompt.slice(0, Math.max(0, requestLeanPrompt.length - requestLeanActionsPrompt.length)),
@@ -2740,20 +2750,26 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
2740
2750
  extra,
2741
2751
  modelOverlay,
2742
2752
  runtimeContext,
2743
- additionalFramework: codeEditingSurfaceRestriction + prodCodeExecPromptNote,
2753
+ additionalFramework: codeEditingSurfaceRestriction +
2754
+ requestProdCodeExecPromptNote +
2755
+ (hostedHarnessPromptNote ? `\n\n${hostedHarnessPromptNote}` : ""),
2744
2756
  });
2745
2757
  return setSystemPromptOnContext(requestBasePrompt +
2746
2758
  SYSTEM_PROMPT_CACHE_SPLIT +
2747
2759
  resources +
2748
2760
  schemaBlock +
2749
2761
  codeEditingSurfaceRestriction +
2750
- prodCodeExecPromptNote +
2762
+ requestProdCodeExecPromptNote +
2763
+ (hostedHarnessPromptNote
2764
+ ? `\n\n${hostedHarnessPromptNote}`
2765
+ : "") +
2751
2766
  extra +
2752
2767
  modelOverlay +
2753
2768
  runtimeContext);
2754
2769
  },
2755
2770
  model: options?.model,
2756
2771
  appId: options?.appId,
2772
+ hostedHarnessConfig,
2757
2773
  apiKey: options?.apiKey,
2758
2774
  ...resolveInteractiveAgentRunOptions(options),
2759
2775
  finalResponseGuard: options?.finalResponseGuard,
@@ -4692,9 +4708,13 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
4692
4708
  // `prodHandler.systemPrompt` then steers source changes to a
4693
4709
  // separate agent surface such as Builder or the dev frame.
4694
4710
  const blockInProductCodeEditing = shouldBlockInProductCodeEditing(event);
4711
+ const hostedHarnessRequest = getHeader(event, "x-agent-native-hosted-harness") === "1";
4695
4712
  const handler = ownerContext.anonymous && anonymousHandler
4696
4713
  ? anonymousHandler
4697
- : !blockInProductCodeEditing && currentDevMode && devHandler
4714
+ : !hostedHarnessRequest &&
4715
+ !blockInProductCodeEditing &&
4716
+ currentDevMode &&
4717
+ devHandler
4698
4718
  ? devHandler
4699
4719
  : prodHandler;
4700
4720
  return handler(event);
@@ -66,6 +66,10 @@ export interface AgentTask {
66
66
  taskId: string;
67
67
  threadId: string;
68
68
  parentThreadId?: string;
69
+ /** Request owner used to scope every browser-visible task read and control. */
70
+ ownerEmail?: string | null;
71
+ /** Organization scope captured when the task was spawned. */
72
+ orgId?: string | null;
69
73
  name?: string;
70
74
  description: string;
71
75
  status: "running" | "completed" | "errored";
@@ -85,6 +89,10 @@ export interface AgentTask {
85
89
  */
86
90
  delegationDepth?: number;
87
91
  }
92
+ export interface AgentTeamOwnerScope {
93
+ ownerEmail: string | null;
94
+ orgId?: string | null;
95
+ }
88
96
  export type AgentTeamBackgroundRun = Omit<BackgroundAgentRun, "kind" | "source" | "sourceRecord" | "status" | "cwd" | "goalId" | "transcriptPath" | "artifactRoot"> & {
89
97
  kind: "agent-team";
90
98
  source: "hosted-agent-team";
@@ -272,25 +280,25 @@ export declare function processAgentTeamRun(opts: ProcessAgentTeamRunOptions): P
272
280
  ok: boolean;
273
281
  skipped?: string;
274
282
  }>;
275
- /** Get task by ID */
276
- export declare function getTask(taskId: string): Promise<AgentTask | undefined>;
277
- /** Get task by thread ID */
278
- export declare function getTaskByThread(threadId: string): Promise<AgentTask | undefined>;
279
- /** List all tasks (most recent first) */
280
- export declare function listTasks(): Promise<AgentTask[]>;
281
- export declare function listAgentTeamBackgroundRuns(): Promise<AgentTeamBackgroundRun[]>;
282
- export declare function getAgentTeamBackgroundRun(runId: string): Promise<AgentTeamBackgroundRun | null>;
283
- export declare function listAgentTeamBackgroundTranscriptEvents(runId: string): Promise<AgentTeamBackgroundTranscriptEvent[]>;
283
+ /** Get task by ID, optionally constrained to the current request scope. */
284
+ export declare function getTask(taskId: string, scope?: AgentTeamOwnerScope): Promise<AgentTask | undefined>;
285
+ /** Get task by thread ID, optionally constrained to the current request scope. */
286
+ export declare function getTaskByThread(threadId: string, scope?: AgentTeamOwnerScope): Promise<AgentTask | undefined>;
287
+ /** List tasks in the current owner/org scope (most recent first). */
288
+ export declare function listTasks(scope?: AgentTeamOwnerScope): Promise<AgentTask[]>;
289
+ export declare function listAgentTeamBackgroundRuns(scope?: AgentTeamOwnerScope): Promise<AgentTeamBackgroundRun[]>;
290
+ export declare function getAgentTeamBackgroundRun(runId: string, scope?: AgentTeamOwnerScope): Promise<AgentTeamBackgroundRun | null>;
291
+ export declare function listAgentTeamBackgroundTranscriptEvents(runId: string, scope?: AgentTeamOwnerScope): Promise<AgentTeamBackgroundTranscriptEvent[]>;
284
292
  export declare function subscribeToAgentTeamBackgroundRun(runId: string, fromSeq?: number): ReadableStream<Uint8Array> | null;
285
293
  /** Send a message/update to a running sub-agent via application state */
286
- export declare function sendToTask(taskId: string, message: string): Promise<{
294
+ export declare function sendToTask(taskId: string, message: string, scope?: AgentTeamOwnerScope): Promise<{
287
295
  ok: boolean;
288
296
  error?: string;
289
297
  messageId?: string;
290
298
  queuedCount?: number;
291
299
  }>;
292
- export declare function sendToAgentTeamBackgroundRun(runId: string, message: string): Promise<SendToAgentTeamBackgroundRunResult>;
293
- export declare function stopAgentTeamBackgroundRun(runId: string, reason?: string): Promise<ControlAgentTeamBackgroundRunResult>;
300
+ export declare function sendToAgentTeamBackgroundRun(runId: string, message: string, scope?: AgentTeamOwnerScope): Promise<SendToAgentTeamBackgroundRunResult>;
301
+ export declare function stopAgentTeamBackgroundRun(runId: string, reason?: string, scope?: AgentTeamOwnerScope): Promise<ControlAgentTeamBackgroundRunResult>;
294
302
  /** Mark a task as errored */
295
303
  export declare function markTaskErrored(taskId: string, error: string): Promise<void>;
296
304
  export declare const _agentTeamsQueueForTests: {
@@ -936,10 +936,24 @@ export async function spawnTask(opts) {
936
936
  }
937
937
  const runId = taskRunId(taskId);
938
938
  const createdAt = Date.now();
939
+ let orgId;
940
+ if (hasRequestContext()) {
941
+ orgId = getRequestOrgId() ?? null;
942
+ }
943
+ else {
944
+ try {
945
+ orgId = await resolveOrgIdForEmail(opts.ownerEmail);
946
+ }
947
+ catch {
948
+ orgId = null;
949
+ }
950
+ }
939
951
  const task = {
940
952
  taskId,
941
953
  threadId: thread.id,
942
954
  ...(opts.parentThreadId ? { parentThreadId: opts.parentThreadId } : {}),
955
+ ownerEmail: opts.ownerEmail,
956
+ orgId,
943
957
  ...(opts.name ? { name: opts.name } : {}),
944
958
  description: opts.description,
945
959
  status: "running",
@@ -972,18 +986,6 @@ export async function spawnTask(opts) {
972
986
  // (integrations/webhook-handler.ts). Execution happens in `processAgentTeamRun`,
973
987
  // invoked by the `/_agent-native/agent-teams/_process-run` route mounted
974
988
  // inside the agent-chat plugin (where the action/prompt/engine closures live).
975
- let orgId;
976
- if (hasRequestContext()) {
977
- orgId = getRequestOrgId() ?? null;
978
- }
979
- else {
980
- try {
981
- orgId = await resolveOrgIdForEmail(opts.ownerEmail);
982
- }
983
- catch {
984
- orgId = null;
985
- }
986
- }
987
989
  const payload = {
988
990
  description: opts.description,
989
991
  instructions: opts.instructions,
@@ -1505,35 +1507,45 @@ export async function processAgentTeamRun(opts) {
1505
1507
  return { ok: true };
1506
1508
  });
1507
1509
  }
1508
- /** Get task by ID */
1509
- export async function getTask(taskId) {
1510
+ /** Get task by ID, optionally constrained to the current request scope. */
1511
+ export async function getTask(taskId, scope) {
1510
1512
  const task = await loadTask(taskId);
1511
- return task ? await reconcileTaskWithRun(task) : undefined;
1513
+ if (!task || !taskMatchesOwnerScope(task, resolveOwnerScope(scope))) {
1514
+ return undefined;
1515
+ }
1516
+ return await reconcileTaskWithRun(task);
1512
1517
  }
1513
- /** Get task by thread ID */
1514
- export async function getTaskByThread(threadId) {
1518
+ /** Get task by thread ID, optionally constrained to the current request scope. */
1519
+ export async function getTaskByThread(threadId, scope) {
1515
1520
  const task = await loadTaskByThread(threadId);
1516
- return task ? await reconcileTaskWithRun(task) : undefined;
1521
+ if (!task || !taskMatchesOwnerScope(task, resolveOwnerScope(scope))) {
1522
+ return undefined;
1523
+ }
1524
+ return await reconcileTaskWithRun(task);
1517
1525
  }
1518
- /** List all tasks (most recent first) */
1519
- export async function listTasks() {
1526
+ /** List tasks in the current owner/org scope (most recent first). */
1527
+ export async function listTasks(scope) {
1528
+ const ownerScope = resolveOwnerScope(scope);
1520
1529
  const entries = await listAppState(TASK_PREFIX);
1521
- const tasks = entries.map((e) => e.value);
1530
+ const tasks = entries
1531
+ .map((e) => e.value)
1532
+ .filter((task) => taskMatchesOwnerScope(task, ownerScope));
1522
1533
  const reconciled = await Promise.all(tasks.map(reconcileTaskWithRun));
1523
1534
  return reconciled.sort((a, b) => (b.updatedAt ?? b.completedAt ?? b.createdAt) -
1524
1535
  (a.updatedAt ?? a.completedAt ?? a.createdAt));
1525
1536
  }
1526
- export async function listAgentTeamBackgroundRuns() {
1527
- return (await listTasks()).map(toAgentTaskBackgroundRun);
1537
+ export async function listAgentTeamBackgroundRuns(scope) {
1538
+ return (await listTasks(scope)).map(toAgentTaskBackgroundRun);
1528
1539
  }
1529
- export async function getAgentTeamBackgroundRun(runId) {
1530
- const task = await loadTask(taskIdFromBackgroundRunId(runId));
1531
- return task
1532
- ? toAgentTaskBackgroundRun(await reconcileTaskWithRun(task))
1533
- : null;
1540
+ export async function getAgentTeamBackgroundRun(runId, scope) {
1541
+ const task = await getTask(taskIdFromBackgroundRunId(runId), scope);
1542
+ return task ? toAgentTaskBackgroundRun(task) : null;
1534
1543
  }
1535
- export async function listAgentTeamBackgroundTranscriptEvents(runId) {
1544
+ export async function listAgentTeamBackgroundTranscriptEvents(runId, scope) {
1536
1545
  const taskId = taskIdFromBackgroundRunId(runId);
1546
+ const ownerScope = resolveOwnerScope(scope);
1547
+ if (ownerScope && !(await getTask(taskId, ownerScope)))
1548
+ return [];
1537
1549
  const normalizedRunId = taskRunId(taskId);
1538
1550
  const runIds = await transcriptRunIdsForTask(taskId);
1539
1551
  const output = [];
@@ -1589,10 +1601,11 @@ async function getPersistedRunEvents(runId) {
1589
1601
  .filter((event) => Boolean(event));
1590
1602
  }
1591
1603
  /** Send a message/update to a running sub-agent via application state */
1592
- export async function sendToTask(taskId, message) {
1604
+ export async function sendToTask(taskId, message, scope) {
1593
1605
  const task = await loadTask(taskId);
1594
- if (!task)
1606
+ if (!task || !taskMatchesOwnerScope(task, resolveOwnerScope(scope))) {
1595
1607
  return { ok: false, error: "Task not found" };
1608
+ }
1596
1609
  if (task.status !== "running")
1597
1610
  return { ok: false, error: "Task is not running" };
1598
1611
  if (message.trim().length === 0)
@@ -1612,8 +1625,8 @@ export async function sendToTask(taskId, message) {
1612
1625
  return { ok: false, error: "Unable to queue message" };
1613
1626
  }
1614
1627
  }
1615
- export async function sendToAgentTeamBackgroundRun(runId, message) {
1616
- return sendToTask(taskIdFromBackgroundRunId(runId), message);
1628
+ export async function sendToAgentTeamBackgroundRun(runId, message, scope) {
1629
+ return sendToTask(taskIdFromBackgroundRunId(runId), message, scope);
1617
1630
  }
1618
1631
  async function sendAgentTeamBackgroundAgentFollowUp(input) {
1619
1632
  const prompt = input.prompt.trim();
@@ -1655,11 +1668,12 @@ async function controlAgentTeamBackgroundAgentRun(input) {
1655
1668
  error: result.error,
1656
1669
  };
1657
1670
  }
1658
- export async function stopAgentTeamBackgroundRun(runId, reason = "user") {
1671
+ export async function stopAgentTeamBackgroundRun(runId, reason = "user", scope) {
1659
1672
  const taskId = taskIdFromBackgroundRunId(runId);
1660
1673
  const task = await loadTask(taskId);
1661
- if (!task)
1674
+ if (!task || !taskMatchesOwnerScope(task, resolveOwnerScope(scope))) {
1662
1675
  return { ok: false, error: "Task not found" };
1676
+ }
1663
1677
  if (task.status !== "running") {
1664
1678
  return { ok: false, error: "Task is not running" };
1665
1679
  }
@@ -1678,6 +1692,26 @@ export async function stopAgentTeamBackgroundRun(runId, reason = "user") {
1678
1692
  await completeAgentTeamRun(task.taskId, "failed");
1679
1693
  return { ok: true };
1680
1694
  }
1695
+ function resolveOwnerScope(scope) {
1696
+ if (scope)
1697
+ return scope;
1698
+ const ownerEmail = getRequestUserEmail();
1699
+ if (ownerEmail === undefined)
1700
+ return undefined;
1701
+ return {
1702
+ ownerEmail,
1703
+ // A few non-HTTP test/CLI hosts only provide the user scope. Keep that
1704
+ // owner check active without requiring every lightweight request-context
1705
+ // adapter to implement organization lookup.
1706
+ orgId: typeof getRequestOrgId === "function" ? getRequestOrgId() : undefined,
1707
+ };
1708
+ }
1709
+ function taskMatchesOwnerScope(task, scope) {
1710
+ if (!scope)
1711
+ return true;
1712
+ return ((task.ownerEmail ?? null) === scope.ownerEmail &&
1713
+ (scope.orgId === undefined || (task.orgId ?? null) === scope.orgId));
1714
+ }
1681
1715
  /** Mark a task as errored */
1682
1716
  export async function markTaskErrored(taskId, error) {
1683
1717
  const task = await loadTask(taskId);
@@ -0,0 +1,17 @@
1
+ import { type HostedHarnessRuntime } from "../agent/harness/hosted.js";
2
+ import type { AgentNativeHarnessSetting } from "../config.js";
3
+ export interface HostedHarnessPolicy {
4
+ enabled: boolean;
5
+ configEnabled: boolean;
6
+ envEnabled: boolean;
7
+ organizationEnabled: boolean;
8
+ runtimes: HostedHarnessRuntime[];
9
+ }
10
+ export declare function isHostedHarnessEnvEnabled(env?: Record<string, string | undefined>): boolean;
11
+ export declare function loadHostedHarnessConfig(cwd?: string): Promise<AgentNativeHarnessSetting | undefined>;
12
+ export declare function resolveHostedHarnessPolicy(options: {
13
+ config?: AgentNativeHarnessSetting;
14
+ orgId?: string | null;
15
+ userEmail?: string | null;
16
+ }): Promise<HostedHarnessPolicy>;
17
+ export declare function hostedHarnessStatusForClient(policy: HostedHarnessPolicy): HostedHarnessPolicy;