@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
|
@@ -53,6 +53,7 @@ export interface ChatThread {
|
|
|
53
53
|
updatedAt: number;
|
|
54
54
|
scope: ChatThreadScope | null;
|
|
55
55
|
pinnedAt: number | null;
|
|
56
|
+
hiddenAt?: number | null;
|
|
56
57
|
archivedAt: number | null;
|
|
57
58
|
source: ChatThreadSource | null;
|
|
58
59
|
orgId: string | null;
|
|
@@ -83,6 +84,7 @@ export interface ChatThreadSummary {
|
|
|
83
84
|
updatedAt: number;
|
|
84
85
|
scope: ChatThreadScope | null;
|
|
85
86
|
pinnedAt: number | null;
|
|
87
|
+
hiddenAt?: number | null;
|
|
86
88
|
archivedAt: number | null;
|
|
87
89
|
source: ChatThreadSource | null;
|
|
88
90
|
orgId: string | null;
|
|
@@ -164,6 +166,8 @@ export interface ListThreadsOptions {
|
|
|
164
166
|
* (e.g. an "Archived" filter or restoring one via `setThreadArchived`).
|
|
165
167
|
*/
|
|
166
168
|
includeArchived?: boolean;
|
|
169
|
+
/** Include chats hidden by this caller. Defaults to false. */
|
|
170
|
+
includeHidden?: boolean;
|
|
167
171
|
/**
|
|
168
172
|
* Include connected and other-app threads. The HTTP chat list defaults this
|
|
169
173
|
* to false so each app shows its own local chats first; internal callers
|
|
@@ -184,6 +188,8 @@ export declare function searchThreads(ownerEmail: string, query: string, limit?:
|
|
|
184
188
|
orgId?: string | null;
|
|
185
189
|
/** See `ListThreadsOptions.includeArchived` — defaults to false. */
|
|
186
190
|
includeArchived?: boolean;
|
|
191
|
+
/** Include chats hidden by this caller. Defaults to false. */
|
|
192
|
+
includeHidden?: boolean;
|
|
187
193
|
/** See `ListThreadsOptions.includeExternal`. */
|
|
188
194
|
includeExternal?: boolean;
|
|
189
195
|
/** Current app id used by the local-only view. */
|
|
@@ -211,19 +217,30 @@ export declare function adoptThreadScopeIfUnscoped(id: string, ownerEmail: strin
|
|
|
211
217
|
* `null` to clear the scope (chat becomes general).
|
|
212
218
|
*/
|
|
213
219
|
export declare function setThreadScope(id: string, ownerEmail: string, scope: ChatThreadScope | null): Promise<void>;
|
|
214
|
-
export declare function
|
|
215
|
-
|
|
220
|
+
export declare function assertThreadOwner(thread: Pick<ChatThread, "ownerEmail">, ownerEmail: string): void;
|
|
221
|
+
export declare function renameThread(id: string, title: string, options: {
|
|
222
|
+
ownerEmail: string;
|
|
216
223
|
}): Promise<boolean>;
|
|
217
|
-
export declare function setThreadPinned(id: string, pinned: boolean, options
|
|
218
|
-
ownerEmail
|
|
224
|
+
export declare function setThreadPinned(id: string, pinned: boolean, options: {
|
|
225
|
+
ownerEmail: string;
|
|
226
|
+
orgId?: string | null;
|
|
219
227
|
}): Promise<boolean>;
|
|
220
|
-
export declare function
|
|
221
|
-
ownerEmail
|
|
228
|
+
export declare function setThreadHidden(id: string, hidden: boolean, options: {
|
|
229
|
+
ownerEmail: string;
|
|
230
|
+
orgId?: string | null;
|
|
231
|
+
}): Promise<boolean>;
|
|
232
|
+
export declare function setThreadArchived(id: string, archived: boolean, options: {
|
|
233
|
+
ownerEmail: string;
|
|
222
234
|
}): Promise<boolean>;
|
|
223
235
|
export interface UpdateThreadDataOptions {
|
|
224
236
|
preserveExistingQueuedMessages?: boolean;
|
|
225
237
|
preserveExistingTopLevelKeys?: boolean;
|
|
226
238
|
preserveCurrentMetadata?: boolean;
|
|
239
|
+
/**
|
|
240
|
+
* Preserve the title and its override from each current CAS revision; a
|
|
241
|
+
* blank title still fills from the merged visible prompts.
|
|
242
|
+
*/
|
|
243
|
+
preserveCurrentTitle?: boolean;
|
|
227
244
|
preserveCurrentTitleAndPreview?: boolean;
|
|
228
245
|
trustedRunSequenceResetIds?: ReadonlySet<string>;
|
|
229
246
|
onAnnotationConflict?: (conflict: ThreadAnnotationSnapshotConflict) => void;
|
|
@@ -13,7 +13,7 @@ import { fromMigrationEntries } from "../db/versioned-store-migrations.js";
|
|
|
13
13
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
14
14
|
import { getRequestOrgId } from "../server/request-context.js";
|
|
15
15
|
import { assertNoInlineImageBytes, stripInlineBytes, } from "../shared/inline-bytes.js";
|
|
16
|
-
import { hasCurrentOrgMembership, resolveAccess, } from "../sharing/access.js";
|
|
16
|
+
import { ForbiddenError, hasCurrentOrgMembership, resolveAccess, } from "../sharing/access.js";
|
|
17
17
|
import { registerShareableResource } from "../sharing/registry.js";
|
|
18
18
|
import { roleSatisfies } from "../sharing/schema.js";
|
|
19
19
|
import { emitChatThreadChange } from "./emitter.js";
|
|
@@ -161,6 +161,17 @@ export const chatThreadsStore = defineStore({
|
|
|
161
161
|
},
|
|
162
162
|
},
|
|
163
163
|
...fromMigrationEntries(CHAT_THREAD_SCHEMA_MIGRATIONS_TABLE, CHAT_THREAD_SCHEMA_MIGRATIONS, { data: [3] }),
|
|
164
|
+
{
|
|
165
|
+
name: "viewer-state",
|
|
166
|
+
sql: `CREATE TABLE IF NOT EXISTS chat_thread_viewer_state (
|
|
167
|
+
thread_id TEXT NOT NULL REFERENCES chat_threads(id) ON DELETE CASCADE,
|
|
168
|
+
user_email TEXT NOT NULL,
|
|
169
|
+
pinned_at BIGINT,
|
|
170
|
+
hidden_at BIGINT,
|
|
171
|
+
updated_at BIGINT NOT NULL,
|
|
172
|
+
PRIMARY KEY (thread_id, user_email)
|
|
173
|
+
)`,
|
|
174
|
+
},
|
|
164
175
|
],
|
|
165
176
|
});
|
|
166
177
|
function ensureTable() {
|
|
@@ -632,6 +643,7 @@ function rowToThread(r) {
|
|
|
632
643
|
updatedAt: Number(r.updated_at),
|
|
633
644
|
scope: readScope(r),
|
|
634
645
|
pinnedAt: readNullableNumber(r.pinned_at),
|
|
646
|
+
hiddenAt: readNullableNumber(r.hidden_at),
|
|
635
647
|
archivedAt: readNullableNumber(r.archived_at),
|
|
636
648
|
source: readSource(r),
|
|
637
649
|
orgId: r.org_id ?? null,
|
|
@@ -662,6 +674,7 @@ function rowToSummary(r) {
|
|
|
662
674
|
updatedAt: Number(r.updated_at),
|
|
663
675
|
scope: readScope(r),
|
|
664
676
|
pinnedAt: readNullableNumber(r.pinned_at),
|
|
677
|
+
hiddenAt: readNullableNumber(r.hidden_at),
|
|
665
678
|
archivedAt: readNullableNumber(r.archived_at),
|
|
666
679
|
source: readSource(r),
|
|
667
680
|
orgId: r.org_id ?? null,
|
|
@@ -765,7 +778,21 @@ export async function resolveThreadAccess(userEmail, threadId, minRole = "viewer
|
|
|
765
778
|
return null;
|
|
766
779
|
if (!(await hasThreadAccess(userEmail, threadId, minRole, ctx)))
|
|
767
780
|
return null;
|
|
768
|
-
|
|
781
|
+
const thread = await getThread(threadId);
|
|
782
|
+
if (!thread)
|
|
783
|
+
return null;
|
|
784
|
+
if (normalizeAccessEmail(thread.ownerEmail) === normalizeAccessEmail(userEmail)) {
|
|
785
|
+
return { ...thread, hiddenAt: null };
|
|
786
|
+
}
|
|
787
|
+
const { rows } = await getDbExec().execute({
|
|
788
|
+
sql: `SELECT pinned_at, hidden_at FROM chat_thread_viewer_state WHERE thread_id = ? AND user_email = ?`,
|
|
789
|
+
args: [threadId, normalizeAccessEmail(userEmail)],
|
|
790
|
+
});
|
|
791
|
+
return {
|
|
792
|
+
...thread,
|
|
793
|
+
pinnedAt: readNullableNumber(rows[0]?.pinned_at),
|
|
794
|
+
hiddenAt: readNullableNumber(rows[0]?.hidden_at),
|
|
795
|
+
};
|
|
769
796
|
}
|
|
770
797
|
export async function resolveThreadAccessScope(userEmail, threadId, minRole = "viewer", ctx = {}) {
|
|
771
798
|
if (!userEmail || !threadId)
|
|
@@ -792,9 +819,10 @@ export async function resolveThreadsAccess(userEmail, threadIds, ctx = {}) {
|
|
|
792
819
|
const access = await chatThreadAccessSql(userEmail, ctx.orgId);
|
|
793
820
|
const client = getDbExec();
|
|
794
821
|
const placeholders = ids.map(() => "?").join(", ");
|
|
822
|
+
const projection = chatThreadViewerSelect(THREAD_COLUMNS, userEmail);
|
|
795
823
|
const { rows } = await client.execute({
|
|
796
|
-
sql: `SELECT ${
|
|
797
|
-
args: [...ids, ...access.args],
|
|
824
|
+
sql: `SELECT ${projection.sql} FROM chat_threads WHERE id IN (${placeholders}) AND ${access.sql}`,
|
|
825
|
+
args: [...projection.args, ...ids, ...access.args],
|
|
798
826
|
});
|
|
799
827
|
for (const row of rows) {
|
|
800
828
|
const thread = rowToThread(row);
|
|
@@ -948,6 +976,14 @@ async function chatThreadAccessSql(userEmail, orgId) {
|
|
|
948
976
|
function normalizeAccessEmail(userEmail) {
|
|
949
977
|
return userEmail.trim().toLowerCase();
|
|
950
978
|
}
|
|
979
|
+
function chatThreadViewerSelect(columns, userEmail) {
|
|
980
|
+
const email = normalizeAccessEmail(userEmail);
|
|
981
|
+
const viewerValue = (column) => `(SELECT ${column} FROM chat_thread_viewer_state WHERE thread_id = chat_threads.id AND user_email = ?)`;
|
|
982
|
+
return {
|
|
983
|
+
sql: `${columns.replace("pinned_at", `CASE WHEN LOWER(owner_email) = ? THEN pinned_at ELSE ${viewerValue("pinned_at")} END AS pinned_at`)}, CASE WHEN LOWER(owner_email) = ? THEN NULL ELSE ${viewerValue("hidden_at")} END AS hidden_at`,
|
|
984
|
+
args: [email, email, email, email],
|
|
985
|
+
};
|
|
986
|
+
}
|
|
951
987
|
/**
|
|
952
988
|
* Summary projection plus the caller's access basis, derived from the same
|
|
953
989
|
* owner and per-user-share comparisons the ACL uses. Anything that is neither
|
|
@@ -955,9 +991,10 @@ function normalizeAccessEmail(userEmail) {
|
|
|
955
991
|
*/
|
|
956
992
|
function chatThreadSummarySelect(userEmail) {
|
|
957
993
|
const normalizedEmail = normalizeAccessEmail(userEmail);
|
|
994
|
+
const viewer = chatThreadViewerSelect(SUMMARY_COLUMNS, userEmail);
|
|
958
995
|
return {
|
|
959
|
-
sql: `${
|
|
960
|
-
args: [normalizedEmail, normalizedEmail],
|
|
996
|
+
sql: `${viewer.sql}, CASE WHEN LOWER(owner_email) = ? THEN 'owner' WHEN EXISTS (SELECT 1 FROM chat_thread_shares WHERE chat_thread_shares.resource_id = chat_threads.id AND chat_thread_shares.principal_type = 'user' AND LOWER(chat_thread_shares.principal_id) = ?) THEN 'direct' ELSE 'organization' END AS access_basis`,
|
|
997
|
+
args: [...viewer.args, normalizedEmail, normalizedEmail],
|
|
961
998
|
};
|
|
962
999
|
}
|
|
963
1000
|
function chatThreadOwnershipSql(userEmail, ownership) {
|
|
@@ -996,6 +1033,10 @@ export async function listThreads(ownerEmail, options = {}, legacyOffset) {
|
|
|
996
1033
|
filters.push(ownershipFilter.sql);
|
|
997
1034
|
args.push(...ownershipFilter.args);
|
|
998
1035
|
}
|
|
1036
|
+
if (!opts.includeHidden) {
|
|
1037
|
+
filters.push(`(LOWER(owner_email) = ? OR NOT EXISTS (SELECT 1 FROM chat_thread_viewer_state WHERE thread_id = chat_threads.id AND user_email = ? AND hidden_at IS NOT NULL))`);
|
|
1038
|
+
args.push(normalizeAccessEmail(ownerEmail), normalizeAccessEmail(ownerEmail));
|
|
1039
|
+
}
|
|
999
1040
|
if (!opts.includeArchived) {
|
|
1000
1041
|
filters.push(`archived_at IS NULL`);
|
|
1001
1042
|
}
|
|
@@ -1015,7 +1056,7 @@ export async function listThreads(ownerEmail, options = {}, legacyOffset) {
|
|
|
1015
1056
|
}
|
|
1016
1057
|
args.push(limit, offset);
|
|
1017
1058
|
const { rows } = await client.execute({
|
|
1018
|
-
sql: `SELECT ${select.sql} FROM chat_threads WHERE ${filters.join(" AND ")} ORDER BY
|
|
1059
|
+
sql: `SELECT ${select.sql} FROM chat_threads WHERE ${filters.join(" AND ")} ORDER BY pinned_at DESC NULLS LAST, updated_at DESC, id ASC LIMIT ? OFFSET ?`,
|
|
1019
1060
|
args,
|
|
1020
1061
|
});
|
|
1021
1062
|
return rows
|
|
@@ -1051,6 +1092,10 @@ export async function searchThreads(ownerEmail, query, limit = 50, options = {})
|
|
|
1051
1092
|
filters.push(ownershipFilter.sql);
|
|
1052
1093
|
args.push(...ownershipFilter.args);
|
|
1053
1094
|
}
|
|
1095
|
+
if (!options.includeHidden) {
|
|
1096
|
+
filters.push(`(LOWER(owner_email) = ? OR NOT EXISTS (SELECT 1 FROM chat_thread_viewer_state WHERE thread_id = chat_threads.id AND user_email = ? AND hidden_at IS NOT NULL))`);
|
|
1097
|
+
args.push(normalizeAccessEmail(ownerEmail), normalizeAccessEmail(ownerEmail));
|
|
1098
|
+
}
|
|
1054
1099
|
if (!options.includeArchived) {
|
|
1055
1100
|
filters.push(`archived_at IS NULL`);
|
|
1056
1101
|
}
|
|
@@ -1067,7 +1112,7 @@ export async function searchThreads(ownerEmail, query, limit = 50, options = {})
|
|
|
1067
1112
|
}
|
|
1068
1113
|
args.push(limit, options.offset ?? 0);
|
|
1069
1114
|
const { rows } = await client.execute({
|
|
1070
|
-
sql: `SELECT ${select.sql} FROM chat_threads WHERE ${filters.join(" AND ")} ORDER BY
|
|
1115
|
+
sql: `SELECT ${select.sql} FROM chat_threads WHERE ${filters.join(" AND ")} ORDER BY pinned_at DESC NULLS LAST, updated_at DESC, id ASC LIMIT ? OFFSET ?`,
|
|
1071
1116
|
args,
|
|
1072
1117
|
});
|
|
1073
1118
|
return rows
|
|
@@ -1131,61 +1176,90 @@ export async function setThreadScope(id, ownerEmail, scope) {
|
|
|
1131
1176
|
});
|
|
1132
1177
|
emitChatThreadChange(id);
|
|
1133
1178
|
}
|
|
1134
|
-
export
|
|
1179
|
+
export function assertThreadOwner(thread, ownerEmail) {
|
|
1180
|
+
if (!ownerEmail?.trim() ||
|
|
1181
|
+
thread.ownerEmail.toLowerCase() !== ownerEmail.trim().toLowerCase()) {
|
|
1182
|
+
throw new ForbiddenError();
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
async function getOwnedThread(id, ownerEmail) {
|
|
1186
|
+
const thread = await getThread(id);
|
|
1187
|
+
if (thread)
|
|
1188
|
+
assertThreadOwner(thread, ownerEmail);
|
|
1189
|
+
return thread;
|
|
1190
|
+
}
|
|
1191
|
+
export async function renameThread(id, title, options) {
|
|
1135
1192
|
const nextTitle = normalizeThreadTitle(title);
|
|
1136
1193
|
if (!nextTitle)
|
|
1137
1194
|
return false;
|
|
1138
1195
|
return await withThreadDataLock(id, async () => {
|
|
1139
|
-
const thread = await
|
|
1196
|
+
const thread = await getOwnedThread(id, options.ownerEmail);
|
|
1140
1197
|
if (!thread)
|
|
1141
1198
|
return false;
|
|
1142
|
-
if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {
|
|
1143
|
-
return false;
|
|
1144
|
-
}
|
|
1145
1199
|
const repo = parseThreadData(thread.threadData);
|
|
1146
1200
|
repo._titleOverride = nextTitle;
|
|
1147
|
-
await updateThreadData(id, JSON.stringify(repo), nextTitle, thread.preview, thread.messageCount);
|
|
1148
|
-
return true;
|
|
1201
|
+
return await updateThreadData(id, JSON.stringify(repo), nextTitle, thread.preview, thread.messageCount);
|
|
1149
1202
|
});
|
|
1150
1203
|
}
|
|
1151
|
-
|
|
1204
|
+
async function setThreadViewerState(id, field, enabled, options) {
|
|
1205
|
+
const email = normalizeAccessEmail(options.ownerEmail);
|
|
1206
|
+
const { rows } = await getDbExec().execute({
|
|
1207
|
+
sql: `SELECT id, owner_email FROM chat_threads WHERE id = ?`,
|
|
1208
|
+
args: [id],
|
|
1209
|
+
});
|
|
1210
|
+
const thread = rows[0];
|
|
1211
|
+
if (!thread)
|
|
1212
|
+
return false;
|
|
1213
|
+
if (!email || normalizeAccessEmail(String(thread.owner_email)) === email) {
|
|
1214
|
+
throw new ForbiddenError();
|
|
1215
|
+
}
|
|
1216
|
+
if (!(await hasThreadAccess(email, id, "viewer", {
|
|
1217
|
+
orgId: (options.orgId === undefined ? getRequestOrgId() : options.orgId) ??
|
|
1218
|
+
undefined,
|
|
1219
|
+
}))) {
|
|
1220
|
+
throw new ForbiddenError();
|
|
1221
|
+
}
|
|
1222
|
+
const now = Math.max(Date.now(), 1);
|
|
1223
|
+
await getDbExec().execute({
|
|
1224
|
+
sql: `INSERT INTO chat_thread_viewer_state (thread_id, user_email, ${field}, updated_at)
|
|
1225
|
+
VALUES (?, ?, ?, ?)
|
|
1226
|
+
ON CONFLICT (thread_id, user_email) DO UPDATE SET ${field} = EXCLUDED.${field}, updated_at = EXCLUDED.updated_at`,
|
|
1227
|
+
args: [id, email, enabled ? now : null, now],
|
|
1228
|
+
});
|
|
1229
|
+
emitChatThreadChange(id);
|
|
1230
|
+
return true;
|
|
1231
|
+
}
|
|
1232
|
+
export async function setThreadPinned(id, pinned, options) {
|
|
1152
1233
|
await ensureTable();
|
|
1153
1234
|
const client = getDbExec();
|
|
1154
1235
|
const now = Math.max(Date.now(), 1);
|
|
1155
|
-
const args = [pinned ? now : null, id];
|
|
1156
|
-
let ownerFilter = "";
|
|
1157
|
-
if (options.ownerEmail) {
|
|
1158
|
-
ownerFilter = " AND owner_email = ?";
|
|
1159
|
-
args.push(options.ownerEmail);
|
|
1160
|
-
}
|
|
1161
1236
|
const result = await client.execute({
|
|
1162
|
-
sql: `UPDATE chat_threads SET pinned_at = ? WHERE id =
|
|
1163
|
-
args,
|
|
1237
|
+
sql: `UPDATE chat_threads SET pinned_at = ? WHERE id = ? AND LOWER(owner_email) = LOWER(?)`,
|
|
1238
|
+
args: [pinned ? now : null, id, options.ownerEmail.trim()],
|
|
1164
1239
|
});
|
|
1165
1240
|
if (result.rowsAffected > 0) {
|
|
1166
1241
|
emitChatThreadChange(id);
|
|
1167
1242
|
return true;
|
|
1168
1243
|
}
|
|
1169
|
-
return
|
|
1244
|
+
return setThreadViewerState(id, "pinned_at", pinned, options);
|
|
1170
1245
|
}
|
|
1171
|
-
export async function
|
|
1246
|
+
export async function setThreadHidden(id, hidden, options) {
|
|
1247
|
+
await ensureTable();
|
|
1248
|
+
return setThreadViewerState(id, "hidden_at", hidden, options);
|
|
1249
|
+
}
|
|
1250
|
+
export async function setThreadArchived(id, archived, options) {
|
|
1172
1251
|
await ensureTable();
|
|
1173
1252
|
const client = getDbExec();
|
|
1174
1253
|
const now = Math.max(Date.now(), 1);
|
|
1175
|
-
const args = [archived ? now : null, id];
|
|
1176
|
-
let ownerFilter = "";
|
|
1177
|
-
if (options.ownerEmail) {
|
|
1178
|
-
ownerFilter = " AND owner_email = ?";
|
|
1179
|
-
args.push(options.ownerEmail);
|
|
1180
|
-
}
|
|
1181
1254
|
const result = await client.execute({
|
|
1182
|
-
sql: `UPDATE chat_threads SET archived_at = ? WHERE id =
|
|
1183
|
-
args,
|
|
1255
|
+
sql: `UPDATE chat_threads SET archived_at = ? WHERE id = ? AND LOWER(owner_email) = LOWER(?)`,
|
|
1256
|
+
args: [archived ? now : null, id, options.ownerEmail.trim()],
|
|
1184
1257
|
});
|
|
1185
1258
|
if (result.rowsAffected > 0) {
|
|
1186
1259
|
emitChatThreadChange(id);
|
|
1187
1260
|
return true;
|
|
1188
1261
|
}
|
|
1262
|
+
await getOwnedThread(id, options.ownerEmail);
|
|
1189
1263
|
return false;
|
|
1190
1264
|
}
|
|
1191
1265
|
export class InlineAttachmentDataNotPersistableError extends Error {
|
|
@@ -1238,6 +1312,14 @@ export async function updateThreadData(id, threadData, title, preview, messageCo
|
|
|
1238
1312
|
trustedRunSequenceResetIds: options.trustedRunSequenceResetIds,
|
|
1239
1313
|
onAnnotationConflict: (conflict) => annotationConflicts.push(conflict),
|
|
1240
1314
|
});
|
|
1315
|
+
if (options.preserveCurrentTitle) {
|
|
1316
|
+
if (Object.prototype.hasOwnProperty.call(safeCurrentThreadData, "_titleOverride")) {
|
|
1317
|
+
merged._titleOverride = safeCurrentThreadData._titleOverride;
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
delete merged._titleOverride;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1241
1323
|
// Client snapshots can predate their upload URL, so inline bytes become a
|
|
1242
1324
|
// visible placeholder rather than failing the save; legacy rows are
|
|
1243
1325
|
// scrubbed on their next write.
|
|
@@ -1251,13 +1333,14 @@ export async function updateThreadData(id, threadData, title, preview, messageCo
|
|
|
1251
1333
|
const preserveCurrentMetadata = options.preserveCurrentMetadata;
|
|
1252
1334
|
const preserveCurrentTitleAndPreview = options.preserveCurrentTitleAndPreview;
|
|
1253
1335
|
const snapshotMetaForBlankFields = !options.preserveCurrentMetadata &&
|
|
1254
|
-
options.preserveCurrentTitleAndPreview
|
|
1336
|
+
(options.preserveCurrentTitleAndPreview ||
|
|
1337
|
+
options.preserveCurrentTitle) &&
|
|
1255
1338
|
(!current.title.trim() || !current.preview.trim())
|
|
1256
1339
|
? extractThreadMeta(merged)
|
|
1257
1340
|
: undefined;
|
|
1258
1341
|
const nextTitle = preserveCurrentMetadata
|
|
1259
1342
|
? current.title
|
|
1260
|
-
: preserveCurrentTitleAndPreview
|
|
1343
|
+
: preserveCurrentTitleAndPreview || options.preserveCurrentTitle
|
|
1261
1344
|
? current.title.trim()
|
|
1262
1345
|
? current.title
|
|
1263
1346
|
: snapshotMetaForBlankFields?.title || current.title
|
|
@@ -403,7 +403,7 @@ const FUNCTION_URL_CORS = JSON.stringify({
|
|
|
403
403
|
],
|
|
404
404
|
AllowMethods: ["POST"],
|
|
405
405
|
AllowOrigins: ["*"],
|
|
406
|
-
ExposeHeaders: ["x-dispatch-mode", "x-run-id"],
|
|
406
|
+
ExposeHeaders: ["x-dispatch-mode", "x-run-id", "x-turn-id"],
|
|
407
407
|
MaxAge: 600,
|
|
408
408
|
});
|
|
409
409
|
const FUNCTION_URL_PERMISSIONS = [
|
|
@@ -20,7 +20,7 @@ export { buildDynamicAgentSuggestions, dedupeSuggestions, mergeAgentSuggestions,
|
|
|
20
20
|
export { clearChatStorage } from "../chat/storage.js";
|
|
21
21
|
export { isAssistantChatHistoryVersion, coerceAssistantChatHistoryDate, type AssistantChatHistoryDate, type AssistantChatHistoryVersion, } from "../chat/assistant-chat-history-version.js";
|
|
22
22
|
export { ChatThreadListConfigError, useChatThreadList, type UseChatThreadListOptions, type UseChatThreadListResult, type ChatThreadListRefreshOutcome, } from "../use-chat-thread-list.js";
|
|
23
|
-
export { ChatThreadListError, fetchChatThreadListPage, notifyChatThreadsUpdated, useChatThreads, type ChatThreadAccessBasis, type ChatThreadListPage, type ChatThreadListRow, type ChatThreadListPageRequest, type ChatThreadOwnership, type ChatThreadVisibility, type ChatThreadScope, type ChatThreadSnapshot, type ChatThreadSummary, type ChatThreadData, type ChatThreadShareLink, type ChatThreadShareState, type UseChatThreadsOptions, } from "../use-chat-threads.js";
|
|
23
|
+
export { ChatThreadListError, ChatThreadMutationError, hideChatThread, fetchChatThreadListPage, notifyChatThreadsUpdated, useChatThreads, type ChatThreadAccessBasis, type ChatThreadListPage, type ChatThreadListRow, type ChatThreadListPageRequest, type ChatThreadOwnership, type ChatThreadVisibility, type ChatThreadScope, type ChatThreadSnapshot, type ChatThreadSummary, type ChatThreadData, type ChatThreadShareLink, type ChatThreadShareState, type UseChatThreadsOptions, } from "../use-chat-threads.js";
|
|
24
24
|
export { type AgentCallProgress, type ContentPart, } from "../sse-event-processor.js";
|
|
25
25
|
export { useRunStuckDetection, useAbortRun, type RunStuckState, type UseRunStuckDetectionOptions, } from "../use-run-stuck-detection.js";
|
|
26
26
|
export { AGENT_CHAT_HOME_HANDOFF_TTL_MS, AGENT_CHAT_VIEW_TRANSITION_CLASS, AGENT_CHAT_VIEW_TRANSITION_NAME, consumeAgentChatHomeHandoff, getAgentChatViewTransitionStyle, isAgentChatHomeHandoffActive, markAgentChatHomeHandoff, navigateWithAgentChatViewTransition, startAgentChatViewTransition, supportsAgentChatViewTransition, type AgentChatHomeHandoffOptions, type AgentChatViewTransition, type AgentChatViewTransitionOptions, } from "../chat-view-transition.js";
|
|
@@ -28,6 +28,7 @@ export { useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, type UseAgentCha
|
|
|
28
28
|
export { AGENT_SIDEBAR_DEFAULT_MAX_WIDTH, AGENT_SIDEBAR_MIN_WIDTH, AGENT_SIDEBAR_WIDE_WIDTH_RATIO, clampAgentSidebarWidth, consumeAgentSidebarUrlOpenOverride, getAgentSidebarMaxWidth, getAgentSidebarWideWidth, dispatchAgentSidebarStateChange, getInitialAgentSidebarOpen, requestAgentSidebarOpen, subscribeAgentSidebarUrlChanges, SIDEBAR_STATE_CHANGE_EVENT, setAgentSidebarOpenPreference, type AgentSidebarStateChangeDetail, type AgentSidebarStateMode, type AgentSidebarStateSource, } from "../agent-sidebar-state.js";
|
|
29
29
|
export * from "../chat/agentkit-protocol.js";
|
|
30
30
|
export { createAgentNativeAgentKitTransport, type CreateAgentNativeAgentKitTransportOptions, } from "../chat/agentkit-agent-native.js";
|
|
31
|
+
export { trackTurnSubmitted } from "../chat/turn-submitted-telemetry.js";
|
|
31
32
|
export { AGENT_CHAT_RUNNING_EVENT, dispatchAgentChatRunning, resolveAgentChatRunningThreadId, useAgentChatRunningThreads, type AgentChatPresentationPhase, type AgentChatRunningEventDetail, type AgentChatRunningThreadsState, type UseAgentChatRunningThreadsOptions, } from "../use-agent-chat-running-threads.js";
|
|
32
33
|
export { CHAT_FIRST_APP_LAYOUT_STORAGE_KEY, CHAT_FIRST_DEFAULT_APP_IDS, CHAT_FIRST_SURFACE_TABS_STORAGE_KEY, CHAT_FIRST_SURFACE_PANEL_STORAGE_KEY, CHAT_FIRST_SURFACE_WIDTH_DEFAULT, CHAT_FIRST_SURFACE_WIDTH_MIN, clampChatFirstSurfaceWidth, CHAT_FIRST_MODE_STORAGE_KEY, CHAT_FIRST_MODE_CHANGED_EVENT, CHAT_FIRST_OPEN_APP_EVENT, CHAT_FIRST_OPEN_BROWSER_EVENT, CHAT_FIRST_WATCH_SESSION_EVENT, CHAT_FIRST_SURFACE_CATALOG, chatFirstSurfaceTabId, closeChatFirstSessionWatch, emitChatFirstOpenApp, emitChatFirstOpenBrowser, emitChatFirstSessionWatch, getChatFirstSessionWatchStore, getChatFirstSurfaceTabsStore, getChatFirstSurfacePanelStore, orderChatFirstAppIds, readChatFirstAppLayout, resolveChatFirstAppTarget, resolveChatFirstBrowserTarget, resolveChatFirstSessionId, normalizeChatFirstSessionReference, readChatFirstMode, readChatFirstModeState, readChatFirstSurfaceWidth, subscribeChatFirstOpenApp, subscribeChatFirstOpenBrowser, subscribeChatFirstSessionWatch, useChatFirstSessionWatch, useChatFirstSurfaceResize, useChatFirstSurfacePanel, useChatFirstSurfaceTabs, writeChatFirstAppLayout, writeChatFirstMode, writeChatFirstSurfaceWidth, type ChatFirstAppLayoutPreference, type ChatFirstModeReadResult, type ChatFirstAppRegistration, type ChatFirstAppResolution, type ChatFirstAppTarget, type ChatFirstAppSurfacePlacement, type ChatFirstAgentActivity, type ChatFirstAgentActivityStatus, type ChatFirstOpenAppDelivery, type ChatFirstOpenAppDetail, type ChatFirstOpenBrowserDetail, type ChatFirstBrowserResolution, type ChatFirstSessionKind, type ChatFirstSessionReference, type ChatFirstSessionWatchDelivery, type ChatFirstSessionWatchState, type ChatFirstSessionWatchStore, type ChatFirstSurfaceKind, type ChatFirstSurfaceTab, type ChatFirstSurfacePanelState, type ChatFirstSurfacePanelStore, type ChatFirstSurfaceTabsState, type ChatFirstSurfaceTabsStore, } from "../chat-first-state.js";
|
|
33
34
|
export { useVoiceProviderStatus } from "../voice-provider-status.js";
|
|
@@ -20,13 +20,14 @@ export { buildDynamicAgentSuggestions, dedupeSuggestions, mergeAgentSuggestions,
|
|
|
20
20
|
export { clearChatStorage } from "../chat/storage.js";
|
|
21
21
|
export { isAssistantChatHistoryVersion, coerceAssistantChatHistoryDate, } from "../chat/assistant-chat-history-version.js";
|
|
22
22
|
export { ChatThreadListConfigError, useChatThreadList, } from "../use-chat-thread-list.js";
|
|
23
|
-
export { ChatThreadListError, fetchChatThreadListPage, notifyChatThreadsUpdated, useChatThreads, } from "../use-chat-threads.js";
|
|
23
|
+
export { ChatThreadListError, ChatThreadMutationError, hideChatThread, fetchChatThreadListPage, notifyChatThreadsUpdated, useChatThreads, } from "../use-chat-threads.js";
|
|
24
24
|
export { useRunStuckDetection, useAbortRun, } from "../use-run-stuck-detection.js";
|
|
25
25
|
export { AGENT_CHAT_HOME_HANDOFF_TTL_MS, AGENT_CHAT_VIEW_TRANSITION_CLASS, AGENT_CHAT_VIEW_TRANSITION_NAME, consumeAgentChatHomeHandoff, getAgentChatViewTransitionStyle, isAgentChatHomeHandoffActive, markAgentChatHomeHandoff, navigateWithAgentChatViewTransition, startAgentChatViewTransition, supportsAgentChatViewTransition, } from "../chat-view-transition.js";
|
|
26
26
|
export { useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, } from "../use-agent-chat-home-handoff.js";
|
|
27
27
|
export { AGENT_SIDEBAR_DEFAULT_MAX_WIDTH, AGENT_SIDEBAR_MIN_WIDTH, AGENT_SIDEBAR_WIDE_WIDTH_RATIO, clampAgentSidebarWidth, consumeAgentSidebarUrlOpenOverride, getAgentSidebarMaxWidth, getAgentSidebarWideWidth, dispatchAgentSidebarStateChange, getInitialAgentSidebarOpen, requestAgentSidebarOpen, subscribeAgentSidebarUrlChanges, SIDEBAR_STATE_CHANGE_EVENT, setAgentSidebarOpenPreference, } from "../agent-sidebar-state.js";
|
|
28
28
|
export * from "../chat/agentkit-protocol.js";
|
|
29
29
|
export { createAgentNativeAgentKitTransport, } from "../chat/agentkit-agent-native.js";
|
|
30
|
+
export { trackTurnSubmitted } from "../chat/turn-submitted-telemetry.js";
|
|
30
31
|
export { AGENT_CHAT_RUNNING_EVENT, dispatchAgentChatRunning, resolveAgentChatRunningThreadId, useAgentChatRunningThreads, } from "../use-agent-chat-running-threads.js";
|
|
31
32
|
export { CHAT_FIRST_APP_LAYOUT_STORAGE_KEY, CHAT_FIRST_DEFAULT_APP_IDS, CHAT_FIRST_SURFACE_TABS_STORAGE_KEY, CHAT_FIRST_SURFACE_PANEL_STORAGE_KEY, CHAT_FIRST_SURFACE_WIDTH_DEFAULT, CHAT_FIRST_SURFACE_WIDTH_MIN, clampChatFirstSurfaceWidth, CHAT_FIRST_MODE_STORAGE_KEY, CHAT_FIRST_MODE_CHANGED_EVENT, CHAT_FIRST_OPEN_APP_EVENT, CHAT_FIRST_OPEN_BROWSER_EVENT, CHAT_FIRST_WATCH_SESSION_EVENT, CHAT_FIRST_SURFACE_CATALOG, chatFirstSurfaceTabId, closeChatFirstSessionWatch, emitChatFirstOpenApp, emitChatFirstOpenBrowser, emitChatFirstSessionWatch, getChatFirstSessionWatchStore, getChatFirstSurfaceTabsStore, getChatFirstSurfacePanelStore, orderChatFirstAppIds, readChatFirstAppLayout, resolveChatFirstAppTarget, resolveChatFirstBrowserTarget, resolveChatFirstSessionId, normalizeChatFirstSessionReference, readChatFirstMode, readChatFirstModeState, readChatFirstSurfaceWidth, subscribeChatFirstOpenApp, subscribeChatFirstOpenBrowser, subscribeChatFirstSessionWatch, useChatFirstSessionWatch, useChatFirstSurfaceResize, useChatFirstSurfacePanel, useChatFirstSurfaceTabs, writeChatFirstAppLayout, writeChatFirstMode, writeChatFirstSurfaceWidth, } from "../chat-first-state.js";
|
|
32
33
|
export { useVoiceProviderStatus } from "../voice-provider-status.js";
|
|
@@ -182,6 +182,8 @@ export interface ParsedSubmitChat {
|
|
|
182
182
|
submissionExpiresAt?: number;
|
|
183
183
|
usageLabel?: string;
|
|
184
184
|
approvedToolCalls?: string[];
|
|
185
|
+
/** The sender's `preset`, reported as the turn's analytics surface. */
|
|
186
|
+
surface?: string;
|
|
185
187
|
}
|
|
186
188
|
export declare function parseSubmitChatMessage(event: MessageEvent): ParsedSubmitChat | null;
|
|
187
189
|
export declare function routesToCodeFrame(opts: Pick<AgentChatMessage, "type" | "requiresCode" | "approvedToolCalls">): boolean;
|
|
@@ -4,6 +4,8 @@ import { trackEvent } from "./analytics.js";
|
|
|
4
4
|
import { agentNativePath } from "./api-path.js";
|
|
5
5
|
import { readClientAppState, writeClientAppState, } from "./application-state.js";
|
|
6
6
|
import { isInBuilderFrame, isTrustedBuilderMessage, sendToBuilderChat, } from "./builder-frame.js";
|
|
7
|
+
import { trackRoutedTurnSubmitted } from "./chat/turn-submitted-telemetry.js";
|
|
8
|
+
import { DEFAULT_TURN_SURFACE } from "./chat/turn-submitted.js";
|
|
7
9
|
import { isEmbedAuthActive, isEmbedMcpChatBridgeActive, markEmbedMcpChatBridgeActive, readEmbedMcpChatBridgeFlagFromUrl, } from "./embed-auth.js";
|
|
8
10
|
import { getFramePostMessageTargetOrigin, isTrustedFrameMessage, } from "./frame.js";
|
|
9
11
|
import { isOpenAiMcpAppHost, sendMcpAppHostMessage, } from "./mcp-app-host.js";
|
|
@@ -782,6 +784,7 @@ export function parseSubmitChatMessage(event) {
|
|
|
782
784
|
: {}),
|
|
783
785
|
usageLabel: nonEmptyString(raw.usageLabel),
|
|
784
786
|
approvedToolCalls: parseSubmitChatApprovedToolCalls(raw.approvedToolCalls),
|
|
787
|
+
surface: nonEmptyString(raw.preset),
|
|
785
788
|
};
|
|
786
789
|
}
|
|
787
790
|
function normalizeStoredAgentChatExecMode(value) {
|
|
@@ -826,6 +829,22 @@ export function routesToCodeFrame(opts) {
|
|
|
826
829
|
return false;
|
|
827
830
|
return !keepsApprovalInAppChat(opts);
|
|
828
831
|
}
|
|
832
|
+
/** A send that delivers a non-blank prompt; only these count as a turn. */
|
|
833
|
+
function submitsMessage(opts) {
|
|
834
|
+
return opts.submit !== false && !!opts.message.trim();
|
|
835
|
+
}
|
|
836
|
+
/** A submitted prompt that leaves this page for Builder or an MCP host. */
|
|
837
|
+
function trackRoutedSend(opts, routedTo, submitMessageId, requestMode) {
|
|
838
|
+
// Typed as a string, but JS callers reach this too; a tracker never throws.
|
|
839
|
+
if (typeof opts.message !== "string" || !submitsMessage(opts))
|
|
840
|
+
return;
|
|
841
|
+
trackRoutedTurnSubmitted({
|
|
842
|
+
routedTo,
|
|
843
|
+
surface: opts.preset ?? DEFAULT_TURN_SURFACE,
|
|
844
|
+
submitMessageId,
|
|
845
|
+
requestMode,
|
|
846
|
+
});
|
|
847
|
+
}
|
|
829
848
|
function sendToAgentChatInternal(opts, forceMcpAppWrapperRelay = false) {
|
|
830
849
|
const tabId = opts.tabId ?? generateTabId();
|
|
831
850
|
const actionScope = opts.actionScope === undefined
|
|
@@ -841,7 +860,7 @@ function sendToAgentChatInternal(opts, forceMcpAppWrapperRelay = false) {
|
|
|
841
860
|
(mcpAppLocalPayload && !routesLocalToChatGpt);
|
|
842
861
|
const requestMode = normalizeAgentChatRequestMode(opts.requestMode ?? opts.mode) ??
|
|
843
862
|
readStoredAgentChatRequestMode();
|
|
844
|
-
if (opts
|
|
863
|
+
if (submitsMessage(opts)) {
|
|
845
864
|
trackEvent("app.first_action", {
|
|
846
865
|
action: "chat_submit",
|
|
847
866
|
surface: opts.preset ?? "chat",
|
|
@@ -851,6 +870,7 @@ function sendToAgentChatInternal(opts, forceMcpAppWrapperRelay = false) {
|
|
|
851
870
|
});
|
|
852
871
|
}
|
|
853
872
|
if (isCodeRequest && isInBuilderFrame()) {
|
|
873
|
+
trackRoutedSend(opts, "builder", opts.submitMessageId, requestMode);
|
|
854
874
|
sendToBuilderChat({
|
|
855
875
|
message: opts.message,
|
|
856
876
|
context: opts.context,
|
|
@@ -872,10 +892,12 @@ function sendToAgentChatInternal(opts, forceMcpAppWrapperRelay = false) {
|
|
|
872
892
|
},
|
|
873
893
|
};
|
|
874
894
|
if (forceMcpAppWrapperRelay && mcpBridgeEnabled) {
|
|
895
|
+
trackRoutedSend(opts, "mcp_host", submitMessageId, requestMode);
|
|
875
896
|
window.parent.postMessage(payload, getFramePostMessageTargetOrigin() || "*");
|
|
876
897
|
return tabId;
|
|
877
898
|
}
|
|
878
899
|
if (opts.submit !== false && !localChatTarget && mcpBridgeEnabled) {
|
|
900
|
+
trackRoutedSend(opts, "mcp_host", submitMessageId, requestMode);
|
|
879
901
|
if (opts.targetTabId) {
|
|
880
902
|
window.parent.postMessage(payload, getFramePostMessageTargetOrigin() || "*");
|
|
881
903
|
return tabId;
|
|
@@ -1004,13 +1026,17 @@ export function sendToAgentChatAndConfirm(opts, options) {
|
|
|
1004
1026
|
return confirmAgentChatSubmit(opts, options, tabId, (submission) => sendToAgentChatInternal(submission, true));
|
|
1005
1027
|
}
|
|
1006
1028
|
return Promise.resolve(hostDelivery)
|
|
1007
|
-
.then((delivered) =>
|
|
1008
|
-
|
|
1009
|
-
|
|
1029
|
+
.then((delivered) => {
|
|
1030
|
+
if (delivered === false) {
|
|
1031
|
+
return confirmAgentChatSubmit(opts, options, tabId, (submission) => sendToAgentChatInternal(submission, true));
|
|
1032
|
+
}
|
|
1033
|
+
trackRoutedSend(opts, "mcp_host", options?.submitMessageId, requestMode);
|
|
1034
|
+
return {
|
|
1010
1035
|
tabId,
|
|
1011
1036
|
delivered: delivered === true,
|
|
1012
1037
|
...(delivered === true ? {} : { reason: "host-unconfirmed" }),
|
|
1013
|
-
}
|
|
1038
|
+
};
|
|
1039
|
+
})
|
|
1014
1040
|
.catch(() => ({ tabId, delivered: false, reason: "host-rejected" }));
|
|
1015
1041
|
}
|
|
1016
1042
|
return confirmAgentChatSubmit(opts, options, tabId, sendToAgentChat);
|
|
@@ -14,6 +14,7 @@ import { AGENT_NATIVE_PROTOCOL_METADATA_KEY, createAgentKitProtocolAdapter, } fr
|
|
|
14
14
|
import { trackRunFeedback, trackRunOutcome } from "./run-outcome-telemetry.js";
|
|
15
15
|
import { runOutcomeForCode } from "./run-outcome.js";
|
|
16
16
|
import { createAgentNativeChatRuntime, isAgentNativeChatRuntime, } from "./runtime.js";
|
|
17
|
+
import { trackTurnSubmitted } from "./turn-submitted-telemetry.js";
|
|
17
18
|
const RUN_SLOT_POLL_INTERVAL_MS = 150;
|
|
18
19
|
const RUN_SLOT_STABLE_POLLS = 2;
|
|
19
20
|
const SAFE_BOUNDARY_POLL_INTERVAL_MS = 500;
|
|
@@ -3386,7 +3387,10 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
3386
3387
|
Object.assign(error, { code: "queue_item_busy", retryable: true });
|
|
3387
3388
|
throw error;
|
|
3388
3389
|
}
|
|
3389
|
-
const runtime = options.runtime ??
|
|
3390
|
+
const runtime = options.runtime ??
|
|
3391
|
+
createAgentNativeChatRuntime(options, {
|
|
3392
|
+
onTurnSubmitted: trackTurnSubmitted,
|
|
3393
|
+
});
|
|
3390
3394
|
const feedbackUrl = options.feedbackUrl ??
|
|
3391
3395
|
agentNativePath("/_agent-native/observability/feedback");
|
|
3392
3396
|
const onRunOutcome = options.adapter?.onRunOutcome ?? trackRunOutcome;
|
|
@@ -3827,6 +3831,9 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
3827
3831
|
onRunOutcome({
|
|
3828
3832
|
runId: record.turnId,
|
|
3829
3833
|
threadId,
|
|
3834
|
+
...(typeof record.requestTurnId === "string"
|
|
3835
|
+
? { turnId: record.requestTurnId }
|
|
3836
|
+
: {}),
|
|
3830
3837
|
outcome: runOutcomeForCode(code),
|
|
3831
3838
|
...(code ? { code } : {}),
|
|
3832
3839
|
...(error instanceof Error && error.message
|
|
@@ -1804,6 +1804,7 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
1804
1804
|
options.onRunOutcome({
|
|
1805
1805
|
runId: run.turn.runId ?? run.runId,
|
|
1806
1806
|
threadId: run.threadId,
|
|
1807
|
+
...(run.turn.requestTurnId ? { turnId: run.turn.requestTurnId } : {}),
|
|
1807
1808
|
outcome: event.type === "run.completed"
|
|
1808
1809
|
? "succeeded"
|
|
1809
1810
|
: event.type === "run.cancelled"
|
|
@@ -3005,7 +3006,12 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
3005
3006
|
run.quietAuthorityReads = 0;
|
|
3006
3007
|
run.successorWaitStartedAtMs = undefined;
|
|
3007
3008
|
}
|
|
3008
|
-
|
|
3009
|
+
// A resumed stream is the same turn; only the response that started it
|
|
3010
|
+
// carries the turn the server stored the run under.
|
|
3011
|
+
run.turn =
|
|
3012
|
+
resumed.requestTurnId || !run.turn.requestTurnId
|
|
3013
|
+
? resumed
|
|
3014
|
+
: { ...resumed, requestTurnId: run.turn.requestTurnId };
|
|
3009
3015
|
indexRun(run, resumed.runId);
|
|
3010
3016
|
}
|
|
3011
3017
|
function ensurePump(run) {
|
|
@@ -80,6 +80,7 @@ export function trackRunOutcome(report, send = trackEvent, random = Math.random)
|
|
|
80
80
|
drain_attempts: report.drainAttempts,
|
|
81
81
|
run_id: report.runId,
|
|
82
82
|
thread_id: report.threadId,
|
|
83
|
+
...(report.turnId ? { turn_id: report.turnId } : {}),
|
|
83
84
|
sample_rate: rate,
|
|
84
85
|
sample_weight: 1 / rate,
|
|
85
86
|
});
|
|
@@ -41,6 +41,11 @@ export type RunTerminalSource = "stream" | "authority" | "pipe" | "local";
|
|
|
41
41
|
export interface RunOutcomeReport {
|
|
42
42
|
readonly runId: string;
|
|
43
43
|
readonly threadId: string;
|
|
44
|
+
/**
|
|
45
|
+
* The turn the server stored the run under; absent when this page did not
|
|
46
|
+
* start it, or for a replay whose turn the server did not name.
|
|
47
|
+
*/
|
|
48
|
+
readonly turnId?: string;
|
|
44
49
|
readonly outcome: RunOutcome;
|
|
45
50
|
/** The legacy code that named this outcome one incident at a time. */
|
|
46
51
|
readonly code?: string;
|
|
@@ -3,6 +3,7 @@ import type { ActionChatUIConfig } from "../../action-ui.js";
|
|
|
3
3
|
import type { AgentMcpAppPayload } from "../../mcp-client/app-result.js";
|
|
4
4
|
import type { ReasoningEffort } from "../../shared/reasoning-effort.js";
|
|
5
5
|
import { type ServerRunState } from "./run-outcome.js";
|
|
6
|
+
import { type TurnSubmittedReport } from "./turn-submitted.js";
|
|
6
7
|
export type { ServerRunState } from "./run-outcome.js";
|
|
7
8
|
export type AgentChatRuntimeId = string;
|
|
8
9
|
export type AgentChatRuntimeSessionId = string;
|
|
@@ -549,6 +550,13 @@ export type AgentChatRuntimeKnownEvent = AgentChatRuntimeMessageStartEvent | Age
|
|
|
549
550
|
export type AgentChatRuntimeEvent<TCustomEvent extends AgentChatRuntimeCustomEvent = never> = AgentChatRuntimeKnownEvent | TCustomEvent;
|
|
550
551
|
export interface AgentChatRuntimeTurn<TEvent extends AgentChatRuntimeEventBase = AgentChatRuntimeKnownEvent> {
|
|
551
552
|
readonly id?: AgentChatRuntimeTurnId;
|
|
553
|
+
/**
|
|
554
|
+
* The turn the server stored the run under (its `X-Turn-Id`), else the one
|
|
555
|
+
* the request carried; absent for a replay the server did not name the turn
|
|
556
|
+
* of. A continuation sends the turn it continues, and an approval resume
|
|
557
|
+
* runs under its pending approval's turn, so this is not always `id`.
|
|
558
|
+
*/
|
|
559
|
+
readonly requestTurnId?: AgentChatRuntimeTurnId;
|
|
552
560
|
readonly sessionId: AgentChatRuntimeSessionId;
|
|
553
561
|
readonly runId?: string;
|
|
554
562
|
readonly metadata?: AgentChatRuntimeMetadata;
|
|
@@ -636,7 +644,25 @@ export interface CreateHttpAgentChatRuntimeOptions<TEvent extends AgentChatRunti
|
|
|
636
644
|
session: AgentChatRuntimeSessionSummary;
|
|
637
645
|
turn: AgentChatRuntimeTurnInput;
|
|
638
646
|
turnId: AgentChatRuntimeTurnId;
|
|
647
|
+
requestTurnId: AgentChatRuntimeTurnId;
|
|
639
648
|
}) => unknown;
|
|
649
|
+
/** The turn id a turn's request carries; the runtime's own id by default. */
|
|
650
|
+
readonly requestTurnId?: (input: {
|
|
651
|
+
turn: AgentChatRuntimeTurnInput;
|
|
652
|
+
turnId: AgentChatRuntimeTurnId;
|
|
653
|
+
}) => AgentChatRuntimeTurnId;
|
|
654
|
+
/**
|
|
655
|
+
* Told once the endpoint accepts a turn, before its events are read. It is
|
|
656
|
+
* not awaited and anything it throws is ignored.
|
|
657
|
+
*/
|
|
658
|
+
readonly onTurnAccepted?: (input: {
|
|
659
|
+
session: AgentChatRuntimeSessionSummary;
|
|
660
|
+
turn: AgentChatRuntimeTurnInput;
|
|
661
|
+
turnId: AgentChatRuntimeTurnId;
|
|
662
|
+
/** The turn the server stored the run under, as on the returned turn. */
|
|
663
|
+
requestTurnId: AgentChatRuntimeTurnId;
|
|
664
|
+
runId: string;
|
|
665
|
+
}) => void;
|
|
640
666
|
/** Called at the final client boundary before a new turn reaches the endpoint. */
|
|
641
667
|
readonly beforeStartTurn?: (input: {
|
|
642
668
|
session: AgentChatRuntimeSessionSummary;
|
|
@@ -682,5 +708,12 @@ export interface CreateAgentNativeChatRuntimeOptions {
|
|
|
682
708
|
export declare function createHttpAgentChatRuntime<TEvent extends AgentChatRuntimeEventBase = AgentChatRuntimeKnownEvent>(options: CreateHttpAgentChatRuntimeOptions<TEvent>): AgentChatRuntime<TEvent>;
|
|
683
709
|
export declare function loadedSkillSlugsFromMessages(messages: readonly AgentChatRuntimeMessage[] | undefined): string[];
|
|
684
710
|
export declare const AGENT_CALL_FAILURE_REASON_KEYS: readonly string[];
|
|
685
|
-
|
|
711
|
+
/**
|
|
712
|
+
* Where the runtime reports telemetry. It is injected rather than imported so
|
|
713
|
+
* the runtime stays loadable without the browser analytics module.
|
|
714
|
+
*/
|
|
715
|
+
export interface AgentNativeChatRuntimeTelemetry {
|
|
716
|
+
readonly onTurnSubmitted?: (report: TurnSubmittedReport) => void;
|
|
717
|
+
}
|
|
718
|
+
export declare function createAgentNativeChatRuntime(options?: CreateAgentNativeChatRuntimeOptions, telemetry?: AgentNativeChatRuntimeTelemetry): AgentChatRuntime<AgentChatRuntimeKnownEvent>;
|
|
686
719
|
export declare function isAgentNativeChatRuntime(runtime: AgentChatRuntime | undefined): boolean;
|