@agent-native/core 0.144.0 → 0.144.2
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +1 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +36 -14
- package/corpus/templates/clips/app/components/player/player-controls.tsx +0 -3
- package/corpus/templates/clips/app/components/player/scrubber.tsx +2 -33
- package/corpus/templates/clips/app/components/player/video-player.tsx +58 -11
- package/corpus/templates/clips/changelog/2026-08-06-trimmed-clips-play-on-a-clean-timeline-without-cut-markers.md +6 -0
- package/corpus/templates/dispatch/AGENTS.md +8 -1
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -0
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +30 -15
- package/corpus/templates/dispatch/app/i18n-data.ts +131 -0
- package/corpus/templates/dispatch/app/routes/admin._index.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.agents.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.approvals.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.apps.$appId.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.apps.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.audit.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.automations.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.destinations.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.dreams.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.identities.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.integrations.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.messaging.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.metrics.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.new-app.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.operations.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.thread-debug.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.transactional-email.$appId.$id.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.transactional-email.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.vault.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.workspace.tsx +1 -0
- package/corpus/templates/dispatch/changelog/2026-08-06-dispatch-keeps-everyday-navigation-focused-while-management-.md +6 -0
- package/corpus/templates/slides/AGENTS.md +10 -12
- package/corpus/templates/slides/actions/export-pptx.ts +68 -0
- package/corpus/templates/slides/actions/get-deck.ts +12 -0
- package/corpus/templates/slides/actions/import-file.ts +130 -55
- package/corpus/templates/slides/actions/import-pptx.ts +108 -8
- package/corpus/templates/slides/actions/patch-deck.ts +51 -0
- package/corpus/templates/slides/actions/update-slide.ts +29 -3
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +11 -0
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +72 -11
- package/corpus/templates/slides/app/lib/import-uploaded-deck.ts +89 -0
- package/corpus/templates/slides/app/pages/Index.tsx +85 -18
- package/corpus/templates/slides/changelog/2026-08-06-uploaded-decks-keep-their-slides-and-visuals-when-you-improv.md +6 -0
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +13 -2
- package/corpus/templates/slides/server/lib/chat-attachments.ts +2 -2
- package/corpus/templates/slides/server/lib/source-import.ts +167 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +8 -0
- package/dist/client/analytics/index.d.ts +1 -1
- package/dist/client/analytics/index.js +1 -1
- package/dist/client/analytics-session.d.ts +13 -0
- package/dist/client/analytics-session.js +57 -0
- package/dist/client/analytics.d.ts +1 -0
- package/dist/client/analytics.js +1 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/track.js +7 -0
- package/dist/client/use-action.js +10 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/jobs/scheduler.js +79 -23
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.js +4 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-run-context.d.ts +4 -2
- package/dist/server/agent-run-context.js +5 -3
- package/dist/server/core-routes-plugin.js +2 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +5 -1
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +20 -3
- package/dist/tracking/index.d.ts +1 -1
- package/dist/tracking/providers.js +12 -1
- package/dist/tracking/registry.d.ts +12 -2
- package/dist/tracking/registry.js +27 -3
- package/dist/tracking/types.d.ts +8 -0
- package/docs/content/creating-templates.mdx +1 -1
- package/docs/content/getting-started-actions.mdx +38 -189
- package/docs/content/getting-started-database.mdx +80 -164
- package/docs/content/getting-started-pages.mdx +44 -155
- package/docs/content/getting-started.mdx +26 -106
- package/docs/content/observability.mdx +16 -0
- package/docs/content/tracking.mdx +17 -2
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +5 -1
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +20 -3
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export type SourceImportFormat = "pdf" | "pptx";
|
|
2
|
+
|
|
3
|
+
export interface SourceImportSlideSnapshot {
|
|
4
|
+
id: string;
|
|
5
|
+
text: string;
|
|
6
|
+
notes: string;
|
|
7
|
+
imageUrls: string[];
|
|
8
|
+
editableText: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SourceImportMetadata {
|
|
12
|
+
mode: "source-preserving";
|
|
13
|
+
format: SourceImportFormat;
|
|
14
|
+
fidelity: "source-faithful" | "partial";
|
|
15
|
+
importedAt: string;
|
|
16
|
+
slideCount: number;
|
|
17
|
+
slideIds: string[];
|
|
18
|
+
slides: SourceImportSlideSnapshot[];
|
|
19
|
+
imagesSkipped?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buildSourceImportMetadata(args: {
|
|
23
|
+
format: SourceImportFormat;
|
|
24
|
+
importedAt?: string;
|
|
25
|
+
slides: SourceImportSlideSnapshot[];
|
|
26
|
+
imagesSkipped?: number;
|
|
27
|
+
}): SourceImportMetadata {
|
|
28
|
+
const importedAt = args.importedAt ?? new Date().toISOString();
|
|
29
|
+
const imagesSkipped = args.imagesSkipped ?? 0;
|
|
30
|
+
return {
|
|
31
|
+
mode: "source-preserving",
|
|
32
|
+
format: args.format,
|
|
33
|
+
fidelity: imagesSkipped > 0 ? "partial" : "source-faithful",
|
|
34
|
+
importedAt,
|
|
35
|
+
slideCount: args.slides.length,
|
|
36
|
+
slideIds: args.slides.map((slide) => slide.id),
|
|
37
|
+
slides: args.slides,
|
|
38
|
+
...(imagesSkipped > 0 ? { imagesSkipped } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function mergeSourceImportMetadata(
|
|
43
|
+
existing: SourceImportMetadata | null,
|
|
44
|
+
incoming: SourceImportMetadata,
|
|
45
|
+
): SourceImportMetadata {
|
|
46
|
+
if (!existing) return incoming;
|
|
47
|
+
if (existing.format !== incoming.format) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Cannot append a ${incoming.format.toUpperCase()} source import to a ${existing.format.toUpperCase()} source-imported deck. Import matching source formats separately so every slide keeps its provenance.`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const slidesById = new Map<string, SourceImportSlideSnapshot>();
|
|
54
|
+
for (const slide of existing.slides) slidesById.set(slide.id, slide);
|
|
55
|
+
for (const slide of incoming.slides) slidesById.set(slide.id, slide);
|
|
56
|
+
const slides = [...slidesById.values()];
|
|
57
|
+
const imagesSkipped =
|
|
58
|
+
(existing.imagesSkipped ?? 0) + (incoming.imagesSkipped ?? 0);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
...incoming,
|
|
62
|
+
fidelity:
|
|
63
|
+
existing.fidelity === "partial" || incoming.fidelity === "partial"
|
|
64
|
+
? "partial"
|
|
65
|
+
: "source-faithful",
|
|
66
|
+
slideCount: slides.length,
|
|
67
|
+
slideIds: slides.map((slide) => slide.id),
|
|
68
|
+
slides,
|
|
69
|
+
...(imagesSkipped > 0 ? { imagesSkipped } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function sourceImportForDeck(
|
|
74
|
+
value: unknown,
|
|
75
|
+
): SourceImportMetadata | null {
|
|
76
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
77
|
+
const record = value as Partial<SourceImportMetadata>;
|
|
78
|
+
if (
|
|
79
|
+
record.mode !== "source-preserving" ||
|
|
80
|
+
(record.format !== "pdf" && record.format !== "pptx") ||
|
|
81
|
+
!Array.isArray(record.slides)
|
|
82
|
+
) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return record as SourceImportMetadata;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function extractImageUrls(content: string): string[] {
|
|
89
|
+
const urls: string[] = [];
|
|
90
|
+
const seen = new Set<string>();
|
|
91
|
+
const imagePattern = /<img\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi;
|
|
92
|
+
for (const match of content.matchAll(imagePattern)) {
|
|
93
|
+
const url = match[1]
|
|
94
|
+
?.replace(/&/g, "&")
|
|
95
|
+
.replace(/"/g, '"')
|
|
96
|
+
.trim();
|
|
97
|
+
if (url && !seen.has(url)) {
|
|
98
|
+
seen.add(url);
|
|
99
|
+
urls.push(url);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return urls;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function sourceWords(value: string): Set<string> {
|
|
106
|
+
return new Set(
|
|
107
|
+
value
|
|
108
|
+
.toLowerCase()
|
|
109
|
+
.match(/[a-z0-9][a-z0-9'-]{3,}/g)
|
|
110
|
+
?.map((word) => word.replace(/^['-]+|['-]+$/g, "")) ?? [],
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Protects an imported deck from the common "make it prettier" failure mode:
|
|
116
|
+
* the agent replaces a source slide with a generic card and silently drops the
|
|
117
|
+
* original artwork or most of its factual copy. Explicit rewrite requests can
|
|
118
|
+
* opt out with preserveSource=false.
|
|
119
|
+
*/
|
|
120
|
+
export function assertSourceSlidePreserved(args: {
|
|
121
|
+
metadata: SourceImportMetadata | null;
|
|
122
|
+
slideId: string;
|
|
123
|
+
nextContent?: string;
|
|
124
|
+
nextNotes?: string;
|
|
125
|
+
preserveSource?: boolean;
|
|
126
|
+
}): void {
|
|
127
|
+
if (args.preserveSource === false || !args.metadata) return;
|
|
128
|
+
const snapshot = args.metadata.slides.find(
|
|
129
|
+
(slide) => slide.id === args.slideId,
|
|
130
|
+
);
|
|
131
|
+
if (!snapshot) return;
|
|
132
|
+
|
|
133
|
+
if (
|
|
134
|
+
args.nextNotes !== undefined &&
|
|
135
|
+
snapshot.notes.length > 0 &&
|
|
136
|
+
args.nextNotes !== snapshot.notes
|
|
137
|
+
) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Source-preserving edit would remove or change imported speaker notes on slide ${args.slideId}. Preserve the source notes, or pass preserveSource=false only when the user explicitly asks for a rewrite.`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (args.nextContent === undefined) return;
|
|
144
|
+
|
|
145
|
+
const nextImageUrls = new Set(extractImageUrls(args.nextContent));
|
|
146
|
+
const missingImages = snapshot.imageUrls.filter(
|
|
147
|
+
(url) => !nextImageUrls.has(url),
|
|
148
|
+
);
|
|
149
|
+
if (missingImages.length > 0) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Source-preserving edit would remove ${missingImages.length} original image(s) from slide ${args.slideId}. Keep every existing source image, or pass preserveSource=false only when the user explicitly asks for a rewrite.`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!snapshot.editableText) return;
|
|
156
|
+
const originalWords = sourceWords(snapshot.text);
|
|
157
|
+
if (originalWords.size < 12) return;
|
|
158
|
+
const nextWords = sourceWords(args.nextContent.replace(/<[^>]+>/g, " "));
|
|
159
|
+
const retainedWords = [...originalWords].filter((word) =>
|
|
160
|
+
nextWords.has(word),
|
|
161
|
+
).length;
|
|
162
|
+
if (retainedWords / originalWords.size < 0.35) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Source-preserving edit would drop most of the original factual copy from slide ${args.slideId}. Preserve the source text, or pass preserveSource=false only when the user explicitly asks for a rewrite.`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -15,6 +15,8 @@ const INITIAL_TOOL_NAMES = [
|
|
|
15
15
|
"get-layout-overflows",
|
|
16
16
|
"list-decks",
|
|
17
17
|
"get-deck",
|
|
18
|
+
"get-design-system",
|
|
19
|
+
"get-workspace-defaults",
|
|
18
20
|
"get-deck-reference-context",
|
|
19
21
|
"create-deck",
|
|
20
22
|
"add-slide",
|
|
@@ -50,6 +52,12 @@ export default createAgentChatPlugin({
|
|
|
50
52
|
prepareRequest: prepareSlidesChatAttachments,
|
|
51
53
|
systemPrompt: `You are an AI deck assistant. You create, edit, import, export, style, share, and navigate decks through actions and shared application state. For a newly created presentation, use create-deck with slides: [] only when you are creating the deck yourself, then add-slide sequentially with full rendered HTML. The legacy generate-slides-ai action returns Markdown drafts and is not part of the persisted presentation workflow.
|
|
52
54
|
|
|
55
|
+
When the user asks to improve, beautify, restyle, or make an uploaded/existing deck on-brand, treat it as an in-place source-preserving edit unless the user explicitly asks to rewrite the story or change slide count. First call view-screen when the active deck is unclear, then get-deck. If get-deck.sourceImport exists, preserve its slide count, order, IDs, factual copy, notes, images, charts, tables, diagrams, freeform objects, and source aspect ratio. Use update-slide on existing slide IDs. Do not use add-slide, delete, reorder, or replace source imagery with generic cards for this workflow. Verify the same slide IDs and count with get-deck after editing. If sourceImport.fidelity is partial or imagesSkipped is nonzero, stop and report the exact fidelity warning instead of claiming a reliable improvement.
|
|
56
|
+
|
|
57
|
+
For source-faithful PDF slides, keep the original full-page image and style around it with restrained design-system chrome such as a frame, edge treatment, caption, or safe overlay that does not obscure source content; never OCR-reconstruct the page from extracted text. For PPTX slides, preserve the imported positioned HTML and every uploaded source image. The update-slide action enforces these preservation rules by default; pass preserveSource=false only when the user explicitly requests a rewrite of that slide.
|
|
58
|
+
|
|
59
|
+
If the deck has designSystemId, call get-design-system before writing and follow its exact agentContext tokens, assets, and custom instructions. If the user asks for on-brand styling and no design system is linked, call get-workspace-defaults, link its usable design system with patch-deck, then call get-design-system. Do not improvise a generic Builder-like palette when configured Builder.io design-system context is available.
|
|
60
|
+
|
|
53
61
|
Layout-fit workflow is strict. When the user asks to fix overflow, first call view-screen and inspect the deck-wide layout-fit section. If it says measurements are unknown, do not claim the deck fits. Call get-layout-overflows when you need the structured per-slide results. Read the affected slides with get-deck, then make one bounded structural repair pass with one patch-slide operation per affected slide in a single patch-deck call. Wait for the action result and verify the persisted HTML with get-deck before saying it is fixed. If a fresh measurement still reports overflow, make at most one focused follow-up repair based on that measurement; never loop, repeatedly re-measure, or claim success after a chat response alone.
|
|
54
62
|
|
|
55
63
|
Fit means the main content fits the native content area. A small outer-wrapper spill is tolerated by the measurement, but cards, text, columns, and other visible content must fit. Never use zoom, transform: scale(), overflow: hidden/scroll, clipping, or a smaller-than-16px body font to hide overflow. Preserve manually positioned freeform objects and their data-slide-object-id values; repair normal-flow structure, copy, gaps, or slide padding instead. A successful action result must include the affected slide IDs; if it does not, report that no verified write occurred.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
|
|
1
|
+
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
|
|
2
2
|
export { track } from "../track.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackEvent, trackSessionStatus, } from "../analytics.js";
|
|
1
|
+
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackEvent, trackSessionStatus, } from "../analytics.js";
|
|
2
2
|
export { track } from "../track.js";
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
export declare function getOrCreateAnalyticsAnonymousId(): string | undefined;
|
|
2
|
+
/**
|
|
3
|
+
* Pin the browser session id an app wants every analytics event, agent run, and
|
|
4
|
+
* action request in this tab to carry. Returns the stored id, or `undefined`
|
|
5
|
+
* when there is no browser storage to pin it in (SSR) — the caller can tell
|
|
6
|
+
* those apart.
|
|
7
|
+
*
|
|
8
|
+
* Throws on an id the transport cannot carry rather than storing it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setAnalyticsSessionId(sessionId: string): string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Drop a pinned id and let the next read start a fresh rotating session.
|
|
13
|
+
*/
|
|
14
|
+
export declare function clearAnalyticsSessionId(): void;
|
|
2
15
|
export declare function getOrCreateAnalyticsSessionId(): string | undefined;
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { serializeAnalyticsAnonymousIdCookie } from "../shared/analytics-anonymous-id.js";
|
|
2
2
|
const ANONYMOUS_ID_STORAGE_KEY = "agent-native.anonymous_id";
|
|
3
3
|
const SESSION_ID_STORAGE_KEY = "agent-native.session_id";
|
|
4
|
+
const SESSION_ID_PIN_STORAGE_KEY = "agent-native.session_id_pin";
|
|
4
5
|
const SESSION_LAST_ACTIVITY_STORAGE_KEY = "agent-native.session_last_activity";
|
|
5
6
|
// 30-minute idle timeout matches GA4 / Mixpanel defaults: a tab left open
|
|
6
7
|
// overnight starts a new session in the morning instead of stretching one visit.
|
|
7
8
|
const SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
9
|
+
// The server drops the session header at 128 characters
|
|
10
|
+
// (`readBrowserSessionIdHeader`), and `fetch` throws on a header value with
|
|
11
|
+
// non-ASCII or newline bytes. Both are rejected at the point the id is set so a
|
|
12
|
+
// bad id fails once, loudly, instead of silently unlinking every later request.
|
|
13
|
+
const MAX_SESSION_ID_LENGTH = 127;
|
|
14
|
+
const SAFE_SESSION_ID = /^[!-~]+$/;
|
|
8
15
|
function generateVisitorId() {
|
|
9
16
|
try {
|
|
10
17
|
if (typeof crypto !== "undefined" &&
|
|
@@ -35,6 +42,15 @@ function safeStorageSet(key, value) {
|
|
|
35
42
|
// private browsing / storage disabled -- best-effort
|
|
36
43
|
}
|
|
37
44
|
}
|
|
45
|
+
function safeStorageRemove(key) {
|
|
46
|
+
try {
|
|
47
|
+
window.localStorage.removeItem(key);
|
|
48
|
+
// coercion-ok: storage that refuses writes holds no key to clear, so the caller's end state already holds
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// private browsing / storage disabled -- best-effort
|
|
52
|
+
}
|
|
53
|
+
}
|
|
38
54
|
function syncAnalyticsAnonymousIdCookie(id) {
|
|
39
55
|
try {
|
|
40
56
|
const cookie = serializeAnalyticsAnonymousIdCookie(id);
|
|
@@ -57,10 +73,51 @@ export function getOrCreateAnalyticsAnonymousId() {
|
|
|
57
73
|
syncAnalyticsAnonymousIdCookie(id);
|
|
58
74
|
return id;
|
|
59
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Pin the browser session id an app wants every analytics event, agent run, and
|
|
78
|
+
* action request in this tab to carry. Returns the stored id, or `undefined`
|
|
79
|
+
* when there is no browser storage to pin it in (SSR) — the caller can tell
|
|
80
|
+
* those apart.
|
|
81
|
+
*
|
|
82
|
+
* Throws on an id the transport cannot carry rather than storing it.
|
|
83
|
+
*/
|
|
84
|
+
export function setAnalyticsSessionId(sessionId) {
|
|
85
|
+
const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
86
|
+
if (!trimmed ||
|
|
87
|
+
trimmed.length > MAX_SESSION_ID_LENGTH ||
|
|
88
|
+
!SAFE_SESSION_ID.test(trimmed)) {
|
|
89
|
+
throw new Error(`Invalid analytics session id: expected 1-${MAX_SESSION_ID_LENGTH} printable ASCII characters with no whitespace`);
|
|
90
|
+
}
|
|
91
|
+
if (typeof window === "undefined")
|
|
92
|
+
return undefined;
|
|
93
|
+
safeStorageSet(SESSION_ID_PIN_STORAGE_KEY, trimmed);
|
|
94
|
+
safeStorageSet(SESSION_ID_STORAGE_KEY, trimmed);
|
|
95
|
+
safeStorageSet(SESSION_LAST_ACTIVITY_STORAGE_KEY, String(Date.now()));
|
|
96
|
+
return trimmed;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Drop a pinned id and let the next read start a fresh rotating session.
|
|
100
|
+
*/
|
|
101
|
+
export function clearAnalyticsSessionId() {
|
|
102
|
+
if (typeof window === "undefined")
|
|
103
|
+
return;
|
|
104
|
+
safeStorageRemove(SESSION_ID_PIN_STORAGE_KEY);
|
|
105
|
+
safeStorageRemove(SESSION_ID_STORAGE_KEY);
|
|
106
|
+
safeStorageRemove(SESSION_LAST_ACTIVITY_STORAGE_KEY);
|
|
107
|
+
}
|
|
60
108
|
export function getOrCreateAnalyticsSessionId() {
|
|
61
109
|
if (typeof window === "undefined")
|
|
62
110
|
return undefined;
|
|
63
111
|
const now = Date.now();
|
|
112
|
+
// A pinned id opts out of idle rotation: the app chose it to correlate its
|
|
113
|
+
// own runs, so expiring it mid-workflow would split one correlated session
|
|
114
|
+
// into two that nothing can join back together.
|
|
115
|
+
const pinned = safeStorageGet(SESSION_ID_PIN_STORAGE_KEY);
|
|
116
|
+
if (pinned) {
|
|
117
|
+
safeStorageSet(SESSION_ID_STORAGE_KEY, pinned);
|
|
118
|
+
safeStorageSet(SESSION_LAST_ACTIVITY_STORAGE_KEY, String(now));
|
|
119
|
+
return pinned;
|
|
120
|
+
}
|
|
64
121
|
const lastActivityRaw = safeStorageGet(SESSION_LAST_ACTIVITY_STORAGE_KEY);
|
|
65
122
|
const lastActivity = lastActivityRaw
|
|
66
123
|
? Number.parseInt(lastActivityRaw, 10)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
|
|
1
2
|
import type { SessionReplayOptions, SessionReplayStartResult } from "./session-replay.js";
|
|
2
3
|
export { scrubUrl } from "./url-scrub.js";
|
|
3
4
|
export { addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, type CaptureExceptionContext, type CapturedExceptionEvent, type ExceptionBreadcrumb, type ExceptionLevel, } from "./error-capture.js";
|
package/dist/client/analytics.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { llmConnectionTrackingProperties, } from "../shared/llm-connection.js";
|
|
2
2
|
import { toPostHogExceptionProperties } from "../tracking/posthog-exception.js";
|
|
3
3
|
import { getOrCreateAnalyticsAnonymousId, getOrCreateAnalyticsSessionId, } from "./analytics-session.js";
|
|
4
|
+
export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
|
|
4
5
|
import { fetchAgentEngineStatus, fetchAuthSessionStatus, } from "./client-status-requests.js";
|
|
5
6
|
import { installErrorCapture, } from "./error-capture.js";
|
|
6
7
|
import { isDynamicImportFailureMessage } from "./route-chunk-recovery.js";
|
package/dist/client/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export { DevDatabaseLink, type DevDatabaseLinkProps, } from "./db-admin/DevDatab
|
|
|
18
18
|
export { DbAdminPage } from "./db-admin/DbAdminPage.js";
|
|
19
19
|
export { installRouteChunkRecovery, reloadForStaleChunk, recoverFromStaleChunkError, } from "./route-chunk-recovery.js";
|
|
20
20
|
export { AgentTerminal, type AgentTerminalProps } from "./terminal/index.js";
|
|
21
|
-
export { trackEvent, trackAgentChatLifecycle, trackSessionStatus, configureTracking, setTrackingContentCaptureEnabled, maybeStartSessionReplay, startSessionReplay, stopSessionReplay, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, setSentryUser, setTrackingIdentity, getSessionReplayContext, getSessionReplayUrl, captureError, captureClientException, AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type AgentChatLifecycleEvent, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayOptions, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "./analytics.js";
|
|
21
|
+
export { trackEvent, trackAgentChatLifecycle, trackSessionStatus, configureTracking, setTrackingContentCaptureEnabled, maybeStartSessionReplay, startSessionReplay, stopSessionReplay, getAnalyticsAnonymousId, getAnalyticsSessionId, setAnalyticsSessionId, clearAnalyticsSessionId, getFirstTouchAttribution, setSentryUser, setTrackingIdentity, getSessionReplayContext, getSessionReplayUrl, captureError, captureClientException, AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type AgentChatLifecycleEvent, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayOptions, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "./analytics.js";
|
|
22
22
|
export { track } from "./track.js";
|
|
23
23
|
export { useCollaborativeDoc, isReconcileLeadClient, emailToColor, emailToName, dedupeCollabUsersByEmail, type UseCollaborativeDocOptions, type UseCollaborativeDocResult, type CollabUser, } from "../collab/client.js";
|
|
24
24
|
export { AGENT_CLIENT_ID } from "../collab/agent-identity.js";
|
package/dist/client/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export { DevDatabaseLink, } from "./db-admin/DevDatabaseLink.js";
|
|
|
18
18
|
export { DbAdminPage } from "./db-admin/DbAdminPage.js";
|
|
19
19
|
export { installRouteChunkRecovery, reloadForStaleChunk, recoverFromStaleChunkError, } from "./route-chunk-recovery.js";
|
|
20
20
|
export { AgentTerminal } from "./terminal/index.js";
|
|
21
|
-
export { trackEvent, trackAgentChatLifecycle, trackSessionStatus, configureTracking, setTrackingContentCaptureEnabled, maybeStartSessionReplay, startSessionReplay, stopSessionReplay, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, setSentryUser, setTrackingIdentity, getSessionReplayContext, getSessionReplayUrl, captureError, captureClientException,
|
|
21
|
+
export { trackEvent, trackAgentChatLifecycle, trackSessionStatus, configureTracking, setTrackingContentCaptureEnabled, maybeStartSessionReplay, startSessionReplay, stopSessionReplay, getAnalyticsAnonymousId, getAnalyticsSessionId, setAnalyticsSessionId, clearAnalyticsSessionId, getFirstTouchAttribution, setSentryUser, setTrackingIdentity, getSessionReplayContext, getSessionReplayUrl, captureError, captureClientException,
|
|
22
22
|
// First-party, Sentry-style error capture (auto + manual API).
|
|
23
23
|
AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, } from "./analytics.js";
|
|
24
24
|
export { track } from "./track.js";
|
package/dist/client/track.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getOrCreateAnalyticsSessionId } from "./analytics-session.js";
|
|
1
2
|
import { agentNativePath } from "./api-path.js";
|
|
2
3
|
/**
|
|
3
4
|
* Fire an analytics event from browser/app code. This is the client-side twin
|
|
@@ -35,6 +36,7 @@ export function track(name, properties) {
|
|
|
35
36
|
// Non-serializable properties — drop rather than throw into the caller.
|
|
36
37
|
return Promise.resolve();
|
|
37
38
|
}
|
|
39
|
+
const browserSessionId = getOrCreateAnalyticsSessionId();
|
|
38
40
|
return fetch(agentNativePath("/_agent-native/track"), {
|
|
39
41
|
method: "POST",
|
|
40
42
|
headers: {
|
|
@@ -43,6 +45,11 @@ export function track(name, properties) {
|
|
|
43
45
|
// middleware trusts it as a first-party marker. Matches the convention
|
|
44
46
|
// used by other client writes (application-state, guided-questions).
|
|
45
47
|
"X-Agent-Native-CSRF": "1",
|
|
48
|
+
// Same session the action client and agent chat send, so a client event
|
|
49
|
+
// and the server events from the same visit share one session.
|
|
50
|
+
...(browserSessionId
|
|
51
|
+
? { "X-Agent-Native-Session-Id": browserSessionId }
|
|
52
|
+
: {}),
|
|
46
53
|
},
|
|
47
54
|
body,
|
|
48
55
|
keepalive: true,
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* compatibility.
|
|
23
23
|
*/
|
|
24
24
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
25
|
+
import { getOrCreateAnalyticsSessionId } from "./analytics-session.js";
|
|
25
26
|
import { trackEvent } from "./analytics.js";
|
|
26
27
|
import { agentNativePath } from "./api-path.js";
|
|
27
28
|
import { getBrowserTabId } from "./browser-tab-id.js";
|
|
@@ -192,8 +193,16 @@ async function performActionFetch(name, method, params, options) {
|
|
|
192
193
|
if (buildId)
|
|
193
194
|
headers["X-Agent-Native-Build-Id"] = buildId;
|
|
194
195
|
const tz = resolveUserTimezone();
|
|
195
|
-
if (tz)
|
|
196
|
+
if (tz) {
|
|
196
197
|
headers["x-user-timezone"] = tz;
|
|
198
|
+
}
|
|
199
|
+
// Same browser session the agent chat sends on an agent run, so an action
|
|
200
|
+
// called from the UI and an action the agent calls during that visit land on
|
|
201
|
+
// one `$session_id` in traces and session replay.
|
|
202
|
+
const browserSessionId = getOrCreateAnalyticsSessionId();
|
|
203
|
+
if (browserSessionId) {
|
|
204
|
+
headers["X-Agent-Native-Session-Id"] = browserSessionId;
|
|
205
|
+
}
|
|
197
206
|
const init = {
|
|
198
207
|
method,
|
|
199
208
|
headers,
|
|
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
17
17
|
id?: undefined;
|
|
18
18
|
provider?: undefined;
|
|
19
19
|
} | {
|
|
20
|
-
error?: undefined;
|
|
21
20
|
configured?: undefined;
|
|
22
21
|
connectPath?: undefined;
|
|
23
22
|
url: string;
|
|
24
23
|
id: string;
|
|
25
24
|
provider: string;
|
|
25
|
+
error?: undefined;
|
|
26
26
|
}>;
|
|
27
27
|
export default _default;
|
package/dist/jobs/scheduler.js
CHANGED
|
@@ -14,7 +14,10 @@ export function buildJobContent(meta, body) {
|
|
|
14
14
|
return buildJobResourceContent(meta, body);
|
|
15
15
|
}
|
|
16
16
|
const MAX_CONCURRENT_SCHEDULED_JOBS = 8;
|
|
17
|
+
const MAX_IDENTITY_PREFLIGHTS_PER_TICK = MAX_CONCURRENT_SCHEDULED_JOBS * 4;
|
|
18
|
+
const IDENTITY_FAILURE_RETRY_MS = 5 * 60_000;
|
|
17
19
|
const _activeScheduledJobs = new Set();
|
|
20
|
+
const _preflightingScheduledJobs = new Set();
|
|
18
21
|
// Skip the DB query on every tick if we recently confirmed no jobs exist.
|
|
19
22
|
// `_hasJobsCache` is invalidated whenever a `jobs/*` resource is written or
|
|
20
23
|
// deleted (subscribed below), and refreshed at most every 5 minutes.
|
|
@@ -88,7 +91,7 @@ export async function processRecurringJobs(deps) {
|
|
|
88
91
|
if (!_hasJobsCache)
|
|
89
92
|
return;
|
|
90
93
|
const now = new Date();
|
|
91
|
-
const
|
|
94
|
+
const dueJobCandidates = [];
|
|
92
95
|
for (const resource of jobResources) {
|
|
93
96
|
// Skip non-markdown or .keep files
|
|
94
97
|
if (!resource.path.endsWith(".md"))
|
|
@@ -136,14 +139,56 @@ export async function processRecurringJobs(deps) {
|
|
|
136
139
|
// Skip if body is empty
|
|
137
140
|
if (!body.trim())
|
|
138
141
|
continue;
|
|
142
|
+
if (hasRecentIdentityFailure(meta, now))
|
|
143
|
+
continue;
|
|
139
144
|
const key = `${resource.owner}:${resource.path}`;
|
|
140
|
-
if (_activeScheduledJobs.has(key)
|
|
145
|
+
if (_activeScheduledJobs.has(key) ||
|
|
146
|
+
_preflightingScheduledJobs.has(key)) {
|
|
141
147
|
continue;
|
|
142
|
-
|
|
148
|
+
}
|
|
149
|
+
dueJobCandidates.push({ key, resource, meta, body });
|
|
150
|
+
}
|
|
151
|
+
const preflightCandidates = [];
|
|
152
|
+
for (const candidate of dueJobCandidates) {
|
|
153
|
+
if (_activeScheduledJobs.size >= MAX_CONCURRENT_SCHEDULED_JOBS ||
|
|
154
|
+
preflightCandidates.length >= MAX_IDENTITY_PREFLIGHTS_PER_TICK) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (_activeScheduledJobs.has(candidate.key) ||
|
|
158
|
+
_preflightingScheduledJobs.has(candidate.key)) {
|
|
143
159
|
continue;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
}
|
|
161
|
+
preflightCandidates.push(candidate);
|
|
162
|
+
}
|
|
163
|
+
// Identity checks can reject stale jobs that remain due for admin review.
|
|
164
|
+
// Bound the checks so a large blocked backlog cannot consume the whole
|
|
165
|
+
// invocation or make valid jobs wait behind an unbounded stale queue.
|
|
166
|
+
const dueJobs = [];
|
|
167
|
+
for (const candidate of preflightCandidates) {
|
|
168
|
+
_preflightingScheduledJobs.add(candidate.key);
|
|
169
|
+
try {
|
|
170
|
+
const identity = await resolveBackgroundAutomationIdentity({
|
|
171
|
+
name: candidate.resource.path
|
|
172
|
+
.replace(/^jobs\//, "")
|
|
173
|
+
.replace(/\.md$/, ""),
|
|
174
|
+
meta: candidate.meta,
|
|
175
|
+
body: candidate.body,
|
|
176
|
+
resource: candidate.resource,
|
|
177
|
+
});
|
|
178
|
+
if (!identity.ok) {
|
|
179
|
+
await recordIdentityFailure(candidate.resource, candidate.meta, candidate.body, now, identity.reason);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (_activeScheduledJobs.size >= MAX_CONCURRENT_SCHEDULED_JOBS) {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
_activeScheduledJobs.add(candidate.key);
|
|
186
|
+
reservedJobKeys.add(candidate.key);
|
|
187
|
+
dueJobs.push(candidate);
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
_preflightingScheduledJobs.delete(candidate.key);
|
|
191
|
+
}
|
|
147
192
|
}
|
|
148
193
|
if (dueJobs.length > 0)
|
|
149
194
|
dispatchedAt = Date.now();
|
|
@@ -219,6 +264,33 @@ async function recoverStaleAutomationHistory(owner, path) {
|
|
|
219
264
|
}
|
|
220
265
|
}
|
|
221
266
|
export const jobRunCutOffReason = backgroundRunCutOffReason;
|
|
267
|
+
async function recordIdentityFailure(resource, meta, body, now, reason, historyId) {
|
|
268
|
+
const jobName = resource.path.replace(/^jobs\//, "").replace(/\.md$/, "");
|
|
269
|
+
console.warn(`[recurring-jobs] Skipping job "${jobName}": ${reason}. ` +
|
|
270
|
+
`User/membership no longer valid — leaving cron entry for admin review.`);
|
|
271
|
+
// Keep blocked jobs due so an admin can find them, but do not let their
|
|
272
|
+
// persistent failure consume an execution slot on every scheduler sweep.
|
|
273
|
+
const alreadyRecorded = meta.lastStatus === "skipped" && meta.lastError === reason;
|
|
274
|
+
meta.lastCheck = now.toISOString();
|
|
275
|
+
meta.lastStatus = "skipped";
|
|
276
|
+
meta.lastError = reason;
|
|
277
|
+
if (!alreadyRecorded)
|
|
278
|
+
await updateResource(resource, meta, body);
|
|
279
|
+
if (historyId) {
|
|
280
|
+
await finishAutomationRun(historyId, "error", `Automation did not run: ${reason}. No delivery was confirmed.`);
|
|
281
|
+
}
|
|
282
|
+
return { status: "skipped", error: reason };
|
|
283
|
+
}
|
|
284
|
+
function hasRecentIdentityFailure(meta, now) {
|
|
285
|
+
if (meta.lastStatus !== "skipped" || !meta.lastCheck || !meta.lastError) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
const lastCheckMs = Date.parse(meta.lastCheck);
|
|
289
|
+
const elapsedMs = now.getTime() - lastCheckMs;
|
|
290
|
+
return (Number.isFinite(lastCheckMs) &&
|
|
291
|
+
elapsedMs >= 0 &&
|
|
292
|
+
elapsedMs < IDENTITY_FAILURE_RETRY_MS);
|
|
293
|
+
}
|
|
222
294
|
async function executeJob(resource, meta, body, deps, now, options = {}) {
|
|
223
295
|
const jobName = resource.path.replace(/^jobs\//, "").replace(/\.md$/, "");
|
|
224
296
|
const jobContext = {
|
|
@@ -234,23 +306,7 @@ async function executeJob(resource, meta, body, deps, now, options = {}) {
|
|
|
234
306
|
// failure; leave the cron entry alone so an admin can purge after
|
|
235
307
|
// investigation.
|
|
236
308
|
if (!identity.ok) {
|
|
237
|
-
|
|
238
|
-
`User/membership no longer valid — leaving cron entry for admin review.`);
|
|
239
|
-
// Mark as skipped without resetting nextRun so an admin can find it.
|
|
240
|
-
// `lastRun` is deliberately untouched: the job did not run, and stamping
|
|
241
|
-
// it here made a permanently blocked job look like it ran every minute.
|
|
242
|
-
// Re-writing an unchanged resource on every tick also churns the poll
|
|
243
|
-
// stream, so only persist when the failure state actually changed.
|
|
244
|
-
const alreadyRecorded = meta.lastStatus === "skipped" && meta.lastError === identity.reason;
|
|
245
|
-
meta.lastCheck = now.toISOString();
|
|
246
|
-
meta.lastStatus = "skipped";
|
|
247
|
-
meta.lastError = identity.reason;
|
|
248
|
-
if (!alreadyRecorded)
|
|
249
|
-
await updateResource(resource, meta, body);
|
|
250
|
-
if (options.historyId) {
|
|
251
|
-
await finishAutomationRun(options.historyId, "error", `Automation did not run: ${identity.reason}. No delivery was confirmed.`);
|
|
252
|
-
}
|
|
253
|
-
return { status: "skipped", error: identity.reason };
|
|
309
|
+
return recordIdentityFailure(resource, meta, body, now, identity.reason, options.historyId);
|
|
254
310
|
}
|
|
255
311
|
const jobUserEmail = identity.identity.userEmail;
|
|
256
312
|
const jobOrgId = identity.identity.orgId;
|
|
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
137
137
|
inputSchema: {
|
|
138
138
|
type: string;
|
|
139
139
|
properties: {
|
|
140
|
-
count?: undefined;
|
|
141
140
|
query?: undefined;
|
|
142
141
|
minutes?: undefined;
|
|
143
142
|
limit?: undefined;
|
|
144
143
|
clientHint?: undefined;
|
|
145
144
|
timestamp?: undefined;
|
|
146
145
|
chapterId?: undefined;
|
|
146
|
+
count?: undefined;
|
|
147
147
|
startAt?: undefined;
|
|
148
148
|
endAt?: undefined;
|
|
149
149
|
reason?: undefined;
|
|
@@ -159,7 +159,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
159
159
|
inputSchema: {
|
|
160
160
|
type: string;
|
|
161
161
|
properties: {
|
|
162
|
-
count?: undefined;
|
|
163
162
|
query: {
|
|
164
163
|
type: string;
|
|
165
164
|
description: string;
|
|
@@ -175,6 +174,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
175
174
|
clientHint?: undefined;
|
|
176
175
|
timestamp?: undefined;
|
|
177
176
|
chapterId?: undefined;
|
|
177
|
+
count?: undefined;
|
|
178
178
|
startAt?: undefined;
|
|
179
179
|
endAt?: undefined;
|
|
180
180
|
reason?: undefined;
|
|
@@ -190,7 +190,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
190
190
|
inputSchema: {
|
|
191
191
|
type: string;
|
|
192
192
|
properties: {
|
|
193
|
-
count?: undefined;
|
|
194
193
|
minutes: {
|
|
195
194
|
type: string;
|
|
196
195
|
description: string;
|
|
@@ -200,6 +199,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
200
199
|
clientHint?: undefined;
|
|
201
200
|
timestamp?: undefined;
|
|
202
201
|
chapterId?: undefined;
|
|
202
|
+
count?: undefined;
|
|
203
203
|
startAt?: undefined;
|
|
204
204
|
endAt?: undefined;
|
|
205
205
|
reason?: undefined;
|
|
@@ -216,7 +216,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
216
216
|
type: string;
|
|
217
217
|
required: string[];
|
|
218
218
|
properties: {
|
|
219
|
-
count?: undefined;
|
|
220
219
|
query: {
|
|
221
220
|
type: string;
|
|
222
221
|
description: string;
|
|
@@ -235,6 +234,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
235
234
|
};
|
|
236
235
|
timestamp?: undefined;
|
|
237
236
|
chapterId?: undefined;
|
|
237
|
+
count?: undefined;
|
|
238
238
|
startAt?: undefined;
|
|
239
239
|
endAt?: undefined;
|
|
240
240
|
reason?: undefined;
|
|
@@ -250,7 +250,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
250
250
|
type: string;
|
|
251
251
|
required: string[];
|
|
252
252
|
properties: {
|
|
253
|
-
count?: undefined;
|
|
254
253
|
query?: undefined;
|
|
255
254
|
minutes?: undefined;
|
|
256
255
|
limit?: undefined;
|
|
@@ -264,6 +263,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
264
263
|
description: string;
|
|
265
264
|
};
|
|
266
265
|
chapterId?: undefined;
|
|
266
|
+
count?: undefined;
|
|
267
267
|
startAt?: undefined;
|
|
268
268
|
endAt?: undefined;
|
|
269
269
|
includeMicrophone?: undefined;
|
|
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
314
314
|
type: string;
|
|
315
315
|
required: string[];
|
|
316
316
|
properties: {
|
|
317
|
-
count?: undefined;
|
|
318
317
|
query?: undefined;
|
|
319
318
|
minutes?: undefined;
|
|
320
319
|
limit?: undefined;
|
|
321
320
|
clientHint?: undefined;
|
|
322
321
|
timestamp?: undefined;
|
|
323
322
|
chapterId?: undefined;
|
|
323
|
+
count?: undefined;
|
|
324
324
|
startAt: {
|
|
325
325
|
type: string;
|
|
326
326
|
description: string;
|
|
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
349
349
|
type: string;
|
|
350
350
|
required: string[];
|
|
351
351
|
properties: {
|
|
352
|
-
count?: undefined;
|
|
353
352
|
query?: undefined;
|
|
354
353
|
minutes?: undefined;
|
|
355
354
|
limit?: undefined;
|
|
356
355
|
clientHint?: undefined;
|
|
357
356
|
timestamp?: undefined;
|
|
358
357
|
chapterId?: undefined;
|
|
358
|
+
count?: undefined;
|
|
359
359
|
startAt?: undefined;
|
|
360
360
|
endAt?: undefined;
|
|
361
361
|
reason?: undefined;
|