@agent-native/core 0.161.8 → 0.161.10
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/calendar/changelog/2026-08-18-calendar-event-time-editing-respects-the-event-timezone.md +6 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +1 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -1
- package/corpus/templates/clips/changelog/2026-08-18-centered-clip-play-button-and-comment-panel-hint.md +6 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +180 -37
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +18 -3
- package/corpus/templates/design/app/components/layout/Layout.tsx +4 -1
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +13 -2
- package/corpus/templates/design/app/i18n-data.ts +11 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +30 -0
- package/corpus/templates/design/app/lib/builder-host-chat.ts +34 -0
- package/corpus/templates/design/app/lib/builder-host-origin.ts +31 -0
- package/corpus/templates/design/app/lib/embed-chrome.ts +70 -0
- package/corpus/templates/design/app/lib/shell-design.ts +113 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +89 -0
- package/corpus/templates/design/app/pages/design-editor/nudge-intent.ts +85 -12
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +43 -19
- package/corpus/templates/design/app/pages/design-editor/screen-command-utils.ts +9 -2
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +13 -0
- package/corpus/templates/design/app/root.tsx +23 -1
- package/corpus/templates/design/server/lib/fusion-screens.ts +17 -1
- package/corpus/templates/design/server/plugins/builder-host-embed-headers.ts +37 -0
- package/corpus/templates/design/server/routes/[...page].get.ts +1 -0
- package/corpus/templates/design/shared/builder-preview-url.ts +113 -0
- package/corpus/templates/design/shared/full-app.ts +19 -0
- package/corpus/templates/design/shared/shell-screens.ts +139 -0
- package/corpus/templates/design/shared/source-mode.ts +10 -0
- package/corpus/templates/factory/actions/start-builder-for-item.ts +241 -22
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -5
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +50 -14
- package/corpus/templates/factory/server/triage/builder-executor.ts +1 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +231 -9
- package/corpus/templates/slides/app/context/DeckContext.tsx +99 -3
- package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +8 -2
- package/dist/agent/chat-health-alert.d.ts +25 -0
- package/dist/agent/chat-health-alert.js +132 -0
- package/dist/client/RuntimeConfigNotice.js +3 -0
- package/dist/client/api-surface.d.ts +19 -0
- package/dist/client/api-surface.js +32 -0
- package/dist/client/application-state.js +4 -0
- package/dist/client/builder-frame.d.ts +6 -0
- package/dist/client/builder-frame.js +1 -1
- package/dist/client/client-status-requests.js +5 -0
- package/dist/client/host/index.d.ts +1 -0
- package/dist/client/host/index.js +1 -0
- package/dist/client/use-action.d.ts +1 -1
- package/dist/client/use-action.js +17 -0
- package/dist/client/use-session.js +5 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.js +15 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
- /package/corpus/templates/design/app/routes/{visual-edit.$id.tsx → visual-edit_.$id.tsx} +0 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { getDbExec } from "../db/client.js";
|
|
2
|
+
import { notify } from "../notifications/registry.js";
|
|
3
|
+
import { getSetting, putSetting } from "../settings/store.js";
|
|
4
|
+
/**
|
|
5
|
+
* Pages the people who can act when an app's chat stops answering.
|
|
6
|
+
*
|
|
7
|
+
* The detector for this already existed as `scripts/chat-health.mjs --strict`,
|
|
8
|
+
* correctly calibrated and exiting 1 on a partial outage — but nothing ever ran
|
|
9
|
+
* it and nothing ever paged, so an app answering 11% of its turns was found by
|
|
10
|
+
* a user posting in Slack. This is the missing half: the same measurement, on
|
|
11
|
+
* the durable sweep that already drives stale reaping, scoped to the one app it
|
|
12
|
+
* runs in so no cross-app credential has to exist anywhere.
|
|
13
|
+
*/
|
|
14
|
+
/** Turns are scored over this window on every sweep. */
|
|
15
|
+
const WINDOW_MS = 60 * 60_000;
|
|
16
|
+
/**
|
|
17
|
+
* Below this, a rate is noise: one failed turn out of two is 50% and means
|
|
18
|
+
* nothing. Chat-health's own fleet view showed apps sitting at 100% on a single
|
|
19
|
+
* turn all day.
|
|
20
|
+
*/
|
|
21
|
+
const MIN_TURNS = 5;
|
|
22
|
+
/**
|
|
23
|
+
* Deliberately far above `chat-health`'s 0.1 review budget. That threshold
|
|
24
|
+
* answers "is this app degraded", which is a question for a dashboard. This one
|
|
25
|
+
* answers "is chat down", which is the only question worth waking someone for.
|
|
26
|
+
*/
|
|
27
|
+
const BAD_RATE_THRESHOLD = 0.5;
|
|
28
|
+
/** One page per outage, not one per sweep. */
|
|
29
|
+
const COOLDOWN_MS = 60 * 60_000;
|
|
30
|
+
const LAST_ALERT_SETTING_KEY = "chat-health-alert:last-paged-at";
|
|
31
|
+
/**
|
|
32
|
+
* Scores the LAST run of each turn in the window, matching how
|
|
33
|
+
* `scripts/chat-health.mjs` reports so a page and the CLI never disagree.
|
|
34
|
+
* User-stopped turns are excluded: someone hitting Stop is not an outage.
|
|
35
|
+
*/
|
|
36
|
+
async function countRecentTurns(since) {
|
|
37
|
+
const { rows } = await getDbExec().execute({
|
|
38
|
+
sql: `WITH ranked AS (
|
|
39
|
+
SELECT turn_id, status,
|
|
40
|
+
ROW_NUMBER() OVER (
|
|
41
|
+
PARTITION BY turn_id ORDER BY started_at DESC
|
|
42
|
+
) AS rn
|
|
43
|
+
FROM agent_runs
|
|
44
|
+
WHERE started_at >= ?
|
|
45
|
+
AND turn_id IS NOT NULL
|
|
46
|
+
AND id NOT LIKE 'job-%'
|
|
47
|
+
)
|
|
48
|
+
SELECT COUNT(*) AS turns,
|
|
49
|
+
SUM(CASE WHEN status = 'errored' THEN 1 ELSE 0 END) AS bad
|
|
50
|
+
FROM ranked
|
|
51
|
+
WHERE rn = 1 AND status <> 'aborted'`,
|
|
52
|
+
args: [since],
|
|
53
|
+
});
|
|
54
|
+
const row = rows[0];
|
|
55
|
+
return {
|
|
56
|
+
turns: Number(row?.turns ?? 0),
|
|
57
|
+
bad: Number(row?.bad ?? 0),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Owners and admins — the people who can actually change a model or a key. */
|
|
61
|
+
async function alertRecipients() {
|
|
62
|
+
const { rows } = await getDbExec().execute({
|
|
63
|
+
sql: `SELECT DISTINCT email FROM org_members
|
|
64
|
+
WHERE role IN ('owner', 'admin')`,
|
|
65
|
+
args: [],
|
|
66
|
+
});
|
|
67
|
+
return rows
|
|
68
|
+
.map((r) => String(r.email ?? ""))
|
|
69
|
+
.filter(Boolean);
|
|
70
|
+
}
|
|
71
|
+
export async function checkChatHealthAndAlert(now = Date.now()) {
|
|
72
|
+
let counts;
|
|
73
|
+
try {
|
|
74
|
+
counts = await countRecentTurns(now - WINDOW_MS);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// A check that could not read the ledger has not found the app healthy.
|
|
78
|
+
return { status: "check-failed", reason: String(error) };
|
|
79
|
+
}
|
|
80
|
+
if (counts.turns < MIN_TURNS) {
|
|
81
|
+
return { status: "insufficient-data", turns: counts.turns };
|
|
82
|
+
}
|
|
83
|
+
const badRate = counts.bad / counts.turns;
|
|
84
|
+
if (badRate < BAD_RATE_THRESHOLD) {
|
|
85
|
+
return { status: "healthy", turns: counts.turns, badRate };
|
|
86
|
+
}
|
|
87
|
+
const stored = await getSetting(LAST_ALERT_SETTING_KEY).catch(() => null);
|
|
88
|
+
const lastPagedAt = Number(stored?.at ?? 0);
|
|
89
|
+
if (Number.isFinite(lastPagedAt) && now - lastPagedAt < COOLDOWN_MS) {
|
|
90
|
+
return {
|
|
91
|
+
status: "cooldown",
|
|
92
|
+
retryAfterMs: COOLDOWN_MS - (now - lastPagedAt),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
let recipients;
|
|
96
|
+
try {
|
|
97
|
+
recipients = await alertRecipients();
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return { status: "check-failed", reason: String(error) };
|
|
101
|
+
}
|
|
102
|
+
const pct = Math.round(badRate * 100);
|
|
103
|
+
for (const owner of recipients) {
|
|
104
|
+
await notify({
|
|
105
|
+
severity: "critical",
|
|
106
|
+
title: `Chat is failing: ${pct}% of turns ended without an answer`,
|
|
107
|
+
body: `${counts.bad} of ${counts.turns} turns in the last hour ended without ` +
|
|
108
|
+
`an answer. Run \`node scripts/chat-health.mjs --hours 1\` for the ` +
|
|
109
|
+
`per-reason breakdown.`,
|
|
110
|
+
metadata: {
|
|
111
|
+
turns: counts.turns,
|
|
112
|
+
bad: counts.bad,
|
|
113
|
+
badRate,
|
|
114
|
+
windowMs: WINDOW_MS,
|
|
115
|
+
},
|
|
116
|
+
}, { owner }).catch((error) => {
|
|
117
|
+
// One undeliverable recipient must not suppress the rest.
|
|
118
|
+
console.error("[chat-health-alert] notify failed for a recipient:", error);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Stamped only after an attempt was actually made, so a failed page retries
|
|
122
|
+
// on the next sweep instead of being silenced by its own cooldown.
|
|
123
|
+
await putSetting(LAST_ALERT_SETTING_KEY, { at: now }).catch((error) => {
|
|
124
|
+
console.error("[chat-health-alert] could not stamp cooldown:", error);
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
status: "alerted",
|
|
128
|
+
turns: counts.turns,
|
|
129
|
+
badRate,
|
|
130
|
+
recipients: recipients.length,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -3,6 +3,7 @@ import { IconAlertCircle, IconAlertTriangle, IconCheck, IconChevronDown, IconChe
|
|
|
3
3
|
import { useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { parseRuntimeConfigReport, } from "../shared/runtime-config.js";
|
|
5
5
|
import { agentNativePath } from "./api-path.js";
|
|
6
|
+
import { agentNativeApiDisabledReason } from "./api-surface.js";
|
|
6
7
|
import { writeClipboardText } from "./clipboard.js";
|
|
7
8
|
import { useT } from "./i18n.js";
|
|
8
9
|
function injectedAppConfig() {
|
|
@@ -31,6 +32,8 @@ export function RuntimeConfigNotice() {
|
|
|
31
32
|
useEffect(() => {
|
|
32
33
|
if (typeof window.fetch !== "function")
|
|
33
34
|
return;
|
|
35
|
+
if (agentNativeApiDisabledReason())
|
|
36
|
+
return;
|
|
34
37
|
const controller = new AbortController();
|
|
35
38
|
let active = true;
|
|
36
39
|
const timeout = window.setTimeout(() => controller.abort(), 5000);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Some surfaces are framed by a host and carry no agent-native session at all —
|
|
3
|
+
* Builder's Design tab frames a canvas that owns no design row and holds no
|
|
4
|
+
* credential. Every `/_agent-native/*` call from one is unauthenticated by
|
|
5
|
+
* construction, so the client must not make them: a 401 per poll buries real
|
|
6
|
+
* failures, and a write can never land.
|
|
7
|
+
*/
|
|
8
|
+
/** Pass `null` to re-enable, so a surface can be entered and left. */
|
|
9
|
+
export declare function setAgentNativeApiDisabled(reason: string | null): void;
|
|
10
|
+
export declare function agentNativeApiDisabledReason(): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Thrown rather than resolved: a caller that cannot tell "no backend" from
|
|
13
|
+
* "empty result" reports success for work that never happened.
|
|
14
|
+
*/
|
|
15
|
+
export declare class AgentNativeApiDisabledError extends Error {
|
|
16
|
+
readonly reason: string;
|
|
17
|
+
constructor(detail: string);
|
|
18
|
+
}
|
|
19
|
+
export declare function assertAgentNativeApiEnabled(detail: string): void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Some surfaces are framed by a host and carry no agent-native session at all —
|
|
3
|
+
* Builder's Design tab frames a canvas that owns no design row and holds no
|
|
4
|
+
* credential. Every `/_agent-native/*` call from one is unauthenticated by
|
|
5
|
+
* construction, so the client must not make them: a 401 per poll buries real
|
|
6
|
+
* failures, and a write can never land.
|
|
7
|
+
*/
|
|
8
|
+
let disabledReason = null;
|
|
9
|
+
/** Pass `null` to re-enable, so a surface can be entered and left. */
|
|
10
|
+
export function setAgentNativeApiDisabled(reason) {
|
|
11
|
+
disabledReason = reason?.trim() ? reason.trim() : null;
|
|
12
|
+
}
|
|
13
|
+
export function agentNativeApiDisabledReason() {
|
|
14
|
+
return disabledReason;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Thrown rather than resolved: a caller that cannot tell "no backend" from
|
|
18
|
+
* "empty result" reports success for work that never happened.
|
|
19
|
+
*/
|
|
20
|
+
export class AgentNativeApiDisabledError extends Error {
|
|
21
|
+
reason;
|
|
22
|
+
constructor(detail) {
|
|
23
|
+
const reason = disabledReason ?? "unknown surface";
|
|
24
|
+
super(`agent-native API is disabled on this surface (${reason}): ${detail}`);
|
|
25
|
+
this.name = "AgentNativeApiDisabledError";
|
|
26
|
+
this.reason = reason;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function assertAgentNativeApiEnabled(detail) {
|
|
30
|
+
if (disabledReason)
|
|
31
|
+
throw new AgentNativeApiDisabledError(detail);
|
|
32
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { agentNativePath } from "./api-path.js";
|
|
2
|
+
import { assertAgentNativeApiEnabled } from "./api-surface.js";
|
|
2
3
|
const APP_STATE_KEY_PATTERN = /^[a-zA-Z0-9_:-]+$/;
|
|
3
4
|
function appStateUrl(key) {
|
|
4
5
|
if (!APP_STATE_KEY_PATTERN.test(key)) {
|
|
@@ -63,6 +64,7 @@ function jsonBody(value) {
|
|
|
63
64
|
/** Server caps a batch at 100 keys; stay under it when splitting. */
|
|
64
65
|
const MAX_BATCH_KEYS = 100;
|
|
65
66
|
export async function readClientAppStateMany(keys, options = {}) {
|
|
67
|
+
assertAgentNativeApiEnabled(`read application state [${keys.join(", ")}]`);
|
|
66
68
|
const unique = [...new Set(keys)];
|
|
67
69
|
for (const key of unique)
|
|
68
70
|
appStateUrl(key); // validates the key shape
|
|
@@ -142,6 +144,7 @@ export async function readClientAppState(key, options = {}) {
|
|
|
142
144
|
return (batch.values[key] ?? null);
|
|
143
145
|
}
|
|
144
146
|
export async function writeClientAppState(key, value, options = {}) {
|
|
147
|
+
assertAgentNativeApiEnabled(`write application state "${key}"`);
|
|
145
148
|
const response = await fetch(appStateUrl(key), {
|
|
146
149
|
method: "PUT",
|
|
147
150
|
headers: buildHeaders(options.requestSource),
|
|
@@ -152,6 +155,7 @@ export async function writeClientAppState(key, value, options = {}) {
|
|
|
152
155
|
return parseAppStateResponse(response, `Write application state "${key}"`);
|
|
153
156
|
}
|
|
154
157
|
export async function deleteClientAppState(key, options = {}) {
|
|
158
|
+
assertAgentNativeApiEnabled(`delete application state "${key}"`);
|
|
155
159
|
const response = await fetch(appStateUrl(key), {
|
|
156
160
|
method: "DELETE",
|
|
157
161
|
// DELETE carries no JSON body, so this custom header is the only
|
|
@@ -19,6 +19,12 @@ export interface BuilderChatMessage {
|
|
|
19
19
|
submit?: boolean;
|
|
20
20
|
mode?: "act" | "plan";
|
|
21
21
|
requestMode?: "act" | "plan";
|
|
22
|
+
/**
|
|
23
|
+
* Origin an embedder already verified for itself. `getBuilderParentOrigin()`
|
|
24
|
+
* needs `?builder.*` params to trust a loopback parent, which a handshake-based
|
|
25
|
+
* embed never carries — without this the message falls back to `"*"`.
|
|
26
|
+
*/
|
|
27
|
+
targetOrigin?: string;
|
|
22
28
|
}
|
|
23
29
|
export declare function sendToBuilderChat(opts: BuilderChatMessage): boolean;
|
|
24
30
|
/**
|
|
@@ -116,7 +116,7 @@ export function sendToBuilderChat(opts) {
|
|
|
116
116
|
if (typeof window === "undefined" || !opts.message?.trim())
|
|
117
117
|
return false;
|
|
118
118
|
const hasParentFrame = window.parent !== window;
|
|
119
|
-
const targetOrigin = getBuilderParentOrigin() ?? "*";
|
|
119
|
+
const targetOrigin = opts.targetOrigin ?? getBuilderParentOrigin() ?? "*";
|
|
120
120
|
const payload = {
|
|
121
121
|
type: "builder.submitChat",
|
|
122
122
|
data: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { agentNativePath } from "./api-path.js";
|
|
2
|
+
import { agentNativeApiDisabledReason } from "./api-surface.js";
|
|
2
3
|
const RESULT_TTL_MS = 500;
|
|
3
4
|
const REQUEST_TIMEOUT_MS = 15_000;
|
|
4
5
|
const cache = new Map();
|
|
@@ -31,6 +32,10 @@ function installInvalidationListeners() {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
async function fetchClientStatus(path) {
|
|
35
|
+
// "unavailable" rather than a fabricated payload: callers already treat it as
|
|
36
|
+
// "could not read", and there is genuinely nothing to read here.
|
|
37
|
+
if (agentNativeApiDisabledReason())
|
|
38
|
+
return { state: "unavailable" };
|
|
34
39
|
installInvalidationListeners();
|
|
35
40
|
const url = agentNativePath(path);
|
|
36
41
|
const cached = cache.get(url);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { initializeAgentNativeClient } from "../client-bootstrap.js";
|
|
2
|
+
export { agentNativeApiDisabledReason, AgentNativeApiDisabledError, setAgentNativeApiDisabled, } from "../api-surface.js";
|
|
2
3
|
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, } from "../embed-auth.js";
|
|
3
4
|
export { sendToFrame, onFrameMessage, requestUserInfo, getFrameOrigin, getFramePostMessageTargetOrigin, getCallbackOrigin, oauthRedirectUri, isInFrame, enterStyleEditing, enterTextEditing, exitSelectionMode, type UserInfo, } from "../frame.js";
|
|
4
5
|
export { getBuilderParentOrigin, isInBuilderFrame, sendToBuilderChat, type BuilderChatMessage, } from "../builder-frame.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { initializeAgentNativeClient } from "../client-bootstrap.js";
|
|
2
|
+
export { agentNativeApiDisabledReason, AgentNativeApiDisabledError, setAgentNativeApiDisabled, } from "../api-surface.js";
|
|
2
3
|
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, } from "../embed-auth.js";
|
|
3
4
|
export { sendToFrame, onFrameMessage, requestUserInfo, getFrameOrigin, getFramePostMessageTargetOrigin, getCallbackOrigin, oauthRedirectUri, isInFrame, enterStyleEditing, enterTextEditing, exitSelectionMode, } from "../frame.js";
|
|
4
5
|
export { getBuilderParentOrigin, isInBuilderFrame, sendToBuilderChat, } from "../builder-frame.js";
|
|
@@ -73,7 +73,7 @@ export declare const ACTION_KEEPALIVE_BODY_BUDGET_BYTES = 48000;
|
|
|
73
73
|
* `/_agent-native/actions/*` in components.
|
|
74
74
|
*/
|
|
75
75
|
export declare function callAction<TResult = undefined, TName extends ActionName = ActionName>(actionName: TName, params?: ActionParams<TName>, options?: ClientActionCallOptions): Promise<TResult extends undefined ? ActionResult<TName> : TResult>;
|
|
76
|
-
export type KeepaliveActionCallRejectionReason = "body-too-large" | "budget-exhausted";
|
|
76
|
+
export type KeepaliveActionCallRejectionReason = "body-too-large" | "budget-exhausted" | "api-disabled";
|
|
77
77
|
export type KeepaliveActionCallResult<TResult> = {
|
|
78
78
|
accepted: true;
|
|
79
79
|
bodyBytes: number;
|
|
@@ -27,6 +27,7 @@ import { getAnalyticsClientPlatform } from "./analytics-platform.js";
|
|
|
27
27
|
import { getOrCreateAnalyticsSessionId } from "./analytics-session.js";
|
|
28
28
|
import { trackEvent } from "./analytics.js";
|
|
29
29
|
import { agentNativePath } from "./api-path.js";
|
|
30
|
+
import { agentNativeApiDisabledReason, assertAgentNativeApiEnabled, } from "./api-surface.js";
|
|
30
31
|
import { getBrowserTabId } from "./browser-tab-id.js";
|
|
31
32
|
import { clientBuildId, clientCompatibilityVersion, reloadForClientCompatibilityMismatch, } from "./build-compatibility.js";
|
|
32
33
|
import { ensureEmbedAuthFetchInterceptor } from "./embed-auth.js";
|
|
@@ -398,6 +399,7 @@ function shouldTrackActionResponse(error, durationMs, response) {
|
|
|
398
399
|
return Math.random() < rate;
|
|
399
400
|
}
|
|
400
401
|
async function actionFetch(name, method, params, options) {
|
|
402
|
+
assertAgentNativeApiEnabled(`${method} ${name}`);
|
|
401
403
|
const startedAt = actionTelemetryNow();
|
|
402
404
|
let response;
|
|
403
405
|
let responseAt;
|
|
@@ -501,6 +503,16 @@ export function callAction(actionName, params, options = {}) {
|
|
|
501
503
|
export function tryCallActionKeepalive(actionName, params, options = {}) {
|
|
502
504
|
const serializedBody = JSON.stringify(params ?? {});
|
|
503
505
|
const bodyBytes = utf8ByteLength(serializedBody);
|
|
506
|
+
// Reported as a refusal rather than thrown: callers keep the work queued on
|
|
507
|
+
// `accepted: false`, which is the honest outcome for a surface with no backend.
|
|
508
|
+
if (agentNativeApiDisabledReason()) {
|
|
509
|
+
return {
|
|
510
|
+
accepted: false,
|
|
511
|
+
bodyBytes,
|
|
512
|
+
reason: "api-disabled",
|
|
513
|
+
completion: null,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
504
516
|
if (bodyBytes > ACTION_KEEPALIVE_BODY_BUDGET_BYTES) {
|
|
505
517
|
return {
|
|
506
518
|
accepted: false,
|
|
@@ -547,6 +559,10 @@ export function tryCallActionKeepalive(actionName, params, options = {}) {
|
|
|
547
559
|
* ```
|
|
548
560
|
*/
|
|
549
561
|
export function useActionQuery(actionName, params, options) {
|
|
562
|
+
// Not `enabled: false` via options: a disabled surface must win over whatever
|
|
563
|
+
// the caller asked for, and an unfired query reads as "no data" rather than
|
|
564
|
+
// as an error the UI has to special-case.
|
|
565
|
+
const apiDisabled = Boolean(agentNativeApiDisabledReason());
|
|
550
566
|
return useQuery({
|
|
551
567
|
queryKey: ["action", actionName, params],
|
|
552
568
|
// Thread React Query's per-fetch AbortSignal into the network request so
|
|
@@ -556,6 +572,7 @@ export function useActionQuery(actionName, params, options) {
|
|
|
556
572
|
retry: defaultActionQueryRetry,
|
|
557
573
|
retryDelay: defaultActionQueryRetryDelay,
|
|
558
574
|
...options,
|
|
575
|
+
...(apiDisabled ? { enabled: false } : {}),
|
|
559
576
|
});
|
|
560
577
|
}
|
|
561
578
|
// ---------------------------------------------------------------------------
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { setSentryUser, trackSessionStatus } from "./analytics.js";
|
|
3
|
+
import { agentNativeApiDisabledReason } from "./api-surface.js";
|
|
3
4
|
import { fetchAuthSessionStatus, invalidateClientStatusRequest, } from "./client-status-requests.js";
|
|
4
5
|
import { getFrameOrigin, getFramePostMessageTargetOrigin } from "./frame.js";
|
|
5
6
|
const SESSION_CACHE_TTL_MS = 30_000;
|
|
@@ -98,6 +99,10 @@ export function notifySessionInvalidated() {
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
function fetchSharedSession() {
|
|
102
|
+
// A surface with no agent-native backend is genuinely signed out. `undefined`
|
|
103
|
+
// would read as "unavailable" and retry until it gave up with an error.
|
|
104
|
+
if (agentNativeApiDisabledReason())
|
|
105
|
+
return Promise.resolve(null);
|
|
101
106
|
if (hasFreshSessionCache())
|
|
102
107
|
return Promise.resolve(cachedSession ?? null);
|
|
103
108
|
if (sessionRequest)
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
-
error?: undefined;
|
|
66
65
|
states: {
|
|
67
66
|
clientId: number;
|
|
68
67
|
state: string;
|
|
69
68
|
}[];
|
|
69
|
+
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
-
error?: undefined;
|
|
81
80
|
users: {
|
|
82
81
|
clientId: number;
|
|
83
82
|
lastSeen: number;
|
|
84
83
|
}[];
|
|
84
|
+
error?: undefined;
|
|
85
85
|
}>>;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
error: string;
|
|
30
29
|
ok?: undefined;
|
|
30
|
+
error: string;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
+
error?: undefined;
|
|
45
|
+
ok?: undefined;
|
|
44
46
|
summary: import("./types.js").TraceSummary;
|
|
45
47
|
spans: import("./types.js").TraceSpan[];
|
|
46
48
|
id?: undefined;
|
|
49
|
+
} | {
|
|
47
50
|
error?: undefined;
|
|
48
51
|
ok?: undefined;
|
|
49
|
-
} | {
|
|
50
52
|
summary?: undefined;
|
|
51
53
|
spans?: undefined;
|
|
52
54
|
id: string;
|
|
53
|
-
error?: undefined;
|
|
54
|
-
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
+
ok?: undefined;
|
|
56
57
|
summary?: undefined;
|
|
57
58
|
spans?: undefined;
|
|
58
59
|
id?: undefined;
|
|
59
60
|
error: any;
|
|
60
|
-
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
+
error?: undefined;
|
|
62
63
|
summary?: undefined;
|
|
63
64
|
spans?: undefined;
|
|
64
65
|
id?: undefined;
|
|
65
|
-
error?: undefined;
|
|
66
66
|
ok: boolean;
|
|
67
67
|
}>>;
|
|
@@ -75,7 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
75
75
|
user: "user";
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
|
-
|
|
78
|
+
id?: undefined;
|
|
79
|
+
provider?: undefined;
|
|
79
80
|
deleted?: undefined;
|
|
80
81
|
providers: {
|
|
81
82
|
id: string;
|
|
@@ -87,47 +88,46 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
87
88
|
updatedAt: number;
|
|
88
89
|
}[];
|
|
89
90
|
count: number;
|
|
90
|
-
|
|
91
|
+
found?: undefined;
|
|
91
92
|
registered?: undefined;
|
|
92
|
-
id?: undefined;
|
|
93
93
|
label?: undefined;
|
|
94
94
|
message?: undefined;
|
|
95
95
|
} | {
|
|
96
|
+
id?: undefined;
|
|
96
97
|
deleted?: undefined;
|
|
97
98
|
providers?: undefined;
|
|
98
99
|
count?: undefined;
|
|
99
100
|
found: boolean;
|
|
100
101
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
101
102
|
registered?: undefined;
|
|
102
|
-
id?: undefined;
|
|
103
103
|
label?: undefined;
|
|
104
104
|
message?: undefined;
|
|
105
105
|
} | {
|
|
106
|
+
provider?: undefined;
|
|
106
107
|
deleted?: undefined;
|
|
107
108
|
providers?: undefined;
|
|
108
109
|
count?: undefined;
|
|
109
|
-
provider?: undefined;
|
|
110
110
|
found: boolean;
|
|
111
111
|
id: string;
|
|
112
112
|
registered?: undefined;
|
|
113
113
|
label?: undefined;
|
|
114
114
|
message?: undefined;
|
|
115
115
|
} | {
|
|
116
|
-
|
|
116
|
+
provider?: undefined;
|
|
117
117
|
providers?: undefined;
|
|
118
118
|
count?: undefined;
|
|
119
|
-
|
|
119
|
+
found?: undefined;
|
|
120
120
|
deleted: boolean;
|
|
121
121
|
id: string;
|
|
122
122
|
registered?: undefined;
|
|
123
123
|
label?: undefined;
|
|
124
124
|
message?: undefined;
|
|
125
125
|
} | {
|
|
126
|
-
|
|
126
|
+
provider?: undefined;
|
|
127
127
|
deleted?: undefined;
|
|
128
128
|
providers?: undefined;
|
|
129
129
|
count?: undefined;
|
|
130
|
-
|
|
130
|
+
found?: undefined;
|
|
131
131
|
registered: boolean;
|
|
132
132
|
id: string;
|
|
133
133
|
label: string;
|
|
@@ -312,9 +312,6 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
312
312
|
scope?: "org" | "user" | undefined;
|
|
313
313
|
}, {
|
|
314
314
|
id?: undefined;
|
|
315
|
-
provider?: undefined;
|
|
316
|
-
deleted?: undefined;
|
|
317
|
-
found?: undefined;
|
|
318
315
|
message?: undefined;
|
|
319
316
|
providers: {
|
|
320
317
|
id: string;
|
|
@@ -326,44 +323,47 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
326
323
|
updatedAt: number;
|
|
327
324
|
}[];
|
|
328
325
|
count: number;
|
|
326
|
+
provider?: undefined;
|
|
327
|
+
found?: undefined;
|
|
328
|
+
deleted?: undefined;
|
|
329
329
|
registered?: undefined;
|
|
330
330
|
label?: undefined;
|
|
331
331
|
} | {
|
|
332
332
|
id?: undefined;
|
|
333
|
-
deleted?: undefined;
|
|
334
333
|
message?: undefined;
|
|
335
334
|
providers?: undefined;
|
|
336
335
|
count?: undefined;
|
|
337
336
|
found: boolean;
|
|
338
337
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
338
|
+
deleted?: undefined;
|
|
339
339
|
registered?: undefined;
|
|
340
340
|
label?: undefined;
|
|
341
341
|
} | {
|
|
342
|
-
provider?: undefined;
|
|
343
|
-
deleted?: undefined;
|
|
344
342
|
message?: undefined;
|
|
345
343
|
providers?: undefined;
|
|
346
344
|
count?: undefined;
|
|
345
|
+
provider?: undefined;
|
|
347
346
|
found: boolean;
|
|
348
347
|
id: string;
|
|
348
|
+
deleted?: undefined;
|
|
349
349
|
registered?: undefined;
|
|
350
350
|
label?: undefined;
|
|
351
351
|
} | {
|
|
352
|
-
provider?: undefined;
|
|
353
|
-
found?: undefined;
|
|
354
352
|
message?: undefined;
|
|
355
353
|
providers?: undefined;
|
|
356
354
|
count?: undefined;
|
|
355
|
+
provider?: undefined;
|
|
356
|
+
found?: undefined;
|
|
357
357
|
deleted: boolean;
|
|
358
358
|
id: string;
|
|
359
359
|
registered?: undefined;
|
|
360
360
|
label?: undefined;
|
|
361
361
|
} | {
|
|
362
|
-
provider?: undefined;
|
|
363
|
-
deleted?: undefined;
|
|
364
|
-
found?: undefined;
|
|
365
362
|
providers?: undefined;
|
|
366
363
|
count?: undefined;
|
|
364
|
+
provider?: undefined;
|
|
365
|
+
found?: undefined;
|
|
366
|
+
deleted?: undefined;
|
|
367
367
|
registered: boolean;
|
|
368
368
|
id: string;
|
|
369
369
|
label: string;
|
|
@@ -73,6 +73,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
73
73
|
offset?: unknown;
|
|
74
74
|
limit?: unknown;
|
|
75
75
|
}, {
|
|
76
|
+
deleted?: undefined;
|
|
76
77
|
jobs: {
|
|
77
78
|
id: string;
|
|
78
79
|
name: string;
|
|
@@ -83,7 +84,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
83
84
|
updatedAt: string;
|
|
84
85
|
}[];
|
|
85
86
|
total: number;
|
|
86
|
-
deleted?: undefined;
|
|
87
87
|
jobId?: undefined;
|
|
88
88
|
} | {
|
|
89
89
|
jobs?: undefined;
|
|
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
91
91
|
deleted: boolean;
|
|
92
92
|
jobId: string;
|
|
93
93
|
} | {
|
|
94
|
+
deleted?: undefined;
|
|
94
95
|
jobs?: undefined;
|
|
95
96
|
total?: undefined;
|
|
96
|
-
deleted?: undefined;
|
|
97
97
|
jobId?: undefined;
|
|
98
98
|
hits: Record<string, unknown>[];
|
|
99
99
|
offset: number;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
status?: undefined;
|
|
38
37
|
ok?: undefined;
|
|
38
|
+
status?: undefined;
|
|
39
39
|
} | {
|
|
40
|
+
error?: undefined;
|
|
40
41
|
ok: boolean;
|
|
41
42
|
status: string;
|
|
42
|
-
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
44
45
|
error: string;
|
|
45
46
|
removed?: undefined;
|
|
46
|
-
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
+
error?: undefined;
|
|
48
49
|
ok: boolean;
|
|
49
50
|
removed: boolean;
|
|
50
|
-
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
+
ok?: undefined;
|
|
57
58
|
error: string;
|
|
58
59
|
note?: undefined;
|
|
59
|
-
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
+
error?: undefined;
|
|
61
62
|
ok: boolean;
|
|
62
63
|
note?: undefined;
|
|
63
|
-
error?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
67
|
note: string;
|
|
67
|
-
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
+
error?: undefined;
|
|
98
99
|
ok: boolean;
|
|
99
100
|
key: string;
|
|
100
|
-
error?: undefined;
|
|
101
101
|
} | {
|
|
102
|
+
error?: undefined;
|
|
102
103
|
ok: boolean;
|
|
103
104
|
removed: boolean;
|
|
104
|
-
error?: undefined;
|
|
105
105
|
}>>;
|