@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renameThread, resolveThreadAccess, setThreadArchived, setThreadPinned, } from "../../chat-threads/store.js";
|
|
1
|
+
import { renameThread, resolveThreadAccess, setThreadArchived, setThreadPinned, setThreadHidden, } from "../../chat-threads/store.js";
|
|
2
2
|
import { assertCanManageSharedResource } from "../../resources/script-helpers.js";
|
|
3
3
|
import { dbExecToolParameters } from "../../scripts/db/tool-schemas.js";
|
|
4
4
|
import { serializeCliArgs } from "../../scripts/parse-args.js";
|
|
@@ -515,6 +515,10 @@ export async function createChatScriptEntries() {
|
|
|
515
515
|
description: "Output format",
|
|
516
516
|
enum: ["json", "text"],
|
|
517
517
|
},
|
|
518
|
+
includeHidden: {
|
|
519
|
+
type: "boolean",
|
|
520
|
+
description: "Also include chats hidden by you. Hidden chats are excluded by default.",
|
|
521
|
+
},
|
|
518
522
|
includeArchived: {
|
|
519
523
|
type: "boolean",
|
|
520
524
|
description: "Also include archived chats in the results. Archived chats are excluded by default.",
|
|
@@ -538,14 +542,23 @@ export async function createChatScriptEntries() {
|
|
|
538
542
|
return {
|
|
539
543
|
"chat-history": {
|
|
540
544
|
tool: {
|
|
541
|
-
description: "Manage past agent chat threads. Use action 'search' to find previous conversations by keyword, 'open' to open a thread in the UI, or '
|
|
545
|
+
description: "Manage past agent chat threads. Use action 'search' to find previous conversations by keyword, 'open' to open a thread in the UI, or 'pin'/'unpin' to pin for yourself, 'hide'/'unhide' to remove or restore shared chats in your lists. Only owners can rename or archive; owners archive instead of hiding.",
|
|
542
546
|
parameters: {
|
|
543
547
|
type: "object",
|
|
544
548
|
properties: {
|
|
545
549
|
action: {
|
|
546
550
|
type: "string",
|
|
547
551
|
description: "The operation to perform",
|
|
548
|
-
enum: [
|
|
552
|
+
enum: [
|
|
553
|
+
"search",
|
|
554
|
+
"open",
|
|
555
|
+
"rename",
|
|
556
|
+
"pin",
|
|
557
|
+
"unpin",
|
|
558
|
+
"archive",
|
|
559
|
+
"hide",
|
|
560
|
+
"unhide",
|
|
561
|
+
],
|
|
549
562
|
},
|
|
550
563
|
query: {
|
|
551
564
|
type: "string",
|
|
@@ -560,13 +573,17 @@ export async function createChatScriptEntries() {
|
|
|
560
573
|
description: "(search) Output format",
|
|
561
574
|
enum: ["json", "text"],
|
|
562
575
|
},
|
|
576
|
+
includeHidden: {
|
|
577
|
+
type: "boolean",
|
|
578
|
+
description: "(search) Include chats hidden by you, with your hiddenAt timestamp.",
|
|
579
|
+
},
|
|
563
580
|
includeArchived: {
|
|
564
581
|
type: "boolean",
|
|
565
582
|
description: "(search) Also include archived chats in the results. Archived chats are excluded by default, matching the 'archive' action's effect on the chat list.",
|
|
566
583
|
},
|
|
567
584
|
id: {
|
|
568
585
|
type: "string",
|
|
569
|
-
description: "(open, rename, pin, unpin, archive) The chat thread ID to manage",
|
|
586
|
+
description: "(open, rename, pin, unpin, archive, hide, unhide) The chat thread ID to manage",
|
|
570
587
|
},
|
|
571
588
|
title: {
|
|
572
589
|
type: "string",
|
|
@@ -588,14 +605,16 @@ export async function createChatScriptEntries() {
|
|
|
588
605
|
if (args?.action === "rename" ||
|
|
589
606
|
args?.action === "pin" ||
|
|
590
607
|
args?.action === "unpin" ||
|
|
591
|
-
args?.action === "archive"
|
|
608
|
+
args?.action === "archive" ||
|
|
609
|
+
args?.action === "hide" ||
|
|
610
|
+
args?.action === "unhide") {
|
|
592
611
|
const id = typeof args?.id === "string" ? args.id : "";
|
|
593
612
|
if (!id)
|
|
594
613
|
return "Missing required id.";
|
|
595
614
|
const owner = getRequestRunContext()?.owner ?? getRequestUserEmail() ?? "";
|
|
596
615
|
if (!owner)
|
|
597
616
|
return "No authenticated user is available.";
|
|
598
|
-
const thread = await resolveThreadAccess(owner, id, "
|
|
617
|
+
const thread = await resolveThreadAccess(owner, id, "viewer", {
|
|
599
618
|
orgId: getRequestOrgId(),
|
|
600
619
|
});
|
|
601
620
|
if (!thread) {
|
|
@@ -608,18 +627,34 @@ export async function createChatScriptEntries() {
|
|
|
608
627
|
: "";
|
|
609
628
|
if (!nextTitle)
|
|
610
629
|
return "Missing required title.";
|
|
611
|
-
const renamed = await renameThread(id, nextTitle
|
|
630
|
+
const renamed = await renameThread(id, nextTitle, {
|
|
631
|
+
ownerEmail: owner,
|
|
632
|
+
});
|
|
612
633
|
if (!renamed)
|
|
613
634
|
return `Chat thread "${id}" could not be renamed.`;
|
|
614
635
|
return `Renamed chat "${title}" to "${nextTitle}".`;
|
|
615
636
|
}
|
|
616
637
|
if (args.action === "archive") {
|
|
617
|
-
const archived = await setThreadArchived(id, true
|
|
638
|
+
const archived = await setThreadArchived(id, true, {
|
|
639
|
+
ownerEmail: owner,
|
|
640
|
+
});
|
|
618
641
|
if (!archived)
|
|
619
642
|
return `Chat thread "${id}" could not be archived.`;
|
|
620
643
|
return `Archived chat: ${title}`;
|
|
621
644
|
}
|
|
622
|
-
|
|
645
|
+
if (args.action === "hide" || args.action === "unhide") {
|
|
646
|
+
const hidden = await setThreadHidden(id, args.action === "hide", {
|
|
647
|
+
ownerEmail: owner,
|
|
648
|
+
orgId: getRequestOrgId(),
|
|
649
|
+
});
|
|
650
|
+
if (!hidden)
|
|
651
|
+
return `Chat thread "${id}" could not be updated.`;
|
|
652
|
+
return `${args.action === "hide" ? "Hidden" : "Unhidden"} chat: ${title}`;
|
|
653
|
+
}
|
|
654
|
+
const pinned = await setThreadPinned(id, args.action === "pin", {
|
|
655
|
+
ownerEmail: owner,
|
|
656
|
+
orgId: getRequestOrgId(),
|
|
657
|
+
});
|
|
623
658
|
if (!pinned)
|
|
624
659
|
return `Chat thread "${id}" could not be updated.`;
|
|
625
660
|
return `${args.action === "pin" ? "Pinned" : "Unpinned"} chat: ${title}`;
|
|
@@ -14,6 +14,8 @@ type AgentChatRunTrackingSource = Pick<TrackingMeta, "userId" | "authUserId" | "
|
|
|
14
14
|
isSyntheticTraffic?: boolean;
|
|
15
15
|
};
|
|
16
16
|
export declare function trackAgentChatRunLifecycle(event: "run_started" | "run_finished" | "run_no_reply", threadId: string | undefined, attemptId: string | undefined, userId?: string, properties?: Record<string, unknown>, appId?: string, trackingSource?: AgentChatRunTrackingSource): void;
|
|
17
|
+
/** `run_started` from every agent-chat entry point, keyed to its turn. */
|
|
18
|
+
export declare function trackAgentChatRunStarted(threadId: string, runId: string, turnId: string, owner: string | undefined, appId: string | undefined): void;
|
|
17
19
|
import { buildSelectedA2AReceiverContext, createA2AEngineToolSurface, DEFAULT_DELEGATED_MAX_ITERATIONS, DEFAULT_DELEGATED_MAX_RUN_INPUT_TOKENS, DEFAULT_DELEGATED_MAX_TOOL_RESULT_CHARS, isSelectedA2AReceiver, runA2AAgentLoop, runMCPAgentLoop, assembleA2AFinalResponse, buildPublicAgentA2ASkills } from "./agent-chat/action-filters-a2a.js";
|
|
18
20
|
import { _agentChatPromptSectionsForTests } from "./agent-chat/framework-prompts.js";
|
|
19
21
|
import { type AgentChatPluginOptions, type NitroPluginDef } from "./agent-chat/plugin-options.js";
|
|
@@ -7,7 +7,7 @@ import { appendA2AArtifactLinks, buildA2ARecoverableArtifactMessage, } from "../
|
|
|
7
7
|
import { hasConfiguredA2ASecret, isLoopbackAddress, isTrustedLocalRuntime, } from "../a2a/auth-policy.js";
|
|
8
8
|
import { sanitizeA2ACorrelationId, sanitizeA2ACorrelationMetadata, } from "../a2a/correlation.js";
|
|
9
9
|
import { createA2AApproval, a2aTaskLeaseThreadId, A2A_TASK_CLAIM_LOST_ERROR_CODE, updateTaskStatusMessage, } from "../a2a/task-store.js";
|
|
10
|
-
import { clientAbortReason } from "../agent/abort-reasons.js";
|
|
10
|
+
import { clientAbortReason, EXECUTION_LEASE_LOST_ABORT_REASON, } from "../agent/abort-reasons.js";
|
|
11
11
|
import { canUpdateAgentAppModelDefaultSettings, normalizeAgentAppModelDefaultAppId, readAgentAppModelDefaultSettings, resetAgentAppModelDefaultSettings, writeAgentAppModelDefaultSettings, } from "../agent/app-model-defaults.js";
|
|
12
12
|
import { approvalKeyWithArguments } from "../agent/approval-key.js";
|
|
13
13
|
import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "../agent/chatgpt-subscription-contract.js";
|
|
@@ -33,7 +33,7 @@ import { attachToolSearch } from "../agent/tool-search.js";
|
|
|
33
33
|
import { getAppConfig } from "../app-config/index.js";
|
|
34
34
|
import { readAppStateForCurrentTab } from "../application-state/script-helpers.js";
|
|
35
35
|
import { runChatThreadDataMigrations } from "../chat-threads/migrations.js";
|
|
36
|
-
import { InlineAttachmentDataNotPersistableError, QueuedSubmissionAdmissionError, adoptThreadScopeIfUnscoped, blockQueuedSubmissionReceipt, clearQueuedSubmissionInterruptRun, createThread, forkThread, getThread, getQueuedSubmissionReceipt, getQueuedSubmissionReceiptStatus, listQueuedSubmissionReceipts, markQueuedSubmissionSubmitted, queuedSubmissionBehaviorMatches, recordQueuedSubmissionInterruptRun, renewQueuedSubmissionInterruptClaim, registerChatThreadsShareable, retryQueuedSubmissionReceipt, resolveRunThreadScope, resolveThreadAccess, resolveThreadAccessScope, listThreads, searchThreads, renameThread, createThreadShareLink, getThreadByShareToken, getThreadShareState, revokeThreadShareLink, setThreadArchived, setThreadPinned, setThreadScope, updateThreadData, withThreadDataLock, deleteThread, mutateThreadQueuedMessages, setThreadSourceIfMissing, isAppOwnedChatScope, isChatThreadOwnership, threadScopeMismatch, } from "../chat-threads/store.js";
|
|
36
|
+
import { InlineAttachmentDataNotPersistableError, QueuedSubmissionAdmissionError, adoptThreadScopeIfUnscoped, blockQueuedSubmissionReceipt, clearQueuedSubmissionInterruptRun, createThread, forkThread, getThread, getQueuedSubmissionReceipt, getQueuedSubmissionReceiptStatus, listQueuedSubmissionReceipts, markQueuedSubmissionSubmitted, queuedSubmissionBehaviorMatches, recordQueuedSubmissionInterruptRun, renewQueuedSubmissionInterruptClaim, registerChatThreadsShareable, retryQueuedSubmissionReceipt, resolveRunThreadScope, resolveThreadAccess, resolveThreadAccessScope, listThreads, searchThreads, renameThread, assertThreadOwner, createThreadShareLink, getThreadByShareToken, getThreadShareState, revokeThreadShareLink, setThreadArchived, setThreadPinned, setThreadHidden, setThreadScope, updateThreadData, withThreadDataLock, deleteThread, mutateThreadQueuedMessages, setThreadSourceIfMissing, isAppOwnedChatScope, isChatThreadOwnership, threadScopeMismatch, } from "../chat-threads/store.js";
|
|
37
37
|
import { isCheckpointRestorePath } from "../checkpoints/route-match.js";
|
|
38
38
|
import { createDbAdminAgentTools } from "../db-admin/agent-tools.js";
|
|
39
39
|
import { isTransientDatabaseError } from "../db/client.js";
|
|
@@ -97,6 +97,10 @@ export function trackAgentChatRunLifecycle(event, threadId, attemptId, userId, p
|
|
|
97
97
|
attempt_id: attemptId,
|
|
98
98
|
}, trackingSource ?? runLifecycleTrackingSource(userId));
|
|
99
99
|
}
|
|
100
|
+
/** `run_started` from every agent-chat entry point, keyed to its turn. */
|
|
101
|
+
export function trackAgentChatRunStarted(threadId, runId, turnId, owner, appId) {
|
|
102
|
+
trackAgentChatRunLifecycle("run_started", threadId, runId, owner, { turn_id: turnId }, appId);
|
|
103
|
+
}
|
|
100
104
|
/**
|
|
101
105
|
* The run's owner is set on the run context only once its system prompt is
|
|
102
106
|
* built, after `run_started`, and a durable worker has no browser session at
|
|
@@ -458,8 +462,8 @@ export async function runA2AAgentLoopWithRunManager(runOptions, pluginOptions, t
|
|
|
458
462
|
: undefined;
|
|
459
463
|
if (reasonCode === "stale_execution_fence" ||
|
|
460
464
|
reasonCode === A2A_TASK_CLAIM_LOST_ERROR_CODE) {
|
|
461
|
-
run.abortReason =
|
|
462
|
-
run.abort.abort(
|
|
465
|
+
run.abortReason = EXECUTION_LEASE_LOST_ABORT_REASON;
|
|
466
|
+
run.abort.abort(EXECUTION_LEASE_LOST_ABORT_REASON);
|
|
463
467
|
return;
|
|
464
468
|
}
|
|
465
469
|
cancellation = abortRunDurably(runId, clientAbortReason(reason)).then((aborted) => ({ status: "fulfilled", aborted }), (error) => ({ status: "rejected", error }));
|
|
@@ -3994,7 +3998,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
3994
3998
|
},
|
|
3995
3999
|
onRunPrepared: persistSubmittedUserMessage,
|
|
3996
4000
|
onRunNotStarted: recordUnstartedTurn,
|
|
3997
|
-
onRunStart: async (send, threadId, runId) => {
|
|
4001
|
+
onRunStart: async (send, threadId, runId, turnId) => {
|
|
3998
4002
|
await recordPreRunGitStatus(threadId);
|
|
3999
4003
|
_runSendByThread.set(threadId, send);
|
|
4000
4004
|
const runCtx = ensureRequestRunContext();
|
|
@@ -4002,7 +4006,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4002
4006
|
runCtx.threadId = threadId;
|
|
4003
4007
|
runCtx.runId = runId;
|
|
4004
4008
|
}
|
|
4005
|
-
|
|
4009
|
+
trackAgentChatRunStarted(threadId, runId, turnId, runCtx?.owner, options?.appId);
|
|
4006
4010
|
await runPreAgentTurnAutosave(options?.onAgentTurnStart, runCtx?.chatScope, { threadId, runId });
|
|
4007
4011
|
},
|
|
4008
4012
|
onRunComplete: async (run, threadId, trackingSource) => {
|
|
@@ -4050,7 +4054,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4050
4054
|
},
|
|
4051
4055
|
onRunPrepared: persistSubmittedUserMessage,
|
|
4052
4056
|
onRunNotStarted: recordUnstartedTurn,
|
|
4053
|
-
onRunStart: async (send, threadId, runId) => {
|
|
4057
|
+
onRunStart: async (send, threadId, runId, turnId) => {
|
|
4054
4058
|
await recordPreRunGitStatus(threadId);
|
|
4055
4059
|
_runSendByThread.set(threadId, send);
|
|
4056
4060
|
const runCtx = ensureRequestRunContext();
|
|
@@ -4058,7 +4062,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4058
4062
|
runCtx.threadId = threadId;
|
|
4059
4063
|
runCtx.runId = runId;
|
|
4060
4064
|
}
|
|
4061
|
-
|
|
4065
|
+
trackAgentChatRunStarted(threadId, runId, turnId, runCtx?.owner, options?.appId);
|
|
4062
4066
|
},
|
|
4063
4067
|
onRunComplete: async (run, threadId, trackingSource) => {
|
|
4064
4068
|
if (threadId)
|
|
@@ -4237,7 +4241,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4237
4241
|
},
|
|
4238
4242
|
onRunPrepared: persistSubmittedUserMessage,
|
|
4239
4243
|
onRunNotStarted: recordUnstartedTurn,
|
|
4240
|
-
onRunStart: async (send, threadId, runId) => {
|
|
4244
|
+
onRunStart: async (send, threadId, runId, turnId) => {
|
|
4241
4245
|
await recordPreRunGitStatus(threadId);
|
|
4242
4246
|
_runSendByThread.set(threadId, send);
|
|
4243
4247
|
const runCtx = ensureRequestRunContext();
|
|
@@ -4245,7 +4249,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4245
4249
|
runCtx.threadId = threadId;
|
|
4246
4250
|
runCtx.runId = runId;
|
|
4247
4251
|
}
|
|
4248
|
-
|
|
4252
|
+
trackAgentChatRunStarted(threadId, runId, turnId, runCtx?.owner, options?.appId);
|
|
4249
4253
|
await runPreAgentTurnAutosave(options?.onAgentTurnStart, runCtx?.chatScope, { threadId, runId });
|
|
4250
4254
|
},
|
|
4251
4255
|
onRunComplete: async (run, threadId, trackingSource) => {
|
|
@@ -6029,6 +6033,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6029
6033
|
// Merge the incoming snapshot delta over the current SQL copy.
|
|
6030
6034
|
// Let updateThreadData apply delta markers to each latest
|
|
6031
6035
|
// revision if its compare-and-swap needs to retry.
|
|
6036
|
+
let titleOverrideChanged = false;
|
|
6032
6037
|
if (hasThreadData) {
|
|
6033
6038
|
let incoming;
|
|
6034
6039
|
try {
|
|
@@ -6044,6 +6049,10 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6044
6049
|
setResponseStatus(event, 400);
|
|
6045
6050
|
return { error: "Invalid threadData JSON" };
|
|
6046
6051
|
}
|
|
6052
|
+
titleOverrideChanged =
|
|
6053
|
+
Object.prototype.hasOwnProperty.call(incoming, "_titleOverride") &&
|
|
6054
|
+
incoming._titleOverride !==
|
|
6055
|
+
existing._titleOverride;
|
|
6047
6056
|
if (containsInlineAttachmentPayload(incoming)) {
|
|
6048
6057
|
setResponseStatus(event, 400);
|
|
6049
6058
|
return {
|
|
@@ -6075,9 +6084,21 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6075
6084
|
else {
|
|
6076
6085
|
preserveTitleOverride(existing);
|
|
6077
6086
|
}
|
|
6087
|
+
// Content saves carry the client's cached title, which can be
|
|
6088
|
+
// stale, and the store keeps the owner's title for a shared
|
|
6089
|
+
// editor. Only an override change or a title-only request is a
|
|
6090
|
+
// rename.
|
|
6091
|
+
if (titleOverrideChanged ||
|
|
6092
|
+
(!hasThreadData &&
|
|
6093
|
+
typeof body.title === "string" &&
|
|
6094
|
+
body.title !== thread.title)) {
|
|
6095
|
+
assertThreadOwner(thread, owner);
|
|
6096
|
+
}
|
|
6078
6097
|
let updated;
|
|
6079
6098
|
try {
|
|
6080
6099
|
updated = await updateThreadData(threadId, newThreadData, nextTitle, nextPreview, newMessageCount, {
|
|
6100
|
+
preserveCurrentTitle: thread.ownerEmail.toLowerCase() !==
|
|
6101
|
+
owner.trim().toLowerCase(),
|
|
6081
6102
|
preserveCurrentTitleAndPreview: isSnapshotDelta,
|
|
6082
6103
|
onAnnotationConflict: (conflict) => annotationConflicts.push(conflict),
|
|
6083
6104
|
});
|
|
@@ -6317,7 +6338,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6317
6338
|
};
|
|
6318
6339
|
}
|
|
6319
6340
|
if (method === "POST" && isThreadSubroute("rename")) {
|
|
6320
|
-
const thread = await resolveThreadAccess(owner, threadId, "
|
|
6341
|
+
const thread = await resolveThreadAccess(owner, threadId, "viewer", { orgId });
|
|
6321
6342
|
if (!thread || !threadMatchesRequestedScope(thread)) {
|
|
6322
6343
|
setResponseStatus(event, 404);
|
|
6323
6344
|
return { error: "Thread not found" };
|
|
@@ -6330,7 +6351,9 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6330
6351
|
setResponseStatus(event, 400);
|
|
6331
6352
|
return { error: "Title is required" };
|
|
6332
6353
|
}
|
|
6333
|
-
const renamed = await renameThread(threadId, title
|
|
6354
|
+
const renamed = await renameThread(threadId, title, {
|
|
6355
|
+
ownerEmail: owner,
|
|
6356
|
+
});
|
|
6334
6357
|
if (!renamed) {
|
|
6335
6358
|
setResponseStatus(event, 404);
|
|
6336
6359
|
return { error: "Thread not found" };
|
|
@@ -6338,7 +6361,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6338
6361
|
return { ok: true };
|
|
6339
6362
|
}
|
|
6340
6363
|
if (method === "POST" && isThreadSubroute("pin")) {
|
|
6341
|
-
const thread = await resolveThreadAccess(owner, threadId, "
|
|
6364
|
+
const thread = await resolveThreadAccess(owner, threadId, "viewer", { orgId });
|
|
6342
6365
|
if (!thread || !threadMatchesRequestedScope(thread)) {
|
|
6343
6366
|
setResponseStatus(event, 404);
|
|
6344
6367
|
return { error: "Thread not found" };
|
|
@@ -6348,15 +6371,39 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6348
6371
|
setResponseStatus(event, 400);
|
|
6349
6372
|
return { error: "pinned boolean is required" };
|
|
6350
6373
|
}
|
|
6351
|
-
const pinned = await setThreadPinned(threadId, body.pinned
|
|
6374
|
+
const pinned = await setThreadPinned(threadId, body.pinned, {
|
|
6375
|
+
ownerEmail: owner,
|
|
6376
|
+
orgId,
|
|
6377
|
+
});
|
|
6352
6378
|
if (!pinned) {
|
|
6353
6379
|
setResponseStatus(event, 404);
|
|
6354
6380
|
return { error: "Thread not found" };
|
|
6355
6381
|
}
|
|
6356
6382
|
return { ok: true };
|
|
6357
6383
|
}
|
|
6384
|
+
if (method === "POST" && isThreadSubroute("hide")) {
|
|
6385
|
+
const thread = await resolveThreadAccessScope(owner, threadId, "viewer", { orgId });
|
|
6386
|
+
if (!thread || !threadMatchesRequestedScope(thread)) {
|
|
6387
|
+
setResponseStatus(event, 404);
|
|
6388
|
+
return { error: "Thread not found" };
|
|
6389
|
+
}
|
|
6390
|
+
const body = await readBody(event);
|
|
6391
|
+
if (typeof body?.hidden !== "boolean") {
|
|
6392
|
+
setResponseStatus(event, 400);
|
|
6393
|
+
return { error: "hidden boolean is required" };
|
|
6394
|
+
}
|
|
6395
|
+
const hidden = await setThreadHidden(threadId, body.hidden, {
|
|
6396
|
+
ownerEmail: owner,
|
|
6397
|
+
orgId,
|
|
6398
|
+
});
|
|
6399
|
+
if (!hidden) {
|
|
6400
|
+
setResponseStatus(event, 404);
|
|
6401
|
+
return { error: "Thread not found" };
|
|
6402
|
+
}
|
|
6403
|
+
return { ok: true };
|
|
6404
|
+
}
|
|
6358
6405
|
if (method === "POST" && isThreadSubroute("archive")) {
|
|
6359
|
-
const thread = await resolveThreadAccess(owner, threadId, "
|
|
6406
|
+
const thread = await resolveThreadAccess(owner, threadId, "viewer", { orgId });
|
|
6360
6407
|
if (!thread || !threadMatchesRequestedScope(thread)) {
|
|
6361
6408
|
setResponseStatus(event, 404);
|
|
6362
6409
|
return { error: "Thread not found" };
|
|
@@ -6366,7 +6413,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6366
6413
|
setResponseStatus(event, 400);
|
|
6367
6414
|
return { error: "archived boolean is required" };
|
|
6368
6415
|
}
|
|
6369
|
-
const archived = await setThreadArchived(threadId, body.archived);
|
|
6416
|
+
const archived = await setThreadArchived(threadId, body.archived, { ownerEmail: owner });
|
|
6370
6417
|
if (!archived) {
|
|
6371
6418
|
setResponseStatus(event, 404);
|
|
6372
6419
|
return { error: "Thread not found" };
|
|
@@ -6480,6 +6527,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6480
6527
|
const unscopedOnly = String(query.unscoped ?? "") === "1";
|
|
6481
6528
|
const includeExternal = String(query.includeExternal ?? "") === "1";
|
|
6482
6529
|
const includeArchived = String(query.includeArchived ?? "") === "1";
|
|
6530
|
+
const includeHidden = String(query.includeHidden ?? "") === "1";
|
|
6483
6531
|
const ownership = query.ownership === undefined ? "all" : query.ownership;
|
|
6484
6532
|
if (!isChatThreadOwnership(ownership)) {
|
|
6485
6533
|
setResponseStatus(event, 400);
|
|
@@ -6494,6 +6542,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6494
6542
|
orgId,
|
|
6495
6543
|
includeExternal,
|
|
6496
6544
|
includeArchived,
|
|
6545
|
+
includeHidden,
|
|
6497
6546
|
sourceAppId: options?.appId ?? null,
|
|
6498
6547
|
ownership,
|
|
6499
6548
|
offset,
|
|
@@ -6506,6 +6555,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6506
6555
|
orgId,
|
|
6507
6556
|
includeExternal,
|
|
6508
6557
|
includeArchived,
|
|
6558
|
+
includeHidden,
|
|
6509
6559
|
sourceAppId: options?.appId ?? null,
|
|
6510
6560
|
ownership,
|
|
6511
6561
|
});
|
|
@@ -67,12 +67,12 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
67
67
|
code: any;
|
|
68
68
|
} | {
|
|
69
69
|
errorCode?: undefined;
|
|
70
|
+
error?: undefined;
|
|
70
71
|
code?: undefined;
|
|
71
72
|
ok: boolean;
|
|
72
73
|
key: string;
|
|
73
74
|
baseUrlKey?: string;
|
|
74
75
|
scope: AgentEngineApiKeyScope;
|
|
75
76
|
defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
|
|
76
|
-
error?: undefined;
|
|
77
77
|
}>>;
|
|
78
78
|
export {};
|
|
@@ -8,8 +8,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
|
|
|
8
8
|
* resolveEngine's credential detection.
|
|
9
9
|
*/
|
|
10
10
|
export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
|
-
ok?: undefined;
|
|
12
11
|
error: string;
|
|
12
|
+
ok?: undefined;
|
|
13
13
|
scope?: undefined;
|
|
14
14
|
} | {
|
|
15
15
|
ok: boolean;
|
|
@@ -17,8 +17,8 @@ export type FirstTouchSyncResult = {
|
|
|
17
17
|
* browser's `an_ft` and `an_lt` again, so Safari keeps them their 30 days.
|
|
18
18
|
*/
|
|
19
19
|
export declare function createFirstTouchHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
20
|
-
error: string;
|
|
21
20
|
reason?: undefined;
|
|
21
|
+
error: string;
|
|
22
22
|
stored?: undefined;
|
|
23
23
|
firstTouch?: undefined;
|
|
24
24
|
} | {
|
|
@@ -32,8 +32,8 @@ export declare function createFirstTouchHandler(): import("h3").EventHandlerWith
|
|
|
32
32
|
reason: "expired";
|
|
33
33
|
firstTouch?: undefined;
|
|
34
34
|
} | {
|
|
35
|
-
error?: undefined;
|
|
36
35
|
reason?: undefined;
|
|
36
|
+
error?: undefined;
|
|
37
37
|
stored: true;
|
|
38
38
|
firstTouch: FirstTouchAttribution;
|
|
39
39
|
}>>;
|
|
@@ -6,5 +6,14 @@ export declare const CHUNK_RECOVERY_CACHE_BUSTER_PARAM = "__agentNativeChunkReco
|
|
|
6
6
|
export declare const CHUNK_RECOVERY_PATH_SUFFIX = "/__agent-native-chunk-recovery";
|
|
7
7
|
export declare const CHUNK_RECOVERY_ORIGINAL_HASH_PARAM = "__agentNativeRecoveryOriginalHash";
|
|
8
8
|
export declare const ROUTE_WARMUP_PRELOAD_ATTRIBUTE = "data-agent-native-route-warmup";
|
|
9
|
+
export declare const EXTERNAL_EMBED_WINDOW_KEY = "__AGENT_NATIVE_EXTERNAL_EMBED";
|
|
10
|
+
export declare const STALE_CHUNK_EXHAUSTED_KEY = "__agentNativeStaleChunkRecoveryExhausted";
|
|
11
|
+
/**
|
|
12
|
+
* Fired on `window` the first time a stale chunk could not be recovered by a
|
|
13
|
+
* reload. The shared noise rules drop the raw dynamic-import failure because
|
|
14
|
+
* recovery handles it; this is the one signal that it did not, so a deploy whose
|
|
15
|
+
* chunks are gone for everyone stays visible.
|
|
16
|
+
*/
|
|
17
|
+
export declare const STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT = "agent-native:stale-chunk-recovery-exhausted";
|
|
9
18
|
export declare function isLegacyChunkRecoveryRequest(url: URL): boolean;
|
|
10
19
|
export declare const ROUTE_CHUNK_RECOVERY_BOOTSTRAP_SCRIPT: string;
|
|
@@ -6,6 +6,20 @@ export const CHUNK_RECOVERY_CACHE_BUSTER_PARAM = "__agentNativeChunkRecoveryRequ
|
|
|
6
6
|
export const CHUNK_RECOVERY_PATH_SUFFIX = "/__agent-native-chunk-recovery";
|
|
7
7
|
export const CHUNK_RECOVERY_ORIGINAL_HASH_PARAM = "__agentNativeRecoveryOriginalHash";
|
|
8
8
|
export const ROUTE_WARMUP_PRELOAD_ATTRIBUTE = "data-agent-native-route-warmup";
|
|
9
|
+
// Set by embedApp's transplant path, which runs the app document inside the MCP
|
|
10
|
+
// host's sandbox frame. There, every recovery URL is on the host's origin, so
|
|
11
|
+
// recovery can only navigate the frame to a page the host blocks.
|
|
12
|
+
export const EXTERNAL_EMBED_WINDOW_KEY = "__AGENT_NATIVE_EXTERNAL_EMBED";
|
|
13
|
+
// Where recovery records that it gave up. The inline bootstrap writes it too, so
|
|
14
|
+
// a failure before the client bundle loads is still reported once it does.
|
|
15
|
+
export const STALE_CHUNK_EXHAUSTED_KEY = "__agentNativeStaleChunkRecoveryExhausted";
|
|
16
|
+
/**
|
|
17
|
+
* Fired on `window` the first time a stale chunk could not be recovered by a
|
|
18
|
+
* reload. The shared noise rules drop the raw dynamic-import failure because
|
|
19
|
+
* recovery handles it; this is the one signal that it did not, so a deploy whose
|
|
20
|
+
* chunks are gone for everyone stays visible.
|
|
21
|
+
*/
|
|
22
|
+
export const STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT = "agent-native:stale-chunk-recovery-exhausted";
|
|
9
23
|
export function isLegacyChunkRecoveryRequest(url) {
|
|
10
24
|
const values = url.searchParams.getAll(CHUNK_RECOVERY_QUERY_PARAM);
|
|
11
25
|
return values.length === 1 && values[0] === CHUNK_RECOVERY_QUERY_VALUE;
|
|
@@ -18,12 +32,23 @@ const pathSuffix=${JSON.stringify(CHUNK_RECOVERY_PATH_SUFFIX)};
|
|
|
18
32
|
const originalHashParam=${JSON.stringify(CHUNK_RECOVERY_ORIGINAL_HASH_PARAM)};
|
|
19
33
|
const reloadKey=${JSON.stringify(STALE_CHUNK_RELOAD_AT_KEY)};
|
|
20
34
|
const warmupAttr=${JSON.stringify(ROUTE_WARMUP_PRELOAD_ATTRIBUTE)};
|
|
35
|
+
const externalEmbedKey=${JSON.stringify(EXTERNAL_EMBED_WINDOW_KEY)};
|
|
36
|
+
const exhaustedKey=${JSON.stringify(STALE_CHUNK_EXHAUSTED_KEY)};
|
|
37
|
+
const exhaustedEvent=${JSON.stringify(STALE_CHUNK_RECOVERY_EXHAUSTED_EVENT)};
|
|
21
38
|
document.addEventListener("error",event=>{
|
|
22
39
|
const target=event.target;
|
|
23
40
|
const tag=target?.tagName?.toUpperCase();
|
|
24
41
|
const failedModuleScript=tag==="SCRIPT"&&target.type?.toLowerCase()==="module";
|
|
25
42
|
const failedModulePreload=tag==="LINK"&&/(?:^|\\s)modulepreload(?:\\s|$)/i.test(target.getAttribute?.("rel")||target.rel||"")&&!target.hasAttribute?.(warmupAttr);
|
|
26
43
|
if(!failedModuleScript&&!failedModulePreload||/AgentNativeDesktop/i.test(navigator.userAgent))return;
|
|
44
|
+
if(window[externalEmbedKey]){
|
|
45
|
+
if(!window[exhaustedKey]){
|
|
46
|
+
const detail={reason:"external-embed"};
|
|
47
|
+
window[exhaustedKey]=detail;
|
|
48
|
+
if(typeof window.dispatchEvent==="function"&&typeof CustomEvent==="function")window.dispatchEvent(new CustomEvent(exhaustedEvent,{detail}));
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
27
52
|
if(/^(localhost|127\\.0\\.0\\.1|\\[::1\\])$/.test(location.hostname))return;
|
|
28
53
|
const url=new URL(location.href);
|
|
29
54
|
if(url.pathname.endsWith(pathSuffix)||url.pathname.endsWith(pathSuffix+"/")||url.searchParams.get(queryParam)===queryValue)return;
|
|
@@ -2,6 +2,7 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
2
2
|
resourceType: string;
|
|
3
3
|
resourceId: string;
|
|
4
4
|
}, {
|
|
5
|
+
role?: undefined;
|
|
5
6
|
ownerEmail: any;
|
|
6
7
|
visibility: any;
|
|
7
8
|
shares: any[];
|
|
@@ -11,7 +12,6 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
11
12
|
supportsGroupShares?: boolean;
|
|
12
13
|
};
|
|
13
14
|
orgId?: undefined;
|
|
14
|
-
role?: undefined;
|
|
15
15
|
agentReadable?: undefined;
|
|
16
16
|
} | {
|
|
17
17
|
ownerEmail: string;
|
|
@@ -172,6 +172,7 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
173
|
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
|
+
- **Do** throw `NoWriteError(message, { errorCode, statusCode })` instead of `fail()` for a rejection raised before the action's first write, such as an edit that matches nothing or a missing target. The agent then retries with corrected arguments instead of stopping on a write whose outcome is unknown. Throw it only before the first write: the framework rejects the claim when a nested write action ran, but it cannot see the handler's own writes.
|
|
175
176
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
177
|
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
178
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
@@ -172,6 +172,7 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
173
|
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
|
+
- **Do** throw `NoWriteError(message, { errorCode, statusCode })` instead of `fail()` for a rejection raised before the action's first write, such as an edit that matches nothing or a missing target. The agent then retries with corrected arguments instead of stopping on a write whose outcome is unknown. Throw it only before the first write: the framework rejects the claim when a nested write action ran, but it cannot see the handler's own writes.
|
|
175
176
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
177
|
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
178
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
@@ -287,6 +287,12 @@ not already settled by Steve's written direction, flag it to Steve and do not
|
|
|
287
287
|
merge until he decides. A routine bug fix or bounded improvement within an
|
|
288
288
|
established workflow is not a major decision.
|
|
289
289
|
|
|
290
|
+
Keep the PR thread silent while a product, UX, or app-owner decision is
|
|
291
|
+
unresolved. Do not comment or reply to request, announce, or explain that the
|
|
292
|
+
decision is needed; record the gate only in the recap. This remains true after
|
|
293
|
+
an author update, so do not send a thanks-only or status-only reply while the
|
|
294
|
+
decision is pending.
|
|
295
|
+
|
|
290
296
|
The app-owner map is for approval exceptions only; an owner approval is not a
|
|
291
297
|
merge requirement:
|
|
292
298
|
|
|
@@ -455,12 +461,29 @@ code or screenshot requests. This also applies when another comment or bot
|
|
|
455
461
|
event is newer than Steve's request; bot activity alone does not reopen the
|
|
456
462
|
handoff.
|
|
457
463
|
|
|
464
|
+
On a contributor-owned PR, do not draft a follow-up unless the author has made
|
|
465
|
+
a substantive update since Steve's last message. Begin a first reply by
|
|
466
|
+
thanking the author for the PR; if replying after an author update, begin by
|
|
467
|
+
thanking them for that update. Never write “I have no further feedback” or
|
|
468
|
+
another vague closing. State the disposition, the exact next action, and who
|
|
469
|
+
owns it. If a product, UX, or app-owner decision is still needed, do not post
|
|
470
|
+
an author-facing status comment or ask the author to get that review; keep the
|
|
471
|
+
thread silent and report the decision gate in the recap. Never comment that a
|
|
472
|
+
product owner needs to review the PR. For a contributor thread paused by an
|
|
473
|
+
unresolved gate, an internal decision does not count as an author update, and
|
|
474
|
+
an author update made while the decision is pending does not qualify for a
|
|
475
|
+
follow-up. Keep the thread silent until the decision is resolved and the author
|
|
476
|
+
makes a substantive update after resolution; then thank them for that update
|
|
477
|
+
and state the resolved disposition, exact next action, and owner. This
|
|
478
|
+
contributor-update gate does not govern review feedback on the active user's
|
|
479
|
+
own PR; follow the own-PR review-update rule in `ship`.
|
|
480
|
+
|
|
458
481
|
If no prior Steve request is awaiting an update and this would be Steve's first
|
|
459
482
|
comment on that PR, begin the draft with a warm thank-you that tags the author
|
|
460
483
|
using the exact GitHub `author.login` from the live PR metadata, for example:
|
|
461
484
|
|
|
462
485
|
```text
|
|
463
|
-
Thanks for the
|
|
486
|
+
Thanks for the PR, @username!
|
|
464
487
|
|
|
465
488
|
Couple requests. Can you please [make the specific update] so [the intended
|
|
466
489
|
behavior or outcome] is preserved?
|
|
@@ -473,8 +496,9 @@ the concrete request in a friendly, conversational way (for example, "Couple
|
|
|
473
496
|
requests. Can you please ...?"). Keep the technical request specific,
|
|
474
497
|
actionable, and supported by the diff or source evidence; ask for a regression
|
|
475
498
|
test when appropriate to the code change rather than as a blanket requirement.
|
|
476
|
-
|
|
477
|
-
|
|
499
|
+
For a follow-up after an author update, thank them for that update rather than
|
|
500
|
+
repeating the first-contact thanks. Do not draft a duplicate request when an
|
|
501
|
+
existing Steve comment already covers it; link or summarize that request
|
|
478
502
|
instead. Drafts are for the user to review and are never posted by this skill
|
|
479
503
|
unless the current invocation explicitly authorizes posting.
|
|
480
504
|
|
|
@@ -172,6 +172,7 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
173
|
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
|
+
- **Do** throw `NoWriteError(message, { errorCode, statusCode })` instead of `fail()` for a rejection raised before the action's first write, such as an edit that matches nothing or a missing target. The agent then retries with corrected arguments instead of stopping on a write whose outcome is unknown. Throw it only before the first write: the framework rejects the claim when a nested write action ran, but it cannot see the handler's own writes.
|
|
175
176
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
177
|
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
178
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
@@ -172,6 +172,7 @@ Everything else — CRUD, settings, search, list/detail reads, auth state, anyth
|
|
|
172
172
|
- **Do** signal failure by throwing, using `fail()` for expected caller-readable failures. Never return `{ error: ... }` as a failure result: a normal return is treated as a successful action. Reserve bare errors for unexpected internal faults.
|
|
173
173
|
- **Do** use `fail()` rather than a bare `Error` for caller-readable failures. It carries the action's message, `errorCode`, and `details` to the browser and to agents; unexpected errors become a generic 500. Reserve bare throws for internal faults.
|
|
174
174
|
- **Do** give `fail()` the status that matches the cause: it defaults to `400`, and `useActionQuery` retries only `429`, `502`, `503`, and `504`. Mutations do not retry; use transient statuses only for retryable failures.
|
|
175
|
+
- **Do** throw `NoWriteError(message, { errorCode, statusCode })` instead of `fail()` for a rejection raised before the action's first write, such as an edit that matches nothing or a missing target. The agent then retries with corrected arguments instead of stopping on a write whose outcome is unknown. Throw it only before the first write: the framework rejects the claim when a nested write action ran, but it cannot see the handler's own writes.
|
|
175
176
|
- **Do** render `actionErrorMessage(error) ?? yourCopy` in UI, never bare `error.message`. The message keeps an `Action <name> failed:` prefix that belongs in a console, so a toast built from it reads "Action update-brand-kit failed: That name is taken." The helper returns only what the action wrote, and `undefined` when nothing did (network drop, proxy HTML page), which is why the fallback is not optional.
|
|
176
177
|
- **Do** pass a real `errorCode` when the agent should branch on the failure rather than re-read it. Codes other than the default `action_failed` are appended to the tool result as `(errorCode: not_found)`, on both the in-app agent and MCP. `details` follow as an `Error details: {...}` JSON line, and as `structuredContent.error` over MCP, capped at 2,000 characters, so keep them to what the agent acts on next: ids, limits, field names. The status never reaches either.
|
|
177
178
|
- **Don't** re-export actions as REST — `/_agent-native/actions/:name` is already the REST surface; duplicating it under `/api/*` hides the operation from agents.
|
|
@@ -162,8 +162,15 @@ actions for Forms, Content, Clips, Slides, and Design. The action should return
|
|
|
162
162
|
concise structured content plus the link; it should not dump large catalogs or
|
|
163
163
|
HTML.
|
|
164
164
|
|
|
165
|
-
Compatibility target: build to the standard once, not per-client shims.
|
|
166
|
-
Apps
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
Compatibility target: build to the standard once, not per-client shims.
|
|
166
|
+
Hosts that render MCP Apps inline today include Claude chat on web, desktop, and
|
|
167
|
+
mobile, plus the Claude Desktop Code tab; ChatGPT; VS Code GitHub Copilot (1.109+);
|
|
168
|
+
Microsoft 365 Copilot; Cursor (2.6+); Goose; Postman; MCPJam; Codex Desktop (26.616+);
|
|
169
|
+
and T3 Code (in Codex threads). Desktop Code rendering is observed in
|
|
170
|
+
[widget bug reports](https://github.com/anthropics/claude-code/issues/103809).
|
|
171
|
+
Treat the Claude Code terminal, Cowork, and Code on claude.ai web as link-out hosts.
|
|
172
|
+
[Cowork doesn't declare MCP Apps yet](https://github.com/anthropics/claude-code/issues/103924),
|
|
173
|
+
and using it on the same connector can make inline apps disappear in Chat until
|
|
174
|
+
you reconnect the connector. Host support varies by plan, release channel, and client version,
|
|
175
|
+
so keep the deep link fallback. See the community-maintained
|
|
176
|
+
[MCP client matrix](https://modelcontextprotocol.io/extensions/client-matrix).
|