teamcopilot 0.4.5 → 0.4.8

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.
Files changed (32) hide show
  1. package/dist/chat/index.js +50 -17
  2. package/dist/frontend/assets/{cssMode-CvRVDXG_.js → cssMode-DZn-mShz.js} +1 -1
  3. package/dist/frontend/assets/{freemarker2-DgQ2M5UY.js → freemarker2-CegGjf81.js} +1 -1
  4. package/dist/frontend/assets/{handlebars-wy-Q6dav.js → handlebars-BrnGJP7k.js} +1 -1
  5. package/dist/frontend/assets/{html-GlWH_zxY.js → html-D2j_zh26.js} +1 -1
  6. package/dist/frontend/assets/{htmlMode-Ca1Ceuuh.js → htmlMode-E6rcAfpa.js} +1 -1
  7. package/dist/frontend/assets/index-BIWriczA.css +1 -0
  8. package/dist/frontend/assets/{index-B26t3H3z.js → index-hJceX1zt.js} +229 -229
  9. package/dist/frontend/assets/{javascript-Bbpts-9Y.js → javascript-BNuAj_rx.js} +1 -1
  10. package/dist/frontend/assets/{jsonMode-CMv1vbd6.js → jsonMode-3kRNhQzL.js} +1 -1
  11. package/dist/frontend/assets/{liquid-BrRrRvaX.js → liquid-CtREgq_v.js} +1 -1
  12. package/dist/frontend/assets/{mdx-CWx6se4s.js → mdx-D8mD0lQ9.js} +1 -1
  13. package/dist/frontend/assets/{python-BVmKLBFr.js → python-BupSEVk8.js} +1 -1
  14. package/dist/frontend/assets/{razor-C8h4gejb.js → razor-CZk9Wcl0.js} +1 -1
  15. package/dist/frontend/assets/{tsMode-7UwZo5hi.js → tsMode-_5D_uZS8.js} +1 -1
  16. package/dist/frontend/assets/{typescript-y-BmH6lW.js → typescript-CEqwV7Eh.js} +1 -1
  17. package/dist/frontend/assets/{xml-B7rKIdpM.js → xml-BLIUxl1L.js} +1 -1
  18. package/dist/frontend/assets/{yaml-Dfxd-sNZ.js → yaml-BS4L7bXU.js} +1 -1
  19. package/dist/frontend/index.html +2 -2
  20. package/dist/utils/chat-usage.js +74 -13
  21. package/dist/utils/session-messages-page.js +49 -0
  22. package/dist/workspace_files/.opencode/plugins/secret-proxy.ts +97 -34
  23. package/package.json +1 -1
  24. package/prisma/dev.db +0 -0
  25. package/prisma/generated/client/edge.js +2 -2
  26. package/prisma/generated/client/index.js +2 -2
  27. package/prisma/generated/client/package.json +1 -1
  28. package/prisma/generated/client/schema.prisma +1 -0
  29. package/prisma/generated/client/wasm.js +2 -2
  30. package/prisma/migrations/20260519172913_add_chat_sessions_recent_poll_index/migration.sql +2 -0
  31. package/prisma/schema.prisma +1 -0
  32. package/dist/frontend/assets/index-D8Z1_xDr.css +0 -1
@@ -9,6 +9,7 @@ const client_1 = __importDefault(require("../prisma/client"));
9
9
  const assert_1 = require("./assert");
10
10
  const opencode_client_1 = require("./opencode-client");
11
11
  const model_pricing_1 = require("./model-pricing");
12
+ const USAGE_SYNC_PAGE_LIMIT = 100;
12
13
  function assertNonNegativeNumber(value, label) {
13
14
  (0, assert_1.assertCondition)(typeof value === "number" && Number.isFinite(value) && value >= 0, `${label} must be a non-negative number`);
14
15
  }
@@ -42,29 +43,65 @@ function assertSessionMessages(value) {
42
43
  }
43
44
  }
44
45
  }
45
- async function syncChatSessionUsage(chatSessionId, opencodeSessionId) {
46
- const client = await (0, opencode_client_1.getOpencodeClient)();
46
+ async function fetchUsageSyncPage(client, opencodeSessionId, before) {
47
47
  const result = await client.session.messages({
48
- path: { id: opencodeSessionId }
48
+ path: { id: opencodeSessionId },
49
+ query: {
50
+ limit: USAGE_SYNC_PAGE_LIMIT,
51
+ ...(before ? { before } : {}),
52
+ },
49
53
  });
50
54
  if (result.error) {
51
55
  throw new Error("Failed to load session messages for usage sync");
52
56
  }
53
57
  assertSessionMessages(result.data);
54
- const messages = result.data;
58
+ return {
59
+ messages: result.data,
60
+ nextCursor: result.response.headers.get("x-next-cursor") || null,
61
+ };
62
+ }
63
+ async function syncChatSessionUsage(chatSessionId, opencodeSessionId) {
64
+ const client = await (0, opencode_client_1.getOpencodeClient)();
55
65
  const existingUsage = await client_1.default.chat_session_usage.findUnique({
56
66
  where: {
57
67
  chat_session_id: chatSessionId,
58
68
  }
59
69
  });
60
- let startIndex = 0;
61
- if (existingUsage) {
62
- const lastSyncedIndex = messages.findIndex((message) => (message.info.role === "assistant"
63
- && message.info.id === existingUsage.last_synced_message_id));
64
- if (lastSyncedIndex === -1) {
65
- return;
70
+ const canResumeFromCursor = existingUsage !== null && existingUsage.last_synced_message_id !== null;
71
+ const fetchedPages = [];
72
+ let before;
73
+ let cursorPageIndex = -1;
74
+ let cursorMessageIndex = -1;
75
+ if (canResumeFromCursor) {
76
+ while (true) {
77
+ const page = await fetchUsageSyncPage(client, opencodeSessionId, before);
78
+ const currentPageIndex = fetchedPages.length;
79
+ fetchedPages.push(page);
80
+ const lastSyncedIndex = page.messages.findIndex((message) => (message.info.role === "assistant"
81
+ && message.info.id === existingUsage.last_synced_message_id));
82
+ if (lastSyncedIndex !== -1) {
83
+ cursorPageIndex = currentPageIndex;
84
+ cursorMessageIndex = lastSyncedIndex;
85
+ break;
86
+ }
87
+ if (!page.nextCursor) {
88
+ return;
89
+ }
90
+ before = page.nextCursor;
91
+ }
92
+ }
93
+ else {
94
+ const result = await client.session.messages({
95
+ path: { id: opencodeSessionId }
96
+ });
97
+ if (result.error) {
98
+ throw new Error("Failed to load session messages for usage sync");
66
99
  }
67
- startIndex = lastSyncedIndex + 1;
100
+ assertSessionMessages(result.data);
101
+ fetchedPages.push({
102
+ messages: result.data,
103
+ nextCursor: null,
104
+ });
68
105
  }
69
106
  let deltaInputTokens = 0;
70
107
  let deltaOutputTokens = 0;
@@ -72,10 +109,13 @@ async function syncChatSessionUsage(chatSessionId, opencodeSessionId) {
72
109
  let latestProcessedAssistantMessageId = null;
73
110
  let latestProcessedProviderId = null;
74
111
  let latestProcessedModelId = null;
75
- for (const message of messages.slice(startIndex)) {
112
+ const processMessage = (message) => {
76
113
  const info = message.info;
77
114
  if (info.role !== "assistant") {
78
- continue;
115
+ return;
116
+ }
117
+ if (info.time.completed === undefined) {
118
+ return;
79
119
  }
80
120
  deltaInputTokens += info.tokens.input;
81
121
  deltaOutputTokens += info.tokens.output;
@@ -83,6 +123,27 @@ async function syncChatSessionUsage(chatSessionId, opencodeSessionId) {
83
123
  latestProcessedAssistantMessageId = info.id;
84
124
  latestProcessedProviderId = info.providerID;
85
125
  latestProcessedModelId = info.modelID;
126
+ };
127
+ if (canResumeFromCursor) {
128
+ (0, assert_1.assertCondition)(cursorPageIndex !== -1, "Usage sync cursor page is missing");
129
+ const pagesInChronologicalOrder = [...fetchedPages].reverse();
130
+ const cursorPageChronologicalIndex = pagesInChronologicalOrder.length - 1 - cursorPageIndex;
131
+ const cursorPage = pagesInChronologicalOrder[cursorPageChronologicalIndex];
132
+ for (const message of cursorPage.messages.slice(cursorMessageIndex + 1)) {
133
+ processMessage(message);
134
+ }
135
+ for (let pageIndex = cursorPageChronologicalIndex + 1; pageIndex < pagesInChronologicalOrder.length; pageIndex += 1) {
136
+ for (const message of pagesInChronologicalOrder[pageIndex].messages) {
137
+ processMessage(message);
138
+ }
139
+ }
140
+ }
141
+ else {
142
+ for (const page of [...fetchedPages].reverse()) {
143
+ for (const message of page.messages) {
144
+ processMessage(message);
145
+ }
146
+ }
86
147
  }
87
148
  if (latestProcessedAssistantMessageId === null
88
149
  || latestProcessedProviderId === null
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseSessionMessagesPageQuery = parseSessionMessagesPageQuery;
4
+ exports.fetchOpencodeSessionMessagesPage = fetchOpencodeSessionMessagesPage;
5
+ const opencode_client_1 = require("./opencode-client");
6
+ const DEFAULT_MESSAGE_PAGE_LIMIT = 10;
7
+ function getErrorMessage(error) {
8
+ if (error && typeof error === "object" && "detail" in error) {
9
+ return String(error.detail);
10
+ }
11
+ return "Failed to get messages from opencode";
12
+ }
13
+ function parseSessionMessagesPageQuery(query) {
14
+ const limit = DEFAULT_MESSAGE_PAGE_LIMIT;
15
+ const rawBefore = query.before;
16
+ if (rawBefore === undefined) {
17
+ return { limit };
18
+ }
19
+ if (typeof rawBefore !== "string" || rawBefore.trim().length === 0) {
20
+ throw {
21
+ status: 400,
22
+ message: "before must be a non-empty string",
23
+ };
24
+ }
25
+ return {
26
+ limit,
27
+ before: rawBefore,
28
+ };
29
+ }
30
+ async function fetchOpencodeSessionMessagesPage(opencodeSessionId, pageQuery) {
31
+ const client = await (0, opencode_client_1.getOpencodeClient)();
32
+ const result = await client.session.messages({
33
+ path: { id: opencodeSessionId },
34
+ query: {
35
+ limit: pageQuery.limit,
36
+ ...(pageQuery.before ? { before: pageQuery.before } : {}),
37
+ },
38
+ });
39
+ if (result.error) {
40
+ throw new Error(getErrorMessage(result.error));
41
+ }
42
+ const nextCursor = result.response.headers.get("x-next-cursor");
43
+ const hasMore = nextCursor !== null && nextCursor.length > 0;
44
+ return {
45
+ messages: result.data,
46
+ nextCursor,
47
+ hasMore,
48
+ };
49
+ }
@@ -24,7 +24,9 @@ const SECRET_PLACEHOLDER_PATTERN = /\{\{SECRET:([A-Za-z_][A-Za-z0-9_]*)\}\}/g
24
24
  const SECRET_ENV_REFERENCE_PATTERN = /\$\{__TEAMCOPILOT_RUNTIME_SECRET_([A-Z][A-Z0-9_]*)\}/g
25
25
  const AGENT_VISIBLE_SECRET_ENV_REFERENCE_PATTERN = /__TEAMCOPILOT_RUNTIME_SECRET_[A-Z][A-Z0-9_]*/
26
26
  const SECRET_ENV_PREFIX = "__TEAMCOPILOT_RUNTIME_SECRET_"
27
- const SHELL_CONTROL_TOKENS = new Set(["&&", "||", ";", "|"])
27
+ const SHELL_CONTROL_TOKENS = new Set(["&&", "||", ";", "|", "\n"])
28
+ const SHELL_REDIRECTION_PATTERN = /^(\d+)?(?:>>?|<<?|<<<)$/
29
+ const SHELL_REDIRECTION_DUPLICATION_PATTERN = /^(\d+)?[<>]&\d+$/
28
30
  const CURL_SAFE_VALUE_OPTIONS = new Set([
29
31
  "-H",
30
32
  "--header",
@@ -160,6 +162,45 @@ type CommandToken = {
160
162
  end: number
161
163
  }
162
164
 
165
+ function readShellRedirectionToken(command: string, start: number): string | null {
166
+ let index = start
167
+ while (/\d/.test(command[index] ?? "")) {
168
+ index += 1
169
+ }
170
+
171
+ if (command[index] !== ">" && command[index] !== "<") {
172
+ if (command[start] !== "&" || command[start + 1] !== ">") {
173
+ return null
174
+ }
175
+ index = start + 2
176
+ } else {
177
+ const operator = command[index]
178
+ index += 1
179
+ while (command[index] === operator && index - start < 4) {
180
+ index += 1
181
+ }
182
+ }
183
+
184
+ if (command[index] === "&") {
185
+ index += 1
186
+ while (/\d/.test(command[index] ?? "")) {
187
+ index += 1
188
+ }
189
+ }
190
+
191
+ return command.slice(start, index)
192
+ }
193
+
194
+ function isEscapedLineContinuation(command: string, newlineIndex: number): boolean {
195
+ let backslashCount = 0
196
+ let index = newlineIndex - 1
197
+ while (index >= 0 && command[index] === "\\") {
198
+ backslashCount += 1
199
+ index -= 1
200
+ }
201
+ return backslashCount % 2 === 1
202
+ }
203
+
163
204
  function tokenizeCommand(command: string): CommandToken[] {
164
205
  const tokens: CommandToken[] = []
165
206
  const length = command.length
@@ -172,6 +213,9 @@ function tokenizeCommand(command: string): CommandToken[] {
172
213
  }
173
214
 
174
215
  if (/\s/.test(char)) {
216
+ if (char === "\n" && !isEscapedLineContinuation(command, index)) {
217
+ tokens.push({ raw: "\n", start: index, end: index + 1 })
218
+ }
175
219
  index += 1
176
220
  continue
177
221
  }
@@ -195,6 +239,13 @@ function tokenizeCommand(command: string): CommandToken[] {
195
239
  continue
196
240
  }
197
241
 
242
+ const redirectionToken = readShellRedirectionToken(command, start)
243
+ if (redirectionToken !== null) {
244
+ index += redirectionToken.length
245
+ tokens.push({ raw: redirectionToken, start, end: index })
246
+ continue
247
+ }
248
+
198
249
  while (index < length) {
199
250
  const current = command[index]
200
251
  if (current === undefined) {
@@ -243,6 +294,10 @@ function tokenizeCommand(command: string): CommandToken[] {
243
294
  break
244
295
  }
245
296
 
297
+ if (current === ">" || current === "<") {
298
+ break
299
+ }
300
+
246
301
  index += 1
247
302
  }
248
303
 
@@ -330,6 +385,16 @@ function isGitExecutableToken(rawToken: string): boolean {
330
385
  return base === "git"
331
386
  }
332
387
 
388
+ function isShellRedirectionToken(rawToken: string): boolean {
389
+ const { inner } = unwrapToken(rawToken)
390
+ return SHELL_REDIRECTION_PATTERN.test(inner) || SHELL_REDIRECTION_DUPLICATION_PATTERN.test(inner) || inner === "&>"
391
+ }
392
+
393
+ function redirectionConsumesNextToken(rawToken: string): boolean {
394
+ const { inner } = unwrapToken(rawToken)
395
+ return !SHELL_REDIRECTION_DUPLICATION_PATTERN.test(inner)
396
+ }
397
+
333
398
  function getLongOptionName(inner: string): string | null {
334
399
  const eqIndex = inner.indexOf("=")
335
400
  const optionName = eqIndex === -1 ? inner : inner.slice(0, eqIndex)
@@ -701,6 +766,13 @@ export const SecretProxyPlugin: Plugin = async ({ client }) => {
701
766
  break
702
767
  }
703
768
 
769
+ if (isShellRedirectionToken(segmentToken.raw)) {
770
+ if (redirectionConsumesNextToken(segmentToken.raw)) {
771
+ j += 1
772
+ }
773
+ continue
774
+ }
775
+
704
776
  const { inner } = unwrapToken(segmentToken.raw)
705
777
 
706
778
  if (expectedValueKind !== null) {
@@ -834,6 +906,13 @@ export const SecretProxyPlugin: Plugin = async ({ client }) => {
834
906
  break
835
907
  }
836
908
 
909
+ if (isShellRedirectionToken(segmentToken.raw)) {
910
+ if (redirectionConsumesNextToken(segmentToken.raw)) {
911
+ j += 1
912
+ }
913
+ continue
914
+ }
915
+
837
916
  const rewritten = rewriteGitRawToken(segmentToken.raw)
838
917
  if (rewritten.rewritten !== segmentToken.raw) {
839
918
  replacements.push({
@@ -879,41 +958,25 @@ export const SecretProxyPlugin: Plugin = async ({ client }) => {
879
958
  assertNoAgentAuthoredSecretEnvReference(input.arguments)
880
959
 
881
960
  const commandCache = new Map<string, { rewritten: string; referencedKeys: string[] }>()
882
- if (typeof input.command === "string" && input.command.includes("{{SECRET:")) {
883
- if ((isCurlExecutableToken(input.command) || isGitExecutableToken(input.command)) && typeof input.arguments === "string") {
884
- const fullCommand = input.arguments.trim().length > 0
885
- ? `${input.command} ${input.arguments}`
886
- : input.command
887
- const rewritten = isCurlExecutableToken(input.command)
888
- ? substitutePlaceholdersInCurlShellString(fullCommand)
889
- : substitutePlaceholdersInGitShellString(fullCommand)
890
- const prefix = `${input.command} `
891
- if (rewritten.rewritten.startsWith(prefix)) {
892
- input.arguments = rewritten.rewritten.slice(prefix.length)
893
- } else {
894
- input.command = rewritten.rewritten
895
- input.arguments = ""
896
- }
961
+ const commandHasPlaceholder = typeof input.command === "string" && input.command.includes("{{SECRET:")
962
+ const argumentsHasPlaceholder = typeof input.arguments === "string" && input.arguments.includes("{{SECRET:")
963
+ if (commandHasPlaceholder || argumentsHasPlaceholder) {
964
+ const originalCommand = typeof input.command === "string" ? input.command : ""
965
+ const originalArguments = typeof input.arguments === "string" ? input.arguments : ""
966
+ const fullCommand = originalArguments.trim().length > 0
967
+ ? `${originalCommand} ${originalArguments}`
968
+ : originalCommand
969
+ const rewritten = await maybeRewriteSupportedString(fullCommand, commandCache)
970
+ const prefix = `${originalCommand} `
971
+
972
+ if (rewritten.rewritten === fullCommand) {
973
+ input.command = originalCommand
974
+ input.arguments = originalArguments
975
+ } else if ((isCurlExecutableToken(originalCommand) || isGitExecutableToken(originalCommand)) && rewritten.rewritten.startsWith(prefix)) {
976
+ input.arguments = rewritten.rewritten.slice(prefix.length)
897
977
  } else {
898
- const rewritten = await maybeRewriteSupportedString(input.command, commandCache)
899
978
  input.command = rewritten.rewritten
900
- }
901
- }
902
- if (typeof input.arguments === "string" && input.arguments.includes("{{SECRET:")) {
903
- if (isCurlExecutableToken(input.command) || isGitExecutableToken(input.command)) {
904
- const fullCommand = input.arguments.trim().length > 0
905
- ? `${input.command} ${input.arguments}`
906
- : input.command
907
- const rewritten = isCurlExecutableToken(input.command)
908
- ? substitutePlaceholdersInCurlShellString(fullCommand)
909
- : substitutePlaceholdersInGitShellString(fullCommand)
910
- const prefix = `${input.command} `
911
- if (rewritten.rewritten.startsWith(prefix)) {
912
- input.arguments = rewritten.rewritten.slice(prefix.length)
913
- }
914
- } else {
915
- const rewritten = await maybeRewriteSupportedString(input.arguments, commandCache)
916
- input.arguments = rewritten.rewritten
979
+ input.arguments = ""
917
980
  }
918
981
  }
919
982
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamcopilot",
3
- "version": "0.4.5",
3
+ "version": "0.4.8",
4
4
  "description": "A shared AI Agent for Teams",
5
5
  "homepage": "https://teamcopilot.ai",
6
6
  "repository": {
package/prisma/dev.db CHANGED
Binary file
@@ -370,8 +370,8 @@ const config = {
370
370
  }
371
371
  }
372
372
  },
373
- "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n output = \"./generated/client\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel users {\n id String @id @default(uuid())\n email String @unique\n name String\n role String // \"User\" or \"Engineer\"\n created_at BigInt\n password_hash String\n must_change_password Boolean @default(false)\n reset_token String?\n reset_token_expires_at BigInt?\n workflowRuns workflow_runs[]\n chatSessions chat_sessions[]\n cronjobs cronjobs[]\n resourcePermissionUsers resource_permission_users[]\n createdResourceMetadata resource_metadata[] @relation(\"ResourceCreatedBy\")\n approvedResourceMetadata resource_metadata[] @relation(\"ResourceApprovedBy\")\n userSecrets user_secrets[]\n}\n\nmodel workflow_runs {\n id String @id @default(uuid())\n ran_by_user_id String?\n status String // \"running\" | \"success\" | \"failed\"\n started_at BigInt\n completed_at BigInt?\n args String? // JSON string of input arguments passed to the workflow\n error_message String?\n output String? // Captured stdout/stderr from the workflow run\n workflow_slug String\n session_id String?\n message_id String?\n run_source String // \"user\" | \"api\" | \"cronjob\"\n workflow_api_key_id String?\n user users? @relation(fields: [ran_by_user_id], references: [id], onDelete: SetNull)\n workflowApiKey workflow_api_keys? @relation(fields: [workflow_api_key_id], references: [id], onDelete: SetNull)\n cronjobRuns cronjob_runs[]\n\n @@index([started_at])\n @@index([session_id, message_id])\n @@index([workflow_api_key_id])\n}\n\nmodel workflow_api_keys {\n id String @id @default(uuid())\n workflow_slug String\n api_key String @unique\n created_by_user_id String\n created_at BigInt\n workflowRuns workflow_runs[]\n\n @@index([workflow_slug])\n @@index([created_by_user_id])\n}\n\nmodel workflow_aborted_sessions {\n session_id String @id\n created_at BigInt\n}\n\nmodel key_value {\n key String @id\n value String\n}\n\nmodel user_secrets {\n id String @id @default(uuid())\n user_id String\n key String\n value String\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([user_id, key])\n @@index([user_id])\n}\n\nmodel global_secrets {\n id String @id @default(uuid())\n key String @unique\n value String\n created_by_user_id String\n updated_by_user_id String\n created_at BigInt\n updated_at BigInt\n\n @@index([created_by_user_id])\n @@index([updated_by_user_id])\n}\n\nmodel chat_sessions {\n id String @id @default(uuid())\n user_id String\n opencode_session_id String @unique\n title String\n last_seen_assistant_message_id String?\n visible_to_user Boolean @default(true)\n created_at BigInt\n updated_at BigInt\n trackedFiles chat_session_tracked_files[]\n usage chat_session_usage?\n cronjobRuns cronjob_runs[]\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@index([visible_to_user])\n}\n\nmodel cronjobs {\n id String @id @default(uuid())\n user_id String\n name String\n enabled Boolean\n target_type String // \"prompt\" | \"workflow\"\n prompt String?\n prompt_allow_workflow_runs_without_permission Boolean?\n workflow_slug String?\n workflow_input_json String?\n cron_expression String\n timezone String\n monitor_timeout_value Float @default(2)\n monitor_timeout_unit String @default(\"hours\") // \"minutes\" | \"hours\" | \"days\"\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n runs cronjob_runs[]\n\n @@unique([user_id, name])\n @@index([user_id])\n @@index([enabled])\n @@index([target_type])\n @@index([workflow_slug])\n}\n\nmodel cronjob_runs {\n id String @id @default(uuid())\n cronjob_id String\n status String // prompt: \"running\" | \"paused\" | \"success\" | \"failed\" | \"skipped\" | \"terminated\"; workflow: \"running\" | \"success\" | \"failed\" | \"skipped\"\n started_at BigInt\n completed_at BigInt?\n workflow_run_id String?\n summary String?\n session_id String?\n opencode_session_id String?\n error_message String?\n todo_list_version Int @default(0)\n cronjob cronjobs @relation(fields: [cronjob_id], references: [id], onDelete: Cascade)\n session chat_sessions? @relation(fields: [session_id], references: [id], onDelete: Cascade)\n workflowRun workflow_runs? @relation(fields: [workflow_run_id], references: [id], onDelete: Cascade)\n todos cronjob_run_todos[]\n\n @@index([cronjob_id, started_at])\n @@index([cronjob_id, status])\n @@index([workflow_run_id])\n @@index([session_id])\n @@index([opencode_session_id, status])\n}\n\nmodel cronjob_run_todos {\n id String @id @default(uuid())\n run_id String\n content String\n status String // \"pending\" = not started yet; \"in_progress\" = current todo; \"completed\" = finished\n position Int\n summary String?\n created_at BigInt\n completed_at BigInt?\n run cronjob_runs @relation(fields: [run_id], references: [id], onDelete: Cascade)\n\n @@index([run_id, status, position])\n @@index([run_id, position])\n}\n\nmodel chat_session_usage {\n chat_session_id String @id\n last_synced_message_id String\n provider_id String\n input_tokens Int\n output_tokens Int\n cached_tokens Int\n cost_usd Float\n model_id String\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n}\n\nmodel chat_session_tracked_files {\n id String @id @default(uuid())\n chat_session_id String\n relative_path String\n existed_at_baseline Boolean\n content_kind String // \"text\" | \"binary\" | \"missing\"\n text_content String?\n binary_content Bytes?\n size_bytes Int?\n content_sha256 String?\n created_at BigInt\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n\n @@unique([chat_session_id, relative_path])\n @@index([chat_session_id])\n}\n\nmodel tool_execution_permissions {\n id String @id @default(uuid())\n opencode_session_id String\n message_id String\n call_id String\n status String // \"pending\" | \"approved\" | \"rejected\"\n created_at BigInt\n responded_at BigInt?\n\n @@unique([opencode_session_id, message_id, call_id])\n @@index([opencode_session_id, status])\n}\n\nmodel resource_metadata {\n resource_kind String\n resource_slug String\n created_by_user_id String?\n approved_by_user_id String?\n created_at BigInt\n updated_at BigInt\n approvedSnapshot resource_approved_snapshots?\n createdByUser users? @relation(\"ResourceCreatedBy\", fields: [created_by_user_id], references: [id], onDelete: SetNull)\n approvedByUser users? @relation(\"ResourceApprovedBy\", fields: [approved_by_user_id], references: [id], onDelete: SetNull)\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n @@index([created_by_user_id])\n @@index([approved_by_user_id])\n}\n\nmodel resource_permissions {\n resource_kind String\n resource_slug String\n permission_mode String // \"restricted\" | \"everyone\"\n created_at BigInt\n updated_at BigInt\n allowedUsers resource_permission_users[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_permission_users {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n user_id String\n created_at BigInt\n permission resource_permissions @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, user_id])\n @@index([resource_kind, resource_slug])\n}\n\nmodel resource_approved_snapshots {\n resource_kind String\n resource_slug String\n file_count Int\n created_at BigInt\n updated_at BigInt\n resource resource_metadata @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n files resource_approved_snapshot_files[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_approved_snapshot_files {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n relative_path String\n content_kind String // \"text\" | \"binary\"\n text_content String?\n binary_content Bytes?\n size_bytes Int\n content_sha256 String\n snapshot resource_approved_snapshots @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, relative_path])\n @@index([resource_kind, resource_slug])\n}\n",
374
- "inlineSchemaHash": "365f7a66ef1a3911b2564a1f1cd26b622414e1a42ad30215b6494a54bdb53b89",
373
+ "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n output = \"./generated/client\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel users {\n id String @id @default(uuid())\n email String @unique\n name String\n role String // \"User\" or \"Engineer\"\n created_at BigInt\n password_hash String\n must_change_password Boolean @default(false)\n reset_token String?\n reset_token_expires_at BigInt?\n workflowRuns workflow_runs[]\n chatSessions chat_sessions[]\n cronjobs cronjobs[]\n resourcePermissionUsers resource_permission_users[]\n createdResourceMetadata resource_metadata[] @relation(\"ResourceCreatedBy\")\n approvedResourceMetadata resource_metadata[] @relation(\"ResourceApprovedBy\")\n userSecrets user_secrets[]\n}\n\nmodel workflow_runs {\n id String @id @default(uuid())\n ran_by_user_id String?\n status String // \"running\" | \"success\" | \"failed\"\n started_at BigInt\n completed_at BigInt?\n args String? // JSON string of input arguments passed to the workflow\n error_message String?\n output String? // Captured stdout/stderr from the workflow run\n workflow_slug String\n session_id String?\n message_id String?\n run_source String // \"user\" | \"api\" | \"cronjob\"\n workflow_api_key_id String?\n user users? @relation(fields: [ran_by_user_id], references: [id], onDelete: SetNull)\n workflowApiKey workflow_api_keys? @relation(fields: [workflow_api_key_id], references: [id], onDelete: SetNull)\n cronjobRuns cronjob_runs[]\n\n @@index([started_at])\n @@index([session_id, message_id])\n @@index([workflow_api_key_id])\n}\n\nmodel workflow_api_keys {\n id String @id @default(uuid())\n workflow_slug String\n api_key String @unique\n created_by_user_id String\n created_at BigInt\n workflowRuns workflow_runs[]\n\n @@index([workflow_slug])\n @@index([created_by_user_id])\n}\n\nmodel workflow_aborted_sessions {\n session_id String @id\n created_at BigInt\n}\n\nmodel key_value {\n key String @id\n value String\n}\n\nmodel user_secrets {\n id String @id @default(uuid())\n user_id String\n key String\n value String\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([user_id, key])\n @@index([user_id])\n}\n\nmodel global_secrets {\n id String @id @default(uuid())\n key String @unique\n value String\n created_by_user_id String\n updated_by_user_id String\n created_at BigInt\n updated_at BigInt\n\n @@index([created_by_user_id])\n @@index([updated_by_user_id])\n}\n\nmodel chat_sessions {\n id String @id @default(uuid())\n user_id String\n opencode_session_id String @unique\n title String\n last_seen_assistant_message_id String?\n visible_to_user Boolean @default(true)\n created_at BigInt\n updated_at BigInt\n trackedFiles chat_session_tracked_files[]\n usage chat_session_usage?\n cronjobRuns cronjob_runs[]\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@index([visible_to_user])\n @@index([user_id, visible_to_user, updated_at])\n}\n\nmodel cronjobs {\n id String @id @default(uuid())\n user_id String\n name String\n enabled Boolean\n target_type String // \"prompt\" | \"workflow\"\n prompt String?\n prompt_allow_workflow_runs_without_permission Boolean?\n workflow_slug String?\n workflow_input_json String?\n cron_expression String\n timezone String\n monitor_timeout_value Float @default(2)\n monitor_timeout_unit String @default(\"hours\") // \"minutes\" | \"hours\" | \"days\"\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n runs cronjob_runs[]\n\n @@unique([user_id, name])\n @@index([user_id])\n @@index([enabled])\n @@index([target_type])\n @@index([workflow_slug])\n}\n\nmodel cronjob_runs {\n id String @id @default(uuid())\n cronjob_id String\n status String // prompt: \"running\" | \"paused\" | \"success\" | \"failed\" | \"skipped\" | \"terminated\"; workflow: \"running\" | \"success\" | \"failed\" | \"skipped\"\n started_at BigInt\n completed_at BigInt?\n workflow_run_id String?\n summary String?\n session_id String?\n opencode_session_id String?\n error_message String?\n todo_list_version Int @default(0)\n cronjob cronjobs @relation(fields: [cronjob_id], references: [id], onDelete: Cascade)\n session chat_sessions? @relation(fields: [session_id], references: [id], onDelete: Cascade)\n workflowRun workflow_runs? @relation(fields: [workflow_run_id], references: [id], onDelete: Cascade)\n todos cronjob_run_todos[]\n\n @@index([cronjob_id, started_at])\n @@index([cronjob_id, status])\n @@index([workflow_run_id])\n @@index([session_id])\n @@index([opencode_session_id, status])\n}\n\nmodel cronjob_run_todos {\n id String @id @default(uuid())\n run_id String\n content String\n status String // \"pending\" = not started yet; \"in_progress\" = current todo; \"completed\" = finished\n position Int\n summary String?\n created_at BigInt\n completed_at BigInt?\n run cronjob_runs @relation(fields: [run_id], references: [id], onDelete: Cascade)\n\n @@index([run_id, status, position])\n @@index([run_id, position])\n}\n\nmodel chat_session_usage {\n chat_session_id String @id\n last_synced_message_id String\n provider_id String\n input_tokens Int\n output_tokens Int\n cached_tokens Int\n cost_usd Float\n model_id String\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n}\n\nmodel chat_session_tracked_files {\n id String @id @default(uuid())\n chat_session_id String\n relative_path String\n existed_at_baseline Boolean\n content_kind String // \"text\" | \"binary\" | \"missing\"\n text_content String?\n binary_content Bytes?\n size_bytes Int?\n content_sha256 String?\n created_at BigInt\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n\n @@unique([chat_session_id, relative_path])\n @@index([chat_session_id])\n}\n\nmodel tool_execution_permissions {\n id String @id @default(uuid())\n opencode_session_id String\n message_id String\n call_id String\n status String // \"pending\" | \"approved\" | \"rejected\"\n created_at BigInt\n responded_at BigInt?\n\n @@unique([opencode_session_id, message_id, call_id])\n @@index([opencode_session_id, status])\n}\n\nmodel resource_metadata {\n resource_kind String\n resource_slug String\n created_by_user_id String?\n approved_by_user_id String?\n created_at BigInt\n updated_at BigInt\n approvedSnapshot resource_approved_snapshots?\n createdByUser users? @relation(\"ResourceCreatedBy\", fields: [created_by_user_id], references: [id], onDelete: SetNull)\n approvedByUser users? @relation(\"ResourceApprovedBy\", fields: [approved_by_user_id], references: [id], onDelete: SetNull)\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n @@index([created_by_user_id])\n @@index([approved_by_user_id])\n}\n\nmodel resource_permissions {\n resource_kind String\n resource_slug String\n permission_mode String // \"restricted\" | \"everyone\"\n created_at BigInt\n updated_at BigInt\n allowedUsers resource_permission_users[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_permission_users {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n user_id String\n created_at BigInt\n permission resource_permissions @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, user_id])\n @@index([resource_kind, resource_slug])\n}\n\nmodel resource_approved_snapshots {\n resource_kind String\n resource_slug String\n file_count Int\n created_at BigInt\n updated_at BigInt\n resource resource_metadata @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n files resource_approved_snapshot_files[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_approved_snapshot_files {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n relative_path String\n content_kind String // \"text\" | \"binary\"\n text_content String?\n binary_content Bytes?\n size_bytes Int\n content_sha256 String\n snapshot resource_approved_snapshots @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, relative_path])\n @@index([resource_kind, resource_slug])\n}\n",
374
+ "inlineSchemaHash": "fc99ad355c76b3380556a915c46b51834e01f3fa7a8ca3ba7f16338c41037be5",
375
375
  "copyEngine": true
376
376
  }
377
377
  config.dirname = '/'
@@ -371,8 +371,8 @@ const config = {
371
371
  }
372
372
  }
373
373
  },
374
- "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n output = \"./generated/client\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel users {\n id String @id @default(uuid())\n email String @unique\n name String\n role String // \"User\" or \"Engineer\"\n created_at BigInt\n password_hash String\n must_change_password Boolean @default(false)\n reset_token String?\n reset_token_expires_at BigInt?\n workflowRuns workflow_runs[]\n chatSessions chat_sessions[]\n cronjobs cronjobs[]\n resourcePermissionUsers resource_permission_users[]\n createdResourceMetadata resource_metadata[] @relation(\"ResourceCreatedBy\")\n approvedResourceMetadata resource_metadata[] @relation(\"ResourceApprovedBy\")\n userSecrets user_secrets[]\n}\n\nmodel workflow_runs {\n id String @id @default(uuid())\n ran_by_user_id String?\n status String // \"running\" | \"success\" | \"failed\"\n started_at BigInt\n completed_at BigInt?\n args String? // JSON string of input arguments passed to the workflow\n error_message String?\n output String? // Captured stdout/stderr from the workflow run\n workflow_slug String\n session_id String?\n message_id String?\n run_source String // \"user\" | \"api\" | \"cronjob\"\n workflow_api_key_id String?\n user users? @relation(fields: [ran_by_user_id], references: [id], onDelete: SetNull)\n workflowApiKey workflow_api_keys? @relation(fields: [workflow_api_key_id], references: [id], onDelete: SetNull)\n cronjobRuns cronjob_runs[]\n\n @@index([started_at])\n @@index([session_id, message_id])\n @@index([workflow_api_key_id])\n}\n\nmodel workflow_api_keys {\n id String @id @default(uuid())\n workflow_slug String\n api_key String @unique\n created_by_user_id String\n created_at BigInt\n workflowRuns workflow_runs[]\n\n @@index([workflow_slug])\n @@index([created_by_user_id])\n}\n\nmodel workflow_aborted_sessions {\n session_id String @id\n created_at BigInt\n}\n\nmodel key_value {\n key String @id\n value String\n}\n\nmodel user_secrets {\n id String @id @default(uuid())\n user_id String\n key String\n value String\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([user_id, key])\n @@index([user_id])\n}\n\nmodel global_secrets {\n id String @id @default(uuid())\n key String @unique\n value String\n created_by_user_id String\n updated_by_user_id String\n created_at BigInt\n updated_at BigInt\n\n @@index([created_by_user_id])\n @@index([updated_by_user_id])\n}\n\nmodel chat_sessions {\n id String @id @default(uuid())\n user_id String\n opencode_session_id String @unique\n title String\n last_seen_assistant_message_id String?\n visible_to_user Boolean @default(true)\n created_at BigInt\n updated_at BigInt\n trackedFiles chat_session_tracked_files[]\n usage chat_session_usage?\n cronjobRuns cronjob_runs[]\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@index([visible_to_user])\n}\n\nmodel cronjobs {\n id String @id @default(uuid())\n user_id String\n name String\n enabled Boolean\n target_type String // \"prompt\" | \"workflow\"\n prompt String?\n prompt_allow_workflow_runs_without_permission Boolean?\n workflow_slug String?\n workflow_input_json String?\n cron_expression String\n timezone String\n monitor_timeout_value Float @default(2)\n monitor_timeout_unit String @default(\"hours\") // \"minutes\" | \"hours\" | \"days\"\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n runs cronjob_runs[]\n\n @@unique([user_id, name])\n @@index([user_id])\n @@index([enabled])\n @@index([target_type])\n @@index([workflow_slug])\n}\n\nmodel cronjob_runs {\n id String @id @default(uuid())\n cronjob_id String\n status String // prompt: \"running\" | \"paused\" | \"success\" | \"failed\" | \"skipped\" | \"terminated\"; workflow: \"running\" | \"success\" | \"failed\" | \"skipped\"\n started_at BigInt\n completed_at BigInt?\n workflow_run_id String?\n summary String?\n session_id String?\n opencode_session_id String?\n error_message String?\n todo_list_version Int @default(0)\n cronjob cronjobs @relation(fields: [cronjob_id], references: [id], onDelete: Cascade)\n session chat_sessions? @relation(fields: [session_id], references: [id], onDelete: Cascade)\n workflowRun workflow_runs? @relation(fields: [workflow_run_id], references: [id], onDelete: Cascade)\n todos cronjob_run_todos[]\n\n @@index([cronjob_id, started_at])\n @@index([cronjob_id, status])\n @@index([workflow_run_id])\n @@index([session_id])\n @@index([opencode_session_id, status])\n}\n\nmodel cronjob_run_todos {\n id String @id @default(uuid())\n run_id String\n content String\n status String // \"pending\" = not started yet; \"in_progress\" = current todo; \"completed\" = finished\n position Int\n summary String?\n created_at BigInt\n completed_at BigInt?\n run cronjob_runs @relation(fields: [run_id], references: [id], onDelete: Cascade)\n\n @@index([run_id, status, position])\n @@index([run_id, position])\n}\n\nmodel chat_session_usage {\n chat_session_id String @id\n last_synced_message_id String\n provider_id String\n input_tokens Int\n output_tokens Int\n cached_tokens Int\n cost_usd Float\n model_id String\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n}\n\nmodel chat_session_tracked_files {\n id String @id @default(uuid())\n chat_session_id String\n relative_path String\n existed_at_baseline Boolean\n content_kind String // \"text\" | \"binary\" | \"missing\"\n text_content String?\n binary_content Bytes?\n size_bytes Int?\n content_sha256 String?\n created_at BigInt\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n\n @@unique([chat_session_id, relative_path])\n @@index([chat_session_id])\n}\n\nmodel tool_execution_permissions {\n id String @id @default(uuid())\n opencode_session_id String\n message_id String\n call_id String\n status String // \"pending\" | \"approved\" | \"rejected\"\n created_at BigInt\n responded_at BigInt?\n\n @@unique([opencode_session_id, message_id, call_id])\n @@index([opencode_session_id, status])\n}\n\nmodel resource_metadata {\n resource_kind String\n resource_slug String\n created_by_user_id String?\n approved_by_user_id String?\n created_at BigInt\n updated_at BigInt\n approvedSnapshot resource_approved_snapshots?\n createdByUser users? @relation(\"ResourceCreatedBy\", fields: [created_by_user_id], references: [id], onDelete: SetNull)\n approvedByUser users? @relation(\"ResourceApprovedBy\", fields: [approved_by_user_id], references: [id], onDelete: SetNull)\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n @@index([created_by_user_id])\n @@index([approved_by_user_id])\n}\n\nmodel resource_permissions {\n resource_kind String\n resource_slug String\n permission_mode String // \"restricted\" | \"everyone\"\n created_at BigInt\n updated_at BigInt\n allowedUsers resource_permission_users[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_permission_users {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n user_id String\n created_at BigInt\n permission resource_permissions @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, user_id])\n @@index([resource_kind, resource_slug])\n}\n\nmodel resource_approved_snapshots {\n resource_kind String\n resource_slug String\n file_count Int\n created_at BigInt\n updated_at BigInt\n resource resource_metadata @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n files resource_approved_snapshot_files[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_approved_snapshot_files {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n relative_path String\n content_kind String // \"text\" | \"binary\"\n text_content String?\n binary_content Bytes?\n size_bytes Int\n content_sha256 String\n snapshot resource_approved_snapshots @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, relative_path])\n @@index([resource_kind, resource_slug])\n}\n",
375
- "inlineSchemaHash": "365f7a66ef1a3911b2564a1f1cd26b622414e1a42ad30215b6494a54bdb53b89",
374
+ "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n output = \"./generated/client\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel users {\n id String @id @default(uuid())\n email String @unique\n name String\n role String // \"User\" or \"Engineer\"\n created_at BigInt\n password_hash String\n must_change_password Boolean @default(false)\n reset_token String?\n reset_token_expires_at BigInt?\n workflowRuns workflow_runs[]\n chatSessions chat_sessions[]\n cronjobs cronjobs[]\n resourcePermissionUsers resource_permission_users[]\n createdResourceMetadata resource_metadata[] @relation(\"ResourceCreatedBy\")\n approvedResourceMetadata resource_metadata[] @relation(\"ResourceApprovedBy\")\n userSecrets user_secrets[]\n}\n\nmodel workflow_runs {\n id String @id @default(uuid())\n ran_by_user_id String?\n status String // \"running\" | \"success\" | \"failed\"\n started_at BigInt\n completed_at BigInt?\n args String? // JSON string of input arguments passed to the workflow\n error_message String?\n output String? // Captured stdout/stderr from the workflow run\n workflow_slug String\n session_id String?\n message_id String?\n run_source String // \"user\" | \"api\" | \"cronjob\"\n workflow_api_key_id String?\n user users? @relation(fields: [ran_by_user_id], references: [id], onDelete: SetNull)\n workflowApiKey workflow_api_keys? @relation(fields: [workflow_api_key_id], references: [id], onDelete: SetNull)\n cronjobRuns cronjob_runs[]\n\n @@index([started_at])\n @@index([session_id, message_id])\n @@index([workflow_api_key_id])\n}\n\nmodel workflow_api_keys {\n id String @id @default(uuid())\n workflow_slug String\n api_key String @unique\n created_by_user_id String\n created_at BigInt\n workflowRuns workflow_runs[]\n\n @@index([workflow_slug])\n @@index([created_by_user_id])\n}\n\nmodel workflow_aborted_sessions {\n session_id String @id\n created_at BigInt\n}\n\nmodel key_value {\n key String @id\n value String\n}\n\nmodel user_secrets {\n id String @id @default(uuid())\n user_id String\n key String\n value String\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([user_id, key])\n @@index([user_id])\n}\n\nmodel global_secrets {\n id String @id @default(uuid())\n key String @unique\n value String\n created_by_user_id String\n updated_by_user_id String\n created_at BigInt\n updated_at BigInt\n\n @@index([created_by_user_id])\n @@index([updated_by_user_id])\n}\n\nmodel chat_sessions {\n id String @id @default(uuid())\n user_id String\n opencode_session_id String @unique\n title String\n last_seen_assistant_message_id String?\n visible_to_user Boolean @default(true)\n created_at BigInt\n updated_at BigInt\n trackedFiles chat_session_tracked_files[]\n usage chat_session_usage?\n cronjobRuns cronjob_runs[]\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@index([visible_to_user])\n @@index([user_id, visible_to_user, updated_at])\n}\n\nmodel cronjobs {\n id String @id @default(uuid())\n user_id String\n name String\n enabled Boolean\n target_type String // \"prompt\" | \"workflow\"\n prompt String?\n prompt_allow_workflow_runs_without_permission Boolean?\n workflow_slug String?\n workflow_input_json String?\n cron_expression String\n timezone String\n monitor_timeout_value Float @default(2)\n monitor_timeout_unit String @default(\"hours\") // \"minutes\" | \"hours\" | \"days\"\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n runs cronjob_runs[]\n\n @@unique([user_id, name])\n @@index([user_id])\n @@index([enabled])\n @@index([target_type])\n @@index([workflow_slug])\n}\n\nmodel cronjob_runs {\n id String @id @default(uuid())\n cronjob_id String\n status String // prompt: \"running\" | \"paused\" | \"success\" | \"failed\" | \"skipped\" | \"terminated\"; workflow: \"running\" | \"success\" | \"failed\" | \"skipped\"\n started_at BigInt\n completed_at BigInt?\n workflow_run_id String?\n summary String?\n session_id String?\n opencode_session_id String?\n error_message String?\n todo_list_version Int @default(0)\n cronjob cronjobs @relation(fields: [cronjob_id], references: [id], onDelete: Cascade)\n session chat_sessions? @relation(fields: [session_id], references: [id], onDelete: Cascade)\n workflowRun workflow_runs? @relation(fields: [workflow_run_id], references: [id], onDelete: Cascade)\n todos cronjob_run_todos[]\n\n @@index([cronjob_id, started_at])\n @@index([cronjob_id, status])\n @@index([workflow_run_id])\n @@index([session_id])\n @@index([opencode_session_id, status])\n}\n\nmodel cronjob_run_todos {\n id String @id @default(uuid())\n run_id String\n content String\n status String // \"pending\" = not started yet; \"in_progress\" = current todo; \"completed\" = finished\n position Int\n summary String?\n created_at BigInt\n completed_at BigInt?\n run cronjob_runs @relation(fields: [run_id], references: [id], onDelete: Cascade)\n\n @@index([run_id, status, position])\n @@index([run_id, position])\n}\n\nmodel chat_session_usage {\n chat_session_id String @id\n last_synced_message_id String\n provider_id String\n input_tokens Int\n output_tokens Int\n cached_tokens Int\n cost_usd Float\n model_id String\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n}\n\nmodel chat_session_tracked_files {\n id String @id @default(uuid())\n chat_session_id String\n relative_path String\n existed_at_baseline Boolean\n content_kind String // \"text\" | \"binary\" | \"missing\"\n text_content String?\n binary_content Bytes?\n size_bytes Int?\n content_sha256 String?\n created_at BigInt\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n\n @@unique([chat_session_id, relative_path])\n @@index([chat_session_id])\n}\n\nmodel tool_execution_permissions {\n id String @id @default(uuid())\n opencode_session_id String\n message_id String\n call_id String\n status String // \"pending\" | \"approved\" | \"rejected\"\n created_at BigInt\n responded_at BigInt?\n\n @@unique([opencode_session_id, message_id, call_id])\n @@index([opencode_session_id, status])\n}\n\nmodel resource_metadata {\n resource_kind String\n resource_slug String\n created_by_user_id String?\n approved_by_user_id String?\n created_at BigInt\n updated_at BigInt\n approvedSnapshot resource_approved_snapshots?\n createdByUser users? @relation(\"ResourceCreatedBy\", fields: [created_by_user_id], references: [id], onDelete: SetNull)\n approvedByUser users? @relation(\"ResourceApprovedBy\", fields: [approved_by_user_id], references: [id], onDelete: SetNull)\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n @@index([created_by_user_id])\n @@index([approved_by_user_id])\n}\n\nmodel resource_permissions {\n resource_kind String\n resource_slug String\n permission_mode String // \"restricted\" | \"everyone\"\n created_at BigInt\n updated_at BigInt\n allowedUsers resource_permission_users[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_permission_users {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n user_id String\n created_at BigInt\n permission resource_permissions @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, user_id])\n @@index([resource_kind, resource_slug])\n}\n\nmodel resource_approved_snapshots {\n resource_kind String\n resource_slug String\n file_count Int\n created_at BigInt\n updated_at BigInt\n resource resource_metadata @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n files resource_approved_snapshot_files[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_approved_snapshot_files {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n relative_path String\n content_kind String // \"text\" | \"binary\"\n text_content String?\n binary_content Bytes?\n size_bytes Int\n content_sha256 String\n snapshot resource_approved_snapshots @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, relative_path])\n @@index([resource_kind, resource_slug])\n}\n",
375
+ "inlineSchemaHash": "fc99ad355c76b3380556a915c46b51834e01f3fa7a8ca3ba7f16338c41037be5",
376
376
  "copyEngine": true
377
377
  }
378
378
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "prisma-client-248a3191403f072633618b7c6c6b22ffb24a422cb2b9f8c74d014961520f8560",
2
+ "name": "prisma-client-9f15e381b7f0b91889a215b6e1f1c0c09a6790702f6b793962363f4b3b558143",
3
3
  "main": "index.js",
4
4
  "types": "index.d.ts",
5
5
  "browser": "default.js",
@@ -113,6 +113,7 @@ model chat_sessions {
113
113
  user users @relation(fields: [user_id], references: [id], onDelete: Cascade)
114
114
 
115
115
  @@index([visible_to_user])
116
+ @@index([user_id, visible_to_user, updated_at])
116
117
  }
117
118
 
118
119
  model cronjobs {
@@ -370,8 +370,8 @@ const config = {
370
370
  }
371
371
  }
372
372
  },
373
- "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n output = \"./generated/client\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel users {\n id String @id @default(uuid())\n email String @unique\n name String\n role String // \"User\" or \"Engineer\"\n created_at BigInt\n password_hash String\n must_change_password Boolean @default(false)\n reset_token String?\n reset_token_expires_at BigInt?\n workflowRuns workflow_runs[]\n chatSessions chat_sessions[]\n cronjobs cronjobs[]\n resourcePermissionUsers resource_permission_users[]\n createdResourceMetadata resource_metadata[] @relation(\"ResourceCreatedBy\")\n approvedResourceMetadata resource_metadata[] @relation(\"ResourceApprovedBy\")\n userSecrets user_secrets[]\n}\n\nmodel workflow_runs {\n id String @id @default(uuid())\n ran_by_user_id String?\n status String // \"running\" | \"success\" | \"failed\"\n started_at BigInt\n completed_at BigInt?\n args String? // JSON string of input arguments passed to the workflow\n error_message String?\n output String? // Captured stdout/stderr from the workflow run\n workflow_slug String\n session_id String?\n message_id String?\n run_source String // \"user\" | \"api\" | \"cronjob\"\n workflow_api_key_id String?\n user users? @relation(fields: [ran_by_user_id], references: [id], onDelete: SetNull)\n workflowApiKey workflow_api_keys? @relation(fields: [workflow_api_key_id], references: [id], onDelete: SetNull)\n cronjobRuns cronjob_runs[]\n\n @@index([started_at])\n @@index([session_id, message_id])\n @@index([workflow_api_key_id])\n}\n\nmodel workflow_api_keys {\n id String @id @default(uuid())\n workflow_slug String\n api_key String @unique\n created_by_user_id String\n created_at BigInt\n workflowRuns workflow_runs[]\n\n @@index([workflow_slug])\n @@index([created_by_user_id])\n}\n\nmodel workflow_aborted_sessions {\n session_id String @id\n created_at BigInt\n}\n\nmodel key_value {\n key String @id\n value String\n}\n\nmodel user_secrets {\n id String @id @default(uuid())\n user_id String\n key String\n value String\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([user_id, key])\n @@index([user_id])\n}\n\nmodel global_secrets {\n id String @id @default(uuid())\n key String @unique\n value String\n created_by_user_id String\n updated_by_user_id String\n created_at BigInt\n updated_at BigInt\n\n @@index([created_by_user_id])\n @@index([updated_by_user_id])\n}\n\nmodel chat_sessions {\n id String @id @default(uuid())\n user_id String\n opencode_session_id String @unique\n title String\n last_seen_assistant_message_id String?\n visible_to_user Boolean @default(true)\n created_at BigInt\n updated_at BigInt\n trackedFiles chat_session_tracked_files[]\n usage chat_session_usage?\n cronjobRuns cronjob_runs[]\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@index([visible_to_user])\n}\n\nmodel cronjobs {\n id String @id @default(uuid())\n user_id String\n name String\n enabled Boolean\n target_type String // \"prompt\" | \"workflow\"\n prompt String?\n prompt_allow_workflow_runs_without_permission Boolean?\n workflow_slug String?\n workflow_input_json String?\n cron_expression String\n timezone String\n monitor_timeout_value Float @default(2)\n monitor_timeout_unit String @default(\"hours\") // \"minutes\" | \"hours\" | \"days\"\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n runs cronjob_runs[]\n\n @@unique([user_id, name])\n @@index([user_id])\n @@index([enabled])\n @@index([target_type])\n @@index([workflow_slug])\n}\n\nmodel cronjob_runs {\n id String @id @default(uuid())\n cronjob_id String\n status String // prompt: \"running\" | \"paused\" | \"success\" | \"failed\" | \"skipped\" | \"terminated\"; workflow: \"running\" | \"success\" | \"failed\" | \"skipped\"\n started_at BigInt\n completed_at BigInt?\n workflow_run_id String?\n summary String?\n session_id String?\n opencode_session_id String?\n error_message String?\n todo_list_version Int @default(0)\n cronjob cronjobs @relation(fields: [cronjob_id], references: [id], onDelete: Cascade)\n session chat_sessions? @relation(fields: [session_id], references: [id], onDelete: Cascade)\n workflowRun workflow_runs? @relation(fields: [workflow_run_id], references: [id], onDelete: Cascade)\n todos cronjob_run_todos[]\n\n @@index([cronjob_id, started_at])\n @@index([cronjob_id, status])\n @@index([workflow_run_id])\n @@index([session_id])\n @@index([opencode_session_id, status])\n}\n\nmodel cronjob_run_todos {\n id String @id @default(uuid())\n run_id String\n content String\n status String // \"pending\" = not started yet; \"in_progress\" = current todo; \"completed\" = finished\n position Int\n summary String?\n created_at BigInt\n completed_at BigInt?\n run cronjob_runs @relation(fields: [run_id], references: [id], onDelete: Cascade)\n\n @@index([run_id, status, position])\n @@index([run_id, position])\n}\n\nmodel chat_session_usage {\n chat_session_id String @id\n last_synced_message_id String\n provider_id String\n input_tokens Int\n output_tokens Int\n cached_tokens Int\n cost_usd Float\n model_id String\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n}\n\nmodel chat_session_tracked_files {\n id String @id @default(uuid())\n chat_session_id String\n relative_path String\n existed_at_baseline Boolean\n content_kind String // \"text\" | \"binary\" | \"missing\"\n text_content String?\n binary_content Bytes?\n size_bytes Int?\n content_sha256 String?\n created_at BigInt\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n\n @@unique([chat_session_id, relative_path])\n @@index([chat_session_id])\n}\n\nmodel tool_execution_permissions {\n id String @id @default(uuid())\n opencode_session_id String\n message_id String\n call_id String\n status String // \"pending\" | \"approved\" | \"rejected\"\n created_at BigInt\n responded_at BigInt?\n\n @@unique([opencode_session_id, message_id, call_id])\n @@index([opencode_session_id, status])\n}\n\nmodel resource_metadata {\n resource_kind String\n resource_slug String\n created_by_user_id String?\n approved_by_user_id String?\n created_at BigInt\n updated_at BigInt\n approvedSnapshot resource_approved_snapshots?\n createdByUser users? @relation(\"ResourceCreatedBy\", fields: [created_by_user_id], references: [id], onDelete: SetNull)\n approvedByUser users? @relation(\"ResourceApprovedBy\", fields: [approved_by_user_id], references: [id], onDelete: SetNull)\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n @@index([created_by_user_id])\n @@index([approved_by_user_id])\n}\n\nmodel resource_permissions {\n resource_kind String\n resource_slug String\n permission_mode String // \"restricted\" | \"everyone\"\n created_at BigInt\n updated_at BigInt\n allowedUsers resource_permission_users[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_permission_users {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n user_id String\n created_at BigInt\n permission resource_permissions @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, user_id])\n @@index([resource_kind, resource_slug])\n}\n\nmodel resource_approved_snapshots {\n resource_kind String\n resource_slug String\n file_count Int\n created_at BigInt\n updated_at BigInt\n resource resource_metadata @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n files resource_approved_snapshot_files[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_approved_snapshot_files {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n relative_path String\n content_kind String // \"text\" | \"binary\"\n text_content String?\n binary_content Bytes?\n size_bytes Int\n content_sha256 String\n snapshot resource_approved_snapshots @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, relative_path])\n @@index([resource_kind, resource_slug])\n}\n",
374
- "inlineSchemaHash": "365f7a66ef1a3911b2564a1f1cd26b622414e1a42ad30215b6494a54bdb53b89",
373
+ "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n output = \"./generated/client\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel users {\n id String @id @default(uuid())\n email String @unique\n name String\n role String // \"User\" or \"Engineer\"\n created_at BigInt\n password_hash String\n must_change_password Boolean @default(false)\n reset_token String?\n reset_token_expires_at BigInt?\n workflowRuns workflow_runs[]\n chatSessions chat_sessions[]\n cronjobs cronjobs[]\n resourcePermissionUsers resource_permission_users[]\n createdResourceMetadata resource_metadata[] @relation(\"ResourceCreatedBy\")\n approvedResourceMetadata resource_metadata[] @relation(\"ResourceApprovedBy\")\n userSecrets user_secrets[]\n}\n\nmodel workflow_runs {\n id String @id @default(uuid())\n ran_by_user_id String?\n status String // \"running\" | \"success\" | \"failed\"\n started_at BigInt\n completed_at BigInt?\n args String? // JSON string of input arguments passed to the workflow\n error_message String?\n output String? // Captured stdout/stderr from the workflow run\n workflow_slug String\n session_id String?\n message_id String?\n run_source String // \"user\" | \"api\" | \"cronjob\"\n workflow_api_key_id String?\n user users? @relation(fields: [ran_by_user_id], references: [id], onDelete: SetNull)\n workflowApiKey workflow_api_keys? @relation(fields: [workflow_api_key_id], references: [id], onDelete: SetNull)\n cronjobRuns cronjob_runs[]\n\n @@index([started_at])\n @@index([session_id, message_id])\n @@index([workflow_api_key_id])\n}\n\nmodel workflow_api_keys {\n id String @id @default(uuid())\n workflow_slug String\n api_key String @unique\n created_by_user_id String\n created_at BigInt\n workflowRuns workflow_runs[]\n\n @@index([workflow_slug])\n @@index([created_by_user_id])\n}\n\nmodel workflow_aborted_sessions {\n session_id String @id\n created_at BigInt\n}\n\nmodel key_value {\n key String @id\n value String\n}\n\nmodel user_secrets {\n id String @id @default(uuid())\n user_id String\n key String\n value String\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([user_id, key])\n @@index([user_id])\n}\n\nmodel global_secrets {\n id String @id @default(uuid())\n key String @unique\n value String\n created_by_user_id String\n updated_by_user_id String\n created_at BigInt\n updated_at BigInt\n\n @@index([created_by_user_id])\n @@index([updated_by_user_id])\n}\n\nmodel chat_sessions {\n id String @id @default(uuid())\n user_id String\n opencode_session_id String @unique\n title String\n last_seen_assistant_message_id String?\n visible_to_user Boolean @default(true)\n created_at BigInt\n updated_at BigInt\n trackedFiles chat_session_tracked_files[]\n usage chat_session_usage?\n cronjobRuns cronjob_runs[]\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@index([visible_to_user])\n @@index([user_id, visible_to_user, updated_at])\n}\n\nmodel cronjobs {\n id String @id @default(uuid())\n user_id String\n name String\n enabled Boolean\n target_type String // \"prompt\" | \"workflow\"\n prompt String?\n prompt_allow_workflow_runs_without_permission Boolean?\n workflow_slug String?\n workflow_input_json String?\n cron_expression String\n timezone String\n monitor_timeout_value Float @default(2)\n monitor_timeout_unit String @default(\"hours\") // \"minutes\" | \"hours\" | \"days\"\n created_at BigInt\n updated_at BigInt\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n runs cronjob_runs[]\n\n @@unique([user_id, name])\n @@index([user_id])\n @@index([enabled])\n @@index([target_type])\n @@index([workflow_slug])\n}\n\nmodel cronjob_runs {\n id String @id @default(uuid())\n cronjob_id String\n status String // prompt: \"running\" | \"paused\" | \"success\" | \"failed\" | \"skipped\" | \"terminated\"; workflow: \"running\" | \"success\" | \"failed\" | \"skipped\"\n started_at BigInt\n completed_at BigInt?\n workflow_run_id String?\n summary String?\n session_id String?\n opencode_session_id String?\n error_message String?\n todo_list_version Int @default(0)\n cronjob cronjobs @relation(fields: [cronjob_id], references: [id], onDelete: Cascade)\n session chat_sessions? @relation(fields: [session_id], references: [id], onDelete: Cascade)\n workflowRun workflow_runs? @relation(fields: [workflow_run_id], references: [id], onDelete: Cascade)\n todos cronjob_run_todos[]\n\n @@index([cronjob_id, started_at])\n @@index([cronjob_id, status])\n @@index([workflow_run_id])\n @@index([session_id])\n @@index([opencode_session_id, status])\n}\n\nmodel cronjob_run_todos {\n id String @id @default(uuid())\n run_id String\n content String\n status String // \"pending\" = not started yet; \"in_progress\" = current todo; \"completed\" = finished\n position Int\n summary String?\n created_at BigInt\n completed_at BigInt?\n run cronjob_runs @relation(fields: [run_id], references: [id], onDelete: Cascade)\n\n @@index([run_id, status, position])\n @@index([run_id, position])\n}\n\nmodel chat_session_usage {\n chat_session_id String @id\n last_synced_message_id String\n provider_id String\n input_tokens Int\n output_tokens Int\n cached_tokens Int\n cost_usd Float\n model_id String\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n}\n\nmodel chat_session_tracked_files {\n id String @id @default(uuid())\n chat_session_id String\n relative_path String\n existed_at_baseline Boolean\n content_kind String // \"text\" | \"binary\" | \"missing\"\n text_content String?\n binary_content Bytes?\n size_bytes Int?\n content_sha256 String?\n created_at BigInt\n updated_at BigInt\n session chat_sessions @relation(fields: [chat_session_id], references: [id], onDelete: Cascade)\n\n @@unique([chat_session_id, relative_path])\n @@index([chat_session_id])\n}\n\nmodel tool_execution_permissions {\n id String @id @default(uuid())\n opencode_session_id String\n message_id String\n call_id String\n status String // \"pending\" | \"approved\" | \"rejected\"\n created_at BigInt\n responded_at BigInt?\n\n @@unique([opencode_session_id, message_id, call_id])\n @@index([opencode_session_id, status])\n}\n\nmodel resource_metadata {\n resource_kind String\n resource_slug String\n created_by_user_id String?\n approved_by_user_id String?\n created_at BigInt\n updated_at BigInt\n approvedSnapshot resource_approved_snapshots?\n createdByUser users? @relation(\"ResourceCreatedBy\", fields: [created_by_user_id], references: [id], onDelete: SetNull)\n approvedByUser users? @relation(\"ResourceApprovedBy\", fields: [approved_by_user_id], references: [id], onDelete: SetNull)\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n @@index([created_by_user_id])\n @@index([approved_by_user_id])\n}\n\nmodel resource_permissions {\n resource_kind String\n resource_slug String\n permission_mode String // \"restricted\" | \"everyone\"\n created_at BigInt\n updated_at BigInt\n allowedUsers resource_permission_users[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_permission_users {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n user_id String\n created_at BigInt\n permission resource_permissions @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n user users @relation(fields: [user_id], references: [id], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, user_id])\n @@index([resource_kind, resource_slug])\n}\n\nmodel resource_approved_snapshots {\n resource_kind String\n resource_slug String\n file_count Int\n created_at BigInt\n updated_at BigInt\n resource resource_metadata @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n files resource_approved_snapshot_files[]\n\n @@id([resource_kind, resource_slug])\n @@index([resource_slug])\n}\n\nmodel resource_approved_snapshot_files {\n id String @id @default(uuid())\n resource_kind String\n resource_slug String\n relative_path String\n content_kind String // \"text\" | \"binary\"\n text_content String?\n binary_content Bytes?\n size_bytes Int\n content_sha256 String\n snapshot resource_approved_snapshots @relation(fields: [resource_kind, resource_slug], references: [resource_kind, resource_slug], onDelete: Cascade)\n\n @@unique([resource_kind, resource_slug, relative_path])\n @@index([resource_kind, resource_slug])\n}\n",
374
+ "inlineSchemaHash": "fc99ad355c76b3380556a915c46b51834e01f3fa7a8ca3ba7f16338c41037be5",
375
375
  "copyEngine": true
376
376
  }
377
377
  config.dirname = '/'
@@ -0,0 +1,2 @@
1
+ -- CreateIndex
2
+ CREATE INDEX "chat_sessions_user_id_visible_to_user_updated_at_idx" ON "chat_sessions"("user_id", "visible_to_user", "updated_at");
@@ -112,6 +112,7 @@ model chat_sessions {
112
112
  cronjobRuns cronjob_runs[]
113
113
  user users @relation(fields: [user_id], references: [id], onDelete: Cascade)
114
114
  @@index([visible_to_user])
115
+ @@index([user_id, visible_to_user, updated_at])
115
116
  }
116
117
 
117
118
  model cronjobs {