@agent-native/core 0.204.0 → 0.204.1-nightly-20261008065100
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.js +5 -6
- package/dist/agent/auto-continue.d.ts +35 -0
- package/dist/agent/auto-continue.js +38 -0
- package/dist/agent/connection-required-note.d.ts +52 -0
- package/dist/agent/connection-required-note.js +187 -0
- package/dist/agent/jev-tool-prefetch.d.ts +5 -1
- package/dist/agent/jev-tool-prefetch.js +19 -6
- package/dist/agent/previous-turn-note.d.ts +9 -0
- package/dist/agent/previous-turn-note.js +48 -0
- package/dist/agent/production-agent.d.ts +17 -13
- package/dist/agent/production-agent.js +185 -43
- package/dist/agent/reaped-run-thread.d.ts +13 -0
- package/dist/agent/reaped-run-thread.js +45 -0
- package/dist/agent/run-store.d.ts +29 -5
- package/dist/agent/run-store.js +144 -30
- package/dist/agent/thread-data-builder.d.ts +16 -0
- package/dist/agent/thread-data-builder.js +30 -8
- package/dist/agent/thread-message-projection.js +5 -3
- package/dist/agent/tool-search.js +5 -1
- package/dist/agent/types.d.ts +3 -0
- package/dist/audit/config.d.ts +6 -0
- package/dist/audit/config.js +12 -1
- package/dist/audit/index.d.ts +1 -1
- package/dist/audit/index.js +1 -1
- package/dist/audit/store.js +39 -4
- package/dist/automation/index.d.ts +1 -1
- package/dist/cli/skills.d.ts +1 -0
- package/dist/cli/skills.js +1 -1
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat/index.js +1 -1
- package/dist/client/chat/agentkit-agent-native.js +83 -7
- package/dist/client/chat/agentkit-protocol.js +140 -67
- package/dist/client/chat/runtime.js +10 -4
- package/dist/client/embed-auth.d.ts +15 -0
- package/dist/client/embed-auth.js +77 -7
- package/dist/client/host/index.d.ts +1 -1
- package/dist/client/host/index.js +1 -1
- package/dist/client/mcp-app-host.d.ts +19 -0
- package/dist/client/mcp-app-host.js +88 -2
- package/dist/client/use-chat-threads.d.ts +1 -1
- package/dist/client/use-chat-threads.js +2 -0
- package/dist/client/use-run-stuck-detection.d.ts +18 -1
- package/dist/client/use-run-stuck-detection.js +115 -75
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/request-telemetry.d.ts +7 -1
- package/dist/db/request-telemetry.js +20 -1
- package/dist/deploy/build.d.ts +25 -1
- package/dist/deploy/build.js +176 -3
- package/dist/deploy/workspace-deploy.js +8 -3
- package/dist/extensions/fetch-tool.js +50 -14
- package/dist/file-upload/builder.js +1 -1
- package/dist/file-upload/s3.js +1 -1
- package/dist/file-upload/types.d.ts +1 -0
- package/dist/integrations/adapters/email.js +11 -3
- package/dist/integrations/adapters/telegram.js +4 -2
- package/dist/jobs/actions/automation-no-op.d.ts +12 -0
- package/dist/jobs/actions/automation-no-op.js +20 -0
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/jobs/background-automation-runner.d.ts +8 -1
- package/dist/jobs/background-automation-runner.js +102 -19
- package/dist/jobs/run-history.d.ts +1 -1
- package/dist/jobs/run-history.js +8 -4
- package/dist/jobs/scheduler.js +25 -12
- package/dist/localization/automation-outcome-messages.d.ts +22 -0
- package/dist/localization/automation-outcome-messages.js +90 -0
- package/dist/localization/default-messages.d.ts +2 -0
- package/dist/localization/default-messages.js +4 -2
- package/dist/mcp/build-server.js +22 -11
- package/dist/mcp/embed-app.js +143 -9
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/observability/metrics.d.ts +3 -0
- package/dist/observability/metrics.js +10 -0
- package/dist/observability/migrations.js +22 -0
- package/dist/observability/routes.d.ts +10 -5
- package/dist/observability/routes.js +88 -14
- package/dist/observability/sentiment.d.ts +5 -0
- package/dist/observability/sentiment.js +75 -25
- package/dist/observability/store.d.ts +28 -0
- package/dist/observability/store.js +58 -0
- package/dist/observability/traces.js +37 -5
- package/dist/org/handlers.d.ts +1 -1
- package/dist/progress/actions.js +1 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/actions/provider-api.js +6 -2
- package/dist/provider-api/index.d.ts +1 -0
- package/dist/provider-api/index.js +18 -0
- package/dist/provider-api/result-outcome.d.ts +1 -0
- package/dist/provider-api/result-outcome.js +20 -0
- package/dist/provider-api/staging.js +10 -9
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/resources/script-helpers.d.ts +1 -0
- package/dist/resources/script-helpers.js +1 -1
- package/dist/resources/store.js +81 -0
- package/dist/scripts/resources/save-memory.js +3 -0
- package/dist/server/action-routes.js +34 -6
- package/dist/server/agent-chat/prompt-resources.d.ts +8 -2
- package/dist/server/agent-chat/prompt-resources.js +31 -8
- package/dist/server/agent-chat/recurring-jobs-runtime.d.ts +19 -4
- package/dist/server/agent-chat/recurring-jobs-runtime.js +52 -16
- package/dist/server/agent-chat/script-entries.js +30 -3
- package/dist/server/agent-chat/sweep-trigger-auth.d.ts +18 -0
- package/dist/server/agent-chat/sweep-trigger-auth.js +36 -0
- package/dist/server/agent-chat-plugin.js +51 -47
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/auth.js +21 -6
- package/dist/server/embed-route.js +5 -2
- package/dist/server/framework-request-handler.js +19 -0
- package/dist/server/http-response-telemetry.js +4 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +6 -0
- package/dist/shared/context-status.d.ts +21 -0
- package/dist/shared/context-status.js +54 -0
- package/dist/shared/mcp-app-display.d.ts +20 -0
- package/dist/shared/mcp-app-display.js +34 -0
- package/dist/shared/mcp-app-widget-embed.d.ts +18 -0
- package/dist/shared/mcp-app-widget-embed.js +41 -0
- package/dist/templates/chat/app/components/chat/ChatRouteContent.tsx +9 -2
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +9 -2
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +8 -1
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +3 -1
- package/dist/triggers/actions/manage-automation.d.ts +5 -5
- package/dist/triggers/dispatcher.js +10 -7
- package/docs/content/audit-log.mdx +3 -3
- package/docs/content/cloudflare.mdx +27 -0
- package/docs/content/deployment-environment-variables.mdx +2 -0
- package/docs/content/durable-resume.mdx +3 -1
- package/docs/content/environment-variables.mdx +2 -0
- package/docs/content/mcp-apps.mdx +1 -1
- package/docs/content/recurring-jobs.mdx +29 -9
- package/docs/content/vercel.mdx +30 -0
- package/package.json +4 -3
- package/src/templates/chat/app/components/chat/ChatRouteContent.tsx +9 -2
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +9 -2
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +8 -1
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +3 -1
|
@@ -365,6 +365,31 @@ function durableRunFailures(messages) {
|
|
|
365
365
|
];
|
|
366
366
|
}));
|
|
367
367
|
}
|
|
368
|
+
/** The turn each durable prompt started, keyed by the run it was submitted to. */
|
|
369
|
+
function durableSubmittedTurns(messages) {
|
|
370
|
+
return new Map(messages.flatMap((message) => {
|
|
371
|
+
const custom = asRecord(asRecord(message.metadata)?.custom);
|
|
372
|
+
return message.role === "user" &&
|
|
373
|
+
typeof custom?.submittedRunId === "string" &&
|
|
374
|
+
typeof custom.submittedTurnId === "string"
|
|
375
|
+
? [[custom.submittedRunId, custom.submittedTurnId]]
|
|
376
|
+
: [];
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
/** The runs that left a durable reply on each turn. */
|
|
380
|
+
function durableReplyRunsByTurn(messages) {
|
|
381
|
+
const result = new Map();
|
|
382
|
+
for (const message of messages) {
|
|
383
|
+
const turnId = asRecord(asRecord(message.metadata)?.custom)?.turnId;
|
|
384
|
+
if (message.role !== "assistant" || typeof turnId !== "string")
|
|
385
|
+
continue;
|
|
386
|
+
const runIds = result.get(turnId) ?? new Set();
|
|
387
|
+
for (const runId of durableRunIds(message))
|
|
388
|
+
runIds.add(runId);
|
|
389
|
+
result.set(turnId, runIds);
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
368
393
|
/** A durable reply's terminal run plus every continuation run folded into it. */
|
|
369
394
|
function durableRunIds(message) {
|
|
370
395
|
const metadata = asRecord(message.metadata);
|
|
@@ -561,6 +586,38 @@ function reconcileDurableMessages(messages, durable, events, runs, toolCalls) {
|
|
|
561
586
|
durableByFoldedRun.set(runId, durableByFoldedRun.has(runId) ? null : message);
|
|
562
587
|
}
|
|
563
588
|
}
|
|
589
|
+
const textOf = (parts) => parts
|
|
590
|
+
.filter((part) => part.type === "text")
|
|
591
|
+
.map((part) => part.text)
|
|
592
|
+
.join("");
|
|
593
|
+
// A run that recovered an interrupted turn records only itself on its reply,
|
|
594
|
+
// while an open page streamed it into the run the prompt was submitted to.
|
|
595
|
+
// The recovering run's events open their own message, so the reply is on
|
|
596
|
+
// screen only when one of that run's messages is exactly it; anything less
|
|
597
|
+
// keeps the saved reply, so the answer is never dropped.
|
|
598
|
+
const submittedRunByTurn = new Map();
|
|
599
|
+
for (const stored of submittedUsers) {
|
|
600
|
+
const turnId = asRecord(asRecord(stored.metadata)?.custom)?.submittedTurnId;
|
|
601
|
+
if (typeof turnId !== "string")
|
|
602
|
+
continue;
|
|
603
|
+
submittedRunByTurn.set(turnId, submittedRunByTurn.has(turnId) ? null : submittedRunId(stored));
|
|
604
|
+
}
|
|
605
|
+
const shownByInterruptedRun = (reply) => {
|
|
606
|
+
const turnId = asRecord(asRecord(reply.metadata)?.custom)?.turnId;
|
|
607
|
+
const submitted = typeof turnId === "string" ? submittedRunByTurn.get(turnId) : undefined;
|
|
608
|
+
if (!submitted ||
|
|
609
|
+
durableRunIds(reply).includes(submitted) ||
|
|
610
|
+
durableByRun.has(submitted)) {
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
const ids = assistantIdsByRun.get(submitted);
|
|
614
|
+
const replyText = textOf(reply.parts);
|
|
615
|
+
if (!ids || !replyText)
|
|
616
|
+
return false;
|
|
617
|
+
return messages.some((message) => message.role === "assistant" &&
|
|
618
|
+
ids.has(message.id) &&
|
|
619
|
+
textOf(message.parts) === replyText);
|
|
620
|
+
};
|
|
564
621
|
const rootAssistantProjection = projectRootAssistantMessages({
|
|
565
622
|
rootMessages: durable,
|
|
566
623
|
snapshotMessages: messages,
|
|
@@ -676,6 +733,8 @@ function reconcileDurableMessages(messages, durable, events, runs, toolCalls) {
|
|
|
676
733
|
if (typeof runId !== "string" || !recoverableAssistantRunIds.has(runId)) {
|
|
677
734
|
continue;
|
|
678
735
|
}
|
|
736
|
+
if (shownByInterruptedRun(message))
|
|
737
|
+
continue;
|
|
679
738
|
missingMessages.push(message);
|
|
680
739
|
representedAssistantIds.add(message.id);
|
|
681
740
|
}
|
|
@@ -694,10 +753,6 @@ function reconcileDurableMessages(messages, durable, events, runs, toolCalls) {
|
|
|
694
753
|
snapshotRunIdsByMessageId.get(message.id) ??
|
|
695
754
|
(typeof metadataRunId === "string" ? metadataRunId : undefined));
|
|
696
755
|
};
|
|
697
|
-
const textOf = (parts) => parts
|
|
698
|
-
.filter((part) => part.type === "text")
|
|
699
|
-
.map((part) => part.text)
|
|
700
|
-
.join("");
|
|
701
756
|
const reconciledMessages = projectedMessages.map((message) => {
|
|
702
757
|
if (message.role === "user") {
|
|
703
758
|
const stored = storedUserBySnapshotId.get(message.id);
|
|
@@ -1652,8 +1707,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1652
1707
|
}
|
|
1653
1708
|
return value;
|
|
1654
1709
|
}
|
|
1655
|
-
async function activeRunSnapshot(threadId) {
|
|
1656
|
-
const value = await activeRunStatus(threadId);
|
|
1710
|
+
async function activeRunSnapshot(threadId, value) {
|
|
1657
1711
|
const status = value.status;
|
|
1658
1712
|
if (typeof value.active !== "boolean")
|
|
1659
1713
|
return undefined;
|
|
@@ -1775,7 +1829,8 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1775
1829
|
const completedRunIds = completedDurableRunIds(durableMessages);
|
|
1776
1830
|
const userStoppedRunIds = userStoppedDurableRunIds(durableMessages);
|
|
1777
1831
|
const durableFailures = durableRunFailures(durableMessages);
|
|
1778
|
-
const
|
|
1832
|
+
const activeStatus = await activeRunStatus(threadId);
|
|
1833
|
+
const activeRun = await activeRunSnapshot(threadId, activeStatus);
|
|
1779
1834
|
if (activeRun === undefined)
|
|
1780
1835
|
return thread;
|
|
1781
1836
|
let discoveredRun = activeRun;
|
|
@@ -1792,9 +1847,30 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1792
1847
|
!["completed", "failed", "cancelled"].includes(discoveredRun.status)
|
|
1793
1848
|
? discoveredRun.id
|
|
1794
1849
|
: undefined;
|
|
1850
|
+
const submittedTurns = durableSubmittedTurns(durableMessages);
|
|
1851
|
+
const replyRunsByTurn = durableReplyRunsByTurn(durableMessages);
|
|
1852
|
+
const discoveredTurnId = typeof activeStatus.turnId === "string" ? activeStatus.turnId : undefined;
|
|
1853
|
+
// The stale-run reaper hands an interrupted turn to a successor run, which
|
|
1854
|
+
// then owns the turn's outcome: the server reports it, or it completed a
|
|
1855
|
+
// reply. A successor that failed or was stopped leaves the failure showing.
|
|
1856
|
+
const carriedOnByNewerRun = (runId) => {
|
|
1857
|
+
const turnId = submittedTurns.get(runId);
|
|
1858
|
+
if (!turnId)
|
|
1859
|
+
return false;
|
|
1860
|
+
if (discoveredRun && discoveredTurnId === turnId)
|
|
1861
|
+
return true;
|
|
1862
|
+
return [...(replyRunsByTurn.get(turnId) ?? [])].some((id) => id !== runId && completedRunIds.has(id));
|
|
1863
|
+
};
|
|
1795
1864
|
const runs = (thread.runs ?? [])
|
|
1796
1865
|
.filter((entry) => entry.id !== discoveredRun?.id)
|
|
1797
1866
|
.map((run) => {
|
|
1867
|
+
if ((["queued", "running"].includes(run.status) ||
|
|
1868
|
+
(run.status === "failed" &&
|
|
1869
|
+
runOutcomeForCode(run.error?.code) === "interrupted")) &&
|
|
1870
|
+
carriedOnByNewerRun(run.id)) {
|
|
1871
|
+
const { error: _error, ...attempt } = run;
|
|
1872
|
+
return { ...attempt, status: "completed" };
|
|
1873
|
+
}
|
|
1798
1874
|
if (activeRun === null &&
|
|
1799
1875
|
run.status === "running" &&
|
|
1800
1876
|
userStoppedRunIds.has(run.id)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE, AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE, AGENTKIT_PROTOCOL_VERSION, AgentProtocolValidationError, approvalResponseFromResume, createCapabilityUnavailableError, createCapabilityUnsupportedError, inferAgentActivityKind, negotiateAgentKitProtocolVersion, resumeEntryFromApproval, resumeOptionId, } from "@agent-native/agentkit/protocol";
|
|
2
|
-
import { AUTO_CONTINUE_OF_RUN_METADATA_KEY, AUTO_CONTINUE_PROMPT, AUTO_CONTINUE_REFUSAL_CODES, } from "../../agent/auto-continue.js";
|
|
2
|
+
import { AUTO_CONTINUE_OF_RUN_METADATA_KEY, AUTO_CONTINUE_PROMPT, AUTO_CONTINUE_REFUSAL_CODES, CONTINUE_OF_RUN_METADATA_KEY, CONTINUE_UNAVAILABLE_CODE, } from "../../agent/auto-continue.js";
|
|
3
3
|
import { emitChatFirstOpenApp, emitChatFirstOpenBrowser, } from "../chat-first-state.js";
|
|
4
4
|
import { decideAfterStreamClosed, runOutcomeForCode, } from "./run-outcome.js";
|
|
5
5
|
function isTerminalRunStatus(status) {
|
|
@@ -1104,6 +1104,67 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
1104
1104
|
ensurePump(run);
|
|
1105
1105
|
return run;
|
|
1106
1106
|
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Registers a run the runtime just started and begins reading it. A disposed
|
|
1109
|
+
* adapter keeps no run it can no longer read.
|
|
1110
|
+
*/
|
|
1111
|
+
async function openStartedRun(threadId, session, turn, turnMetadata, observability) {
|
|
1112
|
+
const runId = turn.runId ?? turn.id ?? createId("run");
|
|
1113
|
+
const runMetadata = mergeTrustedProtocolMetadata(options.metadata, turnMetadata, turn.metadata, {
|
|
1114
|
+
[AGENT_NATIVE_PROTOCOL_METADATA_KEY]: {
|
|
1115
|
+
observability: {
|
|
1116
|
+
protocolRunId: runId,
|
|
1117
|
+
...(turn.runId === undefined ? {} : { runtimeRunId: turn.runId }),
|
|
1118
|
+
runtimeId: runtime.id,
|
|
1119
|
+
sessionId: session.id,
|
|
1120
|
+
turnId: turn.id,
|
|
1121
|
+
threadId,
|
|
1122
|
+
...observability,
|
|
1123
|
+
},
|
|
1124
|
+
},
|
|
1125
|
+
});
|
|
1126
|
+
setRuntimeRunIdMetadata(runMetadata, turn.runId);
|
|
1127
|
+
const run = {
|
|
1128
|
+
runId,
|
|
1129
|
+
threadId,
|
|
1130
|
+
session,
|
|
1131
|
+
turn,
|
|
1132
|
+
events: [],
|
|
1133
|
+
firstRetainedSequence: 1,
|
|
1134
|
+
sequence: 0,
|
|
1135
|
+
status: "queued",
|
|
1136
|
+
lastAccessedAtMs: timeMs(),
|
|
1137
|
+
activeReaders: 0,
|
|
1138
|
+
metadata: runMetadata,
|
|
1139
|
+
activeMessageCompleted: false,
|
|
1140
|
+
pendingWidgets: new Map(),
|
|
1141
|
+
actions: new Map(),
|
|
1142
|
+
activeTools: new Map(),
|
|
1143
|
+
pendingToolMessageAssociations: new Map(),
|
|
1144
|
+
activeActivities: new Map(),
|
|
1145
|
+
terminalAppendDepth: 0,
|
|
1146
|
+
pumpPromise: null,
|
|
1147
|
+
continuationPromise: null,
|
|
1148
|
+
streamClosed: false,
|
|
1149
|
+
terminal: false,
|
|
1150
|
+
waitingForContinuation: false,
|
|
1151
|
+
listeners: new Set(),
|
|
1152
|
+
};
|
|
1153
|
+
if (disposed) {
|
|
1154
|
+
await disposeSession(session).catch(() => undefined);
|
|
1155
|
+
throw new Error("The AgentKit adapter was disposed during turn creation.");
|
|
1156
|
+
}
|
|
1157
|
+
pruneRetainedRuns(run.lastAccessedAtMs);
|
|
1158
|
+
indexRun(run, turn.runId);
|
|
1159
|
+
append(run, {
|
|
1160
|
+
type: "run.started",
|
|
1161
|
+
agentId: runtime.id,
|
|
1162
|
+
metadata: runMetadata,
|
|
1163
|
+
});
|
|
1164
|
+
append(run, { type: "run.status", status: "running" });
|
|
1165
|
+
ensurePump(run);
|
|
1166
|
+
return runId;
|
|
1167
|
+
}
|
|
1107
1168
|
async function disposeSession(session) {
|
|
1108
1169
|
if (disposedSessions.has(session))
|
|
1109
1170
|
return;
|
|
@@ -2012,21 +2073,26 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
2012
2073
|
payload: run.usage,
|
|
2013
2074
|
},
|
|
2014
2075
|
];
|
|
2015
|
-
case "error":
|
|
2076
|
+
case "error": {
|
|
2077
|
+
const error = {
|
|
2078
|
+
code: event.code ?? "runtime_error",
|
|
2079
|
+
message: event.error,
|
|
2080
|
+
retryable: event.retryable ?? event.recoverable,
|
|
2081
|
+
details: event.details ?? event.cause,
|
|
2082
|
+
};
|
|
2083
|
+
// An interruption ends this runtime run, not necessarily the turn.
|
|
2084
|
+
if (runAuthority &&
|
|
2085
|
+
capabilities.resumableRuns === true &&
|
|
2086
|
+
runOutcomeForCode(error.code) === "interrupted") {
|
|
2087
|
+
run.heldInterruption = error;
|
|
2088
|
+
return [];
|
|
2089
|
+
}
|
|
2016
2090
|
run.terminal = true;
|
|
2017
2091
|
return [
|
|
2018
2092
|
{ type: "run.status", ...base, status: "failed" },
|
|
2019
|
-
{
|
|
2020
|
-
type: "run.failed",
|
|
2021
|
-
...base,
|
|
2022
|
-
error: {
|
|
2023
|
-
code: event.code ?? "runtime_error",
|
|
2024
|
-
message: event.error,
|
|
2025
|
-
retryable: event.retryable ?? event.recoverable,
|
|
2026
|
-
details: event.details ?? event.cause,
|
|
2027
|
-
},
|
|
2028
|
-
},
|
|
2093
|
+
{ type: "run.failed", ...base, error },
|
|
2029
2094
|
];
|
|
2095
|
+
}
|
|
2030
2096
|
case "done":
|
|
2031
2097
|
if (!isTerminalReason(event.reason)) {
|
|
2032
2098
|
run.waitingForContinuation = true;
|
|
@@ -2256,6 +2322,18 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
2256
2322
|
}
|
|
2257
2323
|
if (stopped())
|
|
2258
2324
|
return null;
|
|
2325
|
+
// An unreachable server says nothing about a successor; keep holding
|
|
2326
|
+
// through the usual backoff until it answers.
|
|
2327
|
+
if (run.heldInterruption && read.kind !== "unreachable") {
|
|
2328
|
+
const successor = read.kind === "read" &&
|
|
2329
|
+
read.state.status !== "missing" &&
|
|
2330
|
+
read.state.runId !== runtimeRunId;
|
|
2331
|
+
if (!successor) {
|
|
2332
|
+
appendHeldInterruption(run);
|
|
2333
|
+
return null;
|
|
2334
|
+
}
|
|
2335
|
+
run.heldInterruption = undefined;
|
|
2336
|
+
}
|
|
2259
2337
|
const decision = decideAfterStreamClosed(read, {
|
|
2260
2338
|
runId: runtimeRunId,
|
|
2261
2339
|
drain: run.terminalDrain,
|
|
@@ -2406,6 +2484,14 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
2406
2484
|
append(run, { type: "run.cancelled" });
|
|
2407
2485
|
}
|
|
2408
2486
|
}
|
|
2487
|
+
/** The runtime run's own interruption, once no newer run carries the turn. */
|
|
2488
|
+
function appendHeldInterruption(run) {
|
|
2489
|
+
const error = run.heldInterruption;
|
|
2490
|
+
run.heldInterruption = undefined;
|
|
2491
|
+
run.terminal = true;
|
|
2492
|
+
append(run, { type: "run.status", status: "failed" });
|
|
2493
|
+
append(run, { type: "run.failed", error });
|
|
2494
|
+
}
|
|
2409
2495
|
function setResumedRuntimeTurn(run, resumed) {
|
|
2410
2496
|
const runtimeRunId = run.turn.runId ?? run.runId;
|
|
2411
2497
|
if (resumed.runId && resumed.runId !== runtimeRunId) {
|
|
@@ -2439,8 +2525,11 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
2439
2525
|
for (const protocolEvent of runtimeEventToProtocolEvents(run, event)) {
|
|
2440
2526
|
append(run, protocolEvent);
|
|
2441
2527
|
}
|
|
2442
|
-
if (run.waitingForContinuation ||
|
|
2528
|
+
if (run.waitingForContinuation ||
|
|
2529
|
+
run.terminal ||
|
|
2530
|
+
run.heldInterruption) {
|
|
2443
2531
|
break;
|
|
2532
|
+
}
|
|
2444
2533
|
}
|
|
2445
2534
|
}
|
|
2446
2535
|
catch (error) {
|
|
@@ -2452,7 +2541,7 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
2452
2541
|
}
|
|
2453
2542
|
if (run.restoredWithoutStream)
|
|
2454
2543
|
run.restoredWithoutStream = false;
|
|
2455
|
-
else
|
|
2544
|
+
else if (!run.heldInterruption)
|
|
2456
2545
|
run.pipeClosedBeforeTerminal = true;
|
|
2457
2546
|
const resumed = runAuthority && capabilities.resumableRuns === true
|
|
2458
2547
|
? await followRunAuthority(run, runAuthority, stream)
|
|
@@ -2578,59 +2667,7 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
2578
2667
|
metadata: turnMetadata,
|
|
2579
2668
|
abortSignal: readers.signal,
|
|
2580
2669
|
});
|
|
2581
|
-
const runId =
|
|
2582
|
-
const runMetadata = mergeTrustedProtocolMetadata(options.metadata, turnMetadata, turn.metadata, {
|
|
2583
|
-
[AGENT_NATIVE_PROTOCOL_METADATA_KEY]: {
|
|
2584
|
-
observability: {
|
|
2585
|
-
protocolRunId: runId,
|
|
2586
|
-
...(turn.runId === undefined ? {} : { runtimeRunId: turn.runId }),
|
|
2587
|
-
runtimeId: runtime.id,
|
|
2588
|
-
sessionId: session.id,
|
|
2589
|
-
turnId: turn.id,
|
|
2590
|
-
threadId: input.threadId,
|
|
2591
|
-
},
|
|
2592
|
-
},
|
|
2593
|
-
});
|
|
2594
|
-
setRuntimeRunIdMetadata(runMetadata, turn.runId);
|
|
2595
|
-
const run = {
|
|
2596
|
-
runId,
|
|
2597
|
-
threadId: input.threadId,
|
|
2598
|
-
session,
|
|
2599
|
-
turn,
|
|
2600
|
-
events: [],
|
|
2601
|
-
firstRetainedSequence: 1,
|
|
2602
|
-
sequence: 0,
|
|
2603
|
-
status: "queued",
|
|
2604
|
-
lastAccessedAtMs: timeMs(),
|
|
2605
|
-
activeReaders: 0,
|
|
2606
|
-
metadata: runMetadata,
|
|
2607
|
-
activeMessageCompleted: false,
|
|
2608
|
-
pendingWidgets: new Map(),
|
|
2609
|
-
actions: new Map(),
|
|
2610
|
-
activeTools: new Map(),
|
|
2611
|
-
pendingToolMessageAssociations: new Map(),
|
|
2612
|
-
activeActivities: new Map(),
|
|
2613
|
-
terminalAppendDepth: 0,
|
|
2614
|
-
pumpPromise: null,
|
|
2615
|
-
continuationPromise: null,
|
|
2616
|
-
streamClosed: false,
|
|
2617
|
-
terminal: false,
|
|
2618
|
-
waitingForContinuation: false,
|
|
2619
|
-
listeners: new Set(),
|
|
2620
|
-
};
|
|
2621
|
-
if (disposed) {
|
|
2622
|
-
await disposeSession(session).catch(() => undefined);
|
|
2623
|
-
throw new Error("The AgentKit adapter was disposed during turn creation.");
|
|
2624
|
-
}
|
|
2625
|
-
pruneRetainedRuns(run.lastAccessedAtMs);
|
|
2626
|
-
indexRun(run, turn.runId);
|
|
2627
|
-
append(run, {
|
|
2628
|
-
type: "run.started",
|
|
2629
|
-
agentId: runtime.id,
|
|
2630
|
-
metadata: runMetadata,
|
|
2631
|
-
});
|
|
2632
|
-
append(run, { type: "run.status", status: "running" });
|
|
2633
|
-
ensurePump(run);
|
|
2670
|
+
const runId = await openStartedRun(input.threadId, session, turn, turnMetadata);
|
|
2634
2671
|
return { runId, capabilities };
|
|
2635
2672
|
},
|
|
2636
2673
|
async *subscribeToRun(input) {
|
|
@@ -2984,6 +3021,42 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
2984
3021
|
});
|
|
2985
3022
|
};
|
|
2986
3023
|
}
|
|
3024
|
+
if (runAuthority) {
|
|
3025
|
+
const authority = runAuthority;
|
|
3026
|
+
// Continues the turn a stopped run belonged to rather than starting a new
|
|
3027
|
+
// one, so the turn's journal still blocks repeating a finished write. The
|
|
3028
|
+
// server reads which run ended the turn; this page may know only the one
|
|
3029
|
+
// it watched, or none after a reload.
|
|
3030
|
+
transport.continueRun = async (input) => {
|
|
3031
|
+
if (disposed)
|
|
3032
|
+
throw new Error("The AgentKit adapter is disposed.");
|
|
3033
|
+
pruneRetainedRuns();
|
|
3034
|
+
const local = runs.get(input.runId);
|
|
3035
|
+
if (local && local.threadId !== input.threadId) {
|
|
3036
|
+
throw new Error(`Unknown AgentKit run: ${input.runId}`);
|
|
3037
|
+
}
|
|
3038
|
+
const session = local?.session ?? (await getSession(input.threadId));
|
|
3039
|
+
if (!session.continueTurn) {
|
|
3040
|
+
throw new Error("The Core runtime does not support run continuation.");
|
|
3041
|
+
}
|
|
3042
|
+
const state = await authority.readRunState({
|
|
3043
|
+
sessionId: session.id,
|
|
3044
|
+
runId: local?.turn.runId ?? input.runId,
|
|
3045
|
+
abortSignal: readers.signal,
|
|
3046
|
+
});
|
|
3047
|
+
if (state.status === "missing" || !state.turnId) {
|
|
3048
|
+
throw Object.assign(new Error(`Agent run ${input.runId} cannot be continued.`), { code: CONTINUE_UNAVAILABLE_CODE, retryable: false });
|
|
3049
|
+
}
|
|
3050
|
+
const turn = await session.continueTurn({
|
|
3051
|
+
turnId: state.turnId,
|
|
3052
|
+
prompt: AUTO_CONTINUE_PROMPT,
|
|
3053
|
+
metadata: { [CONTINUE_OF_RUN_METADATA_KEY]: state.runId },
|
|
3054
|
+
abortSignal: readers.signal,
|
|
3055
|
+
});
|
|
3056
|
+
const runId = await openStartedRun(input.threadId, session, turn, undefined, { continuedRunId: input.runId });
|
|
3057
|
+
return { runId, capabilities };
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
2987
3060
|
if (runtime.capabilities.rich?.connectionRequests) {
|
|
2988
3061
|
transport.resolveConnectionRequest = async (input) => {
|
|
2989
3062
|
pruneRetainedRuns();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
|
|
2
|
-
import { AUTO_CONTINUE_OF_RUN_METADATA_KEY } from "../../agent/auto-continue.js";
|
|
2
|
+
import { AUTO_CONTINUE_OF_RUN_METADATA_KEY, CONTINUE_OF_RUN_METADATA_KEY, } from "../../agent/auto-continue.js";
|
|
3
3
|
import { agentChatStreamingUrl, agentNativePath } from "../api-path.js";
|
|
4
4
|
import { CHAT_REQUEST_TOO_LARGE_MESSAGE } from "../error-format.js";
|
|
5
5
|
import { appendMissingFinalResponseWarning, } from "../sse-event-processor.js";
|
|
@@ -2449,12 +2449,17 @@ export function createAgentNativeChatRuntime(options = {}) {
|
|
|
2449
2449
|
const approvedToolCalls = metadataStringList(turn.metadata, AGENT_NATIVE_APPROVED_TOOL_CALLS_METADATA_KEY);
|
|
2450
2450
|
const continuationTurnId = metadataString(turn.metadata, AGENT_NATIVE_CONTINUATION_TURN_ID_METADATA_KEY);
|
|
2451
2451
|
const autoContinueOfRunId = metadataString(turn.metadata, AUTO_CONTINUE_OF_RUN_METADATA_KEY);
|
|
2452
|
+
const continueOfRunId = metadataString(turn.metadata, CONTINUE_OF_RUN_METADATA_KEY);
|
|
2452
2453
|
const continuationMessageState = continuationTurnId
|
|
2453
2454
|
? messageStates.get(continuationTurnId)
|
|
2454
2455
|
: undefined;
|
|
2455
2456
|
if (continuationMessageState) {
|
|
2456
2457
|
messageStates.set(turnId, continuationMessageState);
|
|
2457
2458
|
}
|
|
2459
|
+
const turnEngine = turn.metadata?.engine;
|
|
2460
|
+
const engine = typeof turnEngine === "string" && turnEngine.trim()
|
|
2461
|
+
? turnEngine
|
|
2462
|
+
: options.engine;
|
|
2458
2463
|
const history = nativeHistoryFromMessages(turn.messages, prompt);
|
|
2459
2464
|
const pendingApprovalHistory = approvedToolCalls && continuationMessageState
|
|
2460
2465
|
? pendingApprovalRuntimeMessages(continuationMessageState)
|
|
@@ -2481,6 +2486,7 @@ export function createAgentNativeChatRuntime(options = {}) {
|
|
|
2481
2486
|
? { internalContinuation: true }
|
|
2482
2487
|
: {}),
|
|
2483
2488
|
...(autoContinueOfRunId ? { autoContinueOfRunId } : {}),
|
|
2489
|
+
...(continueOfRunId ? { continueOfRunId } : {}),
|
|
2484
2490
|
...(turn.metadata?.agentNativeSkipPendingSelectionContext === true
|
|
2485
2491
|
? { skipPendingSelectionContext: true }
|
|
2486
2492
|
: {}),
|
|
@@ -2489,7 +2495,7 @@ export function createAgentNativeChatRuntime(options = {}) {
|
|
|
2489
2495
|
...((turn.model ?? options.model)
|
|
2490
2496
|
? { model: turn.model ?? options.model }
|
|
2491
2497
|
: {}),
|
|
2492
|
-
...(
|
|
2498
|
+
...(engine ? { engine } : {}),
|
|
2493
2499
|
...((turn.reasoningEffort ?? options.effort)
|
|
2494
2500
|
? { effort: turn.reasoningEffort ?? options.effort }
|
|
2495
2501
|
: {}),
|
|
@@ -2527,8 +2533,8 @@ export function createAgentNativeChatRuntime(options = {}) {
|
|
|
2527
2533
|
},
|
|
2528
2534
|
continueTurn: ({ session, continuation, previousTurn, startTurn }) => {
|
|
2529
2535
|
// Only the continuation that names a stopped run continues it; an approval
|
|
2530
|
-
// or connection answered later is not another
|
|
2531
|
-
const { [AUTO_CONTINUE_OF_RUN_METADATA_KEY]: _autoContinueOf, ...previousMetadata } = previousTurn?.metadata ?? {};
|
|
2536
|
+
// or connection answered later is not another continuation of that run.
|
|
2537
|
+
const { [AUTO_CONTINUE_OF_RUN_METADATA_KEY]: _autoContinueOf, [CONTINUE_OF_RUN_METADATA_KEY]: _continueOf, ...previousMetadata } = previousTurn?.metadata ?? {};
|
|
2532
2538
|
const approval = continuation.approval;
|
|
2533
2539
|
const connection = continuation.connection;
|
|
2534
2540
|
const messageStateKey = continuation.turnId ?? session.id;
|
|
@@ -2,6 +2,21 @@ export declare function readEmbedMcpChatBridgeFlagFromUrl(): boolean;
|
|
|
2
2
|
export declare function markEmbedMcpChatBridgeActive(): void;
|
|
3
3
|
export declare function isEmbedMcpChatBridgeActive(): boolean;
|
|
4
4
|
export declare function getEmbedAuthToken(): string | null;
|
|
5
|
+
/**
|
|
6
|
+
* True when the embed credential is a directory-widget read capability, which
|
|
7
|
+
* only the MCP App widget flows mint. It stays readable after a client
|
|
8
|
+
* navigation drops the URL params, so it identifies a widget document without
|
|
9
|
+
* the chat-bridge flag. The token's claims are only a UI hint (the signature
|
|
10
|
+
* is checked server-side), so this decides what not to attempt, never what is
|
|
11
|
+
* allowed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function hasMcpDirectoryWidgetCapabilityToken(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* True when this document is the app nested in an MCP App widget shell (the
|
|
16
|
+
* chat bridge is active) on a directory-widget read capability, which the
|
|
17
|
+
* server limits to the widget's own resource reads.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isMcpDirectoryWidgetReadOnlyEmbed(): boolean;
|
|
5
20
|
export declare function isEmbedAuthActive(): boolean;
|
|
6
21
|
export declare function _resetEmbedAuthForTests(): void;
|
|
7
22
|
/**
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { EMBED_MODE_QUERY_PARAM, EMBED_START_PATH, EMBED_TARGET_HEADER, EMBED_TARGET_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, MCP_APP_CHAT_BRIDGE_QUERY_PARAM, } from "../shared/embed-auth.js";
|
|
2
|
+
import { isMcpDirectoryWidgetReadCapabilityScope } from "../shared/embed-auth.js";
|
|
2
3
|
import { FRAMEWORK_INTERNAL_ROUTE_PREFIX } from "../shared/framework-route-prefix.js";
|
|
4
|
+
import { MCP_APP_HOST_FILL_ATTRIBUTE } from "../shared/mcp-app-display.js";
|
|
5
|
+
import { EMBED_TOKEN_STORAGE_KEY, MCP_CHAT_BRIDGE_STORAGE_KEY, } from "../shared/mcp-app-widget-embed.js";
|
|
3
6
|
import { SIGN_IN_ENTRY_PATH, SIGN_IN_LEGACY_ENTRY_PATH, } from "../shared/sign-in-journey.js";
|
|
4
7
|
import { frameworkRoutePrefix } from "./api-path.js";
|
|
5
8
|
let installed = false;
|
|
6
9
|
let memoryToken = null;
|
|
7
10
|
let mcpChatBridgeActive = false;
|
|
8
11
|
let mcpChatBridgeScope = null;
|
|
9
|
-
const EMBED_TOKEN_STORAGE_KEY = "agent-native:embed-auth-token";
|
|
10
|
-
const MCP_CHAT_BRIDGE_STORAGE_KEY = "agent-native:mcp-chat-bridge";
|
|
11
12
|
const AUTH_FAILURE_COOLDOWN_MS = 60_000;
|
|
12
13
|
const GUARDED_METHODS = new Set(["GET", "HEAD"]);
|
|
13
14
|
const AUTH_FAILURE_HEADER = "x-agent-native-auth-circuit-breaker";
|
|
@@ -146,6 +147,48 @@ export function getEmbedAuthToken() {
|
|
|
146
147
|
}
|
|
147
148
|
return memoryToken ?? storedToken(win);
|
|
148
149
|
}
|
|
150
|
+
function readEmbedTokenScope(token) {
|
|
151
|
+
const payload = token.split(".")[0] ?? "";
|
|
152
|
+
try {
|
|
153
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
154
|
+
const bytes = Uint8Array.from(atob(base64 + "=".repeat((4 - (base64.length % 4)) % 4)), (char) => char.charCodeAt(0));
|
|
155
|
+
const claims = JSON.parse(new TextDecoder().decode(bytes));
|
|
156
|
+
return typeof claims.scope === "string" ? claims.scope : undefined;
|
|
157
|
+
// coercion-ok: an unreadable token reads as a normal session; the server still enforces the real scope on every request.
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
let readOnlyScopeCache = null;
|
|
164
|
+
/**
|
|
165
|
+
* True when the embed credential is a directory-widget read capability, which
|
|
166
|
+
* only the MCP App widget flows mint. It stays readable after a client
|
|
167
|
+
* navigation drops the URL params, so it identifies a widget document without
|
|
168
|
+
* the chat-bridge flag. The token's claims are only a UI hint (the signature
|
|
169
|
+
* is checked server-side), so this decides what not to attempt, never what is
|
|
170
|
+
* allowed.
|
|
171
|
+
*/
|
|
172
|
+
export function hasMcpDirectoryWidgetCapabilityToken() {
|
|
173
|
+
const token = getEmbedAuthToken();
|
|
174
|
+
if (!token)
|
|
175
|
+
return false;
|
|
176
|
+
if (readOnlyScopeCache?.token !== token) {
|
|
177
|
+
readOnlyScopeCache = {
|
|
178
|
+
token,
|
|
179
|
+
readOnly: isMcpDirectoryWidgetReadCapabilityScope(readEmbedTokenScope(token)),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return readOnlyScopeCache.readOnly;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* True when this document is the app nested in an MCP App widget shell (the
|
|
186
|
+
* chat bridge is active) on a directory-widget read capability, which the
|
|
187
|
+
* server limits to the widget's own resource reads.
|
|
188
|
+
*/
|
|
189
|
+
export function isMcpDirectoryWidgetReadOnlyEmbed() {
|
|
190
|
+
return hasMcpDirectoryWidgetCapabilityToken() && isEmbedMcpChatBridgeActive();
|
|
191
|
+
}
|
|
149
192
|
export function isEmbedAuthActive() {
|
|
150
193
|
const win = browserWindow();
|
|
151
194
|
if (!win)
|
|
@@ -165,18 +208,22 @@ function ensureMcpChatBridgeViewportClamp(win) {
|
|
|
165
208
|
const style = doc.createElement("style");
|
|
166
209
|
style.id = MCP_CHAT_BRIDGE_VIEWPORT_STYLE_ID;
|
|
167
210
|
const height = `${MCP_CHAT_BRIDGE_VIEWPORT_HEIGHT}px`;
|
|
211
|
+
// An inline card sizes itself to this document, so `100vh` must not follow
|
|
212
|
+
// the frame. A host that owns the frame's height (see
|
|
213
|
+
// mcpAppHostFillsContainer) sets the fill attribute and the clamp lifts.
|
|
214
|
+
const inline = `html:not([${MCP_APP_HOST_FILL_ATTRIBUTE}])`;
|
|
168
215
|
style.textContent = `
|
|
169
|
-
|
|
170
|
-
body {
|
|
216
|
+
${inline},
|
|
217
|
+
${inline} body {
|
|
171
218
|
min-height: 0 !important;
|
|
172
219
|
height: ${height} !important;
|
|
173
220
|
max-height: ${height} !important;
|
|
174
221
|
overflow: hidden !important;
|
|
175
222
|
}
|
|
176
223
|
|
|
177
|
-
#root,
|
|
178
|
-
#__next,
|
|
179
|
-
[data-agent-native-app-root] {
|
|
224
|
+
${inline} #root,
|
|
225
|
+
${inline} #__next,
|
|
226
|
+
${inline} [data-agent-native-app-root] {
|
|
180
227
|
min-height: 0 !important;
|
|
181
228
|
height: ${height} !important;
|
|
182
229
|
max-height: ${height} !important;
|
|
@@ -256,6 +303,7 @@ export function _resetEmbedAuthForTests() {
|
|
|
256
303
|
}
|
|
257
304
|
installed = false;
|
|
258
305
|
memoryToken = null;
|
|
306
|
+
readOnlyScopeCache = null;
|
|
259
307
|
mcpChatBridgeActive = false;
|
|
260
308
|
mcpChatBridgeScope = null;
|
|
261
309
|
authFailureCache.clear();
|
|
@@ -337,6 +385,22 @@ function isAgentNativeRuntimePath(pathname) {
|
|
|
337
385
|
pathname.endsWith(prefix) ||
|
|
338
386
|
pathname.includes(`${prefix}/`));
|
|
339
387
|
}
|
|
388
|
+
// What a read-only widget session is refused whatever it asks for: the agent
|
|
389
|
+
// state it can never write.
|
|
390
|
+
function isReadOnlyWidgetRefusedPath(pathname) {
|
|
391
|
+
return [FRAMEWORK_INTERNAL_ROUTE_PREFIX, frameworkRoutePrefix()].some((prefix) => pathname.endsWith(`${prefix}/application-state`) ||
|
|
392
|
+
pathname.includes(`${prefix}/application-state/`));
|
|
393
|
+
}
|
|
394
|
+
// The refusal the server's auth guard already sends such a session, answered
|
|
395
|
+
// locally so each caller takes the error path it takes today without the
|
|
396
|
+
// request or its console error.
|
|
397
|
+
function readOnlyWidgetRefusal() {
|
|
398
|
+
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
399
|
+
status: 401,
|
|
400
|
+
statusText: "Unauthorized",
|
|
401
|
+
headers: { "Content-Type": "application/json" },
|
|
402
|
+
});
|
|
403
|
+
}
|
|
340
404
|
function requestMethod(input, init) {
|
|
341
405
|
return (init?.method ??
|
|
342
406
|
(input instanceof Request ? input.method : undefined) ??
|
|
@@ -480,6 +544,12 @@ export function ensureEmbedAuthFetchInterceptor() {
|
|
|
480
544
|
return;
|
|
481
545
|
const originalFetch = win.fetch.bind(win);
|
|
482
546
|
const patchedFetch = (async (input, init) => {
|
|
547
|
+
if (isMcpDirectoryWidgetReadOnlyEmbed() && sameOrigin(input, win)) {
|
|
548
|
+
const url = inputUrl(input, win);
|
|
549
|
+
if (url && isReadOnlyWidgetRefusedPath(url.pathname)) {
|
|
550
|
+
return readOnlyWidgetRefusal();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
483
553
|
const request = requestUrlAndKey(input, init, win);
|
|
484
554
|
if (request?.shouldGuard) {
|
|
485
555
|
const cached = getCachedAuthFailure(request.key);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { initializeAgentNativeClient } from "../client-bootstrap.js";
|
|
2
2
|
export { agentNativeApiDisabledReason, AgentNativeApiDisabledError, setAgentNativeApiDisabled, } from "../api-surface.js";
|
|
3
|
-
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, } from "../embed-auth.js";
|
|
3
|
+
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, isMcpDirectoryWidgetReadOnlyEmbed, } from "../embed-auth.js";
|
|
4
4
|
export { sendToFrame, onFrameMessage, requestUserInfo, getFrameOrigin, getFramePostMessageTargetOrigin, getCallbackOrigin, oauthRedirectUri, isInFrame, isTrustedFrameMessage, enterStyleEditing, enterTextEditing, exitSelectionMode, type UserInfo, } from "../frame.js";
|
|
5
5
|
export { getBuilderParentOrigin, isInBuilderFrame, sendToBuilderChat, shouldParentFrameOwnAgentPanel, isTrustedBuilderMessage, tryDelegateBuildRequestToBuilder, type BuilderChatMessage, } from "../builder-frame.js";
|
|
6
6
|
export { getClientSurface, type ClientSurface } from "../client-surface.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { initializeAgentNativeClient } from "../client-bootstrap.js";
|
|
2
2
|
export { agentNativeApiDisabledReason, AgentNativeApiDisabledError, setAgentNativeApiDisabled, } from "../api-surface.js";
|
|
3
|
-
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, } from "../embed-auth.js";
|
|
3
|
+
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, isMcpDirectoryWidgetReadOnlyEmbed, } from "../embed-auth.js";
|
|
4
4
|
export { sendToFrame, onFrameMessage, requestUserInfo, getFrameOrigin, getFramePostMessageTargetOrigin, getCallbackOrigin, oauthRedirectUri, isInFrame, isTrustedFrameMessage, enterStyleEditing, enterTextEditing, exitSelectionMode, } from "../frame.js";
|
|
5
5
|
export { getBuilderParentOrigin, isInBuilderFrame, sendToBuilderChat, shouldParentFrameOwnAgentPanel, isTrustedBuilderMessage, tryDelegateBuildRequestToBuilder, } from "../builder-frame.js";
|
|
6
6
|
export { getClientSurface } from "../client-surface.js";
|
|
@@ -45,6 +45,25 @@ export interface McpAppHostContextSnapshot {
|
|
|
45
45
|
version: unknown;
|
|
46
46
|
hostInfo?: McpAppHostInfo;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* True when this document is an app running inside an MCP App widget (a
|
|
50
|
+
* ChatGPT, Codex, or Claude card or panel). The host, not the app, owns
|
|
51
|
+
* navigation and chat there, so apps drop their own navigation chrome.
|
|
52
|
+
*
|
|
53
|
+
* Being a widget is a property of the document, not of its current URL: the
|
|
54
|
+
* first positive answer is kept (and marked on `<html>`, where the first-paint
|
|
55
|
+
* script in `getMcpAppWidgetEmbedBootScriptBody` also marks it), so a client
|
|
56
|
+
* navigation that drops the embed query params, or a token swap that
|
|
57
|
+
* de-enrolls the chat bridge, never brings the app's own chrome back.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isMcpAppWidgetEmbed(): boolean;
|
|
60
|
+
export declare function useIsMcpAppWidgetEmbed(): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* True for a directory widget, whose scoped session can only read its own
|
|
63
|
+
* resource. Apps skip application-state writes and save-failure surfaces there
|
|
64
|
+
* instead of showing errors for requests that can never land.
|
|
65
|
+
*/
|
|
66
|
+
export declare function useIsMcpDirectoryWidgetReadOnlyEmbed(): boolean;
|
|
48
67
|
export declare function isOpenAiMcpAppHost(): boolean;
|
|
49
68
|
export declare function initializeMcpAppHost(): Promise<boolean>;
|
|
50
69
|
export declare function sendMcpAppHostMessage(chat: McpAppHostChatMessage): Promise<boolean | null> | false;
|