@agent-native/core 0.175.3 → 0.175.4-nightly-20260826183934
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.
|
@@ -453,7 +453,14 @@ class AISDKEngine {
|
|
|
453
453
|
yield recovered;
|
|
454
454
|
}
|
|
455
455
|
yield { type: "assistant-content", parts: assistantContent };
|
|
456
|
-
|
|
456
|
+
// Clearing the marker asserts "this credential demonstrably works", so
|
|
457
|
+
// only a turn that actually completed may do it. A turn that ended in
|
|
458
|
+
// error proves nothing about the credential, and this ran on every one:
|
|
459
|
+
// a single unrelated 500 re-admitted a credential a 401 had just pinned,
|
|
460
|
+
// so the next person's first prompt spent itself rediscovering the same
|
|
461
|
+
// rejection. `credentialFailureRecorded` only covers the 401 path.
|
|
462
|
+
const stoppedWithError = bufferedStop?.type === "stop" && bufferedStop.reason === "error";
|
|
463
|
+
if (!credentialFailureRecorded && !stoppedWithError) {
|
|
457
464
|
await clearProviderCredentialAuthFailure({
|
|
458
465
|
key: PROVIDER_ENV_VARS[this.provider][0],
|
|
459
466
|
value: this.apiKey,
|
|
@@ -42,22 +42,22 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
44
|
error?: undefined;
|
|
45
|
+
ok?: undefined;
|
|
45
46
|
summary: import("./types.js").TraceSummary;
|
|
46
47
|
spans: import("./types.js").TraceSpan[];
|
|
47
48
|
id?: undefined;
|
|
48
|
-
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
50
|
error?: undefined;
|
|
51
|
+
ok?: undefined;
|
|
51
52
|
summary?: undefined;
|
|
52
53
|
spans?: undefined;
|
|
53
54
|
id: string;
|
|
54
|
-
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
+
ok?: undefined;
|
|
56
57
|
summary?: undefined;
|
|
57
58
|
spans?: undefined;
|
|
58
59
|
id?: undefined;
|
|
59
60
|
error: any;
|
|
60
|
-
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
62
|
error?: undefined;
|
|
63
63
|
summary?: undefined;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
status?: undefined;
|
|
38
37
|
ok?: undefined;
|
|
38
|
+
status?: undefined;
|
|
39
39
|
} | {
|
|
40
|
+
error?: undefined;
|
|
40
41
|
ok: boolean;
|
|
41
42
|
status: string;
|
|
42
|
-
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
44
45
|
error: string;
|
|
45
46
|
removed?: undefined;
|
|
46
|
-
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
+
error?: undefined;
|
|
48
49
|
ok: boolean;
|
|
49
50
|
removed: boolean;
|
|
50
|
-
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
+
ok?: undefined;
|
|
57
58
|
error: string;
|
|
58
59
|
note?: undefined;
|
|
59
|
-
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
+
error?: undefined;
|
|
61
62
|
ok: boolean;
|
|
62
63
|
note?: undefined;
|
|
63
|
-
error?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
67
|
note: string;
|
|
67
|
-
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
+
error?: undefined;
|
|
98
99
|
ok: boolean;
|
|
99
100
|
key: string;
|
|
100
|
-
error?: undefined;
|
|
101
101
|
} | {
|
|
102
|
+
error?: undefined;
|
|
102
103
|
ok: boolean;
|
|
103
104
|
removed: boolean;
|
|
104
|
-
error?: undefined;
|
|
105
105
|
}>>;
|
|
@@ -841,6 +841,38 @@ export async function resolveBuilderGatewayAuth() {
|
|
|
841
841
|
? { authorization: `Bearer ${legacyKey}`, spaceId: null, userId: null }
|
|
842
842
|
: null;
|
|
843
843
|
}
|
|
844
|
+
/**
|
|
845
|
+
* Both auth-failure markers below are fingerprinted on the credential VALUE, so
|
|
846
|
+
* a rotated or corrected credential never matches the old marker and is usable
|
|
847
|
+
* immediately — the TTL is not what unpins it. The TTL covers only the case
|
|
848
|
+
* where the SAME value starts working again: a plan upgrade, a re-enabled
|
|
849
|
+
* gateway, a transient upstream 401.
|
|
850
|
+
*
|
|
851
|
+
* Re-admitting on a flat timer means a credential that is simply wrong is
|
|
852
|
+
* retested on that cadence forever, and each retest is paid for by whichever
|
|
853
|
+
* user's turn happens to land first — they get a 401 while the next lane serves
|
|
854
|
+
* everyone after them. That is the whole shape of the recurring "the saved
|
|
855
|
+
* provider key was rejected" report. Back off per consecutive strike so a dead
|
|
856
|
+
* credential stops costing a turn every quarter hour, while one that genuinely
|
|
857
|
+
* recovers is still retried within the day.
|
|
858
|
+
*/
|
|
859
|
+
const AUTH_FAILURE_MAX_TTL_MS = 24 * 60 * 60 * 1000;
|
|
860
|
+
const AUTH_FAILURE_MAX_STRIKES = 8;
|
|
861
|
+
function authFailureStrikes(row) {
|
|
862
|
+
const raw = row?.strikes;
|
|
863
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw >= 1
|
|
864
|
+
? Math.min(Math.floor(raw), AUTH_FAILURE_MAX_STRIKES)
|
|
865
|
+
: 1;
|
|
866
|
+
}
|
|
867
|
+
function authFailureTtlMs(baseTtlMs, row) {
|
|
868
|
+
return Math.min(baseTtlMs * 2 ** (authFailureStrikes(row) - 1), AUTH_FAILURE_MAX_TTL_MS);
|
|
869
|
+
}
|
|
870
|
+
/** Next strike count for a marker being (re-)armed on the same fingerprint. */
|
|
871
|
+
async function nextAuthFailureStrikes(settingKey) {
|
|
872
|
+
const { getSetting } = await import("../settings/store.js");
|
|
873
|
+
const prior = await getSetting(settingKey, { bypassCache: true });
|
|
874
|
+
return Math.min(authFailureStrikes(prior) + (prior ? 1 : 0), AUTH_FAILURE_MAX_STRIKES);
|
|
875
|
+
}
|
|
844
876
|
const BUILDER_AUTH_FAILURE_SETTING_PREFIX = "builder-auth-failure:";
|
|
845
877
|
/**
|
|
846
878
|
* Stale markers expire so a rejected model or a transient gateway failure
|
|
@@ -873,10 +905,10 @@ export async function getBuilderCredentialAuthFailure(creds = {}) {
|
|
|
873
905
|
if (!row)
|
|
874
906
|
return null;
|
|
875
907
|
const at = typeof row.at === "number" ? row.at : Date.now();
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
908
|
+
// Expired means "usable again", not "never failed": the row stays so the
|
|
909
|
+
// strike count survives, and a credential that fails on re-admission backs
|
|
910
|
+
// off further instead of resetting to the base TTL. A success clears it.
|
|
911
|
+
if (Date.now() - at > authFailureTtlMs(BUILDER_AUTH_FAILURE_TTL_MS, row)) {
|
|
880
912
|
return null;
|
|
881
913
|
}
|
|
882
914
|
return {
|
|
@@ -902,12 +934,15 @@ export async function recordBuilderCredentialAuthFailure(details) {
|
|
|
902
934
|
if (!fingerprint)
|
|
903
935
|
return;
|
|
904
936
|
const { putSetting } = await import("../settings/store.js");
|
|
905
|
-
|
|
937
|
+
const settingKey = builderAuthFailureSettingKey(fingerprint);
|
|
938
|
+
const strikes = await nextAuthFailureStrikes(settingKey);
|
|
939
|
+
await putSetting(settingKey, {
|
|
906
940
|
fingerprint,
|
|
907
941
|
message: details?.message ||
|
|
908
942
|
"Builder rejected the connected credentials. Reconnect Builder.io (free tier available).",
|
|
909
943
|
...(typeof details?.status === "number" && { status: details.status }),
|
|
910
944
|
...(details?.code && { code: details.code }),
|
|
945
|
+
strikes,
|
|
911
946
|
at: Date.now(),
|
|
912
947
|
ownerEmail: getRequestUserEmail() ?? null,
|
|
913
948
|
orgId: getRequestOrgId() ?? null,
|
|
@@ -961,10 +996,9 @@ export async function getProviderCredentialAuthFailure(opts) {
|
|
|
961
996
|
if (row.fingerprint !== fingerprint)
|
|
962
997
|
return null;
|
|
963
998
|
const at = typeof row.at === "number" ? row.at : Date.now();
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
}
|
|
999
|
+
// See `getBuilderCredentialAuthFailure`: the row outlives its TTL so the
|
|
1000
|
+
// strike count does, and re-admission backs off instead of resetting.
|
|
1001
|
+
if (Date.now() - at > authFailureTtlMs(PROVIDER_AUTH_FAILURE_TTL_MS, row)) {
|
|
968
1002
|
return null;
|
|
969
1003
|
}
|
|
970
1004
|
return {
|
|
@@ -994,12 +1028,15 @@ export async function recordProviderCredentialAuthFailure(opts) {
|
|
|
994
1028
|
if (!fingerprint)
|
|
995
1029
|
return;
|
|
996
1030
|
const { putSetting } = await import("../settings/store.js");
|
|
997
|
-
|
|
1031
|
+
const settingKey = providerAuthFailureSettingKey(fingerprint);
|
|
1032
|
+
const strikes = await nextAuthFailureStrikes(settingKey);
|
|
1033
|
+
await putSetting(settingKey, {
|
|
998
1034
|
fingerprint,
|
|
999
1035
|
key,
|
|
1000
1036
|
message: opts.message || "The model provider rejected the saved API key.",
|
|
1001
1037
|
...(typeof opts.status === "number" && { status: opts.status }),
|
|
1002
1038
|
...(opts.code && { code: opts.code }),
|
|
1039
|
+
strikes,
|
|
1003
1040
|
at: Date.now(),
|
|
1004
1041
|
ownerEmail: getRequestUserEmail() ?? null,
|
|
1005
1042
|
orgId: getRequestOrgId() ?? null,
|
package/docs/content/webmcp.mdx
CHANGED
|
@@ -13,11 +13,11 @@ discover them, and the browser mediates execution.
|
|
|
13
13
|
|
|
14
14
|
The framework supports both directions:
|
|
15
15
|
|
|
16
|
-
| You want to...
|
|
17
|
-
|
|
|
18
|
-
| Make selected actions in your app available to a browser agent
|
|
19
|
-
| Let Agent-Native chat discover and call WebMCP tools in the current page | `createAgentNativeWebMcpClient` plus `webmcp` on `AgentNativeEmbedded` or `AgentNativeFrame`
|
|
20
|
-
| Connect a headless or remote agent to an app
|
|
16
|
+
| You want to... | Use |
|
|
17
|
+
| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| Make selected actions in your app available to a browser agent | `createAgentNativeWebMcpRegistration` |
|
|
19
|
+
| Let Agent-Native chat discover and call WebMCP tools in the current page | `createAgentNativeWebMcpClient` plus `webmcp` on `AgentNativeEmbedded` or `AgentNativeFrame` |
|
|
20
|
+
| Connect a headless or remote agent to an app | [MCP Server](/docs/mcp-protocol), [External Agents](/docs/external-agents), or [A2A](/docs/a2a-protocol) |
|
|
21
21
|
|
|
22
22
|
WebMCP is not a network MCP server and does not make a closed app globally
|
|
23
23
|
discoverable. A consuming agent needs an open, connected browser page and the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.175.
|
|
3
|
+
"version": "0.175.4-nightly-20260826183934",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|