@agent-native/core 0.196.0 → 0.196.1-nightly-20260928200103
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/clips/actions/create-recording.ts +1 -1
- package/corpus/templates/clips/actions/lib/auto-title-candidates.ts +42 -0
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +1 -1
- package/corpus/templates/clips/actions/lib/transcript-text.ts +16 -0
- package/corpus/templates/clips/actions/list-ai-requests.ts +49 -31
- package/corpus/templates/clips/actions/list-recordings.ts +2 -4
- package/corpus/templates/clips/actions/list-transactional-email-ai-requests.ts +12 -6
- package/corpus/templates/clips/actions/regenerate-title.ts +4 -1
- package/corpus/templates/clips/actions/request-transcript.ts +1 -1
- package/corpus/templates/clips/actions/save-browser-transcript.ts +1 -1
- package/corpus/templates/clips/app/components/editable-recording-title.tsx +1 -1
- package/corpus/templates/clips/app/components/library/recording-card.tsx +1 -1
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +194 -219
- package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +2 -1
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
- package/corpus/templates/clips/server/lib/transactional-email-store.ts +42 -2
- package/corpus/templates/clips/shared/ai-request-status.ts +6 -0
- package/corpus/templates/clips/{actions/lib → shared}/title-source.ts +9 -2
- package/dist/agent/production-agent.js +2 -0
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.js +9 -2
- package/dist/agent/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.js +148 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/chat-threads/store.d.ts +1 -0
- package/dist/chat-threads/store.js +244 -21
- package/dist/client/AgentKitAssistantChat.js +23 -12
- package/dist/client/chat/agentkit-agent-native.js +409 -8
- package/dist/client/chat/agentkit-protocol.js +3 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.js +12 -7
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/realtime-token.d.ts +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
|
@@ -26,9 +26,9 @@ import {
|
|
|
26
26
|
allowsSqlRecordingChunkScratch,
|
|
27
27
|
STORAGE_SETUP_REQUIRED_REASON,
|
|
28
28
|
} from "../server/lib/video-storage.js";
|
|
29
|
+
import { DEFAULT_RECORDING_TITLE } from "../shared/title-source.js";
|
|
29
30
|
import { createRecordingSchema } from "./lib/create-recording-schema.js";
|
|
30
31
|
import { validateRecordingScope } from "./lib/recording-scope.js";
|
|
31
|
-
import { DEFAULT_RECORDING_TITLE } from "./lib/title-source.js";
|
|
32
32
|
|
|
33
33
|
export function classifyInitialUploadFailure(error: unknown): {
|
|
34
34
|
failureCode: RecordingFailureCode;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { and, desc, eq, inArray, isNull, or, sql } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
import { getDb, schema } from "../../server/db/index.js";
|
|
4
|
+
import { ownerEmailMatches } from "../../server/lib/recordings.js";
|
|
5
|
+
import type { AutoTitleCandidate } from "../../shared/ai-request-status.js";
|
|
6
|
+
import {
|
|
7
|
+
AUTO_REPLACEABLE_TITLE_SOURCES,
|
|
8
|
+
DEFAULT_RECORDING_TITLE,
|
|
9
|
+
} from "../../shared/title-source.js";
|
|
10
|
+
import { transcriptHasTextSql } from "./transcript-text.js";
|
|
11
|
+
|
|
12
|
+
export const AUTO_TITLE_CANDIDATE_LIMIT = 50;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The owner's ready recordings that still carry a replaceable title and have
|
|
16
|
+
* transcript text to title from — the SQL form of `isAutoTitleReplaceable`.
|
|
17
|
+
*/
|
|
18
|
+
export function listAutoTitleCandidates(
|
|
19
|
+
ownerEmail: string,
|
|
20
|
+
): Promise<AutoTitleCandidate[]> {
|
|
21
|
+
const r = schema.recordings;
|
|
22
|
+
const t = schema.recordingTranscripts;
|
|
23
|
+
return getDb()
|
|
24
|
+
.select({ id: r.id, createdAt: r.createdAt })
|
|
25
|
+
.from(r)
|
|
26
|
+
.innerJoin(t, eq(t.recordingId, r.id))
|
|
27
|
+
.where(
|
|
28
|
+
and(
|
|
29
|
+
ownerEmailMatches(r.ownerEmail, ownerEmail),
|
|
30
|
+
eq(r.status, "ready"),
|
|
31
|
+
isNull(r.trashedAt),
|
|
32
|
+
or(
|
|
33
|
+
inArray(r.titleSource, [...AUTO_REPLACEABLE_TITLE_SOURCES]),
|
|
34
|
+
sql`TRIM(${r.title}) IN ('', ${DEFAULT_RECORDING_TITLE})`,
|
|
35
|
+
),
|
|
36
|
+
eq(t.status, "ready"),
|
|
37
|
+
transcriptHasTextSql(t),
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
.orderBy(desc(r.createdAt))
|
|
41
|
+
.limit(AUTO_TITLE_CANDIDATE_LIMIT);
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { sql, type SQL } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
import { schema } from "../../server/db/index.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* True when a transcript row carries any spoken text, in either the flat text
|
|
7
|
+
* or at least one non-empty segment. NULL for a missing (left-joined) row.
|
|
8
|
+
*/
|
|
9
|
+
export function transcriptHasTextSql(
|
|
10
|
+
transcripts: typeof schema.recordingTranscripts = schema.recordingTranscripts,
|
|
11
|
+
): SQL<boolean | null> {
|
|
12
|
+
return sql<boolean | null>`(
|
|
13
|
+
TRIM(${transcripts.fullText}) <> ''
|
|
14
|
+
OR ${transcripts.segmentsJson} ~ '"text":"[^"]'
|
|
15
|
+
)`;
|
|
16
|
+
}
|
|
@@ -1,48 +1,66 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
2
|
import { listAppState } from "@agent-native/core/application-state";
|
|
3
|
+
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
3
4
|
import { accessFilter } from "@agent-native/core/sharing";
|
|
4
5
|
import { and, eq, inArray } from "drizzle-orm";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
|
|
7
8
|
import { getDb, schema } from "../server/db/index.js";
|
|
9
|
+
import { listAutoTitleCandidates } from "./lib/auto-title-candidates.js";
|
|
8
10
|
|
|
9
11
|
const REQUEST_PREFIX = "clips-ai-request-";
|
|
10
12
|
|
|
13
|
+
type QueuedAiRequest = Record<string, unknown> & { recordingId: string };
|
|
14
|
+
|
|
15
|
+
async function listQueuedRequests(): Promise<QueuedAiRequest[]> {
|
|
16
|
+
const entries = await listAppState(REQUEST_PREFIX);
|
|
17
|
+
|
|
18
|
+
const requests = entries
|
|
19
|
+
.map((e) => e.value as Record<string, unknown>)
|
|
20
|
+
.filter(
|
|
21
|
+
(v): v is QueuedAiRequest =>
|
|
22
|
+
!!v && typeof v.recordingId === "string" && v.recordingId.length > 0,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (requests.length === 0) return [];
|
|
26
|
+
|
|
27
|
+
const recordingIds = [...new Set(requests.map((r) => r.recordingId))];
|
|
28
|
+
|
|
29
|
+
const db = getDb();
|
|
30
|
+
const accessible = await db
|
|
31
|
+
.select({ id: schema.recordings.id, title: schema.recordings.title })
|
|
32
|
+
.from(schema.recordings)
|
|
33
|
+
.where(
|
|
34
|
+
and(
|
|
35
|
+
accessFilter(schema.recordings, schema.recordingShares),
|
|
36
|
+
inArray(schema.recordings.id, recordingIds),
|
|
37
|
+
eq(schema.recordings.status, "ready"),
|
|
38
|
+
),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const titles = new Map(accessible.map((r) => [r.id, r.title]));
|
|
42
|
+
|
|
43
|
+
// Not every request kind stores `currentTitle` when queued; fill it from the
|
|
44
|
+
// recording so every delivered request tells the agent what the clip is.
|
|
45
|
+
return requests
|
|
46
|
+
.filter((r) => titles.has(r.recordingId))
|
|
47
|
+
.map((r) => ({
|
|
48
|
+
...r,
|
|
49
|
+
currentTitle: r.currentTitle ?? titles.get(r.recordingId),
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
|
|
11
53
|
export default defineAction({
|
|
12
54
|
description:
|
|
13
|
-
"List pending clips AI requests (regenerate-title, summary, chapters, etc.) for recordings the current user can access.",
|
|
55
|
+
"List pending clips AI requests (regenerate-title, summary, chapters, etc.) for recordings the current user can access, plus the current user's ready recordings that still need an auto-generated title.",
|
|
14
56
|
schema: z.object({}),
|
|
15
57
|
http: { method: "GET" },
|
|
16
58
|
run: async () => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
!!v && typeof v.recordingId === "string" && v.recordingId.length > 0,
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
if (requests.length === 0) return { requests: [] };
|
|
27
|
-
|
|
28
|
-
const recordingIds = [...new Set(requests.map((r) => r.recordingId))];
|
|
29
|
-
|
|
30
|
-
const db = getDb();
|
|
31
|
-
const accessible = await db
|
|
32
|
-
.select({ id: schema.recordings.id })
|
|
33
|
-
.from(schema.recordings)
|
|
34
|
-
.where(
|
|
35
|
-
and(
|
|
36
|
-
accessFilter(schema.recordings, schema.recordingShares),
|
|
37
|
-
inArray(schema.recordings.id, recordingIds),
|
|
38
|
-
eq(schema.recordings.status, "ready"),
|
|
39
|
-
),
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const accessibleIds = new Set(accessible.map((r) => r.id));
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
requests: requests.filter((r) => accessibleIds.has(r.recordingId)),
|
|
46
|
-
};
|
|
59
|
+
const email = getRequestUserEmail();
|
|
60
|
+
const [requests, titleCandidates] = await Promise.all([
|
|
61
|
+
listQueuedRequests(),
|
|
62
|
+
email ? listAutoTitleCandidates(email) : [],
|
|
63
|
+
]);
|
|
64
|
+
return { requests, titleCandidates };
|
|
47
65
|
},
|
|
48
66
|
});
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
parseSpaceIds,
|
|
32
32
|
} from "../server/lib/recordings.js";
|
|
33
33
|
import { profileNameFor } from "../server/lib/user-identities.js";
|
|
34
|
+
import { transcriptHasTextSql } from "./lib/transcript-text.js";
|
|
34
35
|
|
|
35
36
|
function escapeLike(s: string): string {
|
|
36
37
|
return s.replace(/([\\%_])/g, "\\$1");
|
|
@@ -320,10 +321,7 @@ export default defineAction({
|
|
|
320
321
|
},
|
|
321
322
|
transcriptStatus: schema.recordingTranscripts.status,
|
|
322
323
|
transcriptHasText: sql<number>`(
|
|
323
|
-
CASE WHEN (
|
|
324
|
-
TRIM(COALESCE(${schema.recordingTranscripts.fullText}, '')) <> ''
|
|
325
|
-
OR COALESCE(${schema.recordingTranscripts.segmentsJson}, '') LIKE '%"text":"_%'
|
|
326
|
-
) THEN 1 ELSE 0 END
|
|
324
|
+
CASE WHEN ${transcriptHasTextSql()} THEN 1 ELSE 0 END
|
|
327
325
|
)`,
|
|
328
326
|
})
|
|
329
327
|
.from(schema.recordings)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
2
|
import { accessFilter, resolveAccess } from "@agent-native/core/sharing";
|
|
3
|
-
import { and, eq, gte, inArray, ne, or } from "drizzle-orm";
|
|
3
|
+
import { and, eq, gte, inArray, ne, or, sql } from "drizzle-orm";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
@@ -19,6 +19,9 @@ const MAX_CLAIMS = 10;
|
|
|
19
19
|
const MAX_TITLE_LENGTH = 200;
|
|
20
20
|
const MAX_DESCRIPTION_LENGTH = 600;
|
|
21
21
|
export const MAX_TRANSCRIPT_EXCERPT_LENGTH = 1_200;
|
|
22
|
+
// boundedText collapses whitespace before slicing, so the SQL prefix must be
|
|
23
|
+
// far longer than the excerpt or whitespace-heavy transcripts come back short.
|
|
24
|
+
const TRANSCRIPT_FETCH_LENGTH = 20_000;
|
|
22
25
|
|
|
23
26
|
export type TransactionalEmailContextPacket = {
|
|
24
27
|
recordingId: string;
|
|
@@ -183,7 +186,9 @@ async function loadContextPackets(
|
|
|
183
186
|
db
|
|
184
187
|
.select({
|
|
185
188
|
recordingId: schema.recordingTranscripts.recordingId,
|
|
186
|
-
fullText:
|
|
189
|
+
fullText: sql<
|
|
190
|
+
string | null
|
|
191
|
+
>`left(${schema.recordingTranscripts.fullText}, ${TRANSCRIPT_FETCH_LENGTH})`,
|
|
187
192
|
})
|
|
188
193
|
.from(schema.recordingTranscripts)
|
|
189
194
|
.where(
|
|
@@ -263,12 +268,13 @@ export async function claimTransactionalEmailAiRequests(
|
|
|
263
268
|
): Promise<{ requests: ClaimedTransactionalEmailAiRequest[] }> {
|
|
264
269
|
const claimant = normalizeEmail(claimantEmail);
|
|
265
270
|
const claimLimit = Math.min(Math.max(limit, 1), MAX_CLAIMS);
|
|
266
|
-
const config = await
|
|
271
|
+
const [config, claimCandidates] = await Promise.all([
|
|
272
|
+
transactionalEmailStore.readConfig(),
|
|
273
|
+
transactionalEmailStore.listAiClaimCandidates(claimant),
|
|
274
|
+
]);
|
|
267
275
|
if (!config) return { requests: [] };
|
|
268
276
|
const staleBefore = new Date(Date.now() - AI_DISPATCH_STALE_MS);
|
|
269
|
-
const candidates = (
|
|
270
|
-
await transactionalEmailStore.listJobs(["awaiting_ai", "ai_dispatched"])
|
|
271
|
-
).filter(
|
|
277
|
+
const candidates = claimCandidates.filter(
|
|
272
278
|
(job) =>
|
|
273
279
|
isAiBackedType(job.type) &&
|
|
274
280
|
(job.state === "awaiting_ai" ||
|
|
@@ -8,6 +8,10 @@ import { z } from "zod";
|
|
|
8
8
|
import { getDb, schema } from "../server/db/index.js";
|
|
9
9
|
import { isBuilderCreditsExhaustedMessage } from "../shared/builder-credits.js";
|
|
10
10
|
import { withFullVideoAiInstructions } from "../shared/clips-ai-prefs.js";
|
|
11
|
+
import {
|
|
12
|
+
isAutoTitleReplaceable,
|
|
13
|
+
isDefaultTitle,
|
|
14
|
+
} from "../shared/title-source.js";
|
|
11
15
|
import cleanupTranscript from "./cleanup-transcript.js";
|
|
12
16
|
import { loadAgentsMdContext } from "./lib/agents-md-context.js";
|
|
13
17
|
import {
|
|
@@ -20,7 +24,6 @@ import {
|
|
|
20
24
|
cleanGeneratedTitle,
|
|
21
25
|
fallbackTitleFromTranscript,
|
|
22
26
|
} from "./lib/title-fallback.js";
|
|
23
|
-
import { isAutoTitleReplaceable, isDefaultTitle } from "./lib/title-source.js";
|
|
24
27
|
import regenerateSummary from "./regenerate-summary.js";
|
|
25
28
|
|
|
26
29
|
function transcriptTextFromSegments(raw: string | null | undefined): string {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../server/lib/recordings.js";
|
|
22
22
|
import { isBuilderCreditsExhaustedMessage } from "../shared/builder-credits.js";
|
|
23
23
|
import { normalizeLoomShareUrl } from "../shared/loom.js";
|
|
24
|
+
import { isAutoTitleReplaceable } from "../shared/title-source.js";
|
|
24
25
|
import {
|
|
25
26
|
isRetryableTranscriptFailure,
|
|
26
27
|
transcriptFailureMessage,
|
|
@@ -55,7 +56,6 @@ import {
|
|
|
55
56
|
isLikelyMismatchedTranscriptLanguage,
|
|
56
57
|
normalizeProviderTranscript,
|
|
57
58
|
} from "./lib/provider-transcript.js";
|
|
58
|
-
import { isAutoTitleReplaceable } from "./lib/title-source.js";
|
|
59
59
|
import regenerateSummary from "./regenerate-summary.js";
|
|
60
60
|
import regenerateTitle from "./regenerate-title.js";
|
|
61
61
|
|
|
@@ -8,10 +8,10 @@ import { z } from "zod";
|
|
|
8
8
|
import { getDb, schema } from "../server/db/index.js";
|
|
9
9
|
import { dispatchPostFinalizeJob } from "../server/lib/post-finalize-dispatch.js";
|
|
10
10
|
import { getCurrentOwnerEmail } from "../server/lib/recordings.js";
|
|
11
|
+
import { isAutoTitleReplaceable } from "../shared/title-source.js";
|
|
11
12
|
import { buildCaptionSegmentsFromText } from "../shared/transcript-segments.js";
|
|
12
13
|
import { booleanParam } from "./lib/cli-params.js";
|
|
13
14
|
import { finalizeEndedMeetingsForRecording } from "./lib/finalize-ended-meetings.js";
|
|
14
|
-
import { isAutoTitleReplaceable } from "./lib/title-source.js";
|
|
15
15
|
|
|
16
16
|
function nativeSegmentsJson(
|
|
17
17
|
fullText: string,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useActionMutation } from "@agent-native/core/client/hooks";
|
|
2
2
|
import { useT } from "@agent-native/core/client/i18n";
|
|
3
|
+
import { isDefaultTitle } from "@shared/title-source";
|
|
3
4
|
import { IconEdit } from "@tabler/icons-react";
|
|
4
5
|
import { useQueryClient } from "@tanstack/react-query";
|
|
5
6
|
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
@@ -7,7 +8,6 @@ import { toast } from "sonner";
|
|
|
7
8
|
|
|
8
9
|
import { Input } from "@/components/ui/input";
|
|
9
10
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
10
|
-
import { isDefaultTitle } from "@/hooks/use-auto-title";
|
|
11
11
|
import { cn } from "@/lib/utils";
|
|
12
12
|
|
|
13
13
|
interface EditableRecordingTitleProps {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useFeatureFlag } from "@agent-native/core/client/feature-flags";
|
|
2
2
|
import { useFormatters, useT } from "@agent-native/core/client/i18n";
|
|
3
3
|
import { UPLOAD_RETRY_RESUME_FLAG } from "@shared/feature-flags";
|
|
4
|
+
import { isDefaultTitle } from "@shared/title-source";
|
|
4
5
|
import { isRetryableUploadInterruption } from "@shared/upload-interruption";
|
|
5
6
|
import {
|
|
6
7
|
IconDotsVertical,
|
|
@@ -47,7 +48,6 @@ import {
|
|
|
47
48
|
DropdownMenuTrigger,
|
|
48
49
|
} from "@/components/ui/dropdown-menu";
|
|
49
50
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
50
|
-
import { isDefaultTitle } from "@/hooks/use-auto-title";
|
|
51
51
|
import type { RecordingSummary } from "@/hooks/use-library";
|
|
52
52
|
import { attemptOpenDesktopApp } from "@/lib/capture-install-options";
|
|
53
53
|
import {
|