@agent-native/core 0.210.0-nightly-20261019183757 → 0.210.0
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/agent/action-error-details.d.ts +26 -0
- package/dist/agent/action-error-details.js +99 -0
- package/dist/agent/production-agent.js +9 -0
- package/dist/agent/tool-error-redaction.d.ts +8 -1
- package/dist/agent/tool-error-redaction.js +70 -7
- package/dist/automation/index.d.ts +1 -1
- 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/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/mcp/direct-action-call.js +39 -10
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +7 -7
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/secrets/routes.d.ts +10 -10
- 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/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/actions/SKILL.md +2 -2
- package/dist/templates/default/.agents/skills/actions/SKILL.md +2 -2
- package/dist/templates/factory/.agents/skills/review-prs/SKILL.md +44 -21
- package/dist/templates/headless/.agents/skills/actions/SKILL.md +2 -2
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +2 -2
- package/dist/triggers/actions/manage-automation.d.ts +5 -5
- package/docs/content/actions-defining.mdx +2 -2
- package/package.json +4 -4
- package/src/templates/chat/.agents/skills/actions/SKILL.md +2 -2
- package/src/templates/default/.agents/skills/actions/SKILL.md +2 -2
- package/src/templates/factory/.agents/skills/review-prs/SKILL.md +44 -21
- package/src/templates/headless/.agents/skills/actions/SKILL.md +2 -2
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +2 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Longest serialized `details` an agent sees with a failed action. */
|
|
2
|
+
export declare const MAX_AGENT_ERROR_DETAILS_CHARS = 2000;
|
|
3
|
+
export interface AgentErrorDetails {
|
|
4
|
+
/** The `details` a `fail()` attached, as the browser receives them, redacted and bounded. */
|
|
5
|
+
details: Record<string, unknown>;
|
|
6
|
+
/**
|
|
7
|
+
* Top-level keys left out to stay within the bound, or because they don't
|
|
8
|
+
* serialize: up to ten names, each clipped, with the full count beside them.
|
|
9
|
+
*/
|
|
10
|
+
omittedKeys?: string[];
|
|
11
|
+
omittedKeyCount?: number;
|
|
12
|
+
/** The line a text-only tool result carries, so text clients see the same data. */
|
|
13
|
+
line: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The structured `details` of a failed action, in the form an agent reads.
|
|
17
|
+
*
|
|
18
|
+
* Only a contract error (`fail()`) carries details meant for the caller; the
|
|
19
|
+
* HTTP action route already returns them to the browser, so an agent acting
|
|
20
|
+
* for the same user gets the same object. Values go through a JSON round trip
|
|
21
|
+
* first, so a `Date` reads as the browser would see it rather than as `{}`.
|
|
22
|
+
* When the whole object is too long, small top-level entries are kept in
|
|
23
|
+
* order and the rest are named in `omittedKeys`: a retry id must survive next
|
|
24
|
+
* to a large payload, and a cut must never read as a complete object.
|
|
25
|
+
*/
|
|
26
|
+
export declare function agentErrorDetails(error: unknown): AgentErrorDetails | undefined;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { isActionContractError } from "../action.js";
|
|
2
|
+
import { redactSensitiveFields, sanitizeToolErrorText, } from "./tool-error-redaction.js";
|
|
3
|
+
/** Longest serialized `details` an agent sees with a failed action. */
|
|
4
|
+
export const MAX_AGENT_ERROR_DETAILS_CHARS = 2_000;
|
|
5
|
+
/** A top-level entry longer than this is left out once the whole set is too long. */
|
|
6
|
+
const MAX_KEPT_ENTRY_CHARS = 300;
|
|
7
|
+
const MAX_LISTED_OMITTED_KEYS = 10;
|
|
8
|
+
const MAX_OMITTED_KEY_CHARS = 60;
|
|
9
|
+
/**
|
|
10
|
+
* The structured `details` of a failed action, in the form an agent reads.
|
|
11
|
+
*
|
|
12
|
+
* Only a contract error (`fail()`) carries details meant for the caller; the
|
|
13
|
+
* HTTP action route already returns them to the browser, so an agent acting
|
|
14
|
+
* for the same user gets the same object. Values go through a JSON round trip
|
|
15
|
+
* first, so a `Date` reads as the browser would see it rather than as `{}`.
|
|
16
|
+
* When the whole object is too long, small top-level entries are kept in
|
|
17
|
+
* order and the rest are named in `omittedKeys`: a retry id must survive next
|
|
18
|
+
* to a large payload, and a cut must never read as a complete object.
|
|
19
|
+
*/
|
|
20
|
+
export function agentErrorDetails(error) {
|
|
21
|
+
if (!isActionContractError(error))
|
|
22
|
+
return undefined;
|
|
23
|
+
const raw = error.details;
|
|
24
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
25
|
+
return undefined;
|
|
26
|
+
const entries = [];
|
|
27
|
+
const omittedKeys = [];
|
|
28
|
+
for (const key of Object.keys(raw)) {
|
|
29
|
+
const clean = jsonSafeEntry(raw, key);
|
|
30
|
+
if (clean === UNSERIALIZABLE)
|
|
31
|
+
omittedKeys.push(key);
|
|
32
|
+
else if (clean !== undefined)
|
|
33
|
+
entries.push([key, clean]);
|
|
34
|
+
}
|
|
35
|
+
const cleaned = Object.fromEntries(entries);
|
|
36
|
+
let details = cleaned;
|
|
37
|
+
if (JSON.stringify(cleaned).length > MAX_AGENT_ERROR_DETAILS_CHARS) {
|
|
38
|
+
details = {};
|
|
39
|
+
let budget = MAX_AGENT_ERROR_DETAILS_CHARS - 2;
|
|
40
|
+
for (const [key, value] of entries) {
|
|
41
|
+
const size = JSON.stringify({ [key]: value }).length - 1;
|
|
42
|
+
if (size <= MAX_KEPT_ENTRY_CHARS && size <= budget) {
|
|
43
|
+
details[key] = value;
|
|
44
|
+
budget -= size;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
omittedKeys.push(key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (Object.keys(details).length === 0 && omittedKeys.length === 0) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const listed = omittedKeys
|
|
55
|
+
.slice(0, MAX_LISTED_OMITTED_KEYS)
|
|
56
|
+
.map((key) => key.length > MAX_OMITTED_KEY_CHARS
|
|
57
|
+
? `${key.slice(0, MAX_OMITTED_KEY_CHARS)}…`
|
|
58
|
+
: key);
|
|
59
|
+
const more = omittedKeys.length > listed.length
|
|
60
|
+
? `, and ${omittedKeys.length - listed.length} more`
|
|
61
|
+
: "";
|
|
62
|
+
return {
|
|
63
|
+
details,
|
|
64
|
+
...(omittedKeys.length > 0
|
|
65
|
+
? { omittedKeys: listed, omittedKeyCount: omittedKeys.length }
|
|
66
|
+
: {}),
|
|
67
|
+
line: `Error details${listed.length > 0 ? ` (omitted: ${listed.join(", ")}${more})` : ""}: ${JSON.stringify(details)}`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const UNSERIALIZABLE = Symbol("unserializable");
|
|
71
|
+
function jsonSafeEntry(raw, key) {
|
|
72
|
+
let parsed;
|
|
73
|
+
try {
|
|
74
|
+
// A getter can throw. Reading it here names the key; reading it outside
|
|
75
|
+
// would replace the action's own error.
|
|
76
|
+
const json = JSON.stringify(raw[key]);
|
|
77
|
+
if (json === undefined)
|
|
78
|
+
return undefined;
|
|
79
|
+
parsed = JSON.parse(json);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return UNSERIALIZABLE;
|
|
83
|
+
}
|
|
84
|
+
const redacted = redactSensitiveFields({ [key]: parsed }, { compoundNames: true });
|
|
85
|
+
return sanitizeStrings(redacted[key]);
|
|
86
|
+
}
|
|
87
|
+
function sanitizeStrings(value) {
|
|
88
|
+
if (typeof value === "string")
|
|
89
|
+
return sanitizeToolErrorText(value);
|
|
90
|
+
if (Array.isArray(value))
|
|
91
|
+
return value.map(sanitizeStrings);
|
|
92
|
+
if (value && typeof value === "object") {
|
|
93
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
94
|
+
key,
|
|
95
|
+
sanitizeStrings(entry),
|
|
96
|
+
]));
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
@@ -56,6 +56,7 @@ import { DurableAttachmentReferenceRequiredError, stripInlineBytes, } from "../s
|
|
|
56
56
|
import { isReasoningEffort, normalizeReasoningEffortForRequest, stepDownReasoningEffort, } from "../shared/reasoning-effort.js";
|
|
57
57
|
import { SYNTHETIC_TRAFFIC_BETA_E2E, SYNTHETIC_TRAFFIC_HEADER, } from "../shared/test-traffic.js";
|
|
58
58
|
import { actionPreparationContinuationNote } from "./action-continuation-guidance.js";
|
|
59
|
+
import { agentErrorDetails } from "./action-error-details.js";
|
|
59
60
|
import { drainAgentWarnings, formatAgentWarningsForToolResult, } from "./action-warnings.js";
|
|
60
61
|
import { approvalKeyWithArguments } from "./approval-key.js";
|
|
61
62
|
import { CONTINUE_UNAVAILABLE_CODE, } from "./auto-continue.js";
|
|
@@ -6617,6 +6618,7 @@ export async function runAgentLoop(opts) {
|
|
|
6617
6618
|
// handler returned does not.
|
|
6618
6619
|
let toolResultLost = false;
|
|
6619
6620
|
let toolErrorCode;
|
|
6621
|
+
let toolErrorDetailsLine = "";
|
|
6620
6622
|
let mcpApp;
|
|
6621
6623
|
let toolResultImages;
|
|
6622
6624
|
let toolArtifacts = [];
|
|
@@ -6928,6 +6930,7 @@ export async function runAgentLoop(opts) {
|
|
|
6928
6930
|
toolErrorCode = isActionContractError(err)
|
|
6929
6931
|
? err.errorCode
|
|
6930
6932
|
: undefined;
|
|
6933
|
+
toolErrorDetailsLine = agentErrorDetails(err)?.line ?? "";
|
|
6931
6934
|
result = signal.aborted
|
|
6932
6935
|
? INTERRUPTED_TOOL_RESULT_MARKER
|
|
6933
6936
|
: thrownToolResult(err);
|
|
@@ -6940,7 +6943,13 @@ export async function runAgentLoop(opts) {
|
|
|
6940
6943
|
if (isError) {
|
|
6941
6944
|
receipt = undefined;
|
|
6942
6945
|
if (result !== INTERRUPTED_TOOL_RESULT_MARKER) {
|
|
6946
|
+
// Details go on after the loop breakers and the precondition
|
|
6947
|
+
// stop read the result: a retry id that changes on every attempt
|
|
6948
|
+
// must not make the same failure look like a new one.
|
|
6943
6949
|
result = finalizeToolErrorResult(result);
|
|
6950
|
+
if (toolErrorDetailsLine) {
|
|
6951
|
+
result = `${result}\n${toolErrorDetailsLine}`;
|
|
6952
|
+
}
|
|
6944
6953
|
}
|
|
6945
6954
|
}
|
|
6946
6955
|
else {
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `compoundNames` also redacts names such as `clientSecret`, `x-api-key`,
|
|
3
|
+
* `githubToken` or `embedTicket`.
|
|
4
|
+
* Tool-error text leaves it off and keeps names like `mySecret` readable.
|
|
5
|
+
*/
|
|
6
|
+
export declare function redactSensitiveFields(value: unknown, options?: {
|
|
7
|
+
compoundNames?: boolean;
|
|
8
|
+
}): unknown;
|
|
2
9
|
export declare function sanitizeToolErrorText(value: string): string;
|
|
3
10
|
export declare function sanitizeToolErrorValue(value: unknown): string;
|
|
@@ -1,22 +1,85 @@
|
|
|
1
|
+
import { EMBED_RESULT_SENSITIVE_KEYS } from "../mcp/embed-redaction.js";
|
|
1
2
|
const REDACTED = "[REDACTED]";
|
|
2
3
|
const SENSITIVE_FIELD_PATTERN = /^(authorization|cookie|api[_-]?key|password|secret|token|access[_-]?token|refresh[_-]?token|bearer)$/i;
|
|
4
|
+
// Only the trailing words of a compound name count. A `…Token` is a
|
|
5
|
+
// credential unless it is a cursor such as `nextPageToken`, which an agent
|
|
6
|
+
// needs for a retry. A `…Ticket` can redeem an embed session.
|
|
7
|
+
const SENSITIVE_LAST_WORDS = new Set([
|
|
8
|
+
"authorization",
|
|
9
|
+
"cookie",
|
|
10
|
+
"credential",
|
|
11
|
+
"credentials",
|
|
12
|
+
"passphrase",
|
|
13
|
+
"password",
|
|
14
|
+
"secret",
|
|
15
|
+
"secrets",
|
|
16
|
+
"ticket",
|
|
17
|
+
]);
|
|
18
|
+
const SENSITIVE_LAST_WORD_PAIRS = new Set([
|
|
19
|
+
"access key",
|
|
20
|
+
"api key",
|
|
21
|
+
"private key",
|
|
22
|
+
"secret key",
|
|
23
|
+
]);
|
|
24
|
+
const CURSOR_TOKEN_QUALIFIERS = new Set([
|
|
25
|
+
"continuation",
|
|
26
|
+
"cursor",
|
|
27
|
+
"delta",
|
|
28
|
+
"idempotency",
|
|
29
|
+
"next",
|
|
30
|
+
"page",
|
|
31
|
+
"prev",
|
|
32
|
+
"previous",
|
|
33
|
+
"resume",
|
|
34
|
+
"skip",
|
|
35
|
+
"sync",
|
|
36
|
+
]);
|
|
37
|
+
function isSensitiveCompoundFieldName(key) {
|
|
38
|
+
if (SENSITIVE_FIELD_PATTERN.test(key) ||
|
|
39
|
+
EMBED_RESULT_SENSITIVE_KEYS.has(key)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const words = key
|
|
43
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.split(/[^a-z0-9]+/)
|
|
46
|
+
.filter(Boolean);
|
|
47
|
+
const last = words.at(-1);
|
|
48
|
+
if (!last)
|
|
49
|
+
return false;
|
|
50
|
+
if (SENSITIVE_LAST_WORDS.has(last))
|
|
51
|
+
return true;
|
|
52
|
+
if (last === "token") {
|
|
53
|
+
return words.length > 1 && !CURSOR_TOKEN_QUALIFIERS.has(words.at(-2));
|
|
54
|
+
}
|
|
55
|
+
return (words.length > 1 && SENSITIVE_LAST_WORD_PAIRS.has(`${words.at(-2)} ${last}`));
|
|
56
|
+
}
|
|
3
57
|
const OPENAI_LIKE_SECRET_PATTERN = /(^|[^A-Za-z0-9_-])(sk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{24,})(?=$|[^A-Za-z0-9_-])/g;
|
|
4
|
-
|
|
5
|
-
|
|
58
|
+
/**
|
|
59
|
+
* `compoundNames` also redacts names such as `clientSecret`, `x-api-key`,
|
|
60
|
+
* `githubToken` or `embedTicket`.
|
|
61
|
+
* Tool-error text leaves it off and keeps names like `mySecret` readable.
|
|
62
|
+
*/
|
|
63
|
+
export function redactSensitiveFields(value, options = {}) {
|
|
64
|
+
const isSensitive = options.compoundNames
|
|
65
|
+
? isSensitiveCompoundFieldName
|
|
66
|
+
: (key) => SENSITIVE_FIELD_PATTERN.test(key);
|
|
67
|
+
return redactWalk(value, new WeakSet(), isSensitive);
|
|
6
68
|
}
|
|
7
|
-
function redactWalk(value, seen) {
|
|
69
|
+
function redactWalk(value, seen, isSensitive) {
|
|
8
70
|
if (value === null || typeof value !== "object")
|
|
9
71
|
return value;
|
|
10
72
|
if (seen.has(value))
|
|
11
73
|
return "[Circular]";
|
|
12
74
|
seen.add(value);
|
|
13
|
-
if (Array.isArray(value))
|
|
14
|
-
return value.map((v) => redactWalk(v, seen));
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
return value.map((v) => redactWalk(v, seen, isSensitive));
|
|
77
|
+
}
|
|
15
78
|
const out = {};
|
|
16
79
|
for (const [key, entry] of Object.entries(value)) {
|
|
17
|
-
out[key] =
|
|
80
|
+
out[key] = isSensitive(key)
|
|
18
81
|
? REDACTED
|
|
19
|
-
: redactWalk(entry, seen);
|
|
82
|
+
: redactWalk(entry, seen, isSensitive);
|
|
20
83
|
}
|
|
21
84
|
return out;
|
|
22
85
|
}
|
|
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
139
139
|
duplicate?: undefined;
|
|
140
140
|
eventId?: undefined;
|
|
141
141
|
} | {
|
|
142
|
+
error?: undefined;
|
|
142
143
|
accepted: boolean;
|
|
143
144
|
duplicate: boolean;
|
|
144
145
|
eventId: string;
|
|
145
|
-
error?: undefined;
|
|
146
146
|
}>;
|
|
@@ -38,19 +38,19 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
38
38
|
error: string;
|
|
39
39
|
states?: undefined;
|
|
40
40
|
} | {
|
|
41
|
+
error?: undefined;
|
|
41
42
|
states: {
|
|
42
43
|
clientId: number;
|
|
43
44
|
state: string;
|
|
44
45
|
}[];
|
|
45
|
-
error?: undefined;
|
|
46
46
|
}>>;
|
|
47
47
|
export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
48
48
|
error: string;
|
|
49
49
|
users?: undefined;
|
|
50
50
|
} | {
|
|
51
|
+
error?: undefined;
|
|
51
52
|
users: {
|
|
52
53
|
clientId: number;
|
|
53
54
|
lastSeen: number;
|
|
54
55
|
}[];
|
|
55
|
-
error?: undefined;
|
|
56
56
|
}>>;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -21,8 +21,8 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
|
|
|
21
21
|
}>>;
|
|
22
22
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
23
23
|
ok?: undefined;
|
|
24
|
-
error: string;
|
|
25
24
|
text?: undefined;
|
|
25
|
+
error: string;
|
|
26
26
|
} | {
|
|
27
27
|
error?: undefined;
|
|
28
28
|
ok: boolean;
|
|
@@ -11,9 +11,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
11
11
|
enabled?: undefined;
|
|
12
12
|
nextRun?: undefined;
|
|
13
13
|
} | {
|
|
14
|
+
deleted?: undefined;
|
|
14
15
|
name: string;
|
|
15
16
|
enabled: boolean;
|
|
16
17
|
nextRun: string;
|
|
17
|
-
deleted?: undefined;
|
|
18
18
|
}>;
|
|
19
19
|
export default _default;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { actionCallReadOnly, actionChangeResource, } from "../action-call-classification.js";
|
|
2
2
|
import { ActionOutputContractError, failedCallEmitsChange, isActionOutputContractError, invocationEffect, ranCallEmitsChange, } from "../action.js";
|
|
3
|
+
import { agentErrorDetails } from "../agent/action-error-details.js";
|
|
3
4
|
import { extractAgentImagesFromActionResult } from "../agent/tool-result-images.js";
|
|
4
5
|
import { writeActionChangeMarker } from "../server/action-change-marker-write.js";
|
|
5
6
|
import { captureError } from "../server/capture-error.js";
|
|
@@ -35,6 +36,13 @@ export function createDirectActionCall(scope) {
|
|
|
35
36
|
return async (call, fail) => {
|
|
36
37
|
const { name, args, request, ctx } = call;
|
|
37
38
|
const callableActions = fullCatalogRequested ? actions : advertisedActions;
|
|
39
|
+
// A directory profile may project results arbitrarily, so the derived
|
|
40
|
+
// contract is advertised and enforced on app catalogs only. It compiles
|
|
41
|
+
// before the action runs. A host may hold an error result to the same
|
|
42
|
+
// schema, so a contracted tool's errors carry no structuredContent.
|
|
43
|
+
const outputContract = directoryCatalog
|
|
44
|
+
? undefined
|
|
45
|
+
: callableActions[name]?.mcpOutputContract;
|
|
38
46
|
const errorText = (err) => {
|
|
39
47
|
const projectedError = directoryCatalog && config.directoryProfile?.projectResult
|
|
40
48
|
? config.directoryProfile.projectResult(name, err.message)
|
|
@@ -55,10 +63,37 @@ export function createDirectActionCall(scope) {
|
|
|
55
63
|
try {
|
|
56
64
|
const errorCode = mcpActionErrorCode(err);
|
|
57
65
|
const { errorType, errorMessage } = describeMcpError(err);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
const message = redactEmbedText(errorText(err));
|
|
67
|
+
// A directory profile decides what its catalog shows, and it
|
|
68
|
+
// projects only the message.
|
|
69
|
+
const details = directoryCatalog ? undefined : agentErrorDetails(err);
|
|
70
|
+
const detailsLine = details && redactEmbedText(details.line);
|
|
71
|
+
return {
|
|
72
|
+
...toolCallError({
|
|
73
|
+
errorType: redactEmbedText(errorType),
|
|
74
|
+
errorMessage: redactEmbedText(errorMessage),
|
|
75
|
+
}, `${message}${errorCode !== undefined ? ` (errorCode: ${redactEmbedText(errorCode)})` : ""}${detailsLine ? `\n${detailsLine}` : ""}`),
|
|
76
|
+
// Some clients hand the model structuredContent in place of the
|
|
77
|
+
// text, so it repeats the message rather than carrying details
|
|
78
|
+
// alone. Details the embed check hid in the text stay out of it.
|
|
79
|
+
...(details && detailsLine === details.line && !outputContract
|
|
80
|
+
? {
|
|
81
|
+
structuredContent: {
|
|
82
|
+
error: {
|
|
83
|
+
message,
|
|
84
|
+
errorCode: err.errorCode,
|
|
85
|
+
details: details.details,
|
|
86
|
+
...(details.omittedKeys
|
|
87
|
+
? {
|
|
88
|
+
omittedKeys: details.omittedKeys,
|
|
89
|
+
omittedKeyCount: details.omittedKeyCount,
|
|
90
|
+
}
|
|
91
|
+
: {}),
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
: {}),
|
|
96
|
+
};
|
|
62
97
|
}
|
|
63
98
|
catch {
|
|
64
99
|
return toolCallError({
|
|
@@ -67,12 +102,6 @@ export function createDirectActionCall(scope) {
|
|
|
67
102
|
});
|
|
68
103
|
}
|
|
69
104
|
};
|
|
70
|
-
// A directory profile may project results arbitrarily, so the derived
|
|
71
|
-
// contract is advertised and enforced on app catalogs only. It compiles
|
|
72
|
-
// before the action runs.
|
|
73
|
-
const outputContract = directoryCatalog
|
|
74
|
-
? undefined
|
|
75
|
-
: callableActions[name]?.mcpOutputContract;
|
|
76
105
|
let checkOutput;
|
|
77
106
|
try {
|
|
78
107
|
checkOutput = outputContract
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
2
|
+
error?: undefined;
|
|
2
3
|
count: number;
|
|
3
4
|
updated?: undefined;
|
|
4
|
-
error?: undefined;
|
|
5
5
|
ok?: undefined;
|
|
6
6
|
} | {
|
|
7
|
+
error?: undefined;
|
|
7
8
|
count?: undefined;
|
|
8
9
|
updated: number;
|
|
9
|
-
error?: undefined;
|
|
10
10
|
ok?: undefined;
|
|
11
11
|
} | {
|
|
12
12
|
count?: undefined;
|
|
@@ -14,8 +14,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
14
14
|
error: string;
|
|
15
15
|
ok?: undefined;
|
|
16
16
|
} | {
|
|
17
|
+
error?: undefined;
|
|
17
18
|
count?: undefined;
|
|
18
19
|
updated?: undefined;
|
|
19
|
-
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
21
21
|
}>>;
|
|
@@ -27,43 +27,43 @@ 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
|
+
error?: undefined;
|
|
37
|
+
ok?: undefined;
|
|
36
38
|
message?: undefined;
|
|
37
39
|
summary: import("./types.js").TraceSummary;
|
|
38
40
|
spans: import("./types.js").TraceSpan[];
|
|
41
|
+
} | {
|
|
39
42
|
error?: undefined;
|
|
40
43
|
ok?: undefined;
|
|
41
|
-
} | {
|
|
42
44
|
message?: undefined;
|
|
43
45
|
summary?: undefined;
|
|
44
46
|
spans?: undefined;
|
|
45
47
|
traceMissing: true;
|
|
46
48
|
id: string;
|
|
49
|
+
} | {
|
|
47
50
|
error?: undefined;
|
|
48
51
|
ok?: undefined;
|
|
49
|
-
} | {
|
|
50
52
|
message?: undefined;
|
|
51
53
|
summary?: undefined;
|
|
52
54
|
spans?: undefined;
|
|
53
55
|
traceMissing?: undefined;
|
|
54
56
|
id: string;
|
|
55
|
-
error?: undefined;
|
|
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
|
+
error?: undefined;
|
|
64
65
|
message?: undefined;
|
|
65
66
|
summary?: undefined;
|
|
66
67
|
spans?: undefined;
|
|
67
|
-
error?: undefined;
|
|
68
68
|
ok: boolean;
|
|
69
69
|
}>>;
|
|
@@ -75,6 +75,7 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
75
75
|
user: "user";
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
|
+
message?: undefined;
|
|
78
79
|
deleted?: undefined;
|
|
79
80
|
found?: undefined;
|
|
80
81
|
id?: undefined;
|
|
@@ -91,8 +92,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
91
92
|
provider?: undefined;
|
|
92
93
|
registered?: undefined;
|
|
93
94
|
label?: undefined;
|
|
94
|
-
message?: undefined;
|
|
95
95
|
} | {
|
|
96
|
+
message?: undefined;
|
|
96
97
|
deleted?: undefined;
|
|
97
98
|
id?: undefined;
|
|
98
99
|
providers?: undefined;
|
|
@@ -101,8 +102,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
101
102
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
102
103
|
registered?: undefined;
|
|
103
104
|
label?: undefined;
|
|
104
|
-
message?: undefined;
|
|
105
105
|
} | {
|
|
106
|
+
message?: undefined;
|
|
106
107
|
deleted?: undefined;
|
|
107
108
|
providers?: undefined;
|
|
108
109
|
count?: undefined;
|
|
@@ -111,8 +112,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
111
112
|
id: string;
|
|
112
113
|
registered?: undefined;
|
|
113
114
|
label?: undefined;
|
|
114
|
-
message?: undefined;
|
|
115
115
|
} | {
|
|
116
|
+
message?: undefined;
|
|
116
117
|
found?: undefined;
|
|
117
118
|
providers?: undefined;
|
|
118
119
|
count?: undefined;
|
|
@@ -121,7 +122,6 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
121
122
|
id: string;
|
|
122
123
|
registered?: undefined;
|
|
123
124
|
label?: undefined;
|
|
124
|
-
message?: undefined;
|
|
125
125
|
} | {
|
|
126
126
|
deleted?: undefined;
|
|
127
127
|
found?: undefined;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -57,39 +57,39 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
57
57
|
} | {
|
|
58
58
|
error: string;
|
|
59
59
|
errorCode?: undefined;
|
|
60
|
-
status?: undefined;
|
|
61
60
|
ok?: undefined;
|
|
61
|
+
status?: undefined;
|
|
62
62
|
} | {
|
|
63
63
|
error: string;
|
|
64
64
|
errorCode: string;
|
|
65
|
-
status?: undefined;
|
|
66
65
|
ok?: undefined;
|
|
66
|
+
status?: undefined;
|
|
67
67
|
} | {
|
|
68
|
+
error?: undefined;
|
|
68
69
|
errorCode?: undefined;
|
|
69
70
|
ok: boolean;
|
|
70
71
|
status: string;
|
|
71
|
-
error?: undefined;
|
|
72
72
|
} | {
|
|
73
|
+
ok?: undefined;
|
|
73
74
|
error: string;
|
|
74
75
|
removed?: undefined;
|
|
75
|
-
ok?: undefined;
|
|
76
76
|
} | {
|
|
77
|
+
error?: undefined;
|
|
77
78
|
ok: boolean;
|
|
78
79
|
removed: boolean;
|
|
79
|
-
error?: undefined;
|
|
80
80
|
}>>;
|
|
81
81
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
82
|
+
ok?: undefined;
|
|
82
83
|
error: string;
|
|
83
84
|
note?: undefined;
|
|
84
|
-
ok?: undefined;
|
|
85
85
|
} | {
|
|
86
|
+
error?: undefined;
|
|
86
87
|
ok: boolean;
|
|
87
88
|
note?: undefined;
|
|
88
|
-
error?: undefined;
|
|
89
89
|
} | {
|
|
90
|
+
error?: undefined;
|
|
90
91
|
ok: boolean;
|
|
91
92
|
note: string;
|
|
92
|
-
error?: undefined;
|
|
93
93
|
} | {
|
|
94
94
|
note?: undefined;
|
|
95
95
|
ok: boolean;
|
|
@@ -117,11 +117,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
|
|
|
117
117
|
} | {
|
|
118
118
|
error: string;
|
|
119
119
|
} | {
|
|
120
|
+
error?: undefined;
|
|
120
121
|
ok: boolean;
|
|
121
122
|
key: string;
|
|
122
|
-
error?: undefined;
|
|
123
123
|
} | {
|
|
124
|
+
error?: undefined;
|
|
124
125
|
ok: boolean;
|
|
125
126
|
removed: boolean;
|
|
126
|
-
error?: undefined;
|
|
127
127
|
}>>;
|
|
@@ -66,6 +66,7 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
66
66
|
error: any;
|
|
67
67
|
code: any;
|
|
68
68
|
} | {
|
|
69
|
+
error?: undefined;
|
|
69
70
|
errorCode?: undefined;
|
|
70
71
|
code?: undefined;
|
|
71
72
|
ok: boolean;
|
|
@@ -73,6 +74,5 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
73
74
|
baseUrlKey?: string;
|
|
74
75
|
scope: AgentEngineApiKeyScope;
|
|
75
76
|
defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
|
|
76
|
-
error?: undefined;
|
|
77
77
|
}>>;
|
|
78
78
|
export {};
|
|
@@ -8,8 +8,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
|
|
|
8
8
|
* resolveEngine's credential detection.
|
|
9
9
|
*/
|
|
10
10
|
export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
|
-
ok?: undefined;
|
|
12
11
|
error: string;
|
|
12
|
+
ok?: undefined;
|
|
13
13
|
scope?: undefined;
|
|
14
14
|
} | {
|
|
15
15
|
ok: boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export declare function createAgentEngineOllamaModelsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
2
2
|
error: string;
|
|
3
|
-
models?: undefined;
|
|
4
3
|
ok?: undefined;
|
|
4
|
+
models?: undefined;
|
|
5
5
|
} | {
|
|
6
|
+
error?: undefined;
|
|
6
7
|
ok: boolean;
|
|
7
8
|
models: string[];
|
|
8
|
-
error?: undefined;
|
|
9
9
|
}>>;
|
|
@@ -54,8 +54,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
54
54
|
expiresAt?: undefined;
|
|
55
55
|
ttlSeconds?: undefined;
|
|
56
56
|
} | {
|
|
57
|
+
error?: undefined;
|
|
57
58
|
token: string;
|
|
58
59
|
expiresAt: string;
|
|
59
60
|
ttlSeconds: number;
|
|
60
|
-
error?: undefined;
|
|
61
61
|
}>>;
|
|
@@ -170,10 +170,10 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
170
170
|
- **Do** keep one action, one job; document a reusable action (when to use it, key args, return fields to preserve) in `AGENTS.md` once it's called from outside one narrow screen; promote workflow-heavy actions (provider-backed, cross-app, MCP/A2A, multi-step) into a skill.
|
|
171
171
|
- **Do** use `fail(message, { errorCode, statusCode })` for user-friendly errors and import primitives from `@agent-native/core`(`/action`) instead of redefining them; use the core `upload-image` action or `uploadFile()` for durable images/files — never base64 into SQL, markdown, or action results.
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
|
-
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
173
|
+
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
175
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
|
-
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP
|
|
176
|
+
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
177
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
178
178
|
- **Don't** reach for provider integrations, `outputSchema`, `authorize`, `needsApproval`, or `_agentImages` without checking `references/` first — each has sharp edges covered there, not here.
|
|
179
179
|
|
|
@@ -170,10 +170,10 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
170
170
|
- **Do** keep one action, one job; document a reusable action (when to use it, key args, return fields to preserve) in `AGENTS.md` once it's called from outside one narrow screen; promote workflow-heavy actions (provider-backed, cross-app, MCP/A2A, multi-step) into a skill.
|
|
171
171
|
- **Do** use `fail(message, { errorCode, statusCode })` for user-friendly errors and import primitives from `@agent-native/core`(`/action`) instead of redefining them; use the core `upload-image` action or `uploadFile()` for durable images/files — never base64 into SQL, markdown, or action results.
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
|
-
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
173
|
+
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
175
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
|
-
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP
|
|
176
|
+
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
177
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
178
178
|
- **Don't** reach for provider integrations, `outputSchema`, `authorize`, `needsApproval`, or `_agentImages` without checking `references/` first — each has sharp edges covered there, not here.
|
|
179
179
|
|
|
@@ -3,8 +3,9 @@ name: review-prs
|
|
|
3
3
|
description: >-
|
|
4
4
|
Review recent BuilderIO/agent-native human pull requests, approve eligible
|
|
5
5
|
internal PRs, and merge changes that are ready by Steve's bar. Use for
|
|
6
|
-
scheduled or manual PR sweeps; flag external
|
|
7
|
-
product or UX decisions to
|
|
6
|
+
scheduled or manual PR sweeps; flag external work needing a major product
|
|
7
|
+
call or new framework API, and unresolved major product or UX decisions, to
|
|
8
|
+
Steve.
|
|
8
9
|
user-invocable: true
|
|
9
10
|
scope: dev
|
|
10
11
|
metadata:
|
|
@@ -20,10 +21,12 @@ approve an external or unverified author.
|
|
|
20
21
|
|
|
21
22
|
Approval and merge readiness are separate. A **Ready to merge by Steve's bar**
|
|
22
23
|
result means merge the PR under the guarded merge procedure below. For a
|
|
23
|
-
confirmed external author,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
confirmed external author, a clear bug fix or a bounded improvement that
|
|
25
|
+
follows existing patterns enters evidence review and the possible merge flow;
|
|
26
|
+
external authors are never eligible for an approval review. Surface external
|
|
27
|
+
work that needs a major product call or a new framework API for Steve's explicit
|
|
28
|
+
approval before a full review or merge; a scheduled or manual sweep is not that
|
|
29
|
+
approval.
|
|
27
30
|
|
|
28
31
|
## Selection and evidence
|
|
29
32
|
|
|
@@ -53,6 +56,11 @@ author and draft state:
|
|
|
53
56
|
summary to determine whether the PR already has a current, non-dismissed
|
|
54
57
|
`APPROVED` review.
|
|
55
58
|
|
|
59
|
+
- Before the scope gate, check a confirmed nonmember's PR from a fork for
|
|
60
|
+
assignees. If any assignee is a verified current BuilderIO member, ignore
|
|
61
|
+
the PR completely, like a draft: that person owns it under `community-prs`.
|
|
62
|
+
Take no review, reply, or merge action, and leave it out of the recap.
|
|
63
|
+
|
|
56
64
|
- Reuse that review as the code assessment only after verifying the reviewer
|
|
57
65
|
is a different, current BuilderIO member and is eligible under the PR's
|
|
58
66
|
author- and scope-specific rules, including any independent-review
|
|
@@ -99,17 +107,28 @@ the patch looks safe or the issue is obviously valid.
|
|
|
99
107
|
|
|
100
108
|
## External-contribution scope gate
|
|
101
109
|
|
|
110
|
+
Builder devs decide bounded outside contributions; Steve keeps major product
|
|
111
|
+
calls and new framework APIs.
|
|
112
|
+
|
|
102
113
|
For an author the membership API confirms is not a current BuilderIO member,
|
|
103
|
-
continue into the normal evidence sweep
|
|
104
|
-
corrects a specific reported or reproducible
|
|
105
|
-
behavior. A
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
continue into the normal evidence sweep for a clear bug fix or a bounded
|
|
115
|
+
improvement. A clear bug fix corrects a specific reported or reproducible
|
|
116
|
+
defect by restoring intended behavior. A bounded improvement stays within one
|
|
117
|
+
established workflow, app, or framework area and follows the patterns already
|
|
118
|
+
there.
|
|
119
|
+
|
|
120
|
+
External work needs Steve when it makes a major product call (as defined under
|
|
121
|
+
Product and UX decisions) or adds a new framework API: a new public export,
|
|
122
|
+
configuration option, action, plugin or hook contract, or CLI surface in a
|
|
123
|
+
published package under `packages/`. A fix or bounded improvement to an existing
|
|
124
|
+
API stays with devs when it keeps that API's contract. If the title, issue, and
|
|
125
|
+
initial diff do not establish a clear bug fix or a bounded improvement, treat it
|
|
126
|
+
as needing Steve.
|
|
127
|
+
|
|
128
|
+
For external work that needs Steve, stop before the full code review, approval
|
|
129
|
+
or review action, merge-readiness disposition, author-facing draft, or merge.
|
|
130
|
+
Report it to Steve as **Needs Steve's approval for scope**, with the PR link,
|
|
131
|
+
author, stated scope, and which call it needs. Wait for Steve's explicit go-ahead on
|
|
113
132
|
that PR before continuing. Invoking or scheduling `review-prs`, approving a
|
|
114
133
|
different PR, or prior approval of another external contribution does not
|
|
115
134
|
authorize it. If membership is unknown, use the existing unknown-membership
|
|
@@ -118,6 +137,8 @@ solely because membership could not be verified. Keep the no-approval outcome,
|
|
|
118
137
|
report the missing membership check, and assess merge readiness under the
|
|
119
138
|
existing unknown-membership policy.
|
|
120
139
|
|
|
140
|
+
Builder devs own outside PRs end to end with `community-prs`.
|
|
141
|
+
|
|
121
142
|
## Liamdebeasi approval policy
|
|
122
143
|
|
|
123
144
|
For a PR authored by the exact GitHub login `liamdebeasi` and immutable user
|
|
@@ -296,9 +317,10 @@ merge scope and must remain completely untouched.
|
|
|
296
317
|
For a PR that fails an approval gate, do not submit an approval. Flag the exact
|
|
297
318
|
concern and the evidence needed to resolve it. External or unverified authors
|
|
298
319
|
never receive an approval review, but their PRs can still be merged when they
|
|
299
|
-
meet the readiness gate below. Confirmed external
|
|
300
|
-
external-contribution scope gate;
|
|
301
|
-
stopped at Steve's approval gate. For unknown
|
|
320
|
+
meet the readiness gate below. Confirmed external PRs must also pass the
|
|
321
|
+
external-contribution scope gate; external work that needs a major product call
|
|
322
|
+
or a new framework API remains stopped at Steve's approval gate. For unknown
|
|
323
|
+
membership, do not apply the
|
|
302
324
|
external scope gate solely because verification failed; preserve the
|
|
303
325
|
no-approval outcome, assess readiness under the existing unknown-membership
|
|
304
326
|
policy, and name the missing check.
|
|
@@ -504,7 +526,8 @@ non-auto-approved external PR that needs an update or screenshot, include its
|
|
|
504
526
|
draft reply in a separate section, or mark it waiting on the contributor with
|
|
505
527
|
a link to Steve's outstanding request. For internal PRs, report the needed
|
|
506
528
|
update or screenshot without drafting an author-facing reply. Do not add rows
|
|
507
|
-
for bots, `steve8708`, or
|
|
529
|
+
for bots, `steve8708`, drafts, or external PRs assigned to a BuilderIO member;
|
|
530
|
+
those are ignored completely.
|
|
508
531
|
|
|
509
532
|
Use this shape:
|
|
510
533
|
|
|
@@ -525,4 +548,4 @@ blocker when the PR is ready and the guarded admin merge is available.
|
|
|
525
548
|
|
|
526
549
|
## Related skills
|
|
527
550
|
|
|
528
|
-
`concurrent-agents`, `verifying-changes`, `ship`, `babysit-pr`
|
|
551
|
+
`concurrent-agents`, `verifying-changes`, `ship`, `babysit-pr`, `community-prs`
|
|
@@ -170,10 +170,10 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
170
170
|
- **Do** keep one action, one job; document a reusable action (when to use it, key args, return fields to preserve) in `AGENTS.md` once it's called from outside one narrow screen; promote workflow-heavy actions (provider-backed, cross-app, MCP/A2A, multi-step) into a skill.
|
|
171
171
|
- **Do** use `fail(message, { errorCode, statusCode })` for user-friendly errors and import primitives from `@agent-native/core`(`/action`) instead of redefining them; use the core `upload-image` action or `uploadFile()` for durable images/files — never base64 into SQL, markdown, or action results.
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
|
-
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
173
|
+
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
175
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
|
-
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP
|
|
176
|
+
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
177
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
178
178
|
- **Don't** reach for provider integrations, `outputSchema`, `authorize`, `needsApproval`, or `_agentImages` without checking `references/` first — each has sharp edges covered there, not here.
|
|
179
179
|
|
|
@@ -170,10 +170,10 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
170
170
|
- **Do** keep one action, one job; document a reusable action (when to use it, key args, return fields to preserve) in `AGENTS.md` once it's called from outside one narrow screen; promote workflow-heavy actions (provider-backed, cross-app, MCP/A2A, multi-step) into a skill.
|
|
171
171
|
- **Do** use `fail(message, { errorCode, statusCode })` for user-friendly errors and import primitives from `@agent-native/core`(`/action`) instead of redefining them; use the core `upload-image` action or `uploadFile()` for durable images/files — never base64 into SQL, markdown, or action results.
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
|
-
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
173
|
+
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
175
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
|
-
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP
|
|
176
|
+
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
177
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
178
178
|
- **Don't** reach for provider integrations, `outputSchema`, `authorize`, `needsApproval`, or `_agentImages` without checking `references/` first — each has sharp edges covered there, not here.
|
|
179
179
|
|
|
@@ -13,6 +13,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
13
13
|
executionEngine?: string;
|
|
14
14
|
executionCwd?: string;
|
|
15
15
|
}, {
|
|
16
|
+
deleted?: undefined;
|
|
17
|
+
enabled?: undefined;
|
|
16
18
|
created: boolean;
|
|
17
19
|
name: string;
|
|
18
20
|
scope: "organization" | "personal";
|
|
@@ -23,33 +25,31 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
23
25
|
reasoningEffort: "auto" | "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh";
|
|
24
26
|
webhookPath: string;
|
|
25
27
|
nextRun: string;
|
|
26
|
-
deleted?: undefined;
|
|
27
|
-
enabled?: undefined;
|
|
28
28
|
executionHostId?: undefined;
|
|
29
29
|
executionEngine?: undefined;
|
|
30
30
|
executionCwd?: undefined;
|
|
31
31
|
} | {
|
|
32
|
+
enabled?: undefined;
|
|
33
|
+
nextRun?: undefined;
|
|
32
34
|
created?: undefined;
|
|
33
35
|
scope?: undefined;
|
|
34
36
|
triggerType?: undefined;
|
|
35
37
|
event?: undefined;
|
|
36
38
|
deleted: boolean;
|
|
37
39
|
name: string;
|
|
38
|
-
enabled?: undefined;
|
|
39
40
|
schedule?: undefined;
|
|
40
41
|
timezone?: undefined;
|
|
41
42
|
reasoningEffort?: undefined;
|
|
42
43
|
executionHostId?: undefined;
|
|
43
44
|
executionEngine?: undefined;
|
|
44
45
|
executionCwd?: undefined;
|
|
45
|
-
nextRun?: undefined;
|
|
46
46
|
webhookPath?: undefined;
|
|
47
47
|
} | {
|
|
48
|
+
deleted?: undefined;
|
|
48
49
|
created?: undefined;
|
|
49
50
|
scope?: undefined;
|
|
50
51
|
triggerType?: undefined;
|
|
51
52
|
event?: undefined;
|
|
52
|
-
deleted?: undefined;
|
|
53
53
|
name: string;
|
|
54
54
|
enabled: boolean;
|
|
55
55
|
schedule: string;
|
|
@@ -324,7 +324,7 @@ What you throw decides how much of the message survives. `fail()` raises an `Act
|
|
|
324
324
|
- **message**: the text the caller reads. Write it for a person, not for a log.
|
|
325
325
|
- **options.errorCode**: a stable machine-readable code. Defaults to `"action_failed"`.
|
|
326
326
|
- **options.statusCode**: the HTTP status for the action route. Defaults to `400`.
|
|
327
|
-
- **options.details**: safe structured context for the caller. Never put secrets or raw driver output here.
|
|
327
|
+
- **options.details**: safe structured context for the caller, such as the id a retry needs. The browser and agents both receive it. Never put secrets or raw driver output here.
|
|
328
328
|
|
|
329
329
|
The simplest call needs only a message:
|
|
330
330
|
|
|
@@ -402,7 +402,7 @@ A network drop, a proxy's HTML error page, and a bare status line author no mess
|
|
|
402
402
|
|
|
403
403
|
So the status is not cosmetic. A refusal sent as `404` costs one round trip. The same refusal sent as a `500`, or thrown bare, costs four round trips and four duplicate error reports.
|
|
404
404
|
|
|
405
|
-
Agent callers never see the status. The agent runs `run()` in process, so it receives the message directly whatever the type, as `Error running <action>: <message>`. An `errorCode` other than the default `action_failed` is appended as `(errorCode: not_found)` so the model can branch on it without parsing prose. External agents over MCP get the same text. Secrets in the message are redacted first, and the agent's own repeated-error breakers bound how many times it tries again.
|
|
405
|
+
Agent callers never see the status. The agent runs `run()` in process, so it receives the message directly whatever the type, as `Error running <action>: <message>`. An `errorCode` other than the default `action_failed` is appended as `(errorCode: not_found)` so the model can branch on it without parsing prose. `details` follow on their own line as compact JSON, such as `Error details: {"importId":"imp_1"}`, so an id the model needs for its next call doesn't have to be written into the message. External agents over MCP get the same text, and the same details in `structuredContent.error`. A tool that advertises an output schema (`mcpOutputSchema: true`) returns the text only, since a client may check an error result against that schema too. Details are capped at 2,000 characters: past that, small top-level entries are kept and the rest are listed as omitted. Secrets in the message and details are redacted first, and the agent's own repeated-error breakers bound how many times it tries again.
|
|
406
406
|
|
|
407
407
|
## Keep the action surface small {#small-surface}
|
|
408
408
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.210.0
|
|
3
|
+
"version": "0.210.0",
|
|
4
4
|
"description": "The agentic application framework for building autonomous agents with intuitive UIs",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -711,8 +711,8 @@
|
|
|
711
711
|
"yaml": "^2.9.0",
|
|
712
712
|
"yjs": "^13.6.33",
|
|
713
713
|
"zod": "^4.3.6",
|
|
714
|
-
"@agent-native/
|
|
715
|
-
"@agent-native/
|
|
714
|
+
"@agent-native/agentkit": "^0.210.0",
|
|
715
|
+
"@agent-native/recap-cli": "^0.5.75"
|
|
716
716
|
},
|
|
717
717
|
"devDependencies": {
|
|
718
718
|
"@ai-sdk/anthropic": "^3.0.71",
|
|
@@ -782,7 +782,7 @@
|
|
|
782
782
|
"xlsx": "^0.18.5"
|
|
783
783
|
},
|
|
784
784
|
"peerDependencies": {
|
|
785
|
-
"@agent-native/core-corpus": "0.
|
|
785
|
+
"@agent-native/core-corpus": ">=0.196.0",
|
|
786
786
|
"@ai-sdk/anthropic": ">=3",
|
|
787
787
|
"@ai-sdk/cohere": ">=3",
|
|
788
788
|
"@ai-sdk/google": ">=3",
|
|
@@ -170,10 +170,10 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
170
170
|
- **Do** keep one action, one job; document a reusable action (when to use it, key args, return fields to preserve) in `AGENTS.md` once it's called from outside one narrow screen; promote workflow-heavy actions (provider-backed, cross-app, MCP/A2A, multi-step) into a skill.
|
|
171
171
|
- **Do** use `fail(message, { errorCode, statusCode })` for user-friendly errors and import primitives from `@agent-native/core`(`/action`) instead of redefining them; use the core `upload-image` action or `uploadFile()` for durable images/files — never base64 into SQL, markdown, or action results.
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
|
-
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
173
|
+
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
175
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
|
-
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP
|
|
176
|
+
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
177
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
178
178
|
- **Don't** reach for provider integrations, `outputSchema`, `authorize`, `needsApproval`, or `_agentImages` without checking `references/` first — each has sharp edges covered there, not here.
|
|
179
179
|
|
|
@@ -170,10 +170,10 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
170
170
|
- **Do** keep one action, one job; document a reusable action (when to use it, key args, return fields to preserve) in `AGENTS.md` once it's called from outside one narrow screen; promote workflow-heavy actions (provider-backed, cross-app, MCP/A2A, multi-step) into a skill.
|
|
171
171
|
- **Do** use `fail(message, { errorCode, statusCode })` for user-friendly errors and import primitives from `@agent-native/core`(`/action`) instead of redefining them; use the core `upload-image` action or `uploadFile()` for durable images/files — never base64 into SQL, markdown, or action results.
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
|
-
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
173
|
+
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
175
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
|
-
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP
|
|
176
|
+
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
177
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
178
178
|
- **Don't** reach for provider integrations, `outputSchema`, `authorize`, `needsApproval`, or `_agentImages` without checking `references/` first — each has sharp edges covered there, not here.
|
|
179
179
|
|
|
@@ -3,8 +3,9 @@ name: review-prs
|
|
|
3
3
|
description: >-
|
|
4
4
|
Review recent BuilderIO/agent-native human pull requests, approve eligible
|
|
5
5
|
internal PRs, and merge changes that are ready by Steve's bar. Use for
|
|
6
|
-
scheduled or manual PR sweeps; flag external
|
|
7
|
-
product or UX decisions to
|
|
6
|
+
scheduled or manual PR sweeps; flag external work needing a major product
|
|
7
|
+
call or new framework API, and unresolved major product or UX decisions, to
|
|
8
|
+
Steve.
|
|
8
9
|
user-invocable: true
|
|
9
10
|
scope: dev
|
|
10
11
|
metadata:
|
|
@@ -20,10 +21,12 @@ approve an external or unverified author.
|
|
|
20
21
|
|
|
21
22
|
Approval and merge readiness are separate. A **Ready to merge by Steve's bar**
|
|
22
23
|
result means merge the PR under the guarded merge procedure below. For a
|
|
23
|
-
confirmed external author,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
confirmed external author, a clear bug fix or a bounded improvement that
|
|
25
|
+
follows existing patterns enters evidence review and the possible merge flow;
|
|
26
|
+
external authors are never eligible for an approval review. Surface external
|
|
27
|
+
work that needs a major product call or a new framework API for Steve's explicit
|
|
28
|
+
approval before a full review or merge; a scheduled or manual sweep is not that
|
|
29
|
+
approval.
|
|
27
30
|
|
|
28
31
|
## Selection and evidence
|
|
29
32
|
|
|
@@ -53,6 +56,11 @@ author and draft state:
|
|
|
53
56
|
summary to determine whether the PR already has a current, non-dismissed
|
|
54
57
|
`APPROVED` review.
|
|
55
58
|
|
|
59
|
+
- Before the scope gate, check a confirmed nonmember's PR from a fork for
|
|
60
|
+
assignees. If any assignee is a verified current BuilderIO member, ignore
|
|
61
|
+
the PR completely, like a draft: that person owns it under `community-prs`.
|
|
62
|
+
Take no review, reply, or merge action, and leave it out of the recap.
|
|
63
|
+
|
|
56
64
|
- Reuse that review as the code assessment only after verifying the reviewer
|
|
57
65
|
is a different, current BuilderIO member and is eligible under the PR's
|
|
58
66
|
author- and scope-specific rules, including any independent-review
|
|
@@ -99,17 +107,28 @@ the patch looks safe or the issue is obviously valid.
|
|
|
99
107
|
|
|
100
108
|
## External-contribution scope gate
|
|
101
109
|
|
|
110
|
+
Builder devs decide bounded outside contributions; Steve keeps major product
|
|
111
|
+
calls and new framework APIs.
|
|
112
|
+
|
|
102
113
|
For an author the membership API confirms is not a current BuilderIO member,
|
|
103
|
-
continue into the normal evidence sweep
|
|
104
|
-
corrects a specific reported or reproducible
|
|
105
|
-
behavior. A
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
continue into the normal evidence sweep for a clear bug fix or a bounded
|
|
115
|
+
improvement. A clear bug fix corrects a specific reported or reproducible
|
|
116
|
+
defect by restoring intended behavior. A bounded improvement stays within one
|
|
117
|
+
established workflow, app, or framework area and follows the patterns already
|
|
118
|
+
there.
|
|
119
|
+
|
|
120
|
+
External work needs Steve when it makes a major product call (as defined under
|
|
121
|
+
Product and UX decisions) or adds a new framework API: a new public export,
|
|
122
|
+
configuration option, action, plugin or hook contract, or CLI surface in a
|
|
123
|
+
published package under `packages/`. A fix or bounded improvement to an existing
|
|
124
|
+
API stays with devs when it keeps that API's contract. If the title, issue, and
|
|
125
|
+
initial diff do not establish a clear bug fix or a bounded improvement, treat it
|
|
126
|
+
as needing Steve.
|
|
127
|
+
|
|
128
|
+
For external work that needs Steve, stop before the full code review, approval
|
|
129
|
+
or review action, merge-readiness disposition, author-facing draft, or merge.
|
|
130
|
+
Report it to Steve as **Needs Steve's approval for scope**, with the PR link,
|
|
131
|
+
author, stated scope, and which call it needs. Wait for Steve's explicit go-ahead on
|
|
113
132
|
that PR before continuing. Invoking or scheduling `review-prs`, approving a
|
|
114
133
|
different PR, or prior approval of another external contribution does not
|
|
115
134
|
authorize it. If membership is unknown, use the existing unknown-membership
|
|
@@ -118,6 +137,8 @@ solely because membership could not be verified. Keep the no-approval outcome,
|
|
|
118
137
|
report the missing membership check, and assess merge readiness under the
|
|
119
138
|
existing unknown-membership policy.
|
|
120
139
|
|
|
140
|
+
Builder devs own outside PRs end to end with `community-prs`.
|
|
141
|
+
|
|
121
142
|
## Liamdebeasi approval policy
|
|
122
143
|
|
|
123
144
|
For a PR authored by the exact GitHub login `liamdebeasi` and immutable user
|
|
@@ -296,9 +317,10 @@ merge scope and must remain completely untouched.
|
|
|
296
317
|
For a PR that fails an approval gate, do not submit an approval. Flag the exact
|
|
297
318
|
concern and the evidence needed to resolve it. External or unverified authors
|
|
298
319
|
never receive an approval review, but their PRs can still be merged when they
|
|
299
|
-
meet the readiness gate below. Confirmed external
|
|
300
|
-
external-contribution scope gate;
|
|
301
|
-
stopped at Steve's approval gate. For unknown
|
|
320
|
+
meet the readiness gate below. Confirmed external PRs must also pass the
|
|
321
|
+
external-contribution scope gate; external work that needs a major product call
|
|
322
|
+
or a new framework API remains stopped at Steve's approval gate. For unknown
|
|
323
|
+
membership, do not apply the
|
|
302
324
|
external scope gate solely because verification failed; preserve the
|
|
303
325
|
no-approval outcome, assess readiness under the existing unknown-membership
|
|
304
326
|
policy, and name the missing check.
|
|
@@ -504,7 +526,8 @@ non-auto-approved external PR that needs an update or screenshot, include its
|
|
|
504
526
|
draft reply in a separate section, or mark it waiting on the contributor with
|
|
505
527
|
a link to Steve's outstanding request. For internal PRs, report the needed
|
|
506
528
|
update or screenshot without drafting an author-facing reply. Do not add rows
|
|
507
|
-
for bots, `steve8708`, or
|
|
529
|
+
for bots, `steve8708`, drafts, or external PRs assigned to a BuilderIO member;
|
|
530
|
+
those are ignored completely.
|
|
508
531
|
|
|
509
532
|
Use this shape:
|
|
510
533
|
|
|
@@ -525,4 +548,4 @@ blocker when the PR is ready and the guarded admin merge is available.
|
|
|
525
548
|
|
|
526
549
|
## Related skills
|
|
527
550
|
|
|
528
|
-
`concurrent-agents`, `verifying-changes`, `ship`, `babysit-pr`
|
|
551
|
+
`concurrent-agents`, `verifying-changes`, `ship`, `babysit-pr`, `community-prs`
|
|
@@ -170,10 +170,10 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
170
170
|
- **Do** keep one action, one job; document a reusable action (when to use it, key args, return fields to preserve) in `AGENTS.md` once it's called from outside one narrow screen; promote workflow-heavy actions (provider-backed, cross-app, MCP/A2A, multi-step) into a skill.
|
|
171
171
|
- **Do** use `fail(message, { errorCode, statusCode })` for user-friendly errors and import primitives from `@agent-native/core`(`/action`) instead of redefining them; use the core `upload-image` action or `uploadFile()` for durable images/files — never base64 into SQL, markdown, or action results.
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
|
-
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
173
|
+
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
175
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
|
-
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP
|
|
176
|
+
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
177
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
178
178
|
- **Don't** reach for provider integrations, `outputSchema`, `authorize`, `needsApproval`, or `_agentImages` without checking `references/` first — each has sharp edges covered there, not here.
|
|
179
179
|
|
|
@@ -170,10 +170,10 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
170
170
|
- **Do** keep one action, one job; document a reusable action (when to use it, key args, return fields to preserve) in `AGENTS.md` once it's called from outside one narrow screen; promote workflow-heavy actions (provider-backed, cross-app, MCP/A2A, multi-step) into a skill.
|
|
171
171
|
- **Do** use `fail(message, { errorCode, statusCode })` for user-friendly errors and import primitives from `@agent-native/core`(`/action`) instead of redefining them; use the core `upload-image` action or `uploadFile()` for durable images/files — never base64 into SQL, markdown, or action results.
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
|
-
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
173
|
+
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
175
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
|
-
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP
|
|
176
|
+
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
177
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
178
178
|
- **Don't** reach for provider integrations, `outputSchema`, `authorize`, `needsApproval`, or `_agentImages` without checking `references/` first — each has sharp edges covered there, not here.
|
|
179
179
|
|