@agent-native/core 0.210.2 → 0.211.0-nightly-20261020183616
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/dist/action.d.ts +8 -0
- package/dist/action.js +17 -0
- package/dist/agent/production-agent.js +11 -20
- package/dist/client/chat/agentkit-protocol.js +48 -5
- package/dist/client/chat/runtime.d.ts +2 -0
- package/dist/client/chat/runtime.js +6 -1
- package/dist/client/hooks/index.d.ts +1 -1
- package/dist/client/hooks/index.js +1 -1
- package/dist/client/resources/mcp-integration-logos.js +4 -0
- package/dist/client/session-identity.d.ts +8 -0
- package/dist/client/session-identity.js +13 -0
- package/dist/client/use-action.d.ts +14 -0
- package/dist/client/use-action.js +117 -28
- package/dist/client/use-session.js +2 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +24 -3
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/mcp/actions/revoke-org-service-token.js +5 -1
- package/dist/mcp/execute-action-call.js +8 -17
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/actions/set-app-member-roles.js +4 -0
- package/dist/org/actions/set-app-permission-roles.js +4 -0
- package/dist/org/actions/set-workspace-app-access.js +4 -0
- package/dist/org/index.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +17 -17
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/review/suggestions/actions.d.ts +1 -0
- package/dist/review/suggestions/actions.js +54 -42
- package/dist/secrets/actions/delete-api-key.js +4 -0
- package/dist/secrets/routes.d.ts +4 -4
- package/dist/server/action-approval.d.ts +15 -0
- package/dist/server/action-approval.js +39 -0
- package/dist/server/action-routes.js +33 -28
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/edge.d.ts +1 -0
- package/dist/server/edge.js +1 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/workspace-provider-oauth.js +1 -0
- package/dist/shared/action-error-codes.d.ts +5 -0
- package/dist/shared/action-error-codes.js +5 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/dist/templates/default/.agents/skills/actions/references/action-fields.md +5 -3
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +5 -3
- package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +5 -3
- package/dist/vite/client.js +2 -1
- package/docs/content/actions-access-control.mdx +3 -3
- package/package.json +3 -3
- package/src/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/src/templates/default/.agents/skills/actions/references/action-fields.md +5 -3
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/src/templates/headless/.agents/skills/actions/references/action-fields.md +5 -3
- package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +5 -3
package/dist/deploy/build.js
CHANGED
|
@@ -1127,7 +1127,9 @@ export function generateWorkerEntry(routes, pluginPaths, defaultPluginStems = []
|
|
|
1127
1127
|
actionRegistrations.push(` const ${handlerName} = defineEventHandler(async (event) => {
|
|
1128
1128
|
setResponseHeader(event, "Cache-Control", "no-" + "store");
|
|
1129
1129
|
const actionIsUiOnly = ${a.uiOnly ? "true" : `${varName}.uiOnly === true`};
|
|
1130
|
-
const
|
|
1130
|
+
const actionDeclaresApproval =
|
|
1131
|
+
${varName}.needsApproval !== undefined && ${varName}.needsApproval !== false;
|
|
1132
|
+
const uiActionContext = actionIsUiOnly || actionDeclaresApproval
|
|
1131
1133
|
? await getGeneratedUiActionContext(event)
|
|
1132
1134
|
: undefined;
|
|
1133
1135
|
if (actionIsUiOnly && !uiActionContext) {
|
|
@@ -1175,10 +1177,29 @@ export function generateWorkerEntry(routes, pluginPaths, defaultPluginStems = []
|
|
|
1175
1177
|
actionName: ${JSON.stringify(a.name)},
|
|
1176
1178
|
...(actionContext ?? {}),
|
|
1177
1179
|
};
|
|
1178
|
-
const runAction = () =>
|
|
1180
|
+
const runAction = async () => {
|
|
1181
|
+
if (
|
|
1182
|
+
actionDeclaresApproval &&
|
|
1183
|
+
!(uiActionContext && caller === "frontend")
|
|
1184
|
+
) {
|
|
1185
|
+
const refusal = await generatedActionApprovalRefusal(
|
|
1186
|
+
${varName},
|
|
1187
|
+
params,
|
|
1188
|
+
actionRunContext,
|
|
1189
|
+
);
|
|
1190
|
+
if (refusal) {
|
|
1191
|
+
return new Response(
|
|
1192
|
+
JSON.stringify({ error: refusal.message, errorCode: refusal.errorCode }),
|
|
1193
|
+
{ status: refusal.statusCode, headers: { "Content-Type": "application/json" } },
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
return ${varName}.run(params, actionRunContext);
|
|
1198
|
+
};
|
|
1179
1199
|
const result = actionContext
|
|
1180
1200
|
? await runWithGeneratedRequestContext(actionContext, runAction)
|
|
1181
1201
|
: await runAction();
|
|
1202
|
+
if (result instanceof Response) return result;
|
|
1182
1203
|
if (typeof result === "string") { try { return JSON.parse(result); } catch { return result; } }
|
|
1183
1204
|
return result;
|
|
1184
1205
|
} catch (err) {
|
|
@@ -1200,7 +1221,7 @@ ${["post", "put", "delete"]
|
|
|
1200
1221
|
getFrameworkRoutePrefix as getAgentNativeFrameworkRoutePrefix,
|
|
1201
1222
|
getSsrAuthRedirectScript as getAgentNativeSsrAuthRedirectScript,
|
|
1202
1223
|
resolveAppHomePath as resolveAgentNativeAppHomePath,
|
|
1203
|
-
${hasActions ? " getSession as getGeneratedSession,\n hasUiActionCapability as hasGeneratedUiActionCapability,\n isSameOriginRequest as isGeneratedSameOriginRequest,\n mountUiActionCapabilityRoute as mountGeneratedUiActionCapabilityRoute,\n resolveOrgIdForEmailViaEvent as resolveGeneratedOrgId,\n runWithRequestContext as runWithGeneratedRequestContext,\n" : ""}
|
|
1224
|
+
${hasActions ? " directHttpActionApprovalRefusal as generatedActionApprovalRefusal,\n getSession as getGeneratedSession,\n hasUiActionCapability as hasGeneratedUiActionCapability,\n isSameOriginRequest as isGeneratedSameOriginRequest,\n mountUiActionCapabilityRoute as mountGeneratedUiActionCapabilityRoute,\n resolveOrgIdForEmailViaEvent as resolveGeneratedOrgId,\n runWithRequestContext as runWithGeneratedRequestContext,\n" : ""}
|
|
1204
1225
|
} from "${EDGE_SERVER_ENTRYPOINT}";`);
|
|
1205
1226
|
for (let i = 0; i < edgePlugins.length; i++) {
|
|
1206
1227
|
const varName = `plugin_${i}`;
|
|
@@ -33,9 +33,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
33
33
|
idempotencyKey: string;
|
|
34
34
|
deleted: boolean;
|
|
35
35
|
} | {
|
|
36
|
+
id?: undefined;
|
|
36
37
|
error: string;
|
|
37
38
|
url?: undefined;
|
|
38
|
-
id?: undefined;
|
|
39
39
|
provider?: undefined;
|
|
40
40
|
configured?: undefined;
|
|
41
41
|
} | {
|
|
@@ -45,8 +45,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
45
45
|
configured?: undefined;
|
|
46
46
|
error?: undefined;
|
|
47
47
|
} | {
|
|
48
|
-
url?: undefined;
|
|
49
48
|
id?: undefined;
|
|
49
|
+
url?: undefined;
|
|
50
50
|
provider?: undefined;
|
|
51
51
|
error: string;
|
|
52
52
|
configured: boolean;
|
|
@@ -10,11 +10,15 @@ import { defineAction } from "../../action.js";
|
|
|
10
10
|
import { revokeOrgServiceToken } from "../connect-store.js";
|
|
11
11
|
import { requireServiceTokenCaller } from "./service-token-access.js";
|
|
12
12
|
export default defineAction({
|
|
13
|
-
description: "Revoke one of your organization's service tokens by id (get ids from list-org-service-tokens). The token stops working immediately. Org owner/admin only. Idempotent.",
|
|
13
|
+
description: "Revoke one of your organization's service tokens by id (get ids from list-org-service-tokens). The token stops working immediately. Every call needs the user's approval. Org owner/admin only. Idempotent.",
|
|
14
14
|
schema: z.object({
|
|
15
15
|
id: z.string().min(1).describe("Service token id to revoke"),
|
|
16
16
|
}),
|
|
17
17
|
toolCallable: false,
|
|
18
|
+
// It creates, changes, or revokes a credential or who may use one: every
|
|
19
|
+
// call needs the user's approval, and "always allow" is not offered.
|
|
20
|
+
needsApproval: true,
|
|
21
|
+
allowPersistentApproval: false,
|
|
18
22
|
run: async (args, ctx) => {
|
|
19
23
|
const caller = await requireServiceTokenCaller({
|
|
20
24
|
userEmail: ctx?.userEmail,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isMcpReadOnlyTool } from "../action-call-classification.js";
|
|
2
2
|
import { vouchedOutcome } from "../action-frame.js";
|
|
3
|
-
import { actionCallIntent, isActionContractError, recordInvalidActionCall,
|
|
3
|
+
import { actionCallIntent, isActionContractError, recordInvalidActionCall, preparedCallNeedsApproval, runPreparedActionCall, } from "../action.js";
|
|
4
4
|
import { AGENT_IMAGES_FIELD } from "../agent/tool-result-images.js";
|
|
5
5
|
import { MCP_ACTION_RESULT_MARKER, isMcpActionResult, mcpActionResultText, } from "../mcp-client/app-result.js";
|
|
6
6
|
import { assertServicePrincipalMayCall, assertServicePrincipalMayRun, recordServicePrincipalDenial, ServicePrincipalRefusedError, } from "../org/service-principal-guard.js";
|
|
@@ -564,22 +564,13 @@ async function authorizeApproval(context, call, entry, args, prepared) {
|
|
|
564
564
|
if (entry.needsApproval === undefined) {
|
|
565
565
|
return { status: "authorized", clientAccepted: false };
|
|
566
566
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
appId: context.appId,
|
|
575
|
-
caller: "mcp",
|
|
576
|
-
actionName: name,
|
|
577
|
-
}))
|
|
578
|
-
: entry.needsApproval === true;
|
|
579
|
-
}
|
|
580
|
-
catch {
|
|
581
|
-
mustApprove = true;
|
|
582
|
-
}
|
|
567
|
+
const mustApprove = await preparedCallNeedsApproval(entry, prepared, {
|
|
568
|
+
userEmail: getRequestUserEmail(),
|
|
569
|
+
orgId: getRequestOrgId() ?? null,
|
|
570
|
+
appId: context.appId,
|
|
571
|
+
caller: "mcp",
|
|
572
|
+
actionName: name,
|
|
573
|
+
});
|
|
583
574
|
if (!mustApprove)
|
|
584
575
|
return { status: "authorized", clientAccepted: false };
|
|
585
576
|
if (!hasVerifiedUserIdentity) {
|
|
@@ -140,13 +140,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
140
140
|
clientHint?: undefined;
|
|
141
141
|
timestamp?: undefined;
|
|
142
142
|
chapterId?: undefined;
|
|
143
|
-
count?: undefined;
|
|
144
143
|
startAt?: undefined;
|
|
145
144
|
endAt?: undefined;
|
|
146
145
|
reason?: undefined;
|
|
147
146
|
includeMicrophone?: undefined;
|
|
148
147
|
includeSystemAudio?: undefined;
|
|
149
148
|
requestId?: undefined;
|
|
149
|
+
count?: undefined;
|
|
150
150
|
};
|
|
151
151
|
required?: undefined;
|
|
152
152
|
};
|
|
@@ -171,13 +171,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
171
171
|
clientHint?: undefined;
|
|
172
172
|
timestamp?: undefined;
|
|
173
173
|
chapterId?: undefined;
|
|
174
|
-
count?: undefined;
|
|
175
174
|
startAt?: undefined;
|
|
176
175
|
endAt?: undefined;
|
|
177
176
|
reason?: undefined;
|
|
178
177
|
includeMicrophone?: undefined;
|
|
179
178
|
includeSystemAudio?: undefined;
|
|
180
179
|
requestId?: undefined;
|
|
180
|
+
count?: undefined;
|
|
181
181
|
};
|
|
182
182
|
required?: undefined;
|
|
183
183
|
};
|
|
@@ -196,13 +196,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
196
196
|
clientHint?: undefined;
|
|
197
197
|
timestamp?: undefined;
|
|
198
198
|
chapterId?: undefined;
|
|
199
|
-
count?: undefined;
|
|
200
199
|
startAt?: undefined;
|
|
201
200
|
endAt?: undefined;
|
|
202
201
|
reason?: undefined;
|
|
203
202
|
includeMicrophone?: undefined;
|
|
204
203
|
includeSystemAudio?: undefined;
|
|
205
204
|
requestId?: undefined;
|
|
205
|
+
count?: undefined;
|
|
206
206
|
};
|
|
207
207
|
required?: undefined;
|
|
208
208
|
};
|
|
@@ -231,13 +231,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
231
231
|
};
|
|
232
232
|
timestamp?: undefined;
|
|
233
233
|
chapterId?: undefined;
|
|
234
|
-
count?: undefined;
|
|
235
234
|
startAt?: undefined;
|
|
236
235
|
endAt?: undefined;
|
|
237
236
|
reason?: undefined;
|
|
238
237
|
includeMicrophone?: undefined;
|
|
239
238
|
includeSystemAudio?: undefined;
|
|
240
239
|
requestId?: undefined;
|
|
240
|
+
count?: undefined;
|
|
241
241
|
};
|
|
242
242
|
};
|
|
243
243
|
} | {
|
|
@@ -260,12 +260,12 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
260
260
|
description: string;
|
|
261
261
|
};
|
|
262
262
|
chapterId?: undefined;
|
|
263
|
-
count?: undefined;
|
|
264
263
|
startAt?: undefined;
|
|
265
264
|
endAt?: undefined;
|
|
266
265
|
includeMicrophone?: undefined;
|
|
267
266
|
includeSystemAudio?: undefined;
|
|
268
267
|
requestId?: undefined;
|
|
268
|
+
count?: undefined;
|
|
269
269
|
};
|
|
270
270
|
};
|
|
271
271
|
} | {
|
|
@@ -317,7 +317,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
317
317
|
clientHint?: undefined;
|
|
318
318
|
timestamp?: undefined;
|
|
319
319
|
chapterId?: undefined;
|
|
320
|
-
count?: undefined;
|
|
321
320
|
startAt: {
|
|
322
321
|
type: string;
|
|
323
322
|
description: string;
|
|
@@ -337,6 +336,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
337
336
|
type: string;
|
|
338
337
|
};
|
|
339
338
|
requestId?: undefined;
|
|
339
|
+
count?: undefined;
|
|
340
340
|
};
|
|
341
341
|
};
|
|
342
342
|
} | {
|
|
@@ -352,7 +352,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
352
352
|
clientHint?: undefined;
|
|
353
353
|
timestamp?: undefined;
|
|
354
354
|
chapterId?: undefined;
|
|
355
|
-
count?: undefined;
|
|
356
355
|
startAt?: undefined;
|
|
357
356
|
endAt?: undefined;
|
|
358
357
|
reason?: undefined;
|
|
@@ -361,6 +360,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
361
360
|
requestId: {
|
|
362
361
|
type: string;
|
|
363
362
|
};
|
|
363
|
+
count?: undefined;
|
|
364
364
|
};
|
|
365
365
|
};
|
|
366
366
|
})[];
|
|
@@ -27,39 +27,39 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
27
27
|
thumbsUpRate: number;
|
|
28
28
|
avgEvalScore: number;
|
|
29
29
|
} | {
|
|
30
|
+
ok?: undefined;
|
|
30
31
|
error: string;
|
|
31
32
|
message: string;
|
|
32
33
|
summary?: undefined;
|
|
33
34
|
spans?: undefined;
|
|
34
|
-
ok?: undefined;
|
|
35
35
|
} | {
|
|
36
36
|
error?: undefined;
|
|
37
|
+
ok?: undefined;
|
|
37
38
|
message?: undefined;
|
|
38
39
|
summary: import("./types.js").TraceSummary;
|
|
39
40
|
spans: import("./types.js").TraceSpan[];
|
|
40
|
-
ok?: undefined;
|
|
41
41
|
} | {
|
|
42
42
|
error?: undefined;
|
|
43
|
+
ok?: undefined;
|
|
43
44
|
message?: undefined;
|
|
44
45
|
summary?: undefined;
|
|
45
46
|
spans?: undefined;
|
|
46
47
|
traceMissing: true;
|
|
47
48
|
id: string;
|
|
48
|
-
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
50
|
error?: undefined;
|
|
51
|
+
ok?: undefined;
|
|
51
52
|
message?: undefined;
|
|
52
53
|
summary?: undefined;
|
|
53
54
|
spans?: undefined;
|
|
54
55
|
traceMissing?: undefined;
|
|
55
56
|
id: string;
|
|
56
|
-
ok?: undefined;
|
|
57
57
|
} | {
|
|
58
|
+
ok?: undefined;
|
|
58
59
|
message?: undefined;
|
|
59
60
|
summary?: undefined;
|
|
60
61
|
spans?: undefined;
|
|
61
62
|
error: any;
|
|
62
|
-
ok?: undefined;
|
|
63
63
|
} | {
|
|
64
64
|
error?: undefined;
|
|
65
65
|
message?: undefined;
|
|
@@ -19,6 +19,10 @@ export default defineAction({
|
|
|
19
19
|
return `Updated ${args.appId} roles for ${args.email}: [${(change.previousRoles ?? []).join(", ")}] -> [${(change.roles ?? []).join(", ")}]`;
|
|
20
20
|
},
|
|
21
21
|
}),
|
|
22
|
+
// It changes who may use an app or what they may do in it: every call
|
|
23
|
+
// needs the user's approval, and "always allow" is not offered.
|
|
24
|
+
needsApproval: true,
|
|
25
|
+
allowPersistentApproval: false,
|
|
22
26
|
run: async ({ appId, email, roles }, ctx) => {
|
|
23
27
|
const caller = await requireOrgMember(ctx, true);
|
|
24
28
|
const descriptor = getRegisteredAppRoles(appId);
|
|
@@ -19,6 +19,10 @@ export default defineAction({
|
|
|
19
19
|
return `${args.reset ? "Reset" : "Updated"} ${args.appId} permission ${args.permission}: [${(change.previousRoles ?? []).join(", ")}] -> [${(change.roles ?? []).join(", ")}]`;
|
|
20
20
|
},
|
|
21
21
|
}),
|
|
22
|
+
// It changes who may use an app or what they may do in it: every call
|
|
23
|
+
// needs the user's approval, and "always allow" is not offered.
|
|
24
|
+
needsApproval: true,
|
|
25
|
+
allowPersistentApproval: false,
|
|
22
26
|
run: async ({ appId, permission, roles, reset }, ctx) => {
|
|
23
27
|
const caller = await requireOrgMember(ctx, true);
|
|
24
28
|
const descriptor = getRegisteredAppRoles(appId);
|
|
@@ -16,6 +16,10 @@ export default defineAction({
|
|
|
16
16
|
targetId: (args) => args.appId,
|
|
17
17
|
summary: (args) => `Set ${args.appId} workspace access to ${args.mode}`,
|
|
18
18
|
}),
|
|
19
|
+
// It changes who may use an app or what they may do in it: every call
|
|
20
|
+
// needs the user's approval, and "always allow" is not offered.
|
|
21
|
+
needsApproval: true,
|
|
22
|
+
allowPersistentApproval: false,
|
|
19
23
|
run: async ({ appId, mode }, ctx) => {
|
|
20
24
|
const caller = await requireOrgMember(ctx, true);
|
|
21
25
|
const db = getDbExec();
|
package/dist/org/index.d.ts
CHANGED
|
@@ -162,10 +162,10 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
|
|
|
162
162
|
soloDeploymentAdmin: boolean;
|
|
163
163
|
}>>;
|
|
164
164
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
165
|
-
name?: undefined;
|
|
166
165
|
success: boolean;
|
|
167
|
-
role?: undefined;
|
|
168
166
|
id?: undefined;
|
|
167
|
+
name?: undefined;
|
|
168
|
+
role?: undefined;
|
|
169
169
|
} | {
|
|
170
170
|
success?: undefined;
|
|
171
171
|
id: string;
|
|
@@ -75,10 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
75
75
|
user: "user";
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
|
-
|
|
78
|
+
message?: undefined;
|
|
79
79
|
id?: undefined;
|
|
80
|
-
deleted?: undefined;
|
|
81
|
-
found?: undefined;
|
|
82
80
|
providers: {
|
|
83
81
|
id: string;
|
|
84
82
|
label: string;
|
|
@@ -89,45 +87,47 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
89
87
|
updatedAt: number;
|
|
90
88
|
}[];
|
|
91
89
|
count: number;
|
|
90
|
+
provider?: undefined;
|
|
91
|
+
found?: undefined;
|
|
92
|
+
deleted?: undefined;
|
|
92
93
|
registered?: undefined;
|
|
93
94
|
label?: undefined;
|
|
94
|
-
message?: undefined;
|
|
95
95
|
} | {
|
|
96
|
+
message?: undefined;
|
|
96
97
|
id?: undefined;
|
|
97
|
-
count?: undefined;
|
|
98
|
-
deleted?: undefined;
|
|
99
98
|
providers?: undefined;
|
|
99
|
+
count?: undefined;
|
|
100
100
|
found: boolean;
|
|
101
101
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
102
|
+
deleted?: undefined;
|
|
102
103
|
registered?: undefined;
|
|
103
104
|
label?: undefined;
|
|
104
|
-
message?: undefined;
|
|
105
105
|
} | {
|
|
106
|
-
|
|
107
|
-
count?: undefined;
|
|
108
|
-
deleted?: undefined;
|
|
106
|
+
message?: undefined;
|
|
109
107
|
providers?: undefined;
|
|
108
|
+
count?: undefined;
|
|
109
|
+
provider?: undefined;
|
|
110
110
|
found: boolean;
|
|
111
111
|
id: string;
|
|
112
|
+
deleted?: undefined;
|
|
112
113
|
registered?: undefined;
|
|
113
114
|
label?: undefined;
|
|
114
|
-
message?: undefined;
|
|
115
115
|
} | {
|
|
116
|
-
|
|
116
|
+
message?: undefined;
|
|
117
|
+
providers?: undefined;
|
|
117
118
|
count?: undefined;
|
|
119
|
+
provider?: undefined;
|
|
118
120
|
found?: undefined;
|
|
119
|
-
providers?: undefined;
|
|
120
121
|
deleted: boolean;
|
|
121
122
|
id: string;
|
|
122
123
|
registered?: undefined;
|
|
123
124
|
label?: undefined;
|
|
124
|
-
message?: undefined;
|
|
125
125
|
} | {
|
|
126
|
-
|
|
126
|
+
providers?: undefined;
|
|
127
127
|
count?: undefined;
|
|
128
|
-
|
|
128
|
+
provider?: undefined;
|
|
129
129
|
found?: undefined;
|
|
130
|
-
|
|
130
|
+
deleted?: undefined;
|
|
131
131
|
registered: boolean;
|
|
132
132
|
id: string;
|
|
133
133
|
label: string;
|
|
@@ -316,11 +316,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
316
316
|
notes?: string | undefined;
|
|
317
317
|
scope?: "org" | "user" | undefined;
|
|
318
318
|
}, {
|
|
319
|
-
id?: undefined;
|
|
320
319
|
provider?: undefined;
|
|
321
|
-
|
|
320
|
+
id?: undefined;
|
|
322
321
|
deleted?: undefined;
|
|
323
|
-
found?: undefined;
|
|
324
322
|
providers: {
|
|
325
323
|
id: string;
|
|
326
324
|
label: string;
|
|
@@ -331,44 +329,46 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
331
329
|
updatedAt: number;
|
|
332
330
|
}[];
|
|
333
331
|
count: number;
|
|
332
|
+
found?: undefined;
|
|
334
333
|
registered?: undefined;
|
|
335
334
|
label?: undefined;
|
|
335
|
+
message?: undefined;
|
|
336
336
|
} | {
|
|
337
337
|
id?: undefined;
|
|
338
|
-
count?: undefined;
|
|
339
|
-
message?: undefined;
|
|
340
338
|
deleted?: undefined;
|
|
341
339
|
providers?: undefined;
|
|
340
|
+
count?: undefined;
|
|
342
341
|
found: boolean;
|
|
343
342
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
344
343
|
registered?: undefined;
|
|
345
344
|
label?: undefined;
|
|
345
|
+
message?: undefined;
|
|
346
346
|
} | {
|
|
347
347
|
provider?: undefined;
|
|
348
|
-
count?: undefined;
|
|
349
|
-
message?: undefined;
|
|
350
348
|
deleted?: undefined;
|
|
351
349
|
providers?: undefined;
|
|
350
|
+
count?: undefined;
|
|
352
351
|
found: boolean;
|
|
353
352
|
id: string;
|
|
354
353
|
registered?: undefined;
|
|
355
354
|
label?: undefined;
|
|
355
|
+
message?: undefined;
|
|
356
356
|
} | {
|
|
357
357
|
provider?: undefined;
|
|
358
|
+
providers?: undefined;
|
|
358
359
|
count?: undefined;
|
|
359
|
-
message?: undefined;
|
|
360
360
|
found?: undefined;
|
|
361
|
-
providers?: undefined;
|
|
362
361
|
deleted: boolean;
|
|
363
362
|
id: string;
|
|
364
363
|
registered?: undefined;
|
|
365
364
|
label?: undefined;
|
|
365
|
+
message?: undefined;
|
|
366
366
|
} | {
|
|
367
367
|
provider?: undefined;
|
|
368
|
-
count?: undefined;
|
|
369
368
|
deleted?: undefined;
|
|
370
|
-
found?: undefined;
|
|
371
369
|
providers?: undefined;
|
|
370
|
+
count?: undefined;
|
|
371
|
+
found?: undefined;
|
|
372
372
|
registered: boolean;
|
|
373
373
|
id: string;
|
|
374
374
|
label: string;
|
|
@@ -39,8 +39,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
39
39
|
error: string;
|
|
40
40
|
}>;
|
|
41
41
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
42
|
-
ok?: undefined;
|
|
43
42
|
error: string;
|
|
43
|
+
ok?: undefined;
|
|
44
44
|
} | {
|
|
45
45
|
error?: undefined;
|
|
46
46
|
ok: boolean;
|
|
@@ -83,6 +83,7 @@ export declare const createResourceSuggestionProposal: import("../../action.js")
|
|
|
83
83
|
}[];
|
|
84
84
|
metadata?: Record<string, unknown>;
|
|
85
85
|
}[];
|
|
86
|
+
supersedes?: string[];
|
|
86
87
|
}, {
|
|
87
88
|
proposal: import("./types.js").ResourceSuggestionProposal;
|
|
88
89
|
suggestions: ResourceSuggestion[];
|
|
@@ -58,6 +58,50 @@ function assertCreationReplay(receipt, requestHash, authorEmail, actorKind) {
|
|
|
58
58
|
}
|
|
59
59
|
return receipt.suggestion;
|
|
60
60
|
}
|
|
61
|
+
// One revised proposal can replace every edit of the one before it.
|
|
62
|
+
const MAX_SUPERSEDED = 100;
|
|
63
|
+
async function supersedeSuggestions(tx, args, authorEmail, replacementId) {
|
|
64
|
+
for (const supersededId of new Set(args.supersedes ?? [])) {
|
|
65
|
+
const prior = await getSuggestion(supersededId, tx);
|
|
66
|
+
if (!prior ||
|
|
67
|
+
prior.resourceType !== args.resourceType ||
|
|
68
|
+
prior.resourceId !== args.resourceId) {
|
|
69
|
+
fail("A superseded suggestion was not found on this resource", {
|
|
70
|
+
statusCode: 404,
|
|
71
|
+
errorCode: "not_found",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (!authorEmail || prior.authorEmail !== authorEmail) {
|
|
75
|
+
fail("Only the author can supersede this suggestion", {
|
|
76
|
+
statusCode: 403,
|
|
77
|
+
errorCode: "forbidden",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (prior.status !== "pending")
|
|
81
|
+
continue;
|
|
82
|
+
const claimed = await updateSuggestionStatus(tx, prior.id, "superseded", prior.revision);
|
|
83
|
+
if (!claimed) {
|
|
84
|
+
const latest = await getSuggestion(prior.id, tx);
|
|
85
|
+
// A reviewer decided it first; only pending proposals are replaced.
|
|
86
|
+
if (latest && latest.status !== "pending")
|
|
87
|
+
continue;
|
|
88
|
+
fail("The superseded suggestion changed; refresh and try again", {
|
|
89
|
+
statusCode: 409,
|
|
90
|
+
errorCode: "suggestion_conflict",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
await recordDecision(tx, {
|
|
94
|
+
suggestionId: prior.id,
|
|
95
|
+
idempotencyKey: `${args.idempotencyKey}:supersedes:${prior.id}`,
|
|
96
|
+
reviewer: authorEmail,
|
|
97
|
+
decision: "superseded",
|
|
98
|
+
observedBase: prior.baseRevision,
|
|
99
|
+
outcome: "superseded",
|
|
100
|
+
detail: replacementId,
|
|
101
|
+
});
|
|
102
|
+
await resolveReviewThreadWithClient(tx, prior.threadId, authorEmail, { resourceType: prior.resourceType, resourceId: prior.resourceId }, "superseded");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
61
105
|
export const createResourceSuggestion = defineAction({
|
|
62
106
|
description: "Create a typed pending suggestion without changing the canonical resource. Pass supersedes to replace your own earlier pending suggestions on the same resource in the same step.",
|
|
63
107
|
schema: z.object({
|
|
@@ -70,7 +114,7 @@ export const createResourceSuggestion = defineAction({
|
|
|
70
114
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
71
115
|
supersedes: z
|
|
72
116
|
.array(z.string().min(1))
|
|
73
|
-
.max(
|
|
117
|
+
.max(MAX_SUPERSEDED)
|
|
74
118
|
.optional()
|
|
75
119
|
.describe("IDs of your own earlier suggestions on this resource that this one replaces. Pending ones become superseded atomically with this creation; already decided ones are left as they are."),
|
|
76
120
|
}),
|
|
@@ -133,46 +177,7 @@ export const createResourceSuggestion = defineAction({
|
|
|
133
177
|
threadComment: null,
|
|
134
178
|
};
|
|
135
179
|
}
|
|
136
|
-
|
|
137
|
-
const prior = await getSuggestion(supersededId, tx);
|
|
138
|
-
if (!prior ||
|
|
139
|
-
prior.resourceType !== args.resourceType ||
|
|
140
|
-
prior.resourceId !== args.resourceId) {
|
|
141
|
-
fail("A superseded suggestion was not found on this resource", {
|
|
142
|
-
statusCode: 404,
|
|
143
|
-
errorCode: "not_found",
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
if (!authorEmail || prior.authorEmail !== authorEmail) {
|
|
147
|
-
fail("Only the author can supersede this suggestion", {
|
|
148
|
-
statusCode: 403,
|
|
149
|
-
errorCode: "forbidden",
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
if (prior.status !== "pending")
|
|
153
|
-
continue;
|
|
154
|
-
const claimed = await updateSuggestionStatus(tx, prior.id, "superseded", prior.revision);
|
|
155
|
-
if (!claimed) {
|
|
156
|
-
const latest = await getSuggestion(prior.id, tx);
|
|
157
|
-
// A reviewer decided it first; only pending proposals are replaced.
|
|
158
|
-
if (latest && latest.status !== "pending")
|
|
159
|
-
continue;
|
|
160
|
-
fail("The superseded suggestion changed; refresh and try again", {
|
|
161
|
-
statusCode: 409,
|
|
162
|
-
errorCode: "suggestion_conflict",
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
await recordDecision(tx, {
|
|
166
|
-
suggestionId: prior.id,
|
|
167
|
-
idempotencyKey: `${args.idempotencyKey}:supersedes:${prior.id}`,
|
|
168
|
-
reviewer: authorEmail,
|
|
169
|
-
decision: "superseded",
|
|
170
|
-
observedBase: prior.baseRevision,
|
|
171
|
-
outcome: "superseded",
|
|
172
|
-
detail: created.id,
|
|
173
|
-
});
|
|
174
|
-
await resolveReviewThreadWithClient(tx, prior.threadId, authorEmail, { resourceType: prior.resourceType, resourceId: prior.resourceId }, "superseded");
|
|
175
|
-
}
|
|
180
|
+
await supersedeSuggestions(tx, args, authorEmail, created.id);
|
|
176
181
|
const threadComment = await insertReviewCommentWithClient({
|
|
177
182
|
resourceType: created.resourceType,
|
|
178
183
|
resourceId: created.resourceId,
|
|
@@ -533,6 +538,11 @@ const proposalCreationSchema = z.object({
|
|
|
533
538
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
534
539
|
}))
|
|
535
540
|
.min(1),
|
|
541
|
+
supersedes: z
|
|
542
|
+
.array(z.string().min(1))
|
|
543
|
+
.max(MAX_SUPERSEDED)
|
|
544
|
+
.optional()
|
|
545
|
+
.describe("IDs of your own earlier suggestions on this resource that this proposal replaces. Pending ones become superseded atomically with this creation; already decided ones are left as they are."),
|
|
536
546
|
});
|
|
537
547
|
export const getResourceSuggestionProposalByCreationKey = defineAction({
|
|
538
548
|
description: "Find an earlier proposal creation by its idempotency key before recomputing an edit.",
|
|
@@ -565,7 +575,7 @@ export const getResourceSuggestionProposalByCreationKey = defineAction({
|
|
|
565
575
|
},
|
|
566
576
|
});
|
|
567
577
|
export const createResourceSuggestionProposal = defineAction({
|
|
568
|
-
description: "Create or append independently reviewable suggestions under one durable proposal.",
|
|
578
|
+
description: "Create or append independently reviewable suggestions under one durable proposal. Pass supersedes to replace your own earlier pending suggestions on the same resource in the same step.",
|
|
569
579
|
schema: proposalCreationSchema,
|
|
570
580
|
run: async (args, ctx) => {
|
|
571
581
|
await assertReviewableResourceAccess(args.resourceType, args.resourceId, ctx, "commenter");
|
|
@@ -582,6 +592,7 @@ export const createResourceSuggestionProposal = defineAction({
|
|
|
582
592
|
summary: args.summary,
|
|
583
593
|
operations: args.suggestions,
|
|
584
594
|
metadata: { proposalId: args.proposalId ?? null },
|
|
595
|
+
supersedes: args.supersedes,
|
|
585
596
|
});
|
|
586
597
|
const db = getDbExec();
|
|
587
598
|
await ensureSuggestionTables();
|
|
@@ -701,6 +712,7 @@ export const createResourceSuggestionProposal = defineAction({
|
|
|
701
712
|
const recorded = await recordProposalCreation(tx, args.idempotencyKey, proposal.id, authorEmail, actorKind, requestHash, suggestions.map((suggestion) => suggestion.id));
|
|
702
713
|
if (!recorded)
|
|
703
714
|
throw concurrentCreation;
|
|
715
|
+
await supersedeSuggestions(tx, args, authorEmail, suggestions[0].id);
|
|
704
716
|
return { proposal, suggestions, comments };
|
|
705
717
|
})
|
|
706
718
|
.catch(async (error) => {
|
|
@@ -29,6 +29,10 @@ export default defineAction({
|
|
|
29
29
|
? `Deleted organization key ${args.name}`
|
|
30
30
|
: `Deleted personal key ${args.name}`,
|
|
31
31
|
},
|
|
32
|
+
// It creates, changes, or revokes a credential or who may use one: every
|
|
33
|
+
// call needs the user's approval, and "always allow" is not offered.
|
|
34
|
+
needsApproval: true,
|
|
35
|
+
allowPersistentApproval: false,
|
|
32
36
|
run: async (args, ctx) => {
|
|
33
37
|
const email = ctx?.userEmail?.trim().toLowerCase();
|
|
34
38
|
if (!email)
|