@agent-native/core 0.59.1 → 0.60.0
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/a2a/index.d.ts +2 -0
- package/dist/a2a/index.d.ts.map +1 -1
- package/dist/a2a/index.js +1 -0
- package/dist/a2a/index.js.map +1 -1
- package/dist/a2a/invoke.d.ts +63 -0
- package/dist/a2a/invoke.d.ts.map +1 -0
- package/dist/a2a/invoke.js +157 -0
- package/dist/a2a/invoke.js.map +1 -0
- package/dist/agent/run-store.d.ts +15 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +28 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/chat-threads/store.d.ts +21 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +128 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/agent.d.ts +23 -0
- package/dist/cli/agent.d.ts.map +1 -0
- package/dist/cli/agent.js +300 -0
- package/dist/cli/agent.js.map +1 -0
- package/dist/cli/agents.d.ts +14 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +95 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +264 -2
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/create.d.ts +3 -2
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +154 -83
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +50 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/invoke.d.ts +26 -0
- package/dist/cli/invoke.d.ts.map +1 -0
- package/dist/cli/invoke.js +227 -0
- package/dist/cli/invoke.js.map +1 -0
- package/dist/cli/templates-meta.d.ts +1 -1
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +9 -8
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/cli/workspacify.d.ts +1 -1
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +6 -6
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +5 -4
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/blocks/library/diagram.d.ts.map +1 -1
- package/dist/client/blocks/library/diagram.js +23 -17
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/blocks/types.d.ts +2 -0
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts +13 -0
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +41 -0
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +2 -2
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/onboarding/default-steps.d.ts.map +1 -1
- package/dist/onboarding/default-steps.js +102 -0
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/provider-api/actions/github-repo-files.d.ts +84 -0
- package/dist/provider-api/actions/github-repo-files.d.ts.map +1 -0
- package/dist/provider-api/actions/github-repo-files.js +213 -0
- package/dist/provider-api/actions/github-repo-files.js.map +1 -0
- package/dist/provider-api/github-repo.d.ts +11 -0
- package/dist/provider-api/github-repo.d.ts.map +1 -0
- package/dist/provider-api/github-repo.js +553 -0
- package/dist/provider-api/github-repo.js.map +1 -0
- package/dist/provider-api/index.d.ts +184 -11
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +519 -0
- package/dist/provider-api/index.js.map +1 -1
- package/dist/scripts/docs/search.d.ts.map +1 -1
- package/dist/scripts/docs/search.js +38 -13
- package/dist/scripts/docs/search.js.map +1 -1
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +11 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +32 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +297 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth-marketing.d.ts.map +1 -1
- package/dist/server/auth-marketing.js +17 -7
- package/dist/server/auth-marketing.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +6 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +18 -98
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/styles/blocks.css +30 -8
- package/dist/styles/rich-markdown-editor.css +10 -4
- package/dist/templates/{starter-shell-sync.spec.ts → chat-shell-sync.spec.ts} +21 -21
- package/dist/templates/default/.agents/skills/actions/SKILL.md +5 -5
- package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +63 -0
- package/dist/templates/default/AGENTS.md +22 -1
- package/dist/templates/default/actions/hello.ts +1 -1
- package/dist/templates/default/actions/navigate.ts +1 -1
- package/dist/templates/default/actions/view-screen.ts +1 -1
- package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +63 -0
- package/dist/templates/headless/.env.example +4 -0
- package/dist/templates/headless/.prettierrc +5 -0
- package/dist/templates/headless/AGENTS.md +58 -0
- package/dist/templates/headless/DEVELOPING.md +22 -0
- package/dist/templates/headless/_gitignore +36 -0
- package/dist/templates/headless/actions/hello.ts +14 -0
- package/dist/templates/headless/actions/run.ts +3 -0
- package/dist/templates/headless/package.json +22 -0
- package/dist/templates/headless/tsconfig.json +7 -0
- package/dist/templates/ui-primitives-sync.spec.ts +2 -2
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -5
- package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +63 -0
- package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +93 -0
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +1 -1
- package/dist/templates/workspace-core/AGENTS.md +20 -3
- package/dist/templates/workspace-core/src/server/index.ts +1 -1
- package/dist/templates/workspace-root/AGENTS.md +25 -5
- package/dist/templates/workspace-root/README.md +7 -7
- package/dist/triggers/dispatcher.d.ts +2 -3
- package/dist/triggers/dispatcher.d.ts.map +1 -1
- package/dist/triggers/dispatcher.js +2 -3
- package/dist/triggers/dispatcher.js.map +1 -1
- package/dist/triggers/routes.d.ts +38 -0
- package/dist/triggers/routes.d.ts.map +1 -0
- package/dist/triggers/routes.js +202 -0
- package/dist/triggers/routes.js.map +1 -0
- package/docs/AGENTS.md +57 -0
- package/docs/SKILL.md +40 -0
- package/docs/content/a2a-protocol.md +1 -1
- package/docs/content/actions.md +48 -8
- package/docs/content/agent-surfaces.md +76 -14
- package/docs/content/cli-adapters.md +1 -1
- package/docs/content/cloneable-saas.md +5 -4
- package/docs/content/code-agents-ui.md +1 -1
- package/docs/content/components.md +1 -1
- package/docs/content/context-awareness.md +1 -1
- package/docs/content/creating-templates.md +9 -7
- package/docs/content/drop-in-agent.md +1 -1
- package/docs/content/faq.md +6 -4
- package/docs/content/getting-started.md +63 -73
- package/docs/content/key-concepts.md +24 -24
- package/docs/content/native-chat-ui.md +4 -4
- package/docs/content/pure-agent-apps.md +34 -10
- package/docs/content/security.md +1 -1
- package/docs/content/server.md +1 -1
- package/docs/content/template-chat.md +85 -0
- package/docs/content/template-dispatch.md +1 -1
- package/docs/content/tracking.md +1 -1
- package/docs/content/what-is-agent-native.md +7 -6
- package/package.json +10 -1
- package/src/templates/{starter-shell-sync.spec.ts → chat-shell-sync.spec.ts} +21 -21
- package/src/templates/default/.agents/skills/actions/SKILL.md +5 -5
- package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +63 -0
- package/src/templates/default/AGENTS.md +22 -1
- package/src/templates/default/actions/hello.ts +1 -1
- package/src/templates/default/actions/navigate.ts +1 -1
- package/src/templates/default/actions/view-screen.ts +1 -1
- package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +63 -0
- package/src/templates/headless/.env.example +4 -0
- package/src/templates/headless/.prettierrc +5 -0
- package/src/templates/headless/AGENTS.md +58 -0
- package/src/templates/headless/DEVELOPING.md +22 -0
- package/src/templates/headless/_gitignore +36 -0
- package/src/templates/headless/actions/hello.ts +14 -0
- package/src/templates/headless/actions/run.ts +3 -0
- package/src/templates/headless/package.json +22 -0
- package/src/templates/headless/tsconfig.json +7 -0
- package/src/templates/ui-primitives-sync.spec.ts +2 -2
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -5
- package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +63 -0
- package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +93 -0
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +1 -1
- package/src/templates/workspace-core/AGENTS.md +20 -3
- package/src/templates/workspace-core/src/server/index.ts +1 -1
- package/src/templates/workspace-root/AGENTS.md +25 -5
- package/src/templates/workspace-root/README.md +7 -7
- package/docs/content/template-starter.md +0 -78
|
@@ -123,5 +123,26 @@ export interface QueuedMessage {
|
|
|
123
123
|
* change. Safe to call often — the frontend debounces writes.
|
|
124
124
|
*/
|
|
125
125
|
export declare function setThreadQueuedMessages(threadId: string, queuedMessages: QueuedMessage[]): Promise<void>;
|
|
126
|
+
export interface ChatThreadShareState {
|
|
127
|
+
enabled: boolean;
|
|
128
|
+
createdAt: number | null;
|
|
129
|
+
updatedAt: number | null;
|
|
130
|
+
revokedAt: number | null;
|
|
131
|
+
}
|
|
132
|
+
export interface ChatThreadShareLink extends ChatThreadShareState {
|
|
133
|
+
enabled: true;
|
|
134
|
+
token: string;
|
|
135
|
+
}
|
|
136
|
+
export declare function hashThreadShareToken(token: string): string;
|
|
137
|
+
export declare function getThreadShareState(threadId: string, options?: {
|
|
138
|
+
ownerEmail?: string;
|
|
139
|
+
}): Promise<ChatThreadShareState | null>;
|
|
140
|
+
export declare function createThreadShareLink(threadId: string, options?: {
|
|
141
|
+
ownerEmail?: string;
|
|
142
|
+
}): Promise<ChatThreadShareLink | null>;
|
|
143
|
+
export declare function revokeThreadShareLink(threadId: string, options?: {
|
|
144
|
+
ownerEmail?: string;
|
|
145
|
+
}): Promise<ChatThreadShareState | null>;
|
|
146
|
+
export declare function getThreadByShareToken(token: string): Promise<ChatThread | null>;
|
|
126
147
|
export declare function deleteThread(id: string): Promise<boolean>;
|
|
127
148
|
//# sourceMappingURL=store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/chat-threads/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/chat-threads/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import { getDbExec, intType } from "../db/client.js";
|
|
2
3
|
import { mergeThreadDataForClientSave, normalizeThreadRepository, normalizeThreadTitle, } from "../agent/thread-data-builder.js";
|
|
3
4
|
import { emitChatThreadChange } from "./emitter.js";
|
|
@@ -610,6 +611,133 @@ export async function setThreadQueuedMessages(threadId, queuedMessages) {
|
|
|
610
611
|
await updateThreadData(threadId, JSON.stringify(data), thread.title, thread.preview, thread.messageCount, { preserveExistingQueuedMessages: false });
|
|
611
612
|
});
|
|
612
613
|
}
|
|
614
|
+
const THREAD_SHARE_DATA_KEY = "_share";
|
|
615
|
+
function generateShareToken() {
|
|
616
|
+
return crypto.randomBytes(24).toString("base64url");
|
|
617
|
+
}
|
|
618
|
+
export function hashThreadShareToken(token) {
|
|
619
|
+
return crypto.createHash("sha256").update(token).digest("hex");
|
|
620
|
+
}
|
|
621
|
+
function normalizeThreadShare(value) {
|
|
622
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
623
|
+
return null;
|
|
624
|
+
const r = value;
|
|
625
|
+
const tokenHash = typeof r.tokenHash === "string" && /^[a-f0-9]{64}$/i.test(r.tokenHash)
|
|
626
|
+
? r.tokenHash.toLowerCase()
|
|
627
|
+
: undefined;
|
|
628
|
+
const createdAt = normalizeTimestamp(r.createdAt);
|
|
629
|
+
const updatedAt = normalizeTimestamp(r.updatedAt);
|
|
630
|
+
const revokedAt = normalizeTimestamp(r.revokedAt);
|
|
631
|
+
if (!tokenHash && !createdAt && !updatedAt && !revokedAt)
|
|
632
|
+
return null;
|
|
633
|
+
return {
|
|
634
|
+
...(tokenHash ? { tokenHash } : {}),
|
|
635
|
+
...(createdAt ? { createdAt } : {}),
|
|
636
|
+
...(updatedAt ? { updatedAt } : {}),
|
|
637
|
+
...(revokedAt ? { revokedAt } : {}),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function normalizeTimestamp(value) {
|
|
641
|
+
const n = Number(value);
|
|
642
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
643
|
+
}
|
|
644
|
+
function shareStateFromStored(stored) {
|
|
645
|
+
const revokedAt = stored?.revokedAt ?? null;
|
|
646
|
+
return {
|
|
647
|
+
enabled: Boolean(stored?.tokenHash && !revokedAt),
|
|
648
|
+
createdAt: stored?.createdAt ?? null,
|
|
649
|
+
updatedAt: stored?.updatedAt ?? null,
|
|
650
|
+
revokedAt,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function readStoredThreadShare(threadData) {
|
|
654
|
+
const data = parseThreadData(threadData);
|
|
655
|
+
return normalizeThreadShare(data[THREAD_SHARE_DATA_KEY]);
|
|
656
|
+
}
|
|
657
|
+
export async function getThreadShareState(threadId, options = {}) {
|
|
658
|
+
const thread = await getThread(threadId);
|
|
659
|
+
if (!thread)
|
|
660
|
+
return null;
|
|
661
|
+
if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
return shareStateFromStored(readStoredThreadShare(thread.threadData));
|
|
665
|
+
}
|
|
666
|
+
export async function createThreadShareLink(threadId, options = {}) {
|
|
667
|
+
return withThreadDataLock(threadId, async () => {
|
|
668
|
+
const thread = await getThread(threadId);
|
|
669
|
+
if (!thread)
|
|
670
|
+
return null;
|
|
671
|
+
if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
const now = Date.now();
|
|
675
|
+
const token = generateShareToken();
|
|
676
|
+
const data = parseThreadData(thread.threadData);
|
|
677
|
+
const existing = normalizeThreadShare(data[THREAD_SHARE_DATA_KEY]);
|
|
678
|
+
data[THREAD_SHARE_DATA_KEY] = {
|
|
679
|
+
tokenHash: hashThreadShareToken(token),
|
|
680
|
+
createdAt: existing?.createdAt ?? now,
|
|
681
|
+
updatedAt: now,
|
|
682
|
+
revokedAt: null,
|
|
683
|
+
};
|
|
684
|
+
await updateThreadData(threadId, JSON.stringify(data), thread.title, thread.preview, thread.messageCount);
|
|
685
|
+
return {
|
|
686
|
+
enabled: true,
|
|
687
|
+
token,
|
|
688
|
+
createdAt: existing?.createdAt ?? now,
|
|
689
|
+
updatedAt: now,
|
|
690
|
+
revokedAt: null,
|
|
691
|
+
};
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
export async function revokeThreadShareLink(threadId, options = {}) {
|
|
695
|
+
return withThreadDataLock(threadId, async () => {
|
|
696
|
+
const thread = await getThread(threadId);
|
|
697
|
+
if (!thread)
|
|
698
|
+
return null;
|
|
699
|
+
if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
const now = Date.now();
|
|
703
|
+
const data = parseThreadData(thread.threadData);
|
|
704
|
+
const existing = normalizeThreadShare(data[THREAD_SHARE_DATA_KEY]);
|
|
705
|
+
data[THREAD_SHARE_DATA_KEY] = {
|
|
706
|
+
...(existing?.createdAt ? { createdAt: existing.createdAt } : {}),
|
|
707
|
+
updatedAt: now,
|
|
708
|
+
revokedAt: now,
|
|
709
|
+
};
|
|
710
|
+
await updateThreadData(threadId, JSON.stringify(data), thread.title, thread.preview, thread.messageCount);
|
|
711
|
+
return {
|
|
712
|
+
enabled: false,
|
|
713
|
+
createdAt: existing?.createdAt ?? null,
|
|
714
|
+
updatedAt: now,
|
|
715
|
+
revokedAt: now,
|
|
716
|
+
};
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
export async function getThreadByShareToken(token) {
|
|
720
|
+
const cleanToken = token.trim();
|
|
721
|
+
if (!cleanToken || cleanToken.length < 16)
|
|
722
|
+
return null;
|
|
723
|
+
await ensureTable();
|
|
724
|
+
const tokenHash = hashThreadShareToken(cleanToken);
|
|
725
|
+
const client = getDbExec();
|
|
726
|
+
const { rows } = await client.execute({
|
|
727
|
+
sql: `SELECT ${THREAD_COLUMNS} FROM chat_threads WHERE thread_data LIKE ? LIMIT 10`,
|
|
728
|
+
args: [`%${tokenHash}%`],
|
|
729
|
+
});
|
|
730
|
+
for (const row of rows) {
|
|
731
|
+
const thread = rowToThread(row);
|
|
732
|
+
const stored = readStoredThreadShare(thread.threadData);
|
|
733
|
+
if (!stored?.tokenHash || stored.revokedAt)
|
|
734
|
+
continue;
|
|
735
|
+
if (stored.tokenHash !== tokenHash)
|
|
736
|
+
continue;
|
|
737
|
+
return thread;
|
|
738
|
+
}
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
613
741
|
export async function deleteThread(id) {
|
|
614
742
|
await ensureTable();
|
|
615
743
|
const client = getDbExec();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/chat-threads/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDbExec, intType } from \"../db/client.js\";\nimport {\n mergeThreadDataForClientSave,\n normalizeThreadRepository,\n normalizeThreadTitle,\n} from \"../agent/thread-data-builder.js\";\nimport { emitChatThreadChange } from \"./emitter.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n/**\n * Per-thread async mutex. Read-modify-write on the `thread_data` JSON blob\n * is not atomic at the DB level — two concurrent callers (e.g. the UI\n * persisting queued messages while `onRunComplete` appends agent output)\n * would both read the same row, each mutate it independently, and the\n * second write clobbers the first. Serializing on thread id inside this\n * process eliminates the race for the usual single-process deployment\n * while leaving straight reads and other thread-data-unrelated updates\n * untouched.\n *\n * Cross-process races are handled by `updateThreadData`, which performs a\n * compare-and-swap on `updated_at`, rereads the latest row on conflict, and\n * remerges message history before retrying.\n */\nconst _threadDataLocks = new Map<string, Promise<unknown>>();\nconst DEFAULT_THREAD_DATA_UPDATE_ATTEMPTS = 12;\nconst THREAD_DATA_CONFLICT_BACKOFF_MS = 25;\n\nexport function withThreadDataLock<T>(\n threadId: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const prev = _threadDataLocks.get(threadId) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n _threadDataLocks.set(threadId, next);\n // Use `.then(cleanup, cleanup)` (not `.finally`) so the rejection is\n // observed on this chained promise — otherwise any failure inside `fn`\n // triggers `unhandledRejection` on the discarded `finally()` return.\n // The caller still sees the rejection via `next`.\n const cleanup = () => {\n if (_threadDataLocks.get(threadId) === next) {\n _threadDataLocks.delete(threadId);\n }\n };\n next.then(cleanup, cleanup);\n return next as Promise<T>;\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS chat_threads (\n id TEXT PRIMARY KEY,\n owner_email TEXT NOT NULL,\n title TEXT NOT NULL DEFAULT '',\n preview TEXT NOT NULL DEFAULT '',\n thread_data TEXT NOT NULL DEFAULT '{}',\n message_count ${intType()} NOT NULL DEFAULT 0,\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL,\n scope_type TEXT,\n scope_id TEXT,\n scope_label TEXT,\n pinned_at ${intType()},\n archived_at ${intType()}\n )\n `);\n // Additive migration for existing tables. Both SQLite and Postgres\n // accept `ALTER TABLE ADD COLUMN` and will raise when the column\n // already exists; the try/catch makes the call idempotent across\n // both dialects without requiring an information_schema probe.\n for (const [col, type] of [\n [\"scope_type\", \"TEXT\"],\n [\"scope_id\", \"TEXT\"],\n [\"scope_label\", \"TEXT\"],\n [\"pinned_at\", intType()],\n [\"archived_at\", intType()],\n ] as const) {\n try {\n await client.execute(\n `ALTER TABLE chat_threads ADD COLUMN ${col} ${type}`,\n );\n } catch {\n // Column already exists.\n }\n }\n // Indexes for the hot read paths. Both the sidebar list and the\n // scoped/per-resource list filter on owner_email (and optionally\n // scope) and sort by updated_at. Keep these dialect-agnostic (no\n // DESC, partial, or PG-only syntax) so they apply identically on\n // SQLite and the configured Postgres. `IF NOT EXISTS` makes them\n // idempotent across restarts.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS chat_threads_owner_updated_idx ON chat_threads (owner_email, updated_at)`,\n `CREATE INDEX IF NOT EXISTS chat_threads_scope_updated_idx ON chat_threads (scope_type, scope_id, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n // One-time backfill of message_count for legacy rows written before\n // the column was maintained. The list/summary path now reads\n // message_count directly (instead of re-parsing the thread_data blob)\n // and filters on `message_count > 0`, so any legacy row that has\n // messages but a stale `message_count = 0` would otherwise vanish from\n // the sidebar. Recompute the count from thread_data and persist it so\n // the hot path can stay blob-free. Idempotent: only touches rows where\n // the count is still 0 but the blob clearly contains a messages array.\n await backfillLegacyMessageCounts(client);\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\n/**\n * Recompute `message_count` from `thread_data` for legacy rows that still\n * have a stale `message_count = 0` despite carrying a messages array in the\n * blob. Without this, dropping the `thread_data` payload (and the\n * `OR thread_data LIKE '%\"messages\"%'` filter) from the list path would make\n * those rows disappear from the sidebar. Runs once at table bootstrap and is\n * idempotent — after the first pass no row matches the WHERE clause again.\n */\nasync function backfillLegacyMessageCounts(\n client: ReturnType<typeof getDbExec>,\n): Promise<void> {\n const { rows } = await client.execute({\n sql: `SELECT id, thread_data, message_count FROM chat_threads WHERE message_count = 0 AND thread_data LIKE '%\"messages\"%'`,\n args: [],\n });\n for (const r of rows) {\n const count = deriveMessageCount(r.thread_data, 0);\n if (count <= 0) continue;\n await client.execute({\n sql: `UPDATE chat_threads SET message_count = ? WHERE id = ? AND message_count = 0`,\n args: [count, r.id as string],\n });\n }\n}\n\nfunction generateId(): string {\n return `thread-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * A resource the chat is bound to, e.g. `{ type: \"deck\", id: \"deck-abc\" }`.\n * The framework is opaque to the type string — each template chooses what\n * its primary resource is and the surface it scopes to (deck, design,\n * dashboard, etc.). `label` is a denormalized snapshot for display when\n * the resource isn't on hand at render time; the live template can\n * overwrite it via the next createThread call.\n */\nexport interface ChatThreadScope {\n type: string;\n id: string;\n label?: string;\n}\n\nexport interface ChatThread {\n id: string;\n ownerEmail: string;\n title: string;\n preview: string;\n threadData: string;\n messageCount: number;\n createdAt: number;\n updatedAt: number;\n scope: ChatThreadScope | null;\n pinnedAt: number | null;\n archivedAt: number | null;\n}\n\nexport interface ChatThreadSummary {\n id: string;\n title: string;\n preview: string;\n messageCount: number;\n createdAt: number;\n updatedAt: number;\n scope: ChatThreadScope | null;\n pinnedAt: number | null;\n archivedAt: number | null;\n}\n\nexport interface ForkThreadSourceSnapshot {\n threadData: string;\n title?: string;\n preview?: string;\n messageCount?: number;\n scope?: ChatThreadScope | null;\n}\n\nfunction readScope(r: Record<string, unknown>): ChatThreadScope | null {\n const type = r.scope_type as string | null | undefined;\n const id = r.scope_id as string | null | undefined;\n if (!type || !id) return null;\n const label = r.scope_label as string | null | undefined;\n return label ? { type, id, label } : { type, id };\n}\n\nfunction readNullableNumber(value: unknown): number | null {\n if (value == null) return null;\n const n = Number(value);\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\nfunction normalizeForkSourceSnapshot(\n source: ForkThreadSourceSnapshot | null | undefined,\n): {\n threadData: string;\n title: string;\n preview: string;\n messageCount: number;\n scope?: ChatThreadScope | null;\n} | null {\n if (!source || typeof source.threadData !== \"string\") return null;\n const threadData = source.threadData.trim();\n if (!threadData) return null;\n\n let parsed: any;\n try {\n parsed = normalizeThreadRepository(JSON.parse(threadData));\n } catch {\n return null;\n }\n\n const repoMessageCount = Array.isArray(parsed.messages)\n ? parsed.messages.length\n : 0;\n if (repoMessageCount <= 0) return null;\n\n return {\n threadData: JSON.stringify(parsed),\n title: typeof source.title === \"string\" ? source.title : \"\",\n preview: typeof source.preview === \"string\" ? source.preview : \"\",\n messageCount: repoMessageCount,\n ...(Object.prototype.hasOwnProperty.call(source, \"scope\")\n ? { scope: source.scope ?? null }\n : {}),\n };\n}\n\nfunction deriveMessageCount(threadData: unknown, fallback: number): number {\n if (typeof threadData !== \"string\" || !threadData.trim()) return fallback;\n try {\n const repo = normalizeThreadRepository(JSON.parse(threadData));\n if (Array.isArray(repo.messages)) return repo.messages.length;\n } catch {\n // Keep the stored count if the JSON blob is malformed.\n }\n return fallback;\n}\n\nfunction rowToThread(r: Record<string, unknown>): ChatThread {\n const threadData = (r.thread_data as string) ?? \"{}\";\n const storedCount = Number(r.message_count);\n return {\n id: r.id as string,\n ownerEmail: r.owner_email as string,\n title: r.title as string,\n preview: r.preview as string,\n threadData,\n messageCount: deriveMessageCount(threadData, storedCount),\n createdAt: Number(r.created_at),\n updatedAt: Number(r.updated_at),\n scope: readScope(r),\n pinnedAt: readNullableNumber(r.pinned_at),\n archivedAt: readNullableNumber(r.archived_at),\n };\n}\n\nfunction rowToSummary(r: Record<string, unknown>): ChatThreadSummary | null {\n // The summary path never loads `thread_data`; the count comes from the\n // dedicated `message_count` column (maintained on write, backfilled for\n // legacy rows at bootstrap). Empty threads are filtered out of the list.\n const messageCount = Number(r.message_count);\n if (!Number.isFinite(messageCount) || messageCount <= 0) return null;\n return {\n id: r.id as string,\n title: r.title as string,\n preview: r.preview as string,\n messageCount,\n createdAt: Number(r.created_at),\n updatedAt: Number(r.updated_at),\n scope: readScope(r),\n pinnedAt: readNullableNumber(r.pinned_at),\n archivedAt: readNullableNumber(r.archived_at),\n };\n}\n\nexport async function createThread(\n ownerEmail: string,\n opts?: { id?: string; title?: string; scope?: ChatThreadScope | null },\n): Promise<ChatThread> {\n await ensureTable();\n const client = getDbExec();\n const id = opts?.id ?? generateId();\n const now = Date.now();\n const title = opts?.title ?? \"\";\n const scope = opts?.scope ?? null;\n\n await client.execute({\n sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label) VALUES (?, ?, ?, '', '{}', 0, ?, ?, ?, ?, ?)`,\n args: [\n id,\n ownerEmail,\n title,\n now,\n now,\n scope?.type ?? null,\n scope?.id ?? null,\n scope?.label ?? null,\n ],\n });\n\n return {\n id,\n ownerEmail,\n title,\n preview: \"\",\n threadData: \"{}\",\n messageCount: 0,\n createdAt: now,\n updatedAt: now,\n scope,\n pinnedAt: null,\n archivedAt: null,\n };\n}\n\nconst THREAD_COLUMNS = `id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at`;\n// The list/summary path deliberately omits `thread_data`: it is the full\n// message-history JSON blob and selecting it for every row turns \"open the\n// sidebar\" into \"download every conversation\". The summary derives nothing\n// from the blob anymore — preview and message_count are dedicated columns\n// (message_count is maintained on write and backfilled for legacy rows at\n// bootstrap). The detail path (`THREAD_COLUMNS` / `getThread`) still returns\n// the full blob.\nconst SUMMARY_COLUMNS = `id, title, preview, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at`;\n\nexport async function getThread(id: string): Promise<ChatThread | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT ${THREAD_COLUMNS} FROM chat_threads WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n return rowToThread(rows[0]);\n}\n\nexport async function forkThread(\n sourceId: string,\n ownerEmail: string,\n opts?: { id?: string; source?: ForkThreadSourceSnapshot | null },\n): Promise<ChatThread | null> {\n const snapshot = normalizeForkSourceSnapshot(opts?.source);\n let source = await getThread(sourceId);\n if (!source) {\n if (snapshot) {\n try {\n await createThread(ownerEmail, {\n id: sourceId,\n title: snapshot.title,\n scope: snapshot.scope ?? null,\n });\n } catch {\n // The agent run may have created the row while the user clicked Fork.\n }\n const created = await getThread(sourceId);\n if (created?.ownerEmail === ownerEmail) {\n await updateThreadData(\n sourceId,\n snapshot.threadData,\n snapshot.title || created.title,\n snapshot.preview || created.preview,\n snapshot.messageCount,\n );\n if (Object.prototype.hasOwnProperty.call(snapshot, \"scope\")) {\n await setThreadScope(sourceId, snapshot.scope ?? null);\n }\n source = await getThread(sourceId);\n }\n }\n } else if (\n snapshot &&\n source.ownerEmail === ownerEmail &&\n snapshot.messageCount > source.messageCount\n ) {\n // The source row exists but the in-memory snapshot is fresher — the agent\n // run flushed an older state to SQL, but the tab has additional unflushed\n // messages. Overlay the snapshot before cloning so the fork captures the\n // latest user-visible content. Guard with messageCount > stored to avoid\n // clobbering a fresher persisted row with a stale snapshot from another\n // tab.\n source = {\n ...source,\n threadData: snapshot.threadData,\n title: snapshot.title || source.title,\n preview: snapshot.preview || source.preview,\n messageCount: snapshot.messageCount,\n };\n }\n if (!source || source.ownerEmail !== ownerEmail) return null;\n const id = opts?.id ?? generateId();\n const now = Date.now();\n const title = source.title ? `${source.title} (fork)` : \"\";\n const client = getDbExec();\n await client.execute({\n sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n ownerEmail,\n title,\n source.preview,\n source.threadData,\n source.messageCount,\n now,\n now,\n source.scope?.type ?? null,\n source.scope?.id ?? null,\n source.scope?.label ?? null,\n ],\n });\n return {\n id,\n ownerEmail,\n title,\n preview: source.preview,\n threadData: source.threadData,\n messageCount: source.messageCount,\n createdAt: now,\n updatedAt: now,\n scope: source.scope,\n pinnedAt: null,\n archivedAt: null,\n };\n}\n\nexport interface ListThreadsOptions {\n limit?: number;\n offset?: number;\n /**\n * Filter for chats bound to a specific resource. The default (undefined)\n * returns every thread the user owns. `{ type: \"deck\", id: \"abc\" }`\n * returns only that resource's threads. `{ type: \"deck\", id: null }` is\n * NOT supported — pass `unscopedOnly: true` to get only general chats.\n */\n scope?: { type: string; id: string };\n /** When true, returns only threads with no scope (general chats). */\n unscopedOnly?: boolean;\n}\n\nexport async function listThreads(\n ownerEmail: string,\n options: ListThreadsOptions | number = {},\n legacyOffset?: number,\n): Promise<ChatThreadSummary[]> {\n await ensureTable();\n // Back-compat shim: previous signature was (owner, limit, offset).\n const opts: ListThreadsOptions =\n typeof options === \"number\"\n ? { limit: options, offset: legacyOffset ?? 0 }\n : options;\n const limit = opts.limit ?? 50;\n const offset = opts.offset ?? 0;\n const client = getDbExec();\n // `message_count > 0` is the authoritative \"has messages\" signal: it is\n // maintained on every write and backfilled for legacy rows at bootstrap,\n // so the old `OR thread_data LIKE '%\"messages\"%'` substring scan over the\n // full blob is no longer needed here.\n const filters: string[] = [`owner_email = ?`, `message_count > 0`];\n const args: (string | number)[] = [ownerEmail];\n if (opts.scope) {\n filters.push(`scope_type = ? AND scope_id = ?`);\n args.push(opts.scope.type, opts.scope.id);\n } else if (opts.unscopedOnly) {\n filters.push(`scope_type IS NULL`);\n }\n args.push(limit, offset);\n const { rows } = await client.execute({\n sql: `SELECT ${SUMMARY_COLUMNS} FROM chat_threads WHERE ${filters.join(\" AND \")} ORDER BY CASE WHEN pinned_at IS NULL THEN 1 ELSE 0 END, pinned_at DESC, updated_at DESC LIMIT ? OFFSET ?`,\n args,\n });\n return rows\n .map((r) => rowToSummary(r))\n .filter((r): r is ChatThreadSummary => r !== null);\n}\n\nfunction escapeLike(s: string): string {\n return s.replace(/([\\\\%_])/g, \"\\\\$1\");\n}\n\nexport async function searchThreads(\n ownerEmail: string,\n query: string,\n limit = 50,\n options: { scope?: { type: string; id: string } } = {},\n): Promise<ChatThreadSummary[]> {\n await ensureTable();\n const client = getDbExec();\n const pattern = `%${escapeLike(query)}%`;\n // The count-guard uses the maintained/backfilled `message_count` column\n // (same as listThreads). The content match still scans `thread_data` —\n // search legitimately needs to look inside message history.\n const filters: string[] = [\n `owner_email = ?`,\n `message_count > 0`,\n `(title LIKE ? ESCAPE '\\\\' OR preview LIKE ? ESCAPE '\\\\' OR thread_data LIKE ? ESCAPE '\\\\')`,\n ];\n const args: (string | number)[] = [ownerEmail, pattern, pattern, pattern];\n if (options.scope) {\n filters.push(`scope_type = ? AND scope_id = ?`);\n args.push(options.scope.type, options.scope.id);\n }\n args.push(limit);\n const { rows } = await client.execute({\n sql: `SELECT ${SUMMARY_COLUMNS} FROM chat_threads WHERE ${filters.join(\" AND \")} ORDER BY CASE WHEN pinned_at IS NULL THEN 1 ELSE 0 END, pinned_at DESC, updated_at DESC LIMIT ?`,\n args,\n });\n return rows\n .map((r) => rowToSummary(r))\n .filter((r): r is ChatThreadSummary => r !== null);\n}\n\n/**\n * Detach or rebind a chat's scope. Used by the UI's \"Detach from <resource>\"\n * action and by templates that need to retag a chat after a rename. Pass\n * `null` to clear the scope (chat becomes general).\n */\nexport async function setThreadScope(\n id: string,\n scope: ChatThreadScope | null,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: `UPDATE chat_threads SET scope_type = ?, scope_id = ?, scope_label = ?, updated_at = ? WHERE id = ?`,\n args: [\n scope?.type ?? null,\n scope?.id ?? null,\n scope?.label ?? null,\n Math.max(Date.now(), 1),\n id,\n ],\n });\n emitChatThreadChange(id);\n}\n\nexport async function renameThread(\n id: string,\n title: string,\n options: { ownerEmail?: string } = {},\n): Promise<boolean> {\n const nextTitle = normalizeThreadTitle(title);\n if (!nextTitle) return false;\n\n return await withThreadDataLock(id, async () => {\n const thread = await getThread(id);\n if (!thread) return false;\n if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {\n return false;\n }\n\n const repo = parseThreadData(thread.threadData);\n repo._titleOverride = nextTitle;\n await updateThreadData(\n id,\n JSON.stringify(repo),\n nextTitle,\n thread.preview,\n thread.messageCount,\n );\n return true;\n });\n}\n\nexport async function setThreadPinned(\n id: string,\n pinned: boolean,\n options: { ownerEmail?: string } = {},\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Math.max(Date.now(), 1);\n const args: (string | number | null)[] = [pinned ? now : null, id];\n let ownerFilter = \"\";\n if (options.ownerEmail) {\n ownerFilter = \" AND owner_email = ?\";\n args.push(options.ownerEmail);\n }\n const result = await client.execute({\n sql: `UPDATE chat_threads SET pinned_at = ? WHERE id = ?${ownerFilter}`,\n args,\n });\n if (result.rowsAffected > 0) {\n emitChatThreadChange(id);\n return true;\n }\n return false;\n}\n\nexport async function setThreadArchived(\n id: string,\n archived: boolean,\n options: { ownerEmail?: string } = {},\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Math.max(Date.now(), 1);\n const args: (string | number | null)[] = [archived ? now : null, id];\n let ownerFilter = \"\";\n if (options.ownerEmail) {\n ownerFilter = \" AND owner_email = ?\";\n args.push(options.ownerEmail);\n }\n const result = await client.execute({\n sql: `UPDATE chat_threads SET archived_at = ? WHERE id = ?${ownerFilter}`,\n args,\n });\n if (result.rowsAffected > 0) {\n emitChatThreadChange(id);\n return true;\n }\n return false;\n}\n\nexport interface UpdateThreadDataOptions {\n preserveExistingQueuedMessages?: boolean;\n preserveExistingTopLevelKeys?: boolean;\n maxAttempts?: number;\n}\n\nfunction parseThreadData(value: string): any {\n try {\n return JSON.parse(value || \"{}\");\n } catch {\n return {};\n }\n}\n\nexport async function updateThreadData(\n id: string,\n threadData: string,\n title: string,\n preview: string,\n messageCount: number,\n options: UpdateThreadDataOptions = {},\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const maxAttempts =\n options.maxAttempts ?? DEFAULT_THREAD_DATA_UPDATE_ATTEMPTS;\n let lastConflict = false;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const current = await getThread(id);\n if (!current) return;\n\n let nextThreadData = threadData;\n let nextMessageCount = messageCount;\n try {\n const merged = mergeThreadDataForClientSave(\n parseThreadData(current.threadData),\n parseThreadData(threadData),\n {\n preserveExistingQueuedMessages:\n options.preserveExistingQueuedMessages ?? true,\n preserveExistingTopLevelKeys:\n options.preserveExistingTopLevelKeys ?? true,\n },\n );\n nextThreadData = JSON.stringify(merged);\n if (Array.isArray(merged.messages)) {\n nextMessageCount = merged.messages.length;\n }\n } catch {\n // Keep the caller's serialized value if either JSON blob is malformed.\n }\n\n const nextUpdatedAt = Math.max(Date.now(), current.updatedAt + 1);\n const result = await client.execute({\n sql: `UPDATE chat_threads SET thread_data = ?, title = ?, preview = ?, message_count = ?, updated_at = ? WHERE id = ? AND updated_at = ?`,\n args: [\n nextThreadData,\n title,\n preview,\n nextMessageCount,\n nextUpdatedAt,\n id,\n current.updatedAt,\n ],\n });\n\n if (result.rowsAffected > 0) {\n emitChatThreadChange(id);\n return;\n }\n\n lastConflict = true;\n if (attempt < maxAttempts - 1) {\n await new Promise((resolve) =>\n setTimeout(\n resolve,\n Math.min(250, THREAD_DATA_CONFLICT_BACKOFF_MS * (attempt + 1)),\n ),\n );\n }\n }\n\n if (lastConflict) {\n throw new Error(\n `Failed to update chat thread ${id} after concurrent write conflicts.`,\n );\n }\n}\n\nexport interface ThreadEngineMeta {\n engineName: string;\n model: string;\n}\n\n/**\n * Read the engine pinned to a thread (stored in thread_data JSON).\n * Returns null if no engine is pinned.\n */\nexport async function getThreadEngineMeta(\n threadId: string,\n): Promise<ThreadEngineMeta | null> {\n const thread = await getThread(threadId);\n if (!thread?.threadData) return null;\n try {\n const data = JSON.parse(thread.threadData);\n if (data.engineMeta?.engineName) return data.engineMeta as ThreadEngineMeta;\n } catch {}\n return null;\n}\n\n/**\n * Pin an engine to a thread by storing engineMeta in thread_data JSON.\n * Does not change messages, title, or preview.\n */\nexport async function setThreadEngineMeta(\n threadId: string,\n meta: ThreadEngineMeta,\n): Promise<void> {\n return withThreadDataLock(threadId, async () => {\n const thread = await getThread(threadId);\n if (!thread) return;\n let data: Record<string, unknown> = {};\n try {\n data = JSON.parse(thread.threadData);\n } catch {}\n data.engineMeta = meta;\n await updateThreadData(\n threadId,\n JSON.stringify(data),\n thread.title,\n thread.preview,\n thread.messageCount,\n );\n });\n}\n\nexport interface QueuedMessage {\n id: string;\n text: string;\n images?: string[];\n references?: unknown[];\n}\n\n/**\n * Persist the user's queued (not-yet-sent) messages onto the thread.\n * Stored in thread_data JSON so it survives reloads without a schema\n * change. Safe to call often — the frontend debounces writes.\n */\nexport async function setThreadQueuedMessages(\n threadId: string,\n queuedMessages: QueuedMessage[],\n): Promise<void> {\n return withThreadDataLock(threadId, async () => {\n const thread = await getThread(threadId);\n if (!thread) return;\n let data: Record<string, unknown> = {};\n try {\n data = JSON.parse(thread.threadData);\n } catch {}\n if (queuedMessages.length === 0) {\n delete data.queuedMessages;\n } else {\n data.queuedMessages = queuedMessages;\n }\n await updateThreadData(\n threadId,\n JSON.stringify(data),\n thread.title,\n thread.preview,\n thread.messageCount,\n { preserveExistingQueuedMessages: false },\n );\n });\n}\n\nexport async function deleteThread(id: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM chat_threads WHERE id = ?`,\n args: [id],\n });\n if (result.rowsAffected > 0) {\n emitChatThreadChange(id);\n return true;\n }\n return false;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/chat-threads/store.ts"],"names":[],"mappings":"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","sourcesContent":["import crypto from \"node:crypto\";\nimport { getDbExec, intType } from \"../db/client.js\";\nimport {\n mergeThreadDataForClientSave,\n normalizeThreadRepository,\n normalizeThreadTitle,\n} from \"../agent/thread-data-builder.js\";\nimport { emitChatThreadChange } from \"./emitter.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n/**\n * Per-thread async mutex. Read-modify-write on the `thread_data` JSON blob\n * is not atomic at the DB level — two concurrent callers (e.g. the UI\n * persisting queued messages while `onRunComplete` appends agent output)\n * would both read the same row, each mutate it independently, and the\n * second write clobbers the first. Serializing on thread id inside this\n * process eliminates the race for the usual single-process deployment\n * while leaving straight reads and other thread-data-unrelated updates\n * untouched.\n *\n * Cross-process races are handled by `updateThreadData`, which performs a\n * compare-and-swap on `updated_at`, rereads the latest row on conflict, and\n * remerges message history before retrying.\n */\nconst _threadDataLocks = new Map<string, Promise<unknown>>();\nconst DEFAULT_THREAD_DATA_UPDATE_ATTEMPTS = 12;\nconst THREAD_DATA_CONFLICT_BACKOFF_MS = 25;\n\nexport function withThreadDataLock<T>(\n threadId: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const prev = _threadDataLocks.get(threadId) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n _threadDataLocks.set(threadId, next);\n // Use `.then(cleanup, cleanup)` (not `.finally`) so the rejection is\n // observed on this chained promise — otherwise any failure inside `fn`\n // triggers `unhandledRejection` on the discarded `finally()` return.\n // The caller still sees the rejection via `next`.\n const cleanup = () => {\n if (_threadDataLocks.get(threadId) === next) {\n _threadDataLocks.delete(threadId);\n }\n };\n next.then(cleanup, cleanup);\n return next as Promise<T>;\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS chat_threads (\n id TEXT PRIMARY KEY,\n owner_email TEXT NOT NULL,\n title TEXT NOT NULL DEFAULT '',\n preview TEXT NOT NULL DEFAULT '',\n thread_data TEXT NOT NULL DEFAULT '{}',\n message_count ${intType()} NOT NULL DEFAULT 0,\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL,\n scope_type TEXT,\n scope_id TEXT,\n scope_label TEXT,\n pinned_at ${intType()},\n archived_at ${intType()}\n )\n `);\n // Additive migration for existing tables. Both SQLite and Postgres\n // accept `ALTER TABLE ADD COLUMN` and will raise when the column\n // already exists; the try/catch makes the call idempotent across\n // both dialects without requiring an information_schema probe.\n for (const [col, type] of [\n [\"scope_type\", \"TEXT\"],\n [\"scope_id\", \"TEXT\"],\n [\"scope_label\", \"TEXT\"],\n [\"pinned_at\", intType()],\n [\"archived_at\", intType()],\n ] as const) {\n try {\n await client.execute(\n `ALTER TABLE chat_threads ADD COLUMN ${col} ${type}`,\n );\n } catch {\n // Column already exists.\n }\n }\n // Indexes for the hot read paths. Both the sidebar list and the\n // scoped/per-resource list filter on owner_email (and optionally\n // scope) and sort by updated_at. Keep these dialect-agnostic (no\n // DESC, partial, or PG-only syntax) so they apply identically on\n // SQLite and the configured Postgres. `IF NOT EXISTS` makes them\n // idempotent across restarts.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS chat_threads_owner_updated_idx ON chat_threads (owner_email, updated_at)`,\n `CREATE INDEX IF NOT EXISTS chat_threads_scope_updated_idx ON chat_threads (scope_type, scope_id, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n // One-time backfill of message_count for legacy rows written before\n // the column was maintained. The list/summary path now reads\n // message_count directly (instead of re-parsing the thread_data blob)\n // and filters on `message_count > 0`, so any legacy row that has\n // messages but a stale `message_count = 0` would otherwise vanish from\n // the sidebar. Recompute the count from thread_data and persist it so\n // the hot path can stay blob-free. Idempotent: only touches rows where\n // the count is still 0 but the blob clearly contains a messages array.\n await backfillLegacyMessageCounts(client);\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\n/**\n * Recompute `message_count` from `thread_data` for legacy rows that still\n * have a stale `message_count = 0` despite carrying a messages array in the\n * blob. Without this, dropping the `thread_data` payload (and the\n * `OR thread_data LIKE '%\"messages\"%'` filter) from the list path would make\n * those rows disappear from the sidebar. Runs once at table bootstrap and is\n * idempotent — after the first pass no row matches the WHERE clause again.\n */\nasync function backfillLegacyMessageCounts(\n client: ReturnType<typeof getDbExec>,\n): Promise<void> {\n const { rows } = await client.execute({\n sql: `SELECT id, thread_data, message_count FROM chat_threads WHERE message_count = 0 AND thread_data LIKE '%\"messages\"%'`,\n args: [],\n });\n for (const r of rows) {\n const count = deriveMessageCount(r.thread_data, 0);\n if (count <= 0) continue;\n await client.execute({\n sql: `UPDATE chat_threads SET message_count = ? WHERE id = ? AND message_count = 0`,\n args: [count, r.id as string],\n });\n }\n}\n\nfunction generateId(): string {\n return `thread-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * A resource the chat is bound to, e.g. `{ type: \"deck\", id: \"deck-abc\" }`.\n * The framework is opaque to the type string — each template chooses what\n * its primary resource is and the surface it scopes to (deck, design,\n * dashboard, etc.). `label` is a denormalized snapshot for display when\n * the resource isn't on hand at render time; the live template can\n * overwrite it via the next createThread call.\n */\nexport interface ChatThreadScope {\n type: string;\n id: string;\n label?: string;\n}\n\nexport interface ChatThread {\n id: string;\n ownerEmail: string;\n title: string;\n preview: string;\n threadData: string;\n messageCount: number;\n createdAt: number;\n updatedAt: number;\n scope: ChatThreadScope | null;\n pinnedAt: number | null;\n archivedAt: number | null;\n}\n\nexport interface ChatThreadSummary {\n id: string;\n title: string;\n preview: string;\n messageCount: number;\n createdAt: number;\n updatedAt: number;\n scope: ChatThreadScope | null;\n pinnedAt: number | null;\n archivedAt: number | null;\n}\n\nexport interface ForkThreadSourceSnapshot {\n threadData: string;\n title?: string;\n preview?: string;\n messageCount?: number;\n scope?: ChatThreadScope | null;\n}\n\nfunction readScope(r: Record<string, unknown>): ChatThreadScope | null {\n const type = r.scope_type as string | null | undefined;\n const id = r.scope_id as string | null | undefined;\n if (!type || !id) return null;\n const label = r.scope_label as string | null | undefined;\n return label ? { type, id, label } : { type, id };\n}\n\nfunction readNullableNumber(value: unknown): number | null {\n if (value == null) return null;\n const n = Number(value);\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\nfunction normalizeForkSourceSnapshot(\n source: ForkThreadSourceSnapshot | null | undefined,\n): {\n threadData: string;\n title: string;\n preview: string;\n messageCount: number;\n scope?: ChatThreadScope | null;\n} | null {\n if (!source || typeof source.threadData !== \"string\") return null;\n const threadData = source.threadData.trim();\n if (!threadData) return null;\n\n let parsed: any;\n try {\n parsed = normalizeThreadRepository(JSON.parse(threadData));\n } catch {\n return null;\n }\n\n const repoMessageCount = Array.isArray(parsed.messages)\n ? parsed.messages.length\n : 0;\n if (repoMessageCount <= 0) return null;\n\n return {\n threadData: JSON.stringify(parsed),\n title: typeof source.title === \"string\" ? source.title : \"\",\n preview: typeof source.preview === \"string\" ? source.preview : \"\",\n messageCount: repoMessageCount,\n ...(Object.prototype.hasOwnProperty.call(source, \"scope\")\n ? { scope: source.scope ?? null }\n : {}),\n };\n}\n\nfunction deriveMessageCount(threadData: unknown, fallback: number): number {\n if (typeof threadData !== \"string\" || !threadData.trim()) return fallback;\n try {\n const repo = normalizeThreadRepository(JSON.parse(threadData));\n if (Array.isArray(repo.messages)) return repo.messages.length;\n } catch {\n // Keep the stored count if the JSON blob is malformed.\n }\n return fallback;\n}\n\nfunction rowToThread(r: Record<string, unknown>): ChatThread {\n const threadData = (r.thread_data as string) ?? \"{}\";\n const storedCount = Number(r.message_count);\n return {\n id: r.id as string,\n ownerEmail: r.owner_email as string,\n title: r.title as string,\n preview: r.preview as string,\n threadData,\n messageCount: deriveMessageCount(threadData, storedCount),\n createdAt: Number(r.created_at),\n updatedAt: Number(r.updated_at),\n scope: readScope(r),\n pinnedAt: readNullableNumber(r.pinned_at),\n archivedAt: readNullableNumber(r.archived_at),\n };\n}\n\nfunction rowToSummary(r: Record<string, unknown>): ChatThreadSummary | null {\n // The summary path never loads `thread_data`; the count comes from the\n // dedicated `message_count` column (maintained on write, backfilled for\n // legacy rows at bootstrap). Empty threads are filtered out of the list.\n const messageCount = Number(r.message_count);\n if (!Number.isFinite(messageCount) || messageCount <= 0) return null;\n return {\n id: r.id as string,\n title: r.title as string,\n preview: r.preview as string,\n messageCount,\n createdAt: Number(r.created_at),\n updatedAt: Number(r.updated_at),\n scope: readScope(r),\n pinnedAt: readNullableNumber(r.pinned_at),\n archivedAt: readNullableNumber(r.archived_at),\n };\n}\n\nexport async function createThread(\n ownerEmail: string,\n opts?: { id?: string; title?: string; scope?: ChatThreadScope | null },\n): Promise<ChatThread> {\n await ensureTable();\n const client = getDbExec();\n const id = opts?.id ?? generateId();\n const now = Date.now();\n const title = opts?.title ?? \"\";\n const scope = opts?.scope ?? null;\n\n await client.execute({\n sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label) VALUES (?, ?, ?, '', '{}', 0, ?, ?, ?, ?, ?)`,\n args: [\n id,\n ownerEmail,\n title,\n now,\n now,\n scope?.type ?? null,\n scope?.id ?? null,\n scope?.label ?? null,\n ],\n });\n\n return {\n id,\n ownerEmail,\n title,\n preview: \"\",\n threadData: \"{}\",\n messageCount: 0,\n createdAt: now,\n updatedAt: now,\n scope,\n pinnedAt: null,\n archivedAt: null,\n };\n}\n\nconst THREAD_COLUMNS = `id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at`;\n// The list/summary path deliberately omits `thread_data`: it is the full\n// message-history JSON blob and selecting it for every row turns \"open the\n// sidebar\" into \"download every conversation\". The summary derives nothing\n// from the blob anymore — preview and message_count are dedicated columns\n// (message_count is maintained on write and backfilled for legacy rows at\n// bootstrap). The detail path (`THREAD_COLUMNS` / `getThread`) still returns\n// the full blob.\nconst SUMMARY_COLUMNS = `id, title, preview, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at`;\n\nexport async function getThread(id: string): Promise<ChatThread | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT ${THREAD_COLUMNS} FROM chat_threads WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n return rowToThread(rows[0]);\n}\n\nexport async function forkThread(\n sourceId: string,\n ownerEmail: string,\n opts?: { id?: string; source?: ForkThreadSourceSnapshot | null },\n): Promise<ChatThread | null> {\n const snapshot = normalizeForkSourceSnapshot(opts?.source);\n let source = await getThread(sourceId);\n if (!source) {\n if (snapshot) {\n try {\n await createThread(ownerEmail, {\n id: sourceId,\n title: snapshot.title,\n scope: snapshot.scope ?? null,\n });\n } catch {\n // The agent run may have created the row while the user clicked Fork.\n }\n const created = await getThread(sourceId);\n if (created?.ownerEmail === ownerEmail) {\n await updateThreadData(\n sourceId,\n snapshot.threadData,\n snapshot.title || created.title,\n snapshot.preview || created.preview,\n snapshot.messageCount,\n );\n if (Object.prototype.hasOwnProperty.call(snapshot, \"scope\")) {\n await setThreadScope(sourceId, snapshot.scope ?? null);\n }\n source = await getThread(sourceId);\n }\n }\n } else if (\n snapshot &&\n source.ownerEmail === ownerEmail &&\n snapshot.messageCount > source.messageCount\n ) {\n // The source row exists but the in-memory snapshot is fresher — the agent\n // run flushed an older state to SQL, but the tab has additional unflushed\n // messages. Overlay the snapshot before cloning so the fork captures the\n // latest user-visible content. Guard with messageCount > stored to avoid\n // clobbering a fresher persisted row with a stale snapshot from another\n // tab.\n source = {\n ...source,\n threadData: snapshot.threadData,\n title: snapshot.title || source.title,\n preview: snapshot.preview || source.preview,\n messageCount: snapshot.messageCount,\n };\n }\n if (!source || source.ownerEmail !== ownerEmail) return null;\n const id = opts?.id ?? generateId();\n const now = Date.now();\n const title = source.title ? `${source.title} (fork)` : \"\";\n const client = getDbExec();\n await client.execute({\n sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n ownerEmail,\n title,\n source.preview,\n source.threadData,\n source.messageCount,\n now,\n now,\n source.scope?.type ?? null,\n source.scope?.id ?? null,\n source.scope?.label ?? null,\n ],\n });\n return {\n id,\n ownerEmail,\n title,\n preview: source.preview,\n threadData: source.threadData,\n messageCount: source.messageCount,\n createdAt: now,\n updatedAt: now,\n scope: source.scope,\n pinnedAt: null,\n archivedAt: null,\n };\n}\n\nexport interface ListThreadsOptions {\n limit?: number;\n offset?: number;\n /**\n * Filter for chats bound to a specific resource. The default (undefined)\n * returns every thread the user owns. `{ type: \"deck\", id: \"abc\" }`\n * returns only that resource's threads. `{ type: \"deck\", id: null }` is\n * NOT supported — pass `unscopedOnly: true` to get only general chats.\n */\n scope?: { type: string; id: string };\n /** When true, returns only threads with no scope (general chats). */\n unscopedOnly?: boolean;\n}\n\nexport async function listThreads(\n ownerEmail: string,\n options: ListThreadsOptions | number = {},\n legacyOffset?: number,\n): Promise<ChatThreadSummary[]> {\n await ensureTable();\n // Back-compat shim: previous signature was (owner, limit, offset).\n const opts: ListThreadsOptions =\n typeof options === \"number\"\n ? { limit: options, offset: legacyOffset ?? 0 }\n : options;\n const limit = opts.limit ?? 50;\n const offset = opts.offset ?? 0;\n const client = getDbExec();\n // `message_count > 0` is the authoritative \"has messages\" signal: it is\n // maintained on every write and backfilled for legacy rows at bootstrap,\n // so the old `OR thread_data LIKE '%\"messages\"%'` substring scan over the\n // full blob is no longer needed here.\n const filters: string[] = [`owner_email = ?`, `message_count > 0`];\n const args: (string | number)[] = [ownerEmail];\n if (opts.scope) {\n filters.push(`scope_type = ? AND scope_id = ?`);\n args.push(opts.scope.type, opts.scope.id);\n } else if (opts.unscopedOnly) {\n filters.push(`scope_type IS NULL`);\n }\n args.push(limit, offset);\n const { rows } = await client.execute({\n sql: `SELECT ${SUMMARY_COLUMNS} FROM chat_threads WHERE ${filters.join(\" AND \")} ORDER BY CASE WHEN pinned_at IS NULL THEN 1 ELSE 0 END, pinned_at DESC, updated_at DESC LIMIT ? OFFSET ?`,\n args,\n });\n return rows\n .map((r) => rowToSummary(r))\n .filter((r): r is ChatThreadSummary => r !== null);\n}\n\nfunction escapeLike(s: string): string {\n return s.replace(/([\\\\%_])/g, \"\\\\$1\");\n}\n\nexport async function searchThreads(\n ownerEmail: string,\n query: string,\n limit = 50,\n options: { scope?: { type: string; id: string } } = {},\n): Promise<ChatThreadSummary[]> {\n await ensureTable();\n const client = getDbExec();\n const pattern = `%${escapeLike(query)}%`;\n // The count-guard uses the maintained/backfilled `message_count` column\n // (same as listThreads). The content match still scans `thread_data` —\n // search legitimately needs to look inside message history.\n const filters: string[] = [\n `owner_email = ?`,\n `message_count > 0`,\n `(title LIKE ? ESCAPE '\\\\' OR preview LIKE ? ESCAPE '\\\\' OR thread_data LIKE ? ESCAPE '\\\\')`,\n ];\n const args: (string | number)[] = [ownerEmail, pattern, pattern, pattern];\n if (options.scope) {\n filters.push(`scope_type = ? AND scope_id = ?`);\n args.push(options.scope.type, options.scope.id);\n }\n args.push(limit);\n const { rows } = await client.execute({\n sql: `SELECT ${SUMMARY_COLUMNS} FROM chat_threads WHERE ${filters.join(\" AND \")} ORDER BY CASE WHEN pinned_at IS NULL THEN 1 ELSE 0 END, pinned_at DESC, updated_at DESC LIMIT ?`,\n args,\n });\n return rows\n .map((r) => rowToSummary(r))\n .filter((r): r is ChatThreadSummary => r !== null);\n}\n\n/**\n * Detach or rebind a chat's scope. Used by the UI's \"Detach from <resource>\"\n * action and by templates that need to retag a chat after a rename. Pass\n * `null` to clear the scope (chat becomes general).\n */\nexport async function setThreadScope(\n id: string,\n scope: ChatThreadScope | null,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: `UPDATE chat_threads SET scope_type = ?, scope_id = ?, scope_label = ?, updated_at = ? WHERE id = ?`,\n args: [\n scope?.type ?? null,\n scope?.id ?? null,\n scope?.label ?? null,\n Math.max(Date.now(), 1),\n id,\n ],\n });\n emitChatThreadChange(id);\n}\n\nexport async function renameThread(\n id: string,\n title: string,\n options: { ownerEmail?: string } = {},\n): Promise<boolean> {\n const nextTitle = normalizeThreadTitle(title);\n if (!nextTitle) return false;\n\n return await withThreadDataLock(id, async () => {\n const thread = await getThread(id);\n if (!thread) return false;\n if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {\n return false;\n }\n\n const repo = parseThreadData(thread.threadData);\n repo._titleOverride = nextTitle;\n await updateThreadData(\n id,\n JSON.stringify(repo),\n nextTitle,\n thread.preview,\n thread.messageCount,\n );\n return true;\n });\n}\n\nexport async function setThreadPinned(\n id: string,\n pinned: boolean,\n options: { ownerEmail?: string } = {},\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Math.max(Date.now(), 1);\n const args: (string | number | null)[] = [pinned ? now : null, id];\n let ownerFilter = \"\";\n if (options.ownerEmail) {\n ownerFilter = \" AND owner_email = ?\";\n args.push(options.ownerEmail);\n }\n const result = await client.execute({\n sql: `UPDATE chat_threads SET pinned_at = ? WHERE id = ?${ownerFilter}`,\n args,\n });\n if (result.rowsAffected > 0) {\n emitChatThreadChange(id);\n return true;\n }\n return false;\n}\n\nexport async function setThreadArchived(\n id: string,\n archived: boolean,\n options: { ownerEmail?: string } = {},\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Math.max(Date.now(), 1);\n const args: (string | number | null)[] = [archived ? now : null, id];\n let ownerFilter = \"\";\n if (options.ownerEmail) {\n ownerFilter = \" AND owner_email = ?\";\n args.push(options.ownerEmail);\n }\n const result = await client.execute({\n sql: `UPDATE chat_threads SET archived_at = ? WHERE id = ?${ownerFilter}`,\n args,\n });\n if (result.rowsAffected > 0) {\n emitChatThreadChange(id);\n return true;\n }\n return false;\n}\n\nexport interface UpdateThreadDataOptions {\n preserveExistingQueuedMessages?: boolean;\n preserveExistingTopLevelKeys?: boolean;\n maxAttempts?: number;\n}\n\nfunction parseThreadData(value: string): any {\n try {\n return JSON.parse(value || \"{}\");\n } catch {\n return {};\n }\n}\n\nexport async function updateThreadData(\n id: string,\n threadData: string,\n title: string,\n preview: string,\n messageCount: number,\n options: UpdateThreadDataOptions = {},\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const maxAttempts =\n options.maxAttempts ?? DEFAULT_THREAD_DATA_UPDATE_ATTEMPTS;\n let lastConflict = false;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const current = await getThread(id);\n if (!current) return;\n\n let nextThreadData = threadData;\n let nextMessageCount = messageCount;\n try {\n const merged = mergeThreadDataForClientSave(\n parseThreadData(current.threadData),\n parseThreadData(threadData),\n {\n preserveExistingQueuedMessages:\n options.preserveExistingQueuedMessages ?? true,\n preserveExistingTopLevelKeys:\n options.preserveExistingTopLevelKeys ?? true,\n },\n );\n nextThreadData = JSON.stringify(merged);\n if (Array.isArray(merged.messages)) {\n nextMessageCount = merged.messages.length;\n }\n } catch {\n // Keep the caller's serialized value if either JSON blob is malformed.\n }\n\n const nextUpdatedAt = Math.max(Date.now(), current.updatedAt + 1);\n const result = await client.execute({\n sql: `UPDATE chat_threads SET thread_data = ?, title = ?, preview = ?, message_count = ?, updated_at = ? WHERE id = ? AND updated_at = ?`,\n args: [\n nextThreadData,\n title,\n preview,\n nextMessageCount,\n nextUpdatedAt,\n id,\n current.updatedAt,\n ],\n });\n\n if (result.rowsAffected > 0) {\n emitChatThreadChange(id);\n return;\n }\n\n lastConflict = true;\n if (attempt < maxAttempts - 1) {\n await new Promise((resolve) =>\n setTimeout(\n resolve,\n Math.min(250, THREAD_DATA_CONFLICT_BACKOFF_MS * (attempt + 1)),\n ),\n );\n }\n }\n\n if (lastConflict) {\n throw new Error(\n `Failed to update chat thread ${id} after concurrent write conflicts.`,\n );\n }\n}\n\nexport interface ThreadEngineMeta {\n engineName: string;\n model: string;\n}\n\n/**\n * Read the engine pinned to a thread (stored in thread_data JSON).\n * Returns null if no engine is pinned.\n */\nexport async function getThreadEngineMeta(\n threadId: string,\n): Promise<ThreadEngineMeta | null> {\n const thread = await getThread(threadId);\n if (!thread?.threadData) return null;\n try {\n const data = JSON.parse(thread.threadData);\n if (data.engineMeta?.engineName) return data.engineMeta as ThreadEngineMeta;\n } catch {}\n return null;\n}\n\n/**\n * Pin an engine to a thread by storing engineMeta in thread_data JSON.\n * Does not change messages, title, or preview.\n */\nexport async function setThreadEngineMeta(\n threadId: string,\n meta: ThreadEngineMeta,\n): Promise<void> {\n return withThreadDataLock(threadId, async () => {\n const thread = await getThread(threadId);\n if (!thread) return;\n let data: Record<string, unknown> = {};\n try {\n data = JSON.parse(thread.threadData);\n } catch {}\n data.engineMeta = meta;\n await updateThreadData(\n threadId,\n JSON.stringify(data),\n thread.title,\n thread.preview,\n thread.messageCount,\n );\n });\n}\n\nexport interface QueuedMessage {\n id: string;\n text: string;\n images?: string[];\n references?: unknown[];\n}\n\n/**\n * Persist the user's queued (not-yet-sent) messages onto the thread.\n * Stored in thread_data JSON so it survives reloads without a schema\n * change. Safe to call often — the frontend debounces writes.\n */\nexport async function setThreadQueuedMessages(\n threadId: string,\n queuedMessages: QueuedMessage[],\n): Promise<void> {\n return withThreadDataLock(threadId, async () => {\n const thread = await getThread(threadId);\n if (!thread) return;\n let data: Record<string, unknown> = {};\n try {\n data = JSON.parse(thread.threadData);\n } catch {}\n if (queuedMessages.length === 0) {\n delete data.queuedMessages;\n } else {\n data.queuedMessages = queuedMessages;\n }\n await updateThreadData(\n threadId,\n JSON.stringify(data),\n thread.title,\n thread.preview,\n thread.messageCount,\n { preserveExistingQueuedMessages: false },\n );\n });\n}\n\nconst THREAD_SHARE_DATA_KEY = \"_share\";\n\ninterface StoredThreadShare {\n tokenHash?: string;\n createdAt?: number;\n updatedAt?: number;\n revokedAt?: number | null;\n}\n\nexport interface ChatThreadShareState {\n enabled: boolean;\n createdAt: number | null;\n updatedAt: number | null;\n revokedAt: number | null;\n}\n\nexport interface ChatThreadShareLink extends ChatThreadShareState {\n enabled: true;\n token: string;\n}\n\nfunction generateShareToken(): string {\n return crypto.randomBytes(24).toString(\"base64url\");\n}\n\nexport function hashThreadShareToken(token: string): string {\n return crypto.createHash(\"sha256\").update(token).digest(\"hex\");\n}\n\nfunction normalizeThreadShare(value: unknown): StoredThreadShare | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const r = value as Record<string, unknown>;\n const tokenHash =\n typeof r.tokenHash === \"string\" && /^[a-f0-9]{64}$/i.test(r.tokenHash)\n ? r.tokenHash.toLowerCase()\n : undefined;\n const createdAt = normalizeTimestamp(r.createdAt);\n const updatedAt = normalizeTimestamp(r.updatedAt);\n const revokedAt = normalizeTimestamp(r.revokedAt);\n if (!tokenHash && !createdAt && !updatedAt && !revokedAt) return null;\n return {\n ...(tokenHash ? { tokenHash } : {}),\n ...(createdAt ? { createdAt } : {}),\n ...(updatedAt ? { updatedAt } : {}),\n ...(revokedAt ? { revokedAt } : {}),\n };\n}\n\nfunction normalizeTimestamp(value: unknown): number | undefined {\n const n = Number(value);\n return Number.isFinite(n) && n > 0 ? n : undefined;\n}\n\nfunction shareStateFromStored(\n stored: StoredThreadShare | null,\n): ChatThreadShareState {\n const revokedAt = stored?.revokedAt ?? null;\n return {\n enabled: Boolean(stored?.tokenHash && !revokedAt),\n createdAt: stored?.createdAt ?? null,\n updatedAt: stored?.updatedAt ?? null,\n revokedAt,\n };\n}\n\nfunction readStoredThreadShare(threadData: string): StoredThreadShare | null {\n const data = parseThreadData(threadData);\n return normalizeThreadShare(data[THREAD_SHARE_DATA_KEY]);\n}\n\nexport async function getThreadShareState(\n threadId: string,\n options: { ownerEmail?: string } = {},\n): Promise<ChatThreadShareState | null> {\n const thread = await getThread(threadId);\n if (!thread) return null;\n if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {\n return null;\n }\n return shareStateFromStored(readStoredThreadShare(thread.threadData));\n}\n\nexport async function createThreadShareLink(\n threadId: string,\n options: { ownerEmail?: string } = {},\n): Promise<ChatThreadShareLink | null> {\n return withThreadDataLock(threadId, async () => {\n const thread = await getThread(threadId);\n if (!thread) return null;\n if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {\n return null;\n }\n\n const now = Date.now();\n const token = generateShareToken();\n const data = parseThreadData(thread.threadData);\n const existing = normalizeThreadShare(data[THREAD_SHARE_DATA_KEY]);\n data[THREAD_SHARE_DATA_KEY] = {\n tokenHash: hashThreadShareToken(token),\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n revokedAt: null,\n } satisfies StoredThreadShare;\n\n await updateThreadData(\n threadId,\n JSON.stringify(data),\n thread.title,\n thread.preview,\n thread.messageCount,\n );\n\n return {\n enabled: true,\n token,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n revokedAt: null,\n };\n });\n}\n\nexport async function revokeThreadShareLink(\n threadId: string,\n options: { ownerEmail?: string } = {},\n): Promise<ChatThreadShareState | null> {\n return withThreadDataLock(threadId, async () => {\n const thread = await getThread(threadId);\n if (!thread) return null;\n if (options.ownerEmail && thread.ownerEmail !== options.ownerEmail) {\n return null;\n }\n\n const now = Date.now();\n const data = parseThreadData(thread.threadData);\n const existing = normalizeThreadShare(data[THREAD_SHARE_DATA_KEY]);\n data[THREAD_SHARE_DATA_KEY] = {\n ...(existing?.createdAt ? { createdAt: existing.createdAt } : {}),\n updatedAt: now,\n revokedAt: now,\n } satisfies StoredThreadShare;\n\n await updateThreadData(\n threadId,\n JSON.stringify(data),\n thread.title,\n thread.preview,\n thread.messageCount,\n );\n\n return {\n enabled: false,\n createdAt: existing?.createdAt ?? null,\n updatedAt: now,\n revokedAt: now,\n };\n });\n}\n\nexport async function getThreadByShareToken(\n token: string,\n): Promise<ChatThread | null> {\n const cleanToken = token.trim();\n if (!cleanToken || cleanToken.length < 16) return null;\n await ensureTable();\n const tokenHash = hashThreadShareToken(cleanToken);\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT ${THREAD_COLUMNS} FROM chat_threads WHERE thread_data LIKE ? LIMIT 10`,\n args: [`%${tokenHash}%`],\n });\n for (const row of rows) {\n const thread = rowToThread(row);\n const stored = readStoredThreadShare(thread.threadData);\n if (!stored?.tokenHash || stored.revokedAt) continue;\n if (stored.tokenHash !== tokenHash) continue;\n return thread;\n }\n return null;\n}\n\nexport async function deleteThread(id: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM chat_threads WHERE id = ?`,\n args: [id],\n });\n if (result.rowsAffected > 0) {\n emitChatThreadChange(id);\n return true;\n }\n return false;\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ActionEntry } from "../agent/production-agent.js";
|
|
2
|
+
export interface ParsedAgentArgs {
|
|
3
|
+
prompt?: string;
|
|
4
|
+
engine?: string;
|
|
5
|
+
model?: string;
|
|
6
|
+
userEmail?: string;
|
|
7
|
+
orgId?: string;
|
|
8
|
+
softTimeoutMs?: number;
|
|
9
|
+
maxIterations?: number;
|
|
10
|
+
json: boolean;
|
|
11
|
+
help: boolean;
|
|
12
|
+
errors: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface AgentCliIo {
|
|
15
|
+
stdout?: (text: string) => void;
|
|
16
|
+
stderr?: (text: string) => void;
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
}
|
|
19
|
+
export declare function parseAgentArgs(args: string[]): ParsedAgentArgs;
|
|
20
|
+
export declare function runAgent(args: string[], io?: AgentCliIo): Promise<number>;
|
|
21
|
+
export declare function createHeadlessBuiltinActions(): Promise<Record<string, ActionEntry>>;
|
|
22
|
+
export declare function formatAgentUsage(): string;
|
|
23
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/cli/agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,8BAA8B,CAAC;AAWtC,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAeD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CA6F9D;AAED,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,GAAE,UAAe,GAClB,OAAO,CAAC,MAAM,CAAC,CA6CjB;AAoGD,wBAAsB,4BAA4B,IAAI,OAAO,CAC3D,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAC5B,CAoCA;AAgCD,wBAAgB,gBAAgB,IAAI,MAAM,CAiBzC"}
|