@agent-native/core 0.157.5 → 0.157.7
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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/agent-native.config.ts +5 -0
- package/corpus/templates/analytics/netlify.toml +1 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +23 -5
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/assets/netlify.toml +3 -0
- package/corpus/templates/brain/netlify.toml +3 -0
- package/corpus/templates/calendar/agent-native.config.ts +5 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +4 -1
- package/corpus/templates/calendar/netlify.toml +3 -0
- package/corpus/templates/chat/agent-native.config.ts +5 -0
- package/corpus/templates/chat/netlify.toml +3 -0
- package/corpus/templates/clips/actions/create-meeting.ts +19 -1
- package/corpus/templates/clips/actions/lib/meeting-content.ts +28 -0
- package/corpus/templates/clips/actions/list-meetings.ts +33 -11
- package/corpus/templates/clips/app/components/meetings/bullet-link.tsx +48 -25
- package/corpus/templates/clips/app/components/meetings/canvas-editor.tsx +7 -6
- package/corpus/templates/clips/app/components/meetings/transcript-bubbles.tsx +388 -178
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +7 -13
- package/corpus/templates/clips/app/components/transcript/transcript-segment-row.tsx +98 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +5 -0
- package/corpus/templates/clips/app/lib/meeting-history-query.ts +11 -0
- package/corpus/templates/clips/app/lib/public-meeting.ts +4 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +49 -31
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +47 -21
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +15 -1
- package/corpus/templates/clips/netlify.toml +8 -4
- package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +10 -0
- package/corpus/templates/clips/shared/transcript-segments.ts +20 -2
- package/corpus/templates/content/netlify.toml +3 -0
- package/corpus/templates/design/app/i18n-data.ts +2 -0
- package/corpus/templates/design/netlify.toml +3 -0
- package/corpus/templates/dispatch/netlify.toml +3 -0
- package/corpus/templates/factory/netlify.toml +3 -0
- package/corpus/templates/forms/netlify.toml +3 -0
- package/corpus/templates/macros/netlify.toml +3 -0
- package/corpus/templates/mail/agent-native.config.ts +5 -0
- package/corpus/templates/mail/netlify.toml +3 -0
- package/corpus/templates/plan/netlify.toml +3 -0
- package/corpus/templates/slides/netlify.toml +3 -0
- package/dist/agent/harness/acp-adapter.js +17 -2
- package/dist/agent/harness/ai-sdk-adapter.js +14 -3
- package/dist/agent/harness/background.js +10 -5
- package/dist/agent/harness/hosted.d.ts +20 -0
- package/dist/agent/harness/hosted.js +100 -0
- package/dist/agent/harness/runner.js +100 -77
- package/dist/agent/observational-memory/observer.js +12 -0
- package/dist/agent/production-agent.d.ts +3 -0
- package/dist/agent/production-agent.js +74 -4
- package/dist/agent/thread-data-builder.d.ts +12 -0
- package/dist/agent/thread-data-builder.js +23 -0
- package/dist/agent/types.d.ts +6 -0
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.js +113 -17
- package/dist/client/AgentTaskCard.d.ts +2 -2
- package/dist/client/AgentTaskCard.js +8 -22
- package/dist/client/AssistantChat.d.ts +8 -0
- package/dist/client/AssistantChat.js +106 -59
- package/dist/client/MultiTabAssistantChat.js +50 -6
- package/dist/client/NewWorkspaceAppFlow.js +7 -1
- package/dist/client/agent-chat-adapter.d.ts +6 -0
- package/dist/client/agent-chat-adapter.js +36 -8
- package/dist/client/app-chat-sidebar.d.ts +5 -0
- package/dist/client/app-chat-sidebar.js +53 -9
- package/dist/client/chat/composer-draft.d.ts +4 -0
- package/dist/client/chat/composer-draft.js +63 -0
- package/dist/client/chat-first/apps-rail.js +10 -1
- package/dist/client/chat-first/primary-nav.js +10 -5
- package/dist/client/frame-protocol.d.ts +1 -0
- package/dist/client/hooks/index.d.ts +1 -1
- package/dist/client/hooks/index.js +1 -1
- package/dist/client/i18n.js +5 -2
- package/dist/client/progress/RunsTray.js +129 -16
- package/dist/client/settings/SettingsRow.js +1 -1
- package/dist/client/use-chat-threads.js +11 -6
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.js +52 -0
- package/dist/deploy/build.js +28 -11
- package/dist/extensions/actions.js +22 -1
- package/dist/hosted-harness/actions/get-hosted-harness-config.d.ts +2 -0
- package/dist/hosted-harness/actions/get-hosted-harness-config.js +13 -0
- package/dist/hosted-harness/actions/set-hosted-harness-enabled.d.ts +4 -0
- package/dist/hosted-harness/actions/set-hosted-harness-enabled.js +32 -0
- package/dist/localization/default-messages.d.ts +4 -1
- package/dist/localization/default-messages.js +5 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-discovery.js +12 -0
- package/dist/server/agent-chat-plugin.js +24 -4
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-teams.d.ts +20 -12
- package/dist/server/agent-teams.js +69 -35
- package/dist/server/hosted-harness-policy.d.ts +17 -0
- package/dist/server/hosted-harness-policy.js +38 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +4 -0
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +39 -0
- package/dist/templates/chat/agent-native.config.ts +5 -0
- package/dist/templates/chat/netlify.toml +3 -0
- package/dist/vite/action-types-plugin.js +8 -0
- package/package.json +4 -2
- package/src/templates/chat/agent-native.config.ts +5 -0
- package/src/templates/chat/netlify.toml +3 -0
|
@@ -13,6 +13,9 @@ NPM_CONFIG_PRODUCTION = "false"
|
|
|
13
13
|
# function, including workspace-resource updates delegated by sibling apps.
|
|
14
14
|
AGENT_CHAT_DURABLE_BACKGROUND = "true"
|
|
15
15
|
|
|
16
|
+
[context.production.environment]
|
|
17
|
+
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
18
|
+
|
|
16
19
|
# A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
|
|
17
20
|
# The framework does not impose a default agent-run timeout; if a template sets
|
|
18
21
|
# AGENT_RUN_SOFT_TIMEOUT_MS, keep it below this function timeout so the hidden
|
|
@@ -10,6 +10,9 @@ GTM_CONTAINER_ID = "GTM-N3WSTXZ"
|
|
|
10
10
|
NITRO_PRESET = "netlify"
|
|
11
11
|
NPM_CONFIG_PRODUCTION = "false"
|
|
12
12
|
|
|
13
|
+
[context.production.environment]
|
|
14
|
+
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
15
|
+
|
|
13
16
|
# A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
|
|
14
17
|
# The framework does not impose a default agent-run timeout; if a template sets
|
|
15
18
|
# AGENT_RUN_SOFT_TIMEOUT_MS, keep it below this function timeout so the hidden
|
|
@@ -10,6 +10,9 @@ GTM_CONTAINER_ID = "GTM-N3WSTXZ"
|
|
|
10
10
|
NITRO_PRESET = "netlify"
|
|
11
11
|
NPM_CONFIG_PRODUCTION = "false"
|
|
12
12
|
|
|
13
|
+
[context.production.environment]
|
|
14
|
+
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
15
|
+
|
|
13
16
|
# A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
|
|
14
17
|
# The framework does not impose a default agent-run timeout; if a template sets
|
|
15
18
|
# AGENT_RUN_SOFT_TIMEOUT_MS, keep it below this function timeout so the hidden
|
|
@@ -11,6 +11,9 @@ NITRO_PRESET = "netlify"
|
|
|
11
11
|
NPM_CONFIG_PRODUCTION = "false"
|
|
12
12
|
AGENT_CHAT_DURABLE_BACKGROUND = "true"
|
|
13
13
|
|
|
14
|
+
[context.production.environment]
|
|
15
|
+
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
16
|
+
|
|
14
17
|
# A2A/MCP calls may include plan generation, artifact serialization, and
|
|
15
18
|
# feedback polling. Keep this aligned with other agent-heavy hosted templates.
|
|
16
19
|
# Agent chat runs are dispatched to the emitted Netlify background function;
|
|
@@ -14,6 +14,9 @@ NITRO_PRESET = "netlify"
|
|
|
14
14
|
NPM_CONFIG_PRODUCTION = "false"
|
|
15
15
|
AGENT_CHAT_DURABLE_BACKGROUND = "true"
|
|
16
16
|
|
|
17
|
+
[context.production.environment]
|
|
18
|
+
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
19
|
+
|
|
17
20
|
# Function wall for the non-agent long-request paths (deck import/export,
|
|
18
21
|
# image generation). Agent chat opts into the separate Netlify background
|
|
19
22
|
# function so hosted creative turns can use the longer background budget.
|
|
@@ -167,8 +167,12 @@ class AcpHarnessSession {
|
|
|
167
167
|
this.acpSessionId = resume.sessionId;
|
|
168
168
|
return;
|
|
169
169
|
}
|
|
170
|
-
catch {
|
|
171
|
-
|
|
170
|
+
catch (error) {
|
|
171
|
+
if (!isExplicitlyMissingAcpSession(error))
|
|
172
|
+
throw error;
|
|
173
|
+
// A provider can legitimately evict an old session. Only that
|
|
174
|
+
// explicit condition may fall back to a fresh session; transport or
|
|
175
|
+
// permission failures must remain visible to the caller.
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
178
|
const created = await this.connection.newSession({
|
|
@@ -321,6 +325,9 @@ class AcpHarnessSession {
|
|
|
321
325
|
return { content: sliceTextFile(raw, params.line, params.limit) };
|
|
322
326
|
}
|
|
323
327
|
async handleWriteTextFile(params) {
|
|
328
|
+
if (this.permissionMode === "allow-reads") {
|
|
329
|
+
throw new Error("[acp-harness] File writes are disabled in allow-reads mode.");
|
|
330
|
+
}
|
|
324
331
|
const abs = resolveAcpWorkspacePath(this.cwd, params.path);
|
|
325
332
|
const existed = await fileExists(abs);
|
|
326
333
|
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
@@ -604,6 +611,14 @@ function acpErrorMessage(error) {
|
|
|
604
611
|
}
|
|
605
612
|
return typeof error === "string" ? error : "ACP request failed";
|
|
606
613
|
}
|
|
614
|
+
function isExplicitlyMissingAcpSession(error) {
|
|
615
|
+
const message = error instanceof Error
|
|
616
|
+
? error.message
|
|
617
|
+
: typeof error === "string"
|
|
618
|
+
? error
|
|
619
|
+
: "";
|
|
620
|
+
return /(?:session|conversation).*(?:not found|does not exist|unknown|unsupported)|(?:unknown|unsupported).*(?:session|conversation)/i.test(message);
|
|
621
|
+
}
|
|
607
622
|
/** Minimal async queue bridging ACP's callback updates to an async iterable. */
|
|
608
623
|
class AsyncEventQueue {
|
|
609
624
|
values = [];
|
|
@@ -199,8 +199,11 @@ async function createNativeSession(agent, options) {
|
|
|
199
199
|
try {
|
|
200
200
|
return await agent.createSession({ resumeState: options.resumeState });
|
|
201
201
|
}
|
|
202
|
-
catch {
|
|
203
|
-
|
|
202
|
+
catch (error) {
|
|
203
|
+
if (isExplicitlyMissingHarnessSession(error)) {
|
|
204
|
+
return agent.createSession();
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
209
|
if (typeof agent.createSession !== "function") {
|
|
@@ -237,7 +240,7 @@ class AiSdkHarnessSession {
|
|
|
237
240
|
}
|
|
238
241
|
async *continueTurn(input = {}) {
|
|
239
242
|
if (typeof this.agent.continueStream !== "function") {
|
|
240
|
-
|
|
243
|
+
throw new Error("[agent-harness] This runtime cannot continue a harness turn.");
|
|
241
244
|
}
|
|
242
245
|
const result = await this.agent.continueStream({
|
|
243
246
|
session: this.nativeSession,
|
|
@@ -353,3 +356,11 @@ function normalizeFileOperation(value) {
|
|
|
353
356
|
? value
|
|
354
357
|
: "unknown";
|
|
355
358
|
}
|
|
359
|
+
function isExplicitlyMissingHarnessSession(error) {
|
|
360
|
+
const message = error instanceof Error
|
|
361
|
+
? error.message
|
|
362
|
+
: typeof error === "string"
|
|
363
|
+
? error
|
|
364
|
+
: "";
|
|
365
|
+
return /(?:session|conversation).*(?:not found|does not exist|unknown|unsupported)|(?:unknown|unsupported).*(?:session|conversation)/i.test(message);
|
|
366
|
+
}
|
|
@@ -98,7 +98,7 @@ function toAgentHarnessBackgroundRun(session) {
|
|
|
98
98
|
},
|
|
99
99
|
title: `${session.harnessName} harness`,
|
|
100
100
|
subtitle: session.status === "running" ? "Running" : undefined,
|
|
101
|
-
status: backgroundStatus(session.status),
|
|
101
|
+
status: backgroundStatus(session.status, Boolean(session.pendingApproval)),
|
|
102
102
|
phase: session.status,
|
|
103
103
|
createdAt,
|
|
104
104
|
updatedAt,
|
|
@@ -218,14 +218,17 @@ function parseRunEvent(row) {
|
|
|
218
218
|
return null;
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
-
function backgroundStatus(status) {
|
|
221
|
+
function backgroundStatus(status, needsApproval) {
|
|
222
222
|
if (status === "running")
|
|
223
223
|
return "running";
|
|
224
224
|
if (status === "errored")
|
|
225
225
|
return "errored";
|
|
226
|
-
if (
|
|
226
|
+
if (needsApproval)
|
|
227
|
+
return "needs-approval";
|
|
228
|
+
if (status === "idle")
|
|
229
|
+
return "paused";
|
|
230
|
+
if (status === "stopped" || status === "destroyed")
|
|
227
231
|
return "completed";
|
|
228
|
-
}
|
|
229
232
|
return "unknown";
|
|
230
233
|
}
|
|
231
234
|
async function sendAgentHarnessBackgroundFollowUp(input, scope) {
|
|
@@ -253,7 +256,9 @@ async function controlAgentHarnessBackgroundRun(input, scope) {
|
|
|
253
256
|
}
|
|
254
257
|
if (input.command === "approve" || input.command === "deny") {
|
|
255
258
|
const session = await getAgentHarnessSessionByRunId(input.runId);
|
|
256
|
-
if (!session ||
|
|
259
|
+
if (!session ||
|
|
260
|
+
session.ownerEmail !== scope.ownerEmail ||
|
|
261
|
+
(scope.orgId !== undefined && session.orgId !== scope.orgId)) {
|
|
257
262
|
return missingHarnessRunResult(input.runId);
|
|
258
263
|
}
|
|
259
264
|
const pending = session.pendingApproval;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentNativeHarnessRuntime } from "../../config.js";
|
|
2
|
+
export type HostedHarnessRuntime = AgentNativeHarnessRuntime;
|
|
3
|
+
export declare const HOSTED_HARNESS_ENV_KEY: "AGENT_NATIVE_HOSTED_HARNESS";
|
|
4
|
+
export declare const HOSTED_HARNESS_ORG_SETTING_KEY: "agent-harness.enabled";
|
|
5
|
+
export declare const HOSTED_HARNESS_RUNTIME_ORDER: readonly HostedHarnessRuntime[];
|
|
6
|
+
export declare const HOSTED_HARNESS_AGENT_DESCRIPTIONS: Record<HostedHarnessRuntime, string>;
|
|
7
|
+
export declare const HOSTED_HARNESS_AGENT_LABELS: Record<HostedHarnessRuntime, string>;
|
|
8
|
+
export declare const HOSTED_HARNESS_BLOCKED_TOOL_NAMES: Set<string>;
|
|
9
|
+
export declare function isHostedHarnessRuntime(value: unknown): value is HostedHarnessRuntime;
|
|
10
|
+
export declare function normalizeHostedHarnessRuntime(value: unknown): HostedHarnessRuntime | undefined;
|
|
11
|
+
export declare function normalizeHostedHarnessRuntimes(value: unknown): HostedHarnessRuntime[];
|
|
12
|
+
export declare function isHostedHarnessConfigured(value: unknown): boolean;
|
|
13
|
+
export declare function hostedHarnessAgentOption(runtime: HostedHarnessRuntime): {
|
|
14
|
+
id: AgentNativeHarnessRuntime;
|
|
15
|
+
label: string;
|
|
16
|
+
description: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function isHostedHarnessBlockedToolName(name: string): boolean;
|
|
19
|
+
export declare function filterHostedHarnessToolNames(names: readonly string[]): string[];
|
|
20
|
+
export declare function hostedHarnessSystemPrompt(runtime: HostedHarnessRuntime): string;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export const HOSTED_HARNESS_ENV_KEY = "AGENT_NATIVE_HOSTED_HARNESS";
|
|
2
|
+
export const HOSTED_HARNESS_ORG_SETTING_KEY = "agent-harness.enabled";
|
|
3
|
+
export const HOSTED_HARNESS_RUNTIME_ORDER = [
|
|
4
|
+
"claude-code",
|
|
5
|
+
"codex",
|
|
6
|
+
"pi",
|
|
7
|
+
"opencode",
|
|
8
|
+
];
|
|
9
|
+
export const HOSTED_HARNESS_AGENT_DESCRIPTIONS = {
|
|
10
|
+
"claude-code": "Production mode is tools-only: no repository, shell, or code editing. Use the Electron app for full coding workflows.",
|
|
11
|
+
codex: "Production mode is tools-only: no repository, shell, or code editing. Use the Electron app for full coding workflows.",
|
|
12
|
+
pi: "Production mode is tools-only: no repository, shell, or code editing. Use the Electron app for full coding workflows.",
|
|
13
|
+
opencode: "Production mode is tools-only: no repository, shell, or code editing. Use the Electron app for full coding workflows.",
|
|
14
|
+
};
|
|
15
|
+
export const HOSTED_HARNESS_AGENT_LABELS = {
|
|
16
|
+
"claude-code": "Claude Code",
|
|
17
|
+
codex: "Codex",
|
|
18
|
+
pi: "Pi",
|
|
19
|
+
opencode: "OpenCode",
|
|
20
|
+
};
|
|
21
|
+
export const HOSTED_HARNESS_BLOCKED_TOOL_NAMES = new Set([
|
|
22
|
+
"bash",
|
|
23
|
+
"edit",
|
|
24
|
+
"read",
|
|
25
|
+
"write",
|
|
26
|
+
"run-code",
|
|
27
|
+
"tool-orchestration",
|
|
28
|
+
"get-code-execution",
|
|
29
|
+
"read-file",
|
|
30
|
+
"write-file",
|
|
31
|
+
"read-local-file",
|
|
32
|
+
"write-local-file",
|
|
33
|
+
"apply-source-edit",
|
|
34
|
+
"update-file",
|
|
35
|
+
"delete-file",
|
|
36
|
+
"github-repo-list-files",
|
|
37
|
+
"github-repo-read-file",
|
|
38
|
+
"github-repo-search-code",
|
|
39
|
+
"github-repo-write-file",
|
|
40
|
+
"github-repo-delete-file",
|
|
41
|
+
"save-data-program",
|
|
42
|
+
"preview-data-program",
|
|
43
|
+
"run-data-program",
|
|
44
|
+
"list-data-programs",
|
|
45
|
+
"get-data-program",
|
|
46
|
+
"delete-data-program",
|
|
47
|
+
"workspace-files",
|
|
48
|
+
"show-workspace-file",
|
|
49
|
+
"read-workspace-file",
|
|
50
|
+
"write-workspace-file",
|
|
51
|
+
"delete-workspace-file",
|
|
52
|
+
"grep-workspace-file",
|
|
53
|
+
"connect-builder",
|
|
54
|
+
]);
|
|
55
|
+
export function isHostedHarnessRuntime(value) {
|
|
56
|
+
return (value === "claude-code" ||
|
|
57
|
+
value === "codex" ||
|
|
58
|
+
value === "pi" ||
|
|
59
|
+
value === "opencode");
|
|
60
|
+
}
|
|
61
|
+
export function normalizeHostedHarnessRuntime(value) {
|
|
62
|
+
if (value === "claude")
|
|
63
|
+
return "claude-code";
|
|
64
|
+
return isHostedHarnessRuntime(value) ? value : undefined;
|
|
65
|
+
}
|
|
66
|
+
export function normalizeHostedHarnessRuntimes(value) {
|
|
67
|
+
if (!Array.isArray(value))
|
|
68
|
+
return [...HOSTED_HARNESS_RUNTIME_ORDER];
|
|
69
|
+
const runtimes = value
|
|
70
|
+
.map((runtime) => normalizeHostedHarnessRuntime(runtime))
|
|
71
|
+
.filter((runtime) => runtime !== undefined);
|
|
72
|
+
return [...new Set(runtimes)];
|
|
73
|
+
}
|
|
74
|
+
export function isHostedHarnessConfigured(value) {
|
|
75
|
+
return (value === true ||
|
|
76
|
+
(!!value && typeof value === "object" && !Array.isArray(value)));
|
|
77
|
+
}
|
|
78
|
+
export function hostedHarnessAgentOption(runtime) {
|
|
79
|
+
return {
|
|
80
|
+
id: runtime,
|
|
81
|
+
label: HOSTED_HARNESS_AGENT_LABELS[runtime],
|
|
82
|
+
description: HOSTED_HARNESS_AGENT_DESCRIPTIONS[runtime],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function isHostedHarnessBlockedToolName(name) {
|
|
86
|
+
return (HOSTED_HARNESS_BLOCKED_TOOL_NAMES.has(name) ||
|
|
87
|
+
name.startsWith("workspace-file-") ||
|
|
88
|
+
name.startsWith("data-program-"));
|
|
89
|
+
}
|
|
90
|
+
export function filterHostedHarnessToolNames(names) {
|
|
91
|
+
return names.filter((name) => !isHostedHarnessBlockedToolName(name));
|
|
92
|
+
}
|
|
93
|
+
export function hostedHarnessSystemPrompt(runtime) {
|
|
94
|
+
return `<hosted-tools-only-harness runtime="${runtime}">
|
|
95
|
+
You are running as a hosted ${HOSTED_HARNESS_AGENT_LABELS[runtime]} inside a production app.
|
|
96
|
+
This is a tools-only mode. You have no repository, shell, filesystem, code-editing, or code-execution tools.
|
|
97
|
+
Use the app's typed actions and SQL-backed resources for mail, calendar, analytics, and working state.
|
|
98
|
+
For full coding work, tell the user to use the Agent Native Electron app.
|
|
99
|
+
</hosted-tools-only-harness>`;
|
|
100
|
+
}
|
|
@@ -6,92 +6,115 @@ export function startAgentHarnessRun(opts) {
|
|
|
6
6
|
let harnessSession = opts.session;
|
|
7
7
|
const detachOnComplete = opts.detachOnComplete !== false;
|
|
8
8
|
return startRun(opts.runId, opts.threadId, async (send, signal) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
await
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
await
|
|
54
|
-
|
|
55
|
-
pendingApproval
|
|
56
|
-
|
|
9
|
+
let storedSessionId;
|
|
10
|
+
let keepLiveSession = false;
|
|
11
|
+
try {
|
|
12
|
+
send({
|
|
13
|
+
type: "activity",
|
|
14
|
+
label: `Starting ${opts.adapter.label}`,
|
|
15
|
+
tool: "harness",
|
|
16
|
+
});
|
|
17
|
+
harnessSession ??= await opts.adapter.createSession({
|
|
18
|
+
...(opts.createSession ?? {}),
|
|
19
|
+
threadId: opts.threadId,
|
|
20
|
+
runId: opts.runId,
|
|
21
|
+
ownerEmail: opts.ownerEmail ?? opts.createSession?.ownerEmail ?? null,
|
|
22
|
+
orgId: opts.orgId ?? opts.createSession?.orgId ?? null,
|
|
23
|
+
signal,
|
|
24
|
+
});
|
|
25
|
+
storedSessionId = opts.createSession?.sessionId ?? harnessSession.id;
|
|
26
|
+
await saveAgentHarnessSession({
|
|
27
|
+
id: storedSessionId,
|
|
28
|
+
harnessName: opts.adapter.name,
|
|
29
|
+
threadId: opts.threadId,
|
|
30
|
+
runId: opts.runId,
|
|
31
|
+
providerSessionId: harnessSession.id,
|
|
32
|
+
status: "running",
|
|
33
|
+
resumeState: opts.createSession?.resumeState,
|
|
34
|
+
ownerEmail: opts.ownerEmail ?? opts.createSession?.ownerEmail ?? null,
|
|
35
|
+
orgId: opts.orgId ?? opts.createSession?.orgId ?? null,
|
|
36
|
+
});
|
|
37
|
+
registerLiveAgentHarnessSession({
|
|
38
|
+
sessionId: storedSessionId,
|
|
39
|
+
adapter: opts.adapter,
|
|
40
|
+
session: harnessSession,
|
|
41
|
+
createSession: opts.createSession,
|
|
42
|
+
ownerEmail: opts.ownerEmail ?? opts.createSession?.ownerEmail ?? null,
|
|
43
|
+
orgId: opts.orgId ?? opts.createSession?.orgId ?? null,
|
|
44
|
+
});
|
|
45
|
+
const input = {
|
|
46
|
+
...opts.input,
|
|
47
|
+
abortSignal: signal,
|
|
48
|
+
};
|
|
49
|
+
let pendingApproval = null;
|
|
50
|
+
for await (const event of harnessSession.streamTurn(input)) {
|
|
51
|
+
if (signal.aborted)
|
|
52
|
+
break;
|
|
53
|
+
await opts.onHarnessEvent?.(event);
|
|
54
|
+
if (event.type === "approval-request") {
|
|
55
|
+
pendingApproval = event;
|
|
56
|
+
await updateAgentHarnessSession(storedSessionId, {
|
|
57
|
+
status: "idle",
|
|
58
|
+
pendingApproval: event,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (event.type === "error") {
|
|
62
|
+
throw new Error(event.error);
|
|
63
|
+
}
|
|
64
|
+
for (const chatEvent of agentHarnessEventToAgentChatEvents(event)) {
|
|
65
|
+
send(chatEvent);
|
|
66
|
+
}
|
|
57
67
|
}
|
|
58
|
-
if (
|
|
59
|
-
|
|
68
|
+
if (signal.aborted) {
|
|
69
|
+
await stopHarnessSession(harnessSession);
|
|
70
|
+
releaseLiveAgentHarnessSession(storedSessionId, harnessSession);
|
|
71
|
+
await markAgentHarnessSessionStopped(storedSessionId, "stopped");
|
|
72
|
+
return;
|
|
60
73
|
}
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
if (pendingApproval) {
|
|
75
|
+
const stored = await getAgentHarnessSession(storedSessionId);
|
|
76
|
+
const stillPending = stored?.pendingApproval &&
|
|
77
|
+
typeof stored.pendingApproval === "object" &&
|
|
78
|
+
"id" in stored.pendingApproval &&
|
|
79
|
+
stored.pendingApproval.id === pendingApproval.id;
|
|
80
|
+
if (stillPending) {
|
|
81
|
+
keepLiveSession = true;
|
|
82
|
+
await updateAgentHarnessSession(storedSessionId, {
|
|
83
|
+
status: "idle",
|
|
84
|
+
pendingApproval,
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
let resumeState = opts.createSession?.resumeState;
|
|
90
|
+
if (detachOnComplete && harnessSession.detach) {
|
|
91
|
+
resumeState = await harnessSession.detach();
|
|
63
92
|
}
|
|
64
|
-
}
|
|
65
|
-
if (signal.aborted) {
|
|
66
|
-
await stopHarnessSession(harnessSession);
|
|
67
93
|
releaseLiveAgentHarnessSession(storedSessionId, harnessSession);
|
|
68
|
-
await
|
|
69
|
-
|
|
94
|
+
await updateAgentHarnessSession(storedSessionId, {
|
|
95
|
+
status: "idle",
|
|
96
|
+
resumeState,
|
|
97
|
+
pendingApproval: null,
|
|
98
|
+
});
|
|
70
99
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (stillPending) {
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (harnessSession && !keepLiveSession) {
|
|
102
|
+
await stopHarnessSession(harnessSession).catch(() => undefined);
|
|
103
|
+
}
|
|
104
|
+
if (storedSessionId) {
|
|
105
|
+
releaseLiveAgentHarnessSession(storedSessionId, harnessSession);
|
|
78
106
|
await updateAgentHarnessSession(storedSessionId, {
|
|
79
|
-
status: "
|
|
80
|
-
pendingApproval,
|
|
81
|
-
});
|
|
82
|
-
return;
|
|
107
|
+
status: "errored",
|
|
108
|
+
pendingApproval: null,
|
|
109
|
+
}).catch(() => undefined);
|
|
83
110
|
}
|
|
111
|
+
throw error;
|
|
84
112
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
113
|
+
finally {
|
|
114
|
+
if (!keepLiveSession && storedSessionId) {
|
|
115
|
+
releaseLiveAgentHarnessSession(storedSessionId, harnessSession);
|
|
116
|
+
}
|
|
88
117
|
}
|
|
89
|
-
releaseLiveAgentHarnessSession(storedSessionId, harnessSession);
|
|
90
|
-
await updateAgentHarnessSession(opts.createSession?.sessionId ?? harnessSession.id, {
|
|
91
|
-
status: "idle",
|
|
92
|
-
resumeState,
|
|
93
|
-
pendingApproval: null,
|
|
94
|
-
});
|
|
95
118
|
}, opts.onRunComplete, {
|
|
96
119
|
...(opts.runOptions ?? {}),
|
|
97
120
|
turnId: opts.turnId ?? opts.runOptions?.turnId,
|
|
@@ -31,6 +31,18 @@ export async function runObserver(options) {
|
|
|
31
31
|
threadId: options.threadId,
|
|
32
32
|
});
|
|
33
33
|
const startIndex = observedThrough + 1;
|
|
34
|
+
// The cursor is a position in whatever array the previous pass observed. The
|
|
35
|
+
// live agent loop passes the engine array, where each tool result is its own
|
|
36
|
+
// entry; other callers pass the store-derived array, which folds those into
|
|
37
|
+
// their tool-call parts and runs several times shorter. A cursor past the end
|
|
38
|
+
// of THIS array is a basis mismatch, not an up-to-date thread: the window can
|
|
39
|
+
// never become non-empty again, so the thread silently stops accruing memory.
|
|
40
|
+
// Measured in production — 17 of 19 threads carry a cursor above their stored
|
|
41
|
+
// message count, one at 57 against 9.
|
|
42
|
+
if (startIndex > options.messages.length) {
|
|
43
|
+
console.warn(`[observational-memory] thread ${options.threadId}: cursor ${observedThrough} is past the end of this ${options.messages.length}-message window, so nothing can be observed on this basis.`);
|
|
44
|
+
return { observed: false, unobservedTokens: 0 };
|
|
45
|
+
}
|
|
34
46
|
const unobserved = options.messages.slice(startIndex);
|
|
35
47
|
if (unobserved.length === 0) {
|
|
36
48
|
return { observed: false, unobservedTokens: 0 };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EventHandler as H3EventHandler } from "h3";
|
|
2
2
|
import type { Task } from "../a2a/types.js";
|
|
3
3
|
import { type ActionAutomationContext, type ActionCaller } from "../action.js";
|
|
4
|
+
import type { AgentNativeHarnessSetting } from "../config.js";
|
|
4
5
|
import { fireInternalDispatch } from "../server/self-dispatch.js";
|
|
5
6
|
import { type ReasoningEffort } from "../shared/reasoning-effort.js";
|
|
6
7
|
import { type ResolveEngineConfig } from "./engine/index.js";
|
|
@@ -299,6 +300,8 @@ export interface ProductionAgentOptions {
|
|
|
299
300
|
model?: string;
|
|
300
301
|
/** App/template id used for org-scoped per-app model defaults. */
|
|
301
302
|
appId?: string;
|
|
303
|
+
/** Static app capability for the hosted tools-only harness picker. */
|
|
304
|
+
hostedHarnessConfig?: AgentNativeHarnessSetting;
|
|
302
305
|
/** Default effort for requests that do not supply an override. */
|
|
303
306
|
reasoningEffort?: ReasoningEffort;
|
|
304
307
|
/** Provider-specific options passed through to the engine */
|