@agent-native/core 0.219.0 → 0.220.0-nightly-20261023212848
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action.d.ts +23 -3
- package/dist/action.js +58 -3
- package/dist/agent/abort-reasons.d.ts +6 -0
- package/dist/agent/abort-reasons.js +6 -0
- package/dist/agent/execution-fence.js +5 -1
- package/dist/agent/production-agent.d.ts +22 -6
- package/dist/agent/production-agent.js +136 -37
- package/dist/agent/run-manager.js +92 -44
- package/dist/agent/run-store.d.ts +1 -0
- package/dist/agent/run-store.js +260 -31
- package/dist/agent/run-terminal-tracking.d.ts +29 -0
- package/dist/agent/run-terminal-tracking.js +56 -0
- package/dist/agent/run-turn-id-header.d.ts +9 -0
- package/dist/agent/run-turn-id-header.js +13 -0
- package/dist/agent/types.d.ts +11 -0
- package/dist/chat-threads/store.d.ts +23 -6
- package/dist/chat-threads/store.js +119 -36
- package/dist/cli/amplify-stream.js +1 -1
- package/dist/client/agent-chat/index.d.ts +2 -1
- package/dist/client/agent-chat/index.js +2 -1
- package/dist/client/agent-chat.d.ts +2 -0
- package/dist/client/agent-chat.js +31 -5
- package/dist/client/chat/agentkit-agent-native.js +8 -1
- package/dist/client/chat/agentkit-protocol.js +7 -1
- package/dist/client/chat/run-outcome-telemetry.js +1 -0
- package/dist/client/chat/run-outcome.d.ts +5 -0
- package/dist/client/chat/runtime.d.ts +34 -1
- package/dist/client/chat/runtime.js +96 -12
- package/dist/client/chat/turn-submitted-telemetry.d.ts +4 -0
- package/dist/client/chat/turn-submitted-telemetry.js +39 -0
- package/dist/client/chat/turn-submitted.d.ts +39 -0
- package/dist/client/chat/turn-submitted.js +16 -0
- package/dist/client/route-chunk-recovery.d.ts +3 -8
- package/dist/client/route-chunk-recovery.js +11 -9
- package/dist/client/use-chat-thread-list.d.ts +2 -0
- package/dist/client/use-chat-thread-list.js +48 -2
- package/dist/client/use-chat-threads.d.ts +13 -0
- package/dist/client/use-chat-threads.js +59 -4
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.js +28 -18
- package/dist/db/schema-snapshot.generated.js +5 -0
- package/dist/identity/rekey.d.ts +2 -0
- package/dist/identity/rekey.js +19 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp/agent-connected.d.ts +5 -0
- package/dist/mcp/agent-connected.js +10 -3
- package/dist/mcp/analytics.d.ts +14 -1
- package/dist/mcp/analytics.js +54 -2
- package/dist/mcp/embed-app.js +2 -1
- package/dist/mcp/oauth-route.js +56 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/observability/traces.d.ts +2 -0
- package/dist/observability/traces.js +11 -2
- package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/review/index.d.ts +1 -1
- package/dist/review/index.js +1 -1
- package/dist/review/suggestions/actions.js +25 -17
- package/dist/review/suggestions/store.d.ts +14 -1
- package/dist/review/suggestions/store.js +16 -3
- package/dist/review/suggestions/types.d.ts +15 -1
- package/dist/scripts/chat/search-chats.js +14 -2
- package/dist/server/agent-chat/browser-team-tools.js +39 -6
- package/dist/server/agent-chat/script-entries.js +44 -9
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.js +66 -16
- 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/first-touch-route.d.ts +2 -2
- package/dist/shared/route-chunk-recovery-bootstrap.d.ts +9 -0
- package/dist/shared/route-chunk-recovery-bootstrap.js +25 -0
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/actions/SKILL.md +1 -0
- package/dist/templates/default/.agents/skills/actions/SKILL.md +1 -0
- package/dist/templates/factory/.agents/skills/review-prs/SKILL.md +27 -3
- package/dist/templates/headless/.agents/skills/actions/SKILL.md +1 -0
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +1 -0
- package/dist/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
- package/dist/tracking/providers.js +3 -0
- package/dist/triggers/actions/manage-automation.d.ts +2 -2
- package/docs/content/external-agents.mdx +1 -1
- package/docs/content/mcp-apps.mdx +3 -3
- package/docs/content/mcp-protocol.mdx +1 -1
- package/docs/content/sharing.mdx +1 -0
- package/docs/content/template-assets-integrations.mdx +5 -3
- package/package.json +3 -3
- package/src/templates/chat/.agents/skills/actions/SKILL.md +1 -0
- package/src/templates/default/.agents/skills/actions/SKILL.md +1 -0
- package/src/templates/factory/.agents/skills/review-prs/SKILL.md +27 -3
- package/src/templates/headless/.agents/skills/actions/SKILL.md +1 -0
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +1 -0
- package/src/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
|
@@ -8,6 +8,7 @@ import { agentChatStreamingUrl, agentNativePath } from "../api-path.js";
|
|
|
8
8
|
import { CHAT_REQUEST_TOO_LARGE_MESSAGE } from "../error-format.js";
|
|
9
9
|
import { appendMissingFinalResponseWarning, findApprovalToolCallIndex, } from "../sse-event-processor.js";
|
|
10
10
|
import { runOutcomeForCode } from "./run-outcome.js";
|
|
11
|
+
import { DEFAULT_TURN_SURFACE, } from "./turn-submitted.js";
|
|
11
12
|
export const AGENT_NATIVE_RUN_RESUME_STATE_METADATA_KEY = "agentNativeRunResumeState";
|
|
12
13
|
const AGENTKIT_TOOL_HISTORY_OMISSION_MESSAGE_ID_PREFIX = "agentkit-tool-history-omission";
|
|
13
14
|
const DEFAULT_RUNTIME_CAPABILITIES = {
|
|
@@ -395,12 +396,13 @@ async function readHttpRuntimeError(response) {
|
|
|
395
396
|
return error;
|
|
396
397
|
}
|
|
397
398
|
/**
|
|
398
|
-
* A turn that failed to start keeps its
|
|
399
|
-
* refused under
|
|
399
|
+
* A turn that failed to start keeps its ids: the server records a turn it
|
|
400
|
+
* refused under the request's turn id, and the client reports the failure
|
|
401
|
+
* with both.
|
|
400
402
|
*/
|
|
401
|
-
function withTurnId(error, turnId) {
|
|
403
|
+
function withTurnId(error, turnId, requestTurnId) {
|
|
402
404
|
if (error !== null && typeof error === "object" && !("turnId" in error)) {
|
|
403
|
-
Object.assign(error, { turnId });
|
|
405
|
+
Object.assign(error, { turnId, requestTurnId });
|
|
404
406
|
}
|
|
405
407
|
return error;
|
|
406
408
|
}
|
|
@@ -439,6 +441,7 @@ export function createHttpAgentChatRuntime(options) {
|
|
|
439
441
|
};
|
|
440
442
|
const startTurn = async (turn) => {
|
|
441
443
|
const turnId = turn.queuePromotion?.turnId ?? createRuntimeId("turn");
|
|
444
|
+
const requestTurnId = options.requestTurnId?.({ turn, turnId }) ?? turnId;
|
|
442
445
|
const { controller, cleanup } = createAbortController(turn.abortSignal);
|
|
443
446
|
latestTurn = turn;
|
|
444
447
|
previousTurns.set(turnId, turn);
|
|
@@ -459,8 +462,17 @@ export function createHttpAgentChatRuntime(options) {
|
|
|
459
462
|
headers,
|
|
460
463
|
credentials: options.credentials,
|
|
461
464
|
body: JSON.stringify(options.mapRequest
|
|
462
|
-
? options.mapRequest({
|
|
463
|
-
|
|
465
|
+
? options.mapRequest({
|
|
466
|
+
session: summary,
|
|
467
|
+
turn,
|
|
468
|
+
turnId,
|
|
469
|
+
requestTurnId,
|
|
470
|
+
})
|
|
471
|
+
: defaultHttpRuntimeRequest({
|
|
472
|
+
session: summary,
|
|
473
|
+
turn,
|
|
474
|
+
turnId: requestTurnId,
|
|
475
|
+
})),
|
|
464
476
|
signal: controller.signal,
|
|
465
477
|
});
|
|
466
478
|
}
|
|
@@ -469,7 +481,7 @@ export function createHttpAgentChatRuntime(options) {
|
|
|
469
481
|
if (!isRetryableRuntimeFailure(error)) {
|
|
470
482
|
forgetTurnContext(turnId, turn);
|
|
471
483
|
}
|
|
472
|
-
throw withTurnId(error, turnId);
|
|
484
|
+
throw withTurnId(error, turnId, requestTurnId);
|
|
473
485
|
}
|
|
474
486
|
if (!response.ok) {
|
|
475
487
|
cleanup();
|
|
@@ -477,7 +489,7 @@ export function createHttpAgentChatRuntime(options) {
|
|
|
477
489
|
if (!isRetryableRuntimeFailure(error)) {
|
|
478
490
|
forgetTurnContext(turnId, turn);
|
|
479
491
|
}
|
|
480
|
-
throw withTurnId(error, turnId);
|
|
492
|
+
throw withTurnId(error, turnId, requestTurnId);
|
|
481
493
|
}
|
|
482
494
|
if (response.status === 202) {
|
|
483
495
|
let queuedSubmission;
|
|
@@ -505,7 +517,7 @@ export function createHttpAgentChatRuntime(options) {
|
|
|
505
517
|
code: "invalid_queued_submission_response",
|
|
506
518
|
status: response.status,
|
|
507
519
|
retryable: true,
|
|
508
|
-
}), turnId);
|
|
520
|
+
}), turnId, requestTurnId);
|
|
509
521
|
}
|
|
510
522
|
cleanup();
|
|
511
523
|
forgetTurnContext(turnId, turn);
|
|
@@ -517,6 +529,29 @@ export function createHttpAgentChatRuntime(options) {
|
|
|
517
529
|
};
|
|
518
530
|
}
|
|
519
531
|
const runId = response.headers.get("X-Run-Id") ?? undefined;
|
|
532
|
+
const replay = response.headers.get("X-Dispatch-Mode") === "replay";
|
|
533
|
+
// The server can store the run under another turn than the one asked
|
|
534
|
+
// for: an approval resume runs under its pending approval's turn. A
|
|
535
|
+
// replay streams a run an earlier request started, so without the
|
|
536
|
+
// server's turn its turn is unknown, not this request's.
|
|
537
|
+
const storedTurnId = response.headers.get("X-Turn-Id") || undefined;
|
|
538
|
+
const runTurnId = storedTurnId ?? (replay ? undefined : requestTurnId);
|
|
539
|
+
try {
|
|
540
|
+
// A replay streams a turn the server already finished, and a stream
|
|
541
|
+
// with no run id is a refusal (no model connected), not new work.
|
|
542
|
+
if (runId && !replay) {
|
|
543
|
+
options.onTurnAccepted?.({
|
|
544
|
+
session: summary,
|
|
545
|
+
turn,
|
|
546
|
+
turnId,
|
|
547
|
+
requestTurnId: storedTurnId ?? requestTurnId,
|
|
548
|
+
runId,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
// coercion-ok: an observer of a started turn must not stop it.
|
|
554
|
+
}
|
|
520
555
|
const events = (async function* () {
|
|
521
556
|
let retryableFailure = false;
|
|
522
557
|
try {
|
|
@@ -550,6 +585,7 @@ export function createHttpAgentChatRuntime(options) {
|
|
|
550
585
|
};
|
|
551
586
|
return {
|
|
552
587
|
id: turnId,
|
|
588
|
+
requestTurnId: runTurnId,
|
|
553
589
|
sessionId,
|
|
554
590
|
runId,
|
|
555
591
|
events,
|
|
@@ -3066,6 +3102,33 @@ function metadataStringList(metadata, key) {
|
|
|
3066
3102
|
const strings = value.filter((item) => typeof item === "string" && item.length > 0);
|
|
3067
3103
|
return strings.length > 0 ? strings : undefined;
|
|
3068
3104
|
}
|
|
3105
|
+
/** The turn a continuation continues; a queued turn keeps its queue id. */
|
|
3106
|
+
function agentNativeRequestTurnId(input) {
|
|
3107
|
+
return (metadataString(input.turn.metadata, AGENT_NATIVE_CONTINUATION_TURN_ID_METADATA_KEY) ??
|
|
3108
|
+
input.turn.queuePromotion?.turnId ??
|
|
3109
|
+
input.turnId);
|
|
3110
|
+
}
|
|
3111
|
+
function agentNativeTurnInitiator(metadata) {
|
|
3112
|
+
const custom = asRecord(metadata?.custom);
|
|
3113
|
+
const startedByChat = metadata?.[AGENT_NATIVE_INTERNAL_CONTINUATION_METADATA_KEY] === true ||
|
|
3114
|
+
metadata?.trackInRunsTray === true ||
|
|
3115
|
+
metadata?.hideUserMessage === true ||
|
|
3116
|
+
[
|
|
3117
|
+
AGENT_NATIVE_CONTINUATION_TURN_ID_METADATA_KEY,
|
|
3118
|
+
AGENT_NATIVE_APPROVED_TOOL_CALLS_METADATA_KEY,
|
|
3119
|
+
"approvedToolCalls",
|
|
3120
|
+
AUTO_CONTINUE_OF_RUN_METADATA_KEY,
|
|
3121
|
+
CONTINUE_OF_RUN_METADATA_KEY,
|
|
3122
|
+
].some((key) => metadata?.[key] !== undefined) ||
|
|
3123
|
+
custom?.agentNativeRecoveryAction !== undefined ||
|
|
3124
|
+
custom?.agentNativeResumeAfterSetup === true;
|
|
3125
|
+
if (startedByChat)
|
|
3126
|
+
return "system";
|
|
3127
|
+
// A send deferred while the thread restored is still the sender's own.
|
|
3128
|
+
const fromApp = metadataString(metadata, "submitMessageId") !== undefined ||
|
|
3129
|
+
custom?.agentNativeDeferredFromApp === true;
|
|
3130
|
+
return fromApp ? "app" : "user";
|
|
3131
|
+
}
|
|
3069
3132
|
function terminalRuntimeTurn(input) {
|
|
3070
3133
|
return {
|
|
3071
3134
|
id: input.turnId ?? createRuntimeId("turn"),
|
|
@@ -3075,7 +3138,7 @@ function terminalRuntimeTurn(input) {
|
|
|
3075
3138
|
})(),
|
|
3076
3139
|
};
|
|
3077
3140
|
}
|
|
3078
|
-
export function createAgentNativeChatRuntime(options = {}) {
|
|
3141
|
+
export function createAgentNativeChatRuntime(options = {}, telemetry = {}) {
|
|
3079
3142
|
const apiUrl = options.apiUrl ?? agentNativePath("/_agent-native/agent-chat");
|
|
3080
3143
|
const runtimeId = options.id ?? "agent-native";
|
|
3081
3144
|
const fetchImpl = options.fetch ?? fetch;
|
|
@@ -3157,6 +3220,27 @@ export function createAgentNativeChatRuntime(options = {}) {
|
|
|
3157
3220
|
description: options.description ?? "Agent-Native's built-in chat transport.",
|
|
3158
3221
|
endpoint: apiUrl,
|
|
3159
3222
|
fetch: runtimeFetch,
|
|
3223
|
+
requestTurnId: agentNativeRequestTurnId,
|
|
3224
|
+
onTurnAccepted: ({ session, turn, requestTurnId, runId }) => {
|
|
3225
|
+
const metadata = turn.metadata;
|
|
3226
|
+
const custom = asRecord(metadata?.custom) ?? undefined;
|
|
3227
|
+
const requestMode = metadataString(metadata, "requestMode") ??
|
|
3228
|
+
metadataString(metadata, "mode") ??
|
|
3229
|
+
options.mode;
|
|
3230
|
+
telemetry.onTurnSubmitted?.({
|
|
3231
|
+
turnId: requestTurnId,
|
|
3232
|
+
threadId: session.threadId ?? options.threadId,
|
|
3233
|
+
runId,
|
|
3234
|
+
surface: metadataString(metadata, "agentNativeSurface") ??
|
|
3235
|
+
DEFAULT_TURN_SURFACE,
|
|
3236
|
+
initiator: agentNativeTurnInitiator(metadata),
|
|
3237
|
+
submitMessageId: metadataString(metadata, "submitMessageId"),
|
|
3238
|
+
recoveryOfRunId: metadataString(custom, "agentNativeRecoveryOfRunId") ??
|
|
3239
|
+
metadataString(metadata, CONTINUE_OF_RUN_METADATA_KEY) ??
|
|
3240
|
+
metadataString(metadata, AUTO_CONTINUE_OF_RUN_METADATA_KEY),
|
|
3241
|
+
requestMode,
|
|
3242
|
+
});
|
|
3243
|
+
},
|
|
3160
3244
|
beforeStartTurn: async ({ session, turn, turnId }) => {
|
|
3161
3245
|
const metadata = turn.metadata;
|
|
3162
3246
|
const isAdmittedContinuation = turn.queuePromotion !== undefined ||
|
|
@@ -3213,7 +3297,7 @@ export function createAgentNativeChatRuntime(options = {}) {
|
|
|
3213
3297
|
connectionRequests: true,
|
|
3214
3298
|
},
|
|
3215
3299
|
},
|
|
3216
|
-
mapRequest: ({ session, turn, turnId }) => {
|
|
3300
|
+
mapRequest: ({ session, turn, turnId, requestTurnId }) => {
|
|
3217
3301
|
const latestUserMessage = [...(turn.messages ?? [])]
|
|
3218
3302
|
.reverse()
|
|
3219
3303
|
.find((message) => message.role === "user");
|
|
@@ -3265,7 +3349,7 @@ export function createAgentNativeChatRuntime(options = {}) {
|
|
|
3265
3349
|
history,
|
|
3266
3350
|
...(structuredHistory?.length ? { structuredHistory } : {}),
|
|
3267
3351
|
...(loadedSkillSlugs.length ? { loadedSkillSlugs } : {}),
|
|
3268
|
-
turnId:
|
|
3352
|
+
turnId: requestTurnId,
|
|
3269
3353
|
threadId: session.threadId ?? options.threadId,
|
|
3270
3354
|
...(turn.submissionPayload !== undefined
|
|
3271
3355
|
? { submissionPayload: turn.submissionPayload }
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { trackEvent } from "../analytics.js";
|
|
2
|
+
import { type RoutedTurnSubmittedReport, type TurnSubmittedReport } from "./turn-submitted.js";
|
|
3
|
+
export declare function trackTurnSubmitted(report: TurnSubmittedReport, send?: typeof trackEvent): void;
|
|
4
|
+
export declare function trackRoutedTurnSubmitted(report: RoutedTurnSubmittedReport, send?: typeof trackEvent): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { trackEvent } from "../analytics.js";
|
|
2
|
+
import { TURN_SUBMITTED_EVENT, } from "./turn-submitted.js";
|
|
3
|
+
export function trackTurnSubmitted(report, send = trackEvent) {
|
|
4
|
+
try {
|
|
5
|
+
send(TURN_SUBMITTED_EVENT, {
|
|
6
|
+
turn_id: report.turnId,
|
|
7
|
+
...(report.threadId ? { thread_id: report.threadId } : {}),
|
|
8
|
+
...(report.runId ? { run_id: report.runId } : {}),
|
|
9
|
+
surface: report.surface,
|
|
10
|
+
initiator: report.initiator,
|
|
11
|
+
...(report.submitMessageId
|
|
12
|
+
? { submit_message_id: report.submitMessageId }
|
|
13
|
+
: {}),
|
|
14
|
+
...(report.recoveryOfRunId
|
|
15
|
+
? { recovery_of_run_id: report.recoveryOfRunId }
|
|
16
|
+
: {}),
|
|
17
|
+
...(report.requestMode ? { request_mode: report.requestMode } : {}),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// coercion-ok: telemetry must never change whether a turn runs.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function trackRoutedTurnSubmitted(report, send = trackEvent) {
|
|
25
|
+
try {
|
|
26
|
+
send(TURN_SUBMITTED_EVENT, {
|
|
27
|
+
routed_to: report.routedTo,
|
|
28
|
+
surface: report.surface,
|
|
29
|
+
initiator: "app",
|
|
30
|
+
...(report.submitMessageId
|
|
31
|
+
? { submit_message_id: report.submitMessageId }
|
|
32
|
+
: {}),
|
|
33
|
+
...(report.requestMode ? { request_mode: report.requestMode } : {}),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// coercion-ok: telemetry must never change where a message is sent.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One event per agent request the server accepts, carrying the turn id the
|
|
3
|
+
* server stores on that request's run so the two join. A turn's continuations
|
|
4
|
+
* are further requests: they repeat its `turn_id` as `initiator: "system"`, so
|
|
5
|
+
* count turns by distinct `turn_id`, and the turns people started by
|
|
6
|
+
* `initiator` `user` or `app`. A request refused for a busy run slot and then
|
|
7
|
+
* queued is counted once when the browser promotes it, and not yet when the
|
|
8
|
+
* server's inbox drain admits it. A replay is not counted, including a retry
|
|
9
|
+
* the server answers with the run it already started. A send routed to
|
|
10
|
+
* Builder or an MCP host never starts a local turn; it is reported with
|
|
11
|
+
* `routed_to` and no `turn_id`, also when the host has not confirmed it.
|
|
12
|
+
* Never the message text.
|
|
13
|
+
*/
|
|
14
|
+
export declare const TURN_SUBMITTED_EVENT = "agent_turn_submitted";
|
|
15
|
+
/**
|
|
16
|
+
* `system` is a turn the chat started on its own: a recovery, continuation,
|
|
17
|
+
* approval, setup replay, or background send. `app` came from app code through
|
|
18
|
+
* `sendToAgentChat`, including one deferred while the thread restored. `user`
|
|
19
|
+
* is everything else the person typed or picked.
|
|
20
|
+
*/
|
|
21
|
+
export type TurnInitiator = "user" | "app" | "system";
|
|
22
|
+
/** The surface a turn reports when nothing names one (the legacy `chat_submit` default). */
|
|
23
|
+
export declare const DEFAULT_TURN_SURFACE = "chat";
|
|
24
|
+
export interface TurnSubmittedReport {
|
|
25
|
+
readonly turnId: string;
|
|
26
|
+
readonly threadId?: string;
|
|
27
|
+
readonly runId?: string;
|
|
28
|
+
readonly surface: string;
|
|
29
|
+
readonly initiator: TurnInitiator;
|
|
30
|
+
readonly submitMessageId?: string;
|
|
31
|
+
readonly recoveryOfRunId?: string;
|
|
32
|
+
readonly requestMode?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface RoutedTurnSubmittedReport {
|
|
35
|
+
readonly routedTo: "builder" | "mcp_host";
|
|
36
|
+
readonly surface: string;
|
|
37
|
+
readonly submitMessageId?: string;
|
|
38
|
+
readonly requestMode?: string;
|
|
39
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One event per agent request the server accepts, carrying the turn id the
|
|
3
|
+
* server stores on that request's run so the two join. A turn's continuations
|
|
4
|
+
* are further requests: they repeat its `turn_id` as `initiator: "system"`, so
|
|
5
|
+
* count turns by distinct `turn_id`, and the turns people started by
|
|
6
|
+
* `initiator` `user` or `app`. A request refused for a busy run slot and then
|
|
7
|
+
* queued is counted once when the browser promotes it, and not yet when the
|
|
8
|
+
* server's inbox drain admits it. A replay is not counted, including a retry
|
|
9
|
+
* the server answers with the run it already started. A send routed to
|
|
10
|
+
* Builder or an MCP host never starts a local turn; it is reported with
|
|
11
|
+
* `routed_to` and no `turn_id`, also when the host has not confirmed it.
|
|
12
|
+
* Never the message text.
|
|
13
|
+
*/
|
|
14
|
+
export const TURN_SUBMITTED_EVENT = "agent_turn_submitted";
|
|
15
|
+
/** The surface a turn reports when nothing names one (the legacy `chat_submit` default). */
|
|
16
|
+
export const DEFAULT_TURN_SURFACE = "chat";
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* recovery handles it; this is the one signal that it did not, so a deploy whose
|
|
5
|
-
* chunks are gone for everyone stays visible.
|
|
6
|
-
*/
|
|
7
|
-
export declare const STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT = "agent-native:stale-chunk-recovery-exhausted";
|
|
8
|
-
export type StaleChunkRecoveryExhaustedReason = "cooldown" | "desktop";
|
|
1
|
+
import { STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT } from "../shared/route-chunk-recovery-bootstrap.js";
|
|
2
|
+
export { STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT };
|
|
3
|
+
export type StaleChunkRecoveryExhaustedReason = "cooldown" | "desktop" | "external-embed";
|
|
9
4
|
export interface StaleChunkRecoveryExhausted {
|
|
10
5
|
reason: StaleChunkRecoveryExhaustedReason;
|
|
11
6
|
}
|
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
import { CHUNK_RECOVERY_CACHE_BUSTER_PARAM, CHUNK_RECOVERY_ORIGINAL_HASH_PARAM, CHUNK_RECOVERY_PATH_SUFFIX, CHUNK_RECOVERY_QUERY_PARAM, CHUNK_RECOVERY_QUERY_VALUE, ROUTE_WARMUP_PRELOAD_ATTRIBUTE, STALE_CHUNK_RELOAD_AT_KEY, STALE_CHUNK_RELOAD_COOLDOWN_MS, } from "../shared/route-chunk-recovery-bootstrap.js";
|
|
1
|
+
import { CHUNK_RECOVERY_CACHE_BUSTER_PARAM, CHUNK_RECOVERY_ORIGINAL_HASH_PARAM, CHUNK_RECOVERY_PATH_SUFFIX, CHUNK_RECOVERY_QUERY_PARAM, CHUNK_RECOVERY_QUERY_VALUE, EXTERNAL_EMBED_WINDOW_KEY, ROUTE_WARMUP_PRELOAD_ATTRIBUTE, STALE_CHUNK_EXHAUSTED_KEY, STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT, STALE_CHUNK_RELOAD_AT_KEY, STALE_CHUNK_RELOAD_COOLDOWN_MS, } from "../shared/route-chunk-recovery-bootstrap.js";
|
|
2
2
|
import { recordNav } from "./recorder.js";
|
|
3
3
|
const INSTALL_KEY = "__agentNativeRouteChunkRecoveryInstalled";
|
|
4
4
|
const INTENDED_NAV_MAX_AGE_MS = 15_000;
|
|
5
|
-
const STALE_CHUNK_EXHAUSTED_KEY = "__agentNativeStaleChunkRecoveryExhausted";
|
|
6
5
|
const recoveryStates = new WeakMap();
|
|
7
|
-
|
|
8
|
-
* Fired on `window` the first time a stale chunk could not be recovered by a
|
|
9
|
-
* reload. The shared noise rules drop the raw dynamic-import failure because
|
|
10
|
-
* recovery handles it; this is the one signal that it did not, so a deploy whose
|
|
11
|
-
* chunks are gone for everyone stays visible.
|
|
12
|
-
*/
|
|
13
|
-
export const STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT = "agent-native:stale-chunk-recovery-exhausted";
|
|
6
|
+
export { STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT };
|
|
14
7
|
export function createRouteChunkRecoveryState() {
|
|
15
8
|
return {
|
|
16
9
|
intendedHref: null,
|
|
@@ -187,6 +180,9 @@ function isModuleAssetLoadFailure(event) {
|
|
|
187
180
|
function isAgentNativeDesktop(win) {
|
|
188
181
|
return /AgentNativeDesktop/i.test(win.navigator?.userAgent || "");
|
|
189
182
|
}
|
|
183
|
+
function isExternalEmbed(win) {
|
|
184
|
+
return Boolean(win[EXTERNAL_EMBED_WINDOW_KEY]);
|
|
185
|
+
}
|
|
190
186
|
function hasViteDevRecovery(win) {
|
|
191
187
|
if (win["__agentNativeViteDevRecoveryInstalled"] === true) {
|
|
192
188
|
return true;
|
|
@@ -269,6 +265,10 @@ export function reloadForStaleChunk(win = typeof window === "undefined" ? undefi
|
|
|
269
265
|
reportStaleChunkRecoveryExhausted(win, "desktop");
|
|
270
266
|
return false;
|
|
271
267
|
}
|
|
268
|
+
if (isExternalEmbed(win)) {
|
|
269
|
+
reportStaleChunkRecoveryExhausted(win, "external-embed");
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
272
|
const lastReloadAt = readStaleChunkReloadAt(win);
|
|
273
273
|
if (lastReloadAt > 0 &&
|
|
274
274
|
now - lastReloadAt <= STALE_CHUNK_RELOAD_COOLDOWN_MS) {
|
|
@@ -288,6 +288,8 @@ export function recoverFromStaleChunkError(error, win = typeof window === "undef
|
|
|
288
288
|
return reloadForStaleChunk(win);
|
|
289
289
|
}
|
|
290
290
|
function recoverToIntendedNavigation(win, state) {
|
|
291
|
+
if (isExternalEmbed(win))
|
|
292
|
+
return false;
|
|
291
293
|
const target = getFreshIntendedNavigation(state, win.location.href);
|
|
292
294
|
const sameCurrentTarget = isAgentNativeDesktop(win) &&
|
|
293
295
|
!target &&
|
|
@@ -8,6 +8,7 @@ export interface UseChatThreadListOptions {
|
|
|
8
8
|
includeExternal?: boolean;
|
|
9
9
|
/** Include archived threads. Defaults to false. */
|
|
10
10
|
includeArchived?: boolean;
|
|
11
|
+
includeHidden?: boolean;
|
|
11
12
|
/** Search text. Empty lists the whole set. */
|
|
12
13
|
query?: string;
|
|
13
14
|
/** When false nothing is fetched and the list is empty. Defaults to true. */
|
|
@@ -30,6 +31,7 @@ export interface UseChatThreadListResult {
|
|
|
30
31
|
refresh: () => Promise<void>;
|
|
31
32
|
/** Reports whether this invocation applied the complete refreshed window. */
|
|
32
33
|
refreshWithResult: () => Promise<ChatThreadListRefreshOutcome>;
|
|
34
|
+
hideThread: (id: string, hidden: boolean) => Promise<boolean>;
|
|
33
35
|
}
|
|
34
36
|
/** `pageSize` is not an integer between 1 and the server's page cap. */
|
|
35
37
|
export declare class ChatThreadListConfigError extends Error {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useLayoutEffect, useState } from "react";
|
|
2
2
|
import { agentNativePath } from "./api-path.js";
|
|
3
|
-
import { CHAT_THREADS_MAX_PAGE_SIZE, CHAT_THREADS_UPDATED_EVENT, ChatThreadListError, fetchChatThreadListPage, } from "./use-chat-threads.js";
|
|
3
|
+
import { CHAT_THREADS_MAX_PAGE_SIZE, CHAT_THREADS_UPDATED_EVENT, ChatThreadListError, fetchChatThreadListPage, hideChatThread, } from "./use-chat-threads.js";
|
|
4
4
|
const DEFAULT_PAGE_SIZE = 50;
|
|
5
5
|
/** `pageSize` is not an integer between 1 and the server's page cap. */
|
|
6
6
|
export class ChatThreadListConfigError extends Error {
|
|
@@ -48,7 +48,7 @@ function createListState(identity, enabled) {
|
|
|
48
48
|
* rather than collapsing to the first page.
|
|
49
49
|
*/
|
|
50
50
|
export function useChatThreadList(options) {
|
|
51
|
-
const { ownership, includeExternal = false, includeArchived = false, enabled = true, refreshOnThreadUpdates = true, } = options;
|
|
51
|
+
const { ownership, includeExternal = false, includeArchived = false, includeHidden = false, enabled = true, refreshOnThreadUpdates = true, } = options;
|
|
52
52
|
const apiUrl = options.apiUrl ?? agentNativePath("/_agent-native/agent-chat");
|
|
53
53
|
const pageSize = resolvePageSize(options.pageSize);
|
|
54
54
|
const query = options.query?.trim() ?? "";
|
|
@@ -63,6 +63,7 @@ export function useChatThreadList(options) {
|
|
|
63
63
|
pageSize,
|
|
64
64
|
includeExternal,
|
|
65
65
|
includeArchived,
|
|
66
|
+
includeHidden,
|
|
66
67
|
enabled,
|
|
67
68
|
]);
|
|
68
69
|
const [stored, setState] = useState(() => createListState(identity, enabled));
|
|
@@ -91,6 +92,7 @@ export function useChatThreadList(options) {
|
|
|
91
92
|
ownership,
|
|
92
93
|
includeExternal,
|
|
93
94
|
includeArchived,
|
|
95
|
+
includeHidden,
|
|
94
96
|
scope: scopeType && scopeId ? { type: scopeType, id: scopeId } : null,
|
|
95
97
|
}).then((result) => {
|
|
96
98
|
if (result.hasMore === undefined) {
|
|
@@ -106,6 +108,7 @@ export function useChatThreadList(options) {
|
|
|
106
108
|
apiUrl,
|
|
107
109
|
includeArchived,
|
|
108
110
|
includeExternal,
|
|
111
|
+
includeHidden,
|
|
109
112
|
ownership,
|
|
110
113
|
query,
|
|
111
114
|
scopeId,
|
|
@@ -223,6 +226,48 @@ export function useChatThreadList(options) {
|
|
|
223
226
|
return () => window.removeEventListener(CHAT_THREADS_UPDATED_EVENT, onUpdated);
|
|
224
227
|
}, [enabled, refresh, refreshOnThreadUpdates]);
|
|
225
228
|
const { threads, loading, loadingMore, error, hasMore } = state;
|
|
229
|
+
const hideThread = useCallback(async (id, hidden) => {
|
|
230
|
+
const previous = state.threads.find((thread) => thread.id === id);
|
|
231
|
+
update({
|
|
232
|
+
threads: state.threads.flatMap((thread) => {
|
|
233
|
+
if (thread.id !== id)
|
|
234
|
+
return [thread];
|
|
235
|
+
if (hidden && !includeHidden)
|
|
236
|
+
return [];
|
|
237
|
+
return [{ ...thread, hiddenAt: hidden ? Date.now() : null }];
|
|
238
|
+
}),
|
|
239
|
+
});
|
|
240
|
+
try {
|
|
241
|
+
return await hideChatThread(id, hidden, {
|
|
242
|
+
apiUrl,
|
|
243
|
+
scope: options.scope,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
// A newer refresh may have dropped a removed row because access was
|
|
248
|
+
// revoked, so the server decides whether it comes back.
|
|
249
|
+
if (hidden && !includeHidden)
|
|
250
|
+
void refreshWithResult();
|
|
251
|
+
else if (previous)
|
|
252
|
+
setState((current) => current.session === session
|
|
253
|
+
? {
|
|
254
|
+
...current,
|
|
255
|
+
threads: current.threads.map((thread) => thread.id === id
|
|
256
|
+
? { ...thread, hiddenAt: previous.hiddenAt }
|
|
257
|
+
: thread),
|
|
258
|
+
}
|
|
259
|
+
: current);
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
}, [
|
|
263
|
+
apiUrl,
|
|
264
|
+
includeHidden,
|
|
265
|
+
options.scope,
|
|
266
|
+
refreshWithResult,
|
|
267
|
+
session,
|
|
268
|
+
state.threads,
|
|
269
|
+
update,
|
|
270
|
+
]);
|
|
226
271
|
return {
|
|
227
272
|
threads,
|
|
228
273
|
loading,
|
|
@@ -232,5 +277,6 @@ export function useChatThreadList(options) {
|
|
|
232
277
|
loadMore,
|
|
233
278
|
refresh,
|
|
234
279
|
refreshWithResult,
|
|
280
|
+
hideThread,
|
|
235
281
|
};
|
|
236
282
|
}
|
|
@@ -24,6 +24,7 @@ export interface ChatThreadSummary {
|
|
|
24
24
|
scope: ChatThreadScope | null;
|
|
25
25
|
source?: ChatThreadSource | null;
|
|
26
26
|
pinnedAt?: number | null;
|
|
27
|
+
hiddenAt?: number | null;
|
|
27
28
|
archivedAt?: number | null;
|
|
28
29
|
/**
|
|
29
30
|
* Server-supplied on listed threads. Absent on optimistic local drafts,
|
|
@@ -51,6 +52,7 @@ export interface ChatThreadData {
|
|
|
51
52
|
scope: ChatThreadScope | null;
|
|
52
53
|
source?: ChatThreadSource | null;
|
|
53
54
|
pinnedAt?: number | null;
|
|
55
|
+
hiddenAt?: number | null;
|
|
54
56
|
archivedAt?: number | null;
|
|
55
57
|
}
|
|
56
58
|
export interface ChatThreadSnapshot {
|
|
@@ -76,6 +78,7 @@ export interface UseChatThreadsOptions {
|
|
|
76
78
|
browserTabId?: string;
|
|
77
79
|
routeThreadId?: string | null;
|
|
78
80
|
includeExternal?: boolean;
|
|
81
|
+
includeHidden?: boolean;
|
|
79
82
|
isolateHistoryByScope?: boolean;
|
|
80
83
|
/** Narrows the history list and search. Defaults to `all`. */
|
|
81
84
|
ownership?: ChatThreadOwnership;
|
|
@@ -99,6 +102,7 @@ export interface ChatThreadListPageRequest {
|
|
|
99
102
|
ownership?: ChatThreadOwnership;
|
|
100
103
|
includeExternal?: boolean;
|
|
101
104
|
includeArchived?: boolean;
|
|
105
|
+
includeHidden?: boolean;
|
|
102
106
|
scope?: ChatThreadScope | null;
|
|
103
107
|
}
|
|
104
108
|
/** A listed thread as the server returns it: ownership metadata always present. */
|
|
@@ -124,6 +128,14 @@ export interface ChatThreadListPage {
|
|
|
124
128
|
* of returned rows and use `hasMore`, rather than the requested limit.
|
|
125
129
|
*/
|
|
126
130
|
export declare function fetchChatThreadListPage(request?: ChatThreadListPageRequest): Promise<ChatThreadListPage>;
|
|
131
|
+
export declare class ChatThreadMutationError extends Error {
|
|
132
|
+
readonly status: number;
|
|
133
|
+
constructor(status: number);
|
|
134
|
+
}
|
|
135
|
+
export declare function hideChatThread(threadId: string, hidden: boolean, options?: {
|
|
136
|
+
apiUrl?: string;
|
|
137
|
+
scope?: ChatThreadScope | null;
|
|
138
|
+
}): Promise<boolean>;
|
|
127
139
|
export declare function appendChatThreadScopeParams(params: URLSearchParams, scope?: ChatThreadScope | null): void;
|
|
128
140
|
/**
|
|
129
141
|
* Tell every mounted chat history list the thread set or its sharing changed,
|
|
@@ -141,6 +153,7 @@ export declare function useChatThreads(apiUrl?: string, storageKey?: string, sco
|
|
|
141
153
|
deleteThread: (id: string) => Promise<void>;
|
|
142
154
|
detachThread: (threadId: string) => Promise<void>;
|
|
143
155
|
pinThread: (threadId: string, pinned: boolean) => Promise<boolean>;
|
|
156
|
+
hideThread: (threadId: string, hidden: boolean) => Promise<boolean>;
|
|
144
157
|
archiveThread: (threadId: string) => Promise<boolean>;
|
|
145
158
|
renameThread: (threadId: string, title: string) => Promise<boolean>;
|
|
146
159
|
forkThread: (sourceId: string, sourceSnapshot?: ChatThreadSnapshot | null) => Promise<string | null>;
|