@agent-native/core 0.136.4 → 0.136.5

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.
@@ -78,6 +78,12 @@ export async function dispatchPostFinalizeJob(args: {
78
78
  }
79
79
  : {}),
80
80
  });
81
+ console.log("[post-finalize] dispatching", {
82
+ recordingId: args.recordingId,
83
+ kind: args.kind,
84
+ workerUrl,
85
+ usesDurableBackground,
86
+ });
81
87
  const post = (url: string) =>
82
88
  fetch(url, {
83
89
  method: "POST",
@@ -89,7 +95,14 @@ export async function dispatchPostFinalizeJob(args: {
89
95
  usesDurableBackground && !initialResponse.ok
90
96
  ? await post(resolveWorkerUrl(processorRoute))
91
97
  : initialResponse;
92
- if (response.ok) return;
98
+ if (response.ok) {
99
+ console.log("[post-finalize] dispatch accepted", {
100
+ recordingId: args.recordingId,
101
+ kind: args.kind,
102
+ status: response.status,
103
+ });
104
+ return;
105
+ }
93
106
  const detail = (await response.text().catch(() => "")).trim().slice(0, 300);
94
107
  throw new Error(
95
108
  `Post-finalize ${args.kind} worker returned HTTP ${response.status}${
@@ -45,6 +45,15 @@ export default createAuthPlugin({
45
45
  "/api/video",
46
46
  "/api/thumbnail",
47
47
  "/api/auth/google-calendar",
48
+ // Internal post-finalize worker (media verification, seekable remux,
49
+ // transcript, brain-export, loom-import retries). It's a server-to-server
50
+ // self-dispatch with no session cookie — its own scoped, short-lived
51
+ // signed token (verifyScopedAgentAccessToken) is the real auth check, so
52
+ // it must bypass the session gate to ever reach that check. Exact path
53
+ // only, not the whole `_agent-native-background` namespace — a future
54
+ // route added under that prefix without its own auth check must not
55
+ // become silently public by inheriting this bypass.
56
+ "/api/_agent-native-background/post-finalize-worker",
48
57
  "/_agent-native/google/auth-url",
49
58
  "/_agent-native/google/callback",
50
59
  ],
@@ -51,11 +51,17 @@ export default defineEventHandler(async (event: H3Event) => {
51
51
 
52
52
  const { recordingId, kind, token, delayMs, retryAttempt, regenerate } =
53
53
  parsed.data;
54
+ console.log("[post-finalize-worker] received job", { recordingId, kind });
54
55
  const verified = verifyScopedAgentAccessToken(token, {
55
56
  resourceKind: POST_FINALIZE_JOB_TOKEN_KIND,
56
57
  resourceId: postFinalizeJobResourceId(recordingId, kind),
57
58
  });
58
59
  if (!verified.ok) {
60
+ console.warn("[post-finalize-worker] token verification failed", {
61
+ recordingId,
62
+ kind,
63
+ reason: verified.reason,
64
+ });
59
65
  setResponseStatus(event, 401);
60
66
  return { ok: false, error: "Invalid or expired post-finalize job token" };
61
67
  }
@@ -153,6 +159,9 @@ export default defineEventHandler(async (event: H3Event) => {
153
159
  )
154
160
  .returning({ id: schema.recordings.id });
155
161
  if (!claimed) {
162
+ console.log("[post-finalize-worker] loom-import already running", {
163
+ recordingId,
164
+ });
156
165
  return {
157
166
  ok: true,
158
167
  recordingId,
@@ -161,6 +170,10 @@ export default defineEventHandler(async (event: H3Event) => {
161
170
  reason: "loom-import-already-running",
162
171
  };
163
172
  }
173
+ console.log("[post-finalize-worker] loom-import claimed", {
174
+ recordingId,
175
+ claimId,
176
+ });
164
177
  const result = await runLoomImportJob({
165
178
  recordingId,
166
179
  ownerEmail: recording.ownerEmail,
@@ -98,16 +98,23 @@ function KeysHeader({ availableSecrets = [], onSecret, onCustomKey, }) {
98
98
  }, children: [_jsx(IconPlus, { size: 14 }), "Custom"] }) })] })] }) })] })] }));
99
99
  }
100
100
  function SecretCard({ secret, onChanged, open, onOpenChange, focusInput, }) {
101
+ const t = useT();
101
102
  const [value, setValue] = useState("");
103
+ const [isRotating, setIsRotating] = useState(false);
102
104
  const [busy, setBusy] = useState(null);
103
105
  const [confirmDelete, setConfirmDelete] = useState(false);
104
106
  const [toast, setToast] = useState(null);
105
107
  const inputRef = React.useRef(null);
106
108
  useEffect(() => {
107
- if (open && focusInput && inputRef.current) {
109
+ if (!open) {
110
+ setValue("");
111
+ setIsRotating(false);
112
+ return;
113
+ }
114
+ if ((focusInput || isRotating) && inputRef.current) {
108
115
  inputRef.current.focus();
109
116
  }
110
- }, [focusInput, open]);
117
+ }, [focusInput, isRotating, open]);
111
118
  const setToastAndClear = (kind, text, ms = 2500) => {
112
119
  setToast({ kind, text });
113
120
  setTimeout(() => setToast(null), ms);
@@ -131,6 +138,7 @@ function SecretCard({ secret, onChanged, open, onOpenChange, focusInput, }) {
131
138
  return;
132
139
  }
133
140
  setValue("");
141
+ setIsRotating(false);
134
142
  setConfirmDelete(false);
135
143
  setToastAndClear("ok", "Saved");
136
144
  notifySecretsChanged();
@@ -166,20 +174,32 @@ function SecretCard({ secret, onChanged, open, onOpenChange, focusInput, }) {
166
174
  setBusy(null);
167
175
  }
168
176
  };
169
- const handleTest = async () => {
177
+ const handleTest = async (candidateValue) => {
170
178
  if (busy)
171
179
  return;
172
- setBusy("test");
180
+ const isCandidate = candidateValue !== undefined;
181
+ setBusy(isCandidate ? "test-candidate" : "test");
173
182
  try {
174
183
  const res = await fetch(`${ENDPOINT}/${encodeURIComponent(secret.key)}/test`, {
175
184
  method: "POST",
185
+ ...(isCandidate
186
+ ? {
187
+ headers: { "Content-Type": "application/json" },
188
+ body: JSON.stringify({ value: candidateValue }),
189
+ }
190
+ : {}),
176
191
  });
177
192
  const body = (await res.json().catch(() => ({})));
178
193
  if (res.ok && body.ok) {
179
- setToastAndClear("ok", "Working");
194
+ setToastAndClear("ok", isCandidate
195
+ ? t("secrets.candidateValueWorking")
196
+ : t("secrets.storedValueWorking"));
180
197
  }
181
198
  else {
182
- setToastAndClear("err", body.error ?? (body.ok === false ? "Invalid" : `Test failed`));
199
+ setToastAndClear("err", body.error ??
200
+ (body.ok === false
201
+ ? t("secrets.invalid")
202
+ : t("secrets.testFailed")));
183
203
  }
184
204
  }
185
205
  finally {
@@ -196,12 +216,16 @@ function SecretCard({ secret, onChanged, open, onOpenChange, focusInput, }) {
196
216
  return (_jsx("span", { className: "rounded-full bg-accent/60 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Optional" }));
197
217
  }, [secret.status, secret.required]);
198
218
  const isOAuth = secret.kind === "oauth";
199
- return (_jsxs("div", { className: "border-b border-border last:border-b-0", children: [_jsxs(Button, { type: "button", intent: "neutral", emphasis: "ghost", "aria-expanded": open, onClick: () => onOpenChange(!open), className: "flex w-full items-center gap-2 px-2.5 py-2 text-start transition-colors hover:bg-accent/30", children: [_jsx(IconChevronRight, { size: 13, className: `shrink-0 text-muted-foreground transition-transform ${open ? "rotate-90" : ""}` }), _jsx("span", { className: "min-w-0 flex-1 truncate text-[11px] font-medium text-foreground", children: secret.label }), secret.status === "set" && secret.last4 && (_jsxs("code", { className: "text-[10px] text-muted-foreground", children: ["\u2022\u2022\u2022\u2022", secret.last4] })), _jsx("span", { className: "shrink-0", children: pill })] }), open && (_jsxs("div", { className: "border-t border-border/60 bg-accent/20 px-3 pb-3 pt-2.5", children: [secret.description && (_jsx("p", { className: "mb-2 text-[10px] leading-relaxed text-muted-foreground", children: secret.description })), isOAuth ? (_jsxs("div", { className: "mt-2 flex items-center gap-1.5", children: [secret.oauthConnectUrl && (_jsxs("a", { href: secret.oauthConnectUrl, className: "inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium no-underline", style: { backgroundColor: "#00B5FF", color: "white" }, children: [_jsx(IconPlugConnected, { size: 10 }), secret.status === "set" ? "Reconnect" : "Connect"] })), secret.docsUrl && (_jsxs("a", { href: secret.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] no-underline text-muted-foreground hover:text-foreground", children: ["Docs", _jsx(IconExternalLink, { size: 10 })] }))] })) : (_jsxs("div", { className: "mt-2 space-y-1.5", children: [secret.status === "set" && (_jsxs("div", { className: "flex items-center gap-2 text-[10px] text-muted-foreground", children: [_jsx("span", { children: "Stored value ending in" }), _jsx("code", { className: "rounded bg-background px-1 py-0.5 text-foreground", children: secret.last4 })] })), _jsxs("div", { className: "flex gap-1.5", children: [_jsx(TextField, { inputRef: inputRef, type: "password", "aria-label": secret.label, value: value, onChange: setValue, onKeyDown: (event) => {
219
+ const showRotationForm = secret.status !== "set" || isRotating;
220
+ return (_jsxs("div", { className: "border-b border-border last:border-b-0", children: [_jsxs(Button, { type: "button", intent: "neutral", emphasis: "ghost", "aria-expanded": open, onClick: () => onOpenChange(!open), className: "flex w-full items-center gap-2 px-2.5 py-2 text-start transition-colors hover:bg-accent/30", children: [_jsx(IconChevronRight, { size: 13, className: `shrink-0 text-muted-foreground transition-transform ${open ? "rotate-90" : ""}` }), _jsx("span", { className: "min-w-0 flex-1 truncate text-[11px] font-medium text-foreground", children: secret.label }), secret.status === "set" && secret.last4 && (_jsxs("code", { className: "text-[10px] text-muted-foreground", children: ["\u2022\u2022\u2022\u2022", secret.last4] })), _jsx("span", { className: "shrink-0", children: pill })] }), open && (_jsxs("div", { className: "border-t border-border/60 bg-accent/20 px-3 pb-3 pt-2.5", children: [secret.description && (_jsx("p", { className: "mb-2 text-[10px] leading-relaxed text-muted-foreground", children: secret.description })), isOAuth ? (_jsxs("div", { className: "mt-2 flex items-center gap-1.5", children: [secret.oauthConnectUrl && (_jsxs("a", { href: secret.oauthConnectUrl, className: "inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium no-underline", style: { backgroundColor: "#00B5FF", color: "white" }, children: [_jsx(IconPlugConnected, { size: 10 }), secret.status === "set" ? "Reconnect" : "Connect"] })), secret.docsUrl && (_jsxs("a", { href: secret.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] no-underline text-muted-foreground hover:text-foreground", children: ["Docs", _jsx(IconExternalLink, { size: 10 })] }))] })) : (_jsxs("div", { className: "mt-2 space-y-2", children: [secret.status === "set" && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2 text-[10px] text-muted-foreground", children: [_jsx("span", { children: "Stored value ending in" }), _jsx("code", { className: "mt-1 block w-fit rounded bg-background px-1 py-0.5 text-foreground", children: secret.last4 })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => handleTest(), disabled: busy !== null, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40", children: busy === "test" ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : (t("secrets.testStoredValue")) }), _jsxs(Button, { type: "button", intent: "primary", emphasis: "solid", onClick: () => setIsRotating(true), disabled: busy !== null, className: "inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium disabled:opacity-40", style: { backgroundColor: "#00B5FF", color: "white" }, children: [_jsx(IconRefresh, { size: 10 }), "Rotate"] }), _jsxs(Button, { type: "button", intent: "danger", emphasis: "outline", onClick: () => setConfirmDelete(true), disabled: busy !== null, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:text-red-500 disabled:opacity-40", children: [_jsx(IconTrash, { size: 10 }), "Delete"] })] })] })), showRotationForm && (_jsxs("div", { className: "space-y-1.5", children: [_jsx("hr", { className: "my-4" }), _jsx(TextField, { inputRef: inputRef, type: "password", "aria-label": secret.label, value: value, onChange: setValue, onKeyDown: (event) => {
200
221
  if (event.key === "Enter")
201
222
  handleSave();
202
223
  }, placeholder: secret.status === "set"
203
224
  ? "Enter new value to rotate"
204
- : "Paste key", className: "flex-1 text-[11px]" }), _jsx(Button, { type: "button", intent: "primary", emphasis: "solid", onClick: handleSave, disabled: !value.trim() || busy !== null, className: "inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium disabled:opacity-40", style: { backgroundColor: "#00B5FF", color: "white" }, children: busy === "save" ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : secret.status === "set" ? (_jsxs(_Fragment, { children: [_jsx(IconRefresh, { size: 10 }), "Rotate"] })) : ("Save") })] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [secret.status === "set" && (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: handleTest, disabled: busy !== null, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40", children: busy === "test" ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : ("Test") }), _jsxs(Button, { type: "button", intent: "danger", emphasis: "outline", onClick: () => setConfirmDelete(true), disabled: busy !== null, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:text-red-500 disabled:opacity-40", children: [_jsx(IconTrash, { size: 10 }), "Remove"] })] })), secret.docsUrl && (_jsxs("a", { href: secret.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] no-underline text-muted-foreground hover:text-foreground ms-auto", children: ["Get key", _jsx(IconExternalLink, { size: 10 })] }))] }), confirmDelete && (_jsxs("div", { className: "flex items-center gap-1.5 rounded border border-red-500/30 bg-red-500/10 px-2 py-1.5 text-[10px] text-red-500", children: [_jsx("span", { className: "min-w-0 flex-1", children: "Remove this saved value?" }), _jsx(Button, { type: "button", intent: "danger", emphasis: "solid", onClick: handleDelete, disabled: busy !== null, className: "inline-flex items-center gap-1 rounded border border-red-500/40 px-1.5 py-0.5 font-medium disabled:opacity-40", children: busy === "delete" ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : ("Confirm") }), _jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => setConfirmDelete(false), disabled: busy !== null, className: "rounded border border-border px-1.5 py-0.5 text-muted-foreground hover:text-foreground disabled:opacity-40", children: "Cancel" })] }))] })), toast && (_jsx("p", { className: `mt-1.5 text-[10px] ${toast.kind === "ok" ? "text-green-500" : "text-red-500"}`, children: toast.text }))] }))] }));
225
+ : "Paste key", className: "w-full text-[11px]" }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => handleTest(value.trim()), disabled: !value.trim() || busy !== null, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40", children: busy === "test-candidate" ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : (t("secrets.testStoredValue")) }), secret.docsUrl && (_jsxs("a", { href: secret.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] no-underline text-muted-foreground hover:text-foreground", children: ["Get key", _jsx(IconExternalLink, { size: 10 })] })), secret.status === "set" && (_jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => {
226
+ setValue("");
227
+ setIsRotating(false);
228
+ }, disabled: busy !== null, className: "rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40", children: "Discard" })), _jsx(Button, { type: "button", intent: "primary", emphasis: "solid", onClick: handleSave, disabled: !value.trim() || busy !== null, className: "inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium disabled:opacity-40", style: { backgroundColor: "#00B5FF", color: "white" }, children: busy === "save" ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : ("Save") })] })] })), confirmDelete && (_jsxs("div", { className: "flex items-center gap-1.5 rounded border border-red-500/30 bg-red-500/10 px-2 py-1.5 text-[10px] text-red-500", children: [_jsx("span", { className: "min-w-0 flex-1", children: "Remove this saved value?" }), _jsx(Button, { type: "button", intent: "danger", emphasis: "solid", onClick: handleDelete, disabled: busy !== null, className: "inline-flex items-center gap-1 rounded border border-red-500/40 px-1.5 py-0.5 font-medium disabled:opacity-40", children: busy === "delete" ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : ("Confirm") }), _jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => setConfirmDelete(false), disabled: busy !== null, className: "rounded border border-border px-1.5 py-0.5 text-muted-foreground hover:text-foreground disabled:opacity-40", children: "Cancel" })] }))] })), toast && (_jsx("p", { className: `mt-1.5 text-[10px] ${toast.kind === "ok" ? "text-green-500" : "text-red-500"}`, children: toast.text }))] }))] }));
205
229
  }
206
230
  const ADHOC_ENDPOINT = agentNativePath("/_agent-native/secrets/adhoc");
207
231
  function AdHocKeysSection({ showForm, onShowFormChange, showEmptyState, }) {
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -17,12 +17,12 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
- error?: undefined;
21
20
  configured?: undefined;
22
21
  connectPath?: undefined;
23
22
  url: string;
24
23
  id: string;
25
24
  provider: string;
25
+ error?: undefined;
26
26
  }>;
27
27
  export default _default;
28
28
  //# sourceMappingURL=upload-image.d.ts.map
@@ -57,6 +57,11 @@ declare const messages: {
57
57
  scopeLabel: string;
58
58
  scopePersonal: string;
59
59
  scopeWorkspace: string;
60
+ testStoredValue: string;
61
+ candidateValueWorking: string;
62
+ storedValueWorking: string;
63
+ invalid: string;
64
+ testFailed: string;
60
65
  scopePersonalDescription: string;
61
66
  scopeWorkspaceDescription: string;
62
67
  };
@@ -62,6 +62,11 @@ const messages = {
62
62
  scopeLabel: "Scope",
63
63
  scopePersonal: "Personal",
64
64
  scopeWorkspace: "Workspace",
65
+ testStoredValue: "Test",
66
+ candidateValueWorking: "New value works",
67
+ storedValueWorking: "Working",
68
+ invalid: "Invalid",
69
+ testFailed: "Test failed",
65
70
  scopePersonalDescription: "Only your own signed-in sessions use this key. Integration, webhook, scheduled job, automation, and agent-to-agent runs sign in as their owner rather than as you, so they cannot read it.",
66
71
  scopeWorkspaceDescription: "Everyone in this workspace uses this key, including integration, webhook, scheduled job, automation, and agent-to-agent runs.",
67
72
  },
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
137
137
  inputSchema: {
138
138
  type: string;
139
139
  properties: {
140
- count?: undefined;
141
140
  query?: undefined;
142
141
  minutes?: undefined;
143
142
  limit?: undefined;
144
143
  clientHint?: undefined;
145
144
  timestamp?: undefined;
146
145
  chapterId?: undefined;
146
+ count?: undefined;
147
147
  startAt?: undefined;
148
148
  endAt?: undefined;
149
149
  reason?: undefined;
@@ -159,7 +159,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
159
159
  inputSchema: {
160
160
  type: string;
161
161
  properties: {
162
- count?: undefined;
163
162
  query: {
164
163
  type: string;
165
164
  description: string;
@@ -175,6 +174,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
175
174
  clientHint?: undefined;
176
175
  timestamp?: undefined;
177
176
  chapterId?: undefined;
177
+ count?: undefined;
178
178
  startAt?: undefined;
179
179
  endAt?: undefined;
180
180
  reason?: undefined;
@@ -190,7 +190,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
190
190
  inputSchema: {
191
191
  type: string;
192
192
  properties: {
193
- count?: undefined;
194
193
  minutes: {
195
194
  type: string;
196
195
  description: string;
@@ -200,6 +199,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
200
199
  clientHint?: undefined;
201
200
  timestamp?: undefined;
202
201
  chapterId?: undefined;
202
+ count?: undefined;
203
203
  startAt?: undefined;
204
204
  endAt?: undefined;
205
205
  reason?: undefined;
@@ -216,7 +216,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
216
216
  type: string;
217
217
  required: string[];
218
218
  properties: {
219
- count?: undefined;
220
219
  query: {
221
220
  type: string;
222
221
  description: string;
@@ -235,6 +234,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
235
234
  };
236
235
  timestamp?: undefined;
237
236
  chapterId?: undefined;
237
+ count?: undefined;
238
238
  startAt?: undefined;
239
239
  endAt?: undefined;
240
240
  reason?: undefined;
@@ -250,7 +250,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
250
250
  type: string;
251
251
  required: string[];
252
252
  properties: {
253
- count?: undefined;
254
253
  query?: undefined;
255
254
  minutes?: undefined;
256
255
  limit?: undefined;
@@ -264,6 +263,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
264
263
  description: string;
265
264
  };
266
265
  chapterId?: undefined;
266
+ count?: undefined;
267
267
  startAt?: undefined;
268
268
  endAt?: undefined;
269
269
  includeMicrophone?: undefined;
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
314
314
  type: string;
315
315
  required: string[];
316
316
  properties: {
317
- count?: undefined;
318
317
  query?: undefined;
319
318
  minutes?: undefined;
320
319
  limit?: undefined;
321
320
  clientHint?: undefined;
322
321
  timestamp?: undefined;
323
322
  chapterId?: undefined;
323
+ count?: undefined;
324
324
  startAt: {
325
325
  type: string;
326
326
  description: string;
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
349
349
  type: string;
350
350
  required: string[];
351
351
  properties: {
352
- count?: undefined;
353
352
  query?: undefined;
354
353
  minutes?: undefined;
355
354
  limit?: undefined;
356
355
  clientHint?: undefined;
357
356
  timestamp?: undefined;
358
357
  chapterId?: undefined;
358
+ count?: undefined;
359
359
  startAt?: undefined;
360
360
  endAt?: undefined;
361
361
  reason?: undefined;
@@ -13,22 +13,22 @@
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
14
  count: number;
15
15
  updated?: undefined;
16
- ok?: undefined;
17
16
  error?: undefined;
17
+ ok?: undefined;
18
18
  } | {
19
- count?: undefined;
20
19
  updated: number;
21
- ok?: undefined;
22
20
  error?: undefined;
23
- } | {
21
+ ok?: undefined;
24
22
  count?: undefined;
23
+ } | {
25
24
  updated?: undefined;
26
25
  error: string;
27
26
  ok?: undefined;
28
- } | {
29
27
  count?: undefined;
28
+ } | {
30
29
  updated?: undefined;
31
30
  ok: boolean;
32
31
  error?: undefined;
32
+ count?: undefined;
33
33
  }>>;
34
34
  //# sourceMappingURL=routes.d.ts.map
@@ -62,7 +62,7 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
62
62
  summary?: undefined;
63
63
  spans?: undefined;
64
64
  id?: undefined;
65
- error?: undefined;
66
65
  ok: boolean;
66
+ error?: undefined;
67
67
  }>>;
68
68
  //# sourceMappingURL=routes.d.ts.map
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
48
48
  }>;
49
49
  /** DELETE /_agent-native/resources/:id — delete a resource */
50
50
  export declare function handleDeleteResource(event: any): Promise<{
51
- ok?: undefined;
52
51
  error: string;
52
+ ok?: undefined;
53
53
  } | {
54
54
  error?: undefined;
55
55
  ok: boolean;
@@ -50,8 +50,8 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
50
50
  removed: boolean;
51
51
  }>>;
52
52
  /**
53
- * POST /_agent-native/secrets/:key/test — re-run the validator against the
54
- * current stored value without changing anything. Useful for the "Test" button.
53
+ * POST /_agent-native/secrets/:key/test — validate an optional candidate value
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
57
  ok?: undefined;
@@ -277,8 +277,8 @@ async function handleDelete(event, secret) {
277
277
  return { ok: true, removed };
278
278
  }
279
279
  /**
280
- * POST /_agent-native/secrets/:key/test — re-run the validator against the
281
- * current stored value without changing anything. Useful for the "Test" button.
280
+ * POST /_agent-native/secrets/:key/test — validate an optional candidate value
281
+ * or the current stored value without changing anything.
282
282
  */
283
283
  export function createTestSecretHandler() {
284
284
  return defineEventHandler(async (event) => {
@@ -301,25 +301,49 @@ export function createTestSecretHandler() {
301
301
  const has = await hasOAuthSecretForEvent(event, secret).catch(() => false);
302
302
  return { ok: has };
303
303
  }
304
- if (!secret.validator) {
305
- return { ok: true, note: "No validator registered" };
304
+ const body = (await readBody(event).catch(() => ({})));
305
+ const hasCandidateValue = Object.hasOwn(body, "value");
306
+ const candidateValue = typeof body.value === "string" ? body.value.trim() : undefined;
307
+ if (hasCandidateValue && !candidateValue) {
308
+ setResponseStatus(event, 400);
309
+ return { error: "value must be a non-empty string" };
306
310
  }
307
- const { scopeId } = await resolveScopeId(event, secret.scope);
311
+ const { scopeId, reason } = await resolveScopeId(event, secret.scope);
308
312
  if (!scopeId) {
309
313
  setResponseStatus(event, 401);
310
- return { error: "Unable to resolve scope" };
314
+ return { error: reason ?? "Unable to resolve scope" };
311
315
  }
312
- const stored = await readAppSecret({
313
- key: secret.key,
314
- scope: secret.scope,
315
- scopeId,
316
- });
317
- if (!stored) {
318
- setResponseStatus(event, 404);
319
- return { error: "No value stored" };
316
+ if (secret.scope === "workspace" &&
317
+ !(await canMutateWorkspaceScope(event, scopeId))) {
318
+ setResponseStatus(event, 403);
319
+ return {
320
+ error: "Only organization owners and admins can set workspace-scoped secrets",
321
+ };
322
+ }
323
+ if (secret.scope === "org" && !(await canMutateOrgScope(event, scopeId))) {
324
+ setResponseStatus(event, 403);
325
+ return {
326
+ error: "Only organization owners and admins can set org-scoped secrets",
327
+ };
328
+ }
329
+ if (!secret.validator) {
330
+ return { ok: true, note: "No validator registered" };
331
+ }
332
+ let value = candidateValue;
333
+ if (!value) {
334
+ const stored = await readAppSecret({
335
+ key: secret.key,
336
+ scope: secret.scope,
337
+ scopeId,
338
+ });
339
+ if (!stored) {
340
+ setResponseStatus(event, 404);
341
+ return { error: "No value stored" };
342
+ }
343
+ value = stored.value;
320
344
  }
321
345
  try {
322
- const result = await secret.validator(stored.value);
346
+ const result = await secret.validator(value);
323
347
  const ok = typeof result === "boolean" ? result : result?.ok === true;
324
348
  if (!ok) {
325
349
  const err = typeof result === "object" && result && result.error
@@ -327,7 +351,7 @@ export function createTestSecretHandler() {
327
351
  : "Validator rejected the value";
328
352
  return {
329
353
  ok: false,
330
- error: redactSecretFromMessage(err, stored.value),
354
+ error: redactSecretFromMessage(err, value),
331
355
  };
332
356
  }
333
357
  return { ok: true };
@@ -338,7 +362,7 @@ export function createTestSecretHandler() {
338
362
  : "Validator threw";
339
363
  return {
340
364
  ok: false,
341
- error: redactSecretFromMessage(message, stored.value),
365
+ error: redactSecretFromMessage(message, value),
342
366
  };
343
367
  }
344
368
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.136.4",
3
+ "version": "0.136.5",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {