@crewx/sdk 0.9.0-rc.8 → 0.9.0-rc.80

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 (168) hide show
  1. package/README.md +1 -1
  2. package/dist/activity-log/builder.d.ts +55 -6
  3. package/dist/config/canonicalize-agent-options.d.ts +17 -0
  4. package/dist/config/global-settings.d.ts +33 -0
  5. package/dist/config/migrate-grok-mode.d.ts +6 -0
  6. package/dist/config/model-compat.d.ts +31 -0
  7. package/dist/config/models.generated.d.ts +33 -0
  8. package/dist/config/models.remote.d.ts +1 -0
  9. package/dist/config/overdrive.d.ts +7 -0
  10. package/dist/config/pricing.d.ts +8 -5
  11. package/dist/config/pricing.generated.d.ts +4 -0
  12. package/dist/config/pricing.types.d.ts +4 -10
  13. package/dist/config/timeout.config.d.ts +3 -0
  14. package/dist/config/wi-boards.d.ts +19 -0
  15. package/dist/context-budget/continuity.d.ts +61 -0
  16. package/dist/context-budget/index.d.ts +140 -0
  17. package/dist/conversation/index.d.ts +1 -1
  18. package/dist/conversation/sqlite-provider.d.ts +5 -1
  19. package/dist/conversation/to-template-messages.d.ts +7 -0
  20. package/dist/conversation/types.d.ts +43 -0
  21. package/dist/desktop/command-runner.d.ts +4 -0
  22. package/dist/desktop/default-adapters.d.ts +4 -0
  23. package/dist/desktop/global-install.d.ts +19 -0
  24. package/dist/desktop/index.d.ts +2 -0
  25. package/dist/desktop/index.js +3 -0
  26. package/dist/desktop/install.d.ts +5 -0
  27. package/dist/desktop/lnk.d.ts +22 -0
  28. package/dist/desktop/manifest.d.ts +21 -0
  29. package/dist/desktop/types.d.ts +67 -0
  30. package/dist/desktop/version.d.ts +1 -0
  31. package/dist/desktop/windows-paths.d.ts +21 -0
  32. package/dist/desktop/wrapper.d.ts +18 -0
  33. package/dist/esm/desktop/index.js +3 -0
  34. package/dist/esm/index.js +345 -96
  35. package/dist/esm/plugins/index.js +187 -93
  36. package/dist/esm/publish/index.js +1 -0
  37. package/dist/esm/repository/index.js +203 -94
  38. package/dist/esm/testing/index.js +1 -1
  39. package/dist/events/types.d.ts +4 -0
  40. package/dist/facade/Crewx.d.ts +38 -2
  41. package/dist/highlight/thread-highlight-prompt.d.ts +2 -0
  42. package/dist/highlight/thread-highlighter.d.ts +326 -0
  43. package/dist/index.browser.d.ts +9 -1
  44. package/dist/index.browser.js +6 -2
  45. package/dist/index.d.ts +57 -8
  46. package/dist/index.js +345 -96
  47. package/dist/internal/windows-spawn.d.ts +10 -0
  48. package/dist/layout/types.d.ts +17 -1
  49. package/dist/local-server/base-url.d.ts +18 -0
  50. package/dist/local-server/index.d.ts +8 -0
  51. package/dist/local-server/opencode.d.ts +8 -0
  52. package/dist/local-server/probe.d.ts +3 -0
  53. package/dist/local-server/runtime.d.ts +26 -0
  54. package/dist/local-server/types.d.ts +43 -0
  55. package/dist/migrations/0008_careful_black_bird.sql +17 -0
  56. package/dist/migrations/0009_hot_dark_phoenix.sql +1 -0
  57. package/dist/migrations/0010_analyze_stats.sql +6 -0
  58. package/dist/migrations/0011_far_black_tarantula.sql +1 -0
  59. package/dist/migrations/0012_blue_turbo.sql +23 -0
  60. package/dist/migrations/0013_hesitant_adam_destine.sql +15 -0
  61. package/dist/migrations/0014_organic_mongu.sql +73 -0
  62. package/dist/migrations/0015_panoramic_gorilla_man.sql +71 -0
  63. package/dist/migrations/0016_fuzzy_hemingway.sql +14 -0
  64. package/dist/migrations/0017_project_stage_sdk_migration.sql +29 -0
  65. package/dist/migrations/0018_bored_jackal.sql +33 -0
  66. package/dist/migrations/0019_backfill_task_thread_ids.sql +74 -0
  67. package/dist/migrations/0020_useful_iron_lad.sql +55 -0
  68. package/dist/migrations/0021_dashing_peter_quill.sql +51 -0
  69. package/dist/migrations/meta/0008_snapshot.json +1327 -0
  70. package/dist/migrations/meta/0009_snapshot.json +1335 -0
  71. package/dist/migrations/meta/0010_snapshot.json +1335 -0
  72. package/dist/migrations/meta/0011_snapshot.json +1342 -0
  73. package/dist/migrations/meta/0012_snapshot.json +1494 -0
  74. package/dist/migrations/meta/0013_snapshot.json +1592 -0
  75. package/dist/migrations/meta/0014_snapshot.json +1975 -0
  76. package/dist/migrations/meta/0015_snapshot.json +2315 -0
  77. package/dist/migrations/meta/0016_snapshot.json +2419 -0
  78. package/dist/migrations/meta/0017_snapshot.json +2419 -0
  79. package/dist/migrations/meta/0018_snapshot.json +2570 -0
  80. package/dist/migrations/meta/0020_snapshot.json +2895 -0
  81. package/dist/migrations/meta/0021_snapshot.json +3031 -0
  82. package/dist/migrations/meta/_journal.json +98 -0
  83. package/dist/paths.d.ts +1 -0
  84. package/dist/plugin/types.d.ts +1 -0
  85. package/dist/plugins/index.js +187 -93
  86. package/dist/plugins/sqlite-tracing.d.ts +22 -0
  87. package/dist/provider/acp/connection.d.ts +4 -0
  88. package/dist/provider/acp/meta.d.ts +11 -0
  89. package/dist/provider/bridge.d.ts +6 -0
  90. package/dist/provider/classify-task-error.d.ts +2 -0
  91. package/dist/provider/cli/adapter.types.d.ts +6 -0
  92. package/dist/provider/cli/adapters/cli-knob.util.d.ts +7 -0
  93. package/dist/provider/cli/adapters/grok.d.ts +2 -0
  94. package/dist/provider/cli/adapters/index.d.ts +1 -0
  95. package/dist/provider/cli/index.d.ts +1 -1
  96. package/dist/provider/cli/meta.d.ts +5 -0
  97. package/dist/provider/errors.d.ts +1 -0
  98. package/dist/provider/idle-watchdog.d.ts +19 -0
  99. package/dist/provider/models.d.ts +17 -12
  100. package/dist/provider/order.d.ts +1 -1
  101. package/dist/provider/parse-usage.d.ts +6 -0
  102. package/dist/provider/provider-selection.d.ts +8 -0
  103. package/dist/provider/redact-technical-message.d.ts +1 -0
  104. package/dist/provider/task-error.types.d.ts +16 -0
  105. package/dist/publish/auth-boundary.d.ts +22 -0
  106. package/dist/publish/auth-json.d.ts +11 -0
  107. package/dist/publish/constants.d.ts +6 -0
  108. package/dist/publish/index.d.ts +30 -0
  109. package/dist/publish/index.js +1 -0
  110. package/dist/publish/manifest.d.ts +17 -0
  111. package/dist/publish/marketplace-api.constants.d.ts +1 -0
  112. package/dist/publish/pack.d.ts +2 -0
  113. package/dist/publish/scan.d.ts +9 -0
  114. package/dist/publish/secrets.d.ts +6 -0
  115. package/dist/publish/upload.d.ts +18 -0
  116. package/dist/repository/agent-suggestion.repository.d.ts +34 -0
  117. package/dist/repository/common-code.repository.d.ts +39 -0
  118. package/dist/repository/db.d.ts +8 -0
  119. package/dist/repository/index.d.ts +28 -4
  120. package/dist/repository/index.js +203 -94
  121. package/dist/repository/notification.repository.d.ts +58 -0
  122. package/dist/repository/project.repository.d.ts +90 -0
  123. package/dist/repository/request-log.repository.d.ts +1 -0
  124. package/dist/repository/task-log-migration.d.ts +74 -0
  125. package/dist/repository/task-log.repository.d.ts +44 -0
  126. package/dist/repository/task.repository.d.ts +119 -0
  127. package/dist/repository/thread-history.repository.d.ts +193 -0
  128. package/dist/repository/thread-title-common-code-migration.d.ts +13 -0
  129. package/dist/repository/thread-title.types.d.ts +29 -0
  130. package/dist/repository/thread.repository.d.ts +21 -2
  131. package/dist/repository/usage-report.repository.d.ts +27 -0
  132. package/dist/schema/agent-suggestions.d.ts +199 -0
  133. package/dist/schema/common-codes.d.ts +216 -0
  134. package/dist/schema/index.d.ts +9 -0
  135. package/dist/schema/notifications.d.ts +303 -0
  136. package/dist/schema/projects.d.ts +610 -0
  137. package/dist/schema/task-log-events.d.ts +119 -0
  138. package/dist/schema/tasks.d.ts +53 -0
  139. package/dist/schema/thread-history.d.ts +734 -0
  140. package/dist/schema/threads.d.ts +2 -0
  141. package/dist/schema/ui-action-receipts.d.ts +303 -0
  142. package/dist/schema/ui-instances.d.ts +267 -0
  143. package/dist/schema/usage-reports.d.ts +252 -0
  144. package/dist/testing/index.js +1 -1
  145. package/dist/types/index.d.ts +1536 -3
  146. package/dist/ui-card/index.d.ts +2 -0
  147. package/dist/ui-card/legacy-card-parser.d.ts +11 -0
  148. package/dist/ui-instance/definition.d.ts +6 -0
  149. package/dist/ui-instance/errors.d.ts +17 -0
  150. package/dist/ui-instance/index.d.ts +6 -0
  151. package/dist/ui-instance/json.d.ts +5 -0
  152. package/dist/ui-instance/runtime.d.ts +13 -0
  153. package/dist/ui-instance/store.d.ts +65 -0
  154. package/dist/ui-instance/types.d.ts +200 -0
  155. package/dist/ui-plugin/index.d.ts +3 -0
  156. package/dist/ui-plugin/manifest.d.ts +3 -0
  157. package/dist/ui-plugin/types.d.ts +76 -0
  158. package/dist/ui-plugin/validator.d.ts +9 -0
  159. package/dist/utils/crewx-home.d.ts +1 -0
  160. package/dist/utils/id.d.ts +1 -1
  161. package/dist/utils/timestamp.d.ts +1 -0
  162. package/dist/wi/create.d.ts +48 -0
  163. package/dist/wi/select.d.ts +20 -0
  164. package/package.json +18 -2
  165. package/templates/agents/default.yaml +240 -116
  166. package/templates/documents/crewx-manual.md +138 -138
  167. package/templates/workflows/wi-default-flow.yaml +65 -0
  168. package/templates/workflows/wi-plan-flow.yaml +140 -0
@@ -20,15 +20,25 @@ export type WindowsSpawnInvocation = {
20
20
  shell?: boolean;
21
21
  windowsHide?: boolean;
22
22
  };
23
+ export type BinResolutionMode = 'global_first' | 'local_first';
24
+ export type ResolveExecutablePathOptions = {
25
+ cwd?: string;
26
+ env?: NodeJS.ProcessEnv;
27
+ platform?: NodeJS.Platform;
28
+ resolution?: BinResolutionMode;
29
+ };
23
30
  export type ResolveWindowsSpawnProgramParams = {
24
31
  command: string;
25
32
  platform?: NodeJS.Platform;
26
33
  env?: NodeJS.ProcessEnv;
27
34
  execPath?: string;
28
35
  packageName?: string;
36
+ cwd?: string;
37
+ resolution?: BinResolutionMode;
29
38
  allowShellFallback?: boolean;
30
39
  };
31
40
  export type ResolveWindowsSpawnProgramCandidateParams = Omit<ResolveWindowsSpawnProgramParams, 'allowShellFallback'>;
41
+ export declare function resolveExecutablePath(command: string, options?: ResolveExecutablePathOptions): string;
32
42
  export declare function resolveWindowsExecutablePath(command: string, env: NodeJS.ProcessEnv): string;
33
43
  export declare function resolveWindowsSpawnProgramCandidate(params: ResolveWindowsSpawnProgramCandidateParams): WindowsSpawnProgramCandidate;
34
44
  export declare function applyWindowsSpawnProgramPolicy(params: {
@@ -25,6 +25,13 @@ export interface LayoutDefinition {
25
25
  template: string;
26
26
  propsSchema: Record<string, PropSchema>;
27
27
  defaultProps: Record<string, any>;
28
+ capabilities?: LayoutCapabilities;
29
+ }
30
+ export interface LayoutCapabilities {
31
+ semanticContinuity?: boolean;
32
+ continuity?: boolean;
33
+ continuitySlot?: string;
34
+ slot?: string;
28
35
  }
29
36
  export interface CustomLayoutDefinition {
30
37
  template: string;
@@ -32,10 +39,15 @@ export interface CustomLayoutDefinition {
32
39
  version?: string;
33
40
  propsSchema?: Record<string, any>;
34
41
  defaultProps?: Record<string, any>;
42
+ capabilities?: LayoutCapabilities;
35
43
  }
36
44
  export type InlineLayoutSpec = string | {
37
45
  id: string;
38
46
  props?: Record<string, any>;
47
+ capabilities?: LayoutCapabilities;
48
+ } | {
49
+ template: string;
50
+ capabilities?: LayoutCapabilities;
39
51
  };
40
52
  export interface ValidationResult {
41
53
  valid: boolean;
@@ -97,6 +109,9 @@ export interface RenderContext {
97
109
  [key: string]: any;
98
110
  };
99
111
  context?: Record<string, any>;
112
+ semanticContinuity?: import('../conversation/types.js').SemanticContinuityContext;
113
+ thread_state?: import('../conversation/types.js').ConversationThreadState;
114
+ threadState?: import('../conversation/types.js').ConversationThreadState;
100
115
  contextOptions?: {
101
116
  goals: boolean | string | string[];
102
117
  daily: boolean;
@@ -109,7 +124,8 @@ export interface RawLayoutYaml {
109
124
  description?: string;
110
125
  propsSchema?: Record<string, any>;
111
126
  template?: string;
112
- layouts?: Record<string, string>;
127
+ layouts?: Record<string, any>;
128
+ capabilities?: LayoutCapabilities;
113
129
  }
114
130
  export declare class LayoutLoadError extends Error {
115
131
  readonly layoutId?: string | undefined;
@@ -0,0 +1,18 @@
1
+ export declare const LOCAL_SERVER_BASE_URL_MAX_LENGTH = 2048;
2
+ export type LocalServerHostClass = 'loopback' | 'private-ipv4' | 'cgnat' | 'unique-local-ipv6';
3
+ export type LocalServerBaseUrlErrorCode = 'invalid_input' | 'url_too_long' | 'invalid_scheme' | 'invalid_url' | 'userinfo_not_allowed' | 'query_not_allowed' | 'fragment_not_allowed' | 'invalid_port' | 'host_not_ip_literal' | 'zone_id' | 'loopback_not_allowed' | 'metadata_address' | 'link_local' | 'multicast' | 'unspecified' | 'ipv4_mapped_ipv6' | 'ip_not_allowed';
4
+ export interface LocalServerBaseUrlOptions {
5
+ allowLoopback?: boolean;
6
+ }
7
+ export interface LocalServerBaseUrlSuccess {
8
+ ok: true;
9
+ normalizedBaseUrl: string;
10
+ hostClass: LocalServerHostClass;
11
+ }
12
+ export interface LocalServerBaseUrlFailure {
13
+ ok: false;
14
+ code: LocalServerBaseUrlErrorCode;
15
+ }
16
+ export type LocalServerBaseUrlResult = LocalServerBaseUrlSuccess | LocalServerBaseUrlFailure;
17
+ export declare function parseLocalServerBaseUrl(value: unknown, options?: LocalServerBaseUrlOptions): LocalServerBaseUrlResult;
18
+ export declare function isAllowedLocalServerIp(value: string, options?: LocalServerBaseUrlOptions): boolean;
@@ -0,0 +1,8 @@
1
+ export { isAllowedLocalServerIp, parseLocalServerBaseUrl, } from './base-url';
2
+ export type { LocalServerBaseUrlErrorCode, LocalServerBaseUrlFailure, LocalServerBaseUrlOptions, LocalServerBaseUrlResult, LocalServerBaseUrlSuccess, LocalServerHostClass, } from './base-url';
3
+ export { DEFAULT_LOCAL_SERVER_PROBE_TIMEOUT_MS, probeLocalServer } from './probe';
4
+ export type { LocalServerDefinition, LocalServerFetch, LocalServerKind, LocalServerModel, LocalServerOpenCodeOverlay, LocalServerProbeOptions, LocalServerProbeReason, LocalServerProbeResponse, LocalServerProbeResult, LocalServerSettings, } from './types';
5
+ export { buildLocalServerOpenCodeOverlay, LocalServerOpenCodeOverlayError, } from './opencode';
6
+ export type { LocalServerOpenCodeOverlayErrorCode } from './opencode';
7
+ export { isLocalServerRuntimeEligible, resolveLocalServerRuntime, LocalServerRuntimeError, LOCAL_AI_SERVER_CONFIG_INVALID, } from './runtime';
8
+ export type { LocalServerRuntimeMetadata, LocalServerRuntimeOptions, LocalServerRuntimeResolution, } from './runtime';
@@ -0,0 +1,8 @@
1
+ import type { LocalServerOpenCodeOverlay, LocalServerSettings } from './types';
2
+ export type LocalServerOpenCodeOverlayErrorCode = 'missing_default_model' | 'invalid_default_model' | 'invalid_label' | 'invalid_provider_id' | 'invalid_api_key_env' | 'invalid_base_url';
3
+ export declare class LocalServerOpenCodeOverlayError extends Error {
4
+ readonly code: LocalServerOpenCodeOverlayErrorCode;
5
+ readonly name = "LocalServerOpenCodeOverlayError";
6
+ constructor(code: LocalServerOpenCodeOverlayErrorCode);
7
+ }
8
+ export declare function buildLocalServerOpenCodeOverlay(settings: LocalServerSettings): LocalServerOpenCodeOverlay;
@@ -0,0 +1,3 @@
1
+ import type { LocalServerProbeOptions, LocalServerProbeResult } from './types.js';
2
+ export declare const DEFAULT_LOCAL_SERVER_PROBE_TIMEOUT_MS = 3000;
3
+ export declare function probeLocalServer(baseUrl: string, options?: LocalServerProbeOptions): Promise<LocalServerProbeResult>;
@@ -0,0 +1,26 @@
1
+ import type { GlobalSettingsStore } from '../config/global-settings';
2
+ export declare const LOCAL_AI_SERVER_CONFIG_INVALID: "LOCAL_AI_SERVER_CONFIG_INVALID";
3
+ export interface LocalServerRuntimeMetadata {
4
+ providerId: string;
5
+ model: string;
6
+ source: 'global_settings';
7
+ }
8
+ export interface LocalServerRuntimeResolution {
9
+ model?: string;
10
+ env?: {
11
+ OPENCODE_CONFIG_CONTENT: string;
12
+ };
13
+ metadata?: LocalServerRuntimeMetadata;
14
+ }
15
+ export interface LocalServerRuntimeOptions {
16
+ effectiveProviderStr: string;
17
+ effectiveModel?: string;
18
+ globalSettings?: GlobalSettingsStore;
19
+ }
20
+ export declare class LocalServerRuntimeError extends Error {
21
+ readonly name = "LocalServerRuntimeError";
22
+ readonly code: "LOCAL_AI_SERVER_CONFIG_INVALID";
23
+ constructor();
24
+ }
25
+ export declare function isLocalServerRuntimeEligible(effectiveProviderStr: string, effectiveModel: string | undefined): boolean;
26
+ export declare function resolveLocalServerRuntime(options: LocalServerRuntimeOptions): LocalServerRuntimeResolution;
@@ -0,0 +1,43 @@
1
+ export type LocalServerKind = 'ollama' | 'lm-studio' | 'unsloth-desktop';
2
+ export interface LocalServerDefinition {
3
+ id: string;
4
+ label: string;
5
+ kind: LocalServerKind;
6
+ baseUrl: string;
7
+ discovery: 'tailscale' | 'manual';
8
+ defaultModel?: string;
9
+ auth?: {
10
+ apiKeyEnv: string;
11
+ };
12
+ }
13
+ export interface LocalServerSettings {
14
+ version: 1;
15
+ definition: LocalServerDefinition;
16
+ verifiedAt: string;
17
+ }
18
+ export interface LocalServerOpenCodeOverlay {
19
+ env: {
20
+ OPENCODE_CONFIG_CONTENT: string;
21
+ };
22
+ model: string;
23
+ providerId: string;
24
+ }
25
+ export interface LocalServerModel {
26
+ id: string;
27
+ }
28
+ export type LocalServerProbeReason = 'http_error' | 'timeout' | 'network_error' | 'invalid_json' | 'invalid_envelope';
29
+ export interface LocalServerProbeResponse {
30
+ readonly ok: boolean;
31
+ json(): Promise<unknown>;
32
+ }
33
+ export type LocalServerFetch = (url: string, init?: RequestInit) => Promise<LocalServerProbeResponse>;
34
+ export interface LocalServerProbeOptions {
35
+ timeoutMs?: number;
36
+ fetch?: LocalServerFetch;
37
+ }
38
+ export interface LocalServerProbeResult {
39
+ ok: boolean;
40
+ models: LocalServerModel[];
41
+ latencyMs: number;
42
+ reason?: LocalServerProbeReason;
43
+ }
@@ -0,0 +1,17 @@
1
+ CREATE TABLE IF NOT EXISTS `usage_reports` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `workspace_id` text NOT NULL,
4
+ `month` text NOT NULL,
5
+ `generated_at` text NOT NULL,
6
+ `source_range` text NOT NULL,
7
+ `tier` text NOT NULL,
8
+ `total_tokens` integer NOT NULL,
9
+ `total_cost_usd` real NOT NULL,
10
+ `payload` text NOT NULL,
11
+ `payload_hash` text,
12
+ `signature` text,
13
+ `signed_at` text,
14
+ `issuer` text
15
+ );
16
+ --> statement-breakpoint
17
+ CREATE UNIQUE INDEX IF NOT EXISTS `uniq_usage_reports_ws_month` ON `usage_reports` (`workspace_id`,`month`);
@@ -0,0 +1 @@
1
+ CREATE INDEX IF NOT EXISTS `idx_threads_ws_updated` ON `threads` (`workspace_id`,`updated_at`);
@@ -0,0 +1,6 @@
1
+ -- Custom SQL migration file, put your code below! --
2
+ -- WI-20260706-006: seed sqlite_stat1 so the query planner has index
3
+ -- selectivity data from the very first run on a fresh DB (idempotent).
4
+ PRAGMA analysis_limit = 400;
5
+ --> statement-breakpoint
6
+ ANALYZE;
@@ -0,0 +1 @@
1
+ CREATE INDEX IF NOT EXISTS `idx_threads_title` ON `threads` (`title`);
@@ -0,0 +1,23 @@
1
+ CREATE TABLE IF NOT EXISTS `notification_reads` (
2
+ `notification_id` text NOT NULL,
3
+ `user_id` text NOT NULL,
4
+ `read_at` text NOT NULL,
5
+ PRIMARY KEY(`notification_id`, `user_id`)
6
+ );
7
+ --> statement-breakpoint
8
+ CREATE TABLE IF NOT EXISTS `notifications` (
9
+ `id` text PRIMARY KEY NOT NULL,
10
+ `workspace_id` text NOT NULL,
11
+ `agent_id` text,
12
+ `task_id` text,
13
+ `thread_id` text,
14
+ `source` text DEFAULT 'agent' NOT NULL,
15
+ `level` text DEFAULT 'info' NOT NULL,
16
+ `title` text NOT NULL,
17
+ `body` text,
18
+ `target_user_id` text,
19
+ `created_at` text NOT NULL,
20
+ `metadata` text
21
+ );
22
+ --> statement-breakpoint
23
+ CREATE INDEX IF NOT EXISTS `idx_notifications_ws_created` ON `notifications` (`workspace_id`,"created_at" DESC);
@@ -0,0 +1,15 @@
1
+ CREATE TABLE IF NOT EXISTS `agent_suggestions` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `workspace_id` text NOT NULL,
4
+ `agent_id` text NOT NULL,
5
+ `task_id` text,
6
+ `type` text NOT NULL,
7
+ `status` text DEFAULT 'pending' NOT NULL,
8
+ `payload` text NOT NULL,
9
+ `applied_commit_sha` text,
10
+ `created_at` text NOT NULL,
11
+ `updated_at` text NOT NULL
12
+ );
13
+ --> statement-breakpoint
14
+ CREATE INDEX IF NOT EXISTS `idx_agent_suggestions_ws_agent` ON `agent_suggestions` (`workspace_id`,`agent_id`);--> statement-breakpoint
15
+ CREATE INDEX IF NOT EXISTS `idx_agent_suggestions_ws_created` ON `agent_suggestions` (`workspace_id`,`created_at`);
@@ -0,0 +1,73 @@
1
+ CREATE TABLE IF NOT EXISTS `ui_instances` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `definition_id` text NOT NULL,
4
+ `definition_version` integer NOT NULL,
5
+ `definition_hash` text NOT NULL,
6
+ `workspace_id` text NOT NULL,
7
+ `thread_id` text NOT NULL,
8
+ `anchor_task_id` text,
9
+ `domain_ref_json` text NOT NULL,
10
+ `state_json` text NOT NULL,
11
+ `revision` integer DEFAULT 0 NOT NULL,
12
+ `status` text DEFAULT 'active' NOT NULL,
13
+ `granted_capabilities_json` text DEFAULT '[]' NOT NULL,
14
+ `created_at` integer NOT NULL,
15
+ `updated_at` integer NOT NULL,
16
+ CONSTRAINT "ui_instances_id_ck" CHECK(length("ui_instances"."id") BETWEEN 5 AND 128 AND "ui_instances"."id" GLOB 'uic_*'),
17
+ CONSTRAINT "ui_instances_definition_id_ck" CHECK(length("ui_instances"."definition_id") BETWEEN 1 AND 128),
18
+ CONSTRAINT "ui_instances_definition_version_ck" CHECK("ui_instances"."definition_version" >= 1),
19
+ CONSTRAINT "ui_instances_definition_hash_ck" CHECK(length("ui_instances"."definition_hash") = 64 AND "ui_instances"."definition_hash" NOT GLOB '*[^0-9a-f]*'),
20
+ CONSTRAINT "ui_instances_workspace_id_ck" CHECK(length("ui_instances"."workspace_id") BETWEEN 1 AND 256),
21
+ CONSTRAINT "ui_instances_thread_id_ck" CHECK(length("ui_instances"."thread_id") BETWEEN 1 AND 256),
22
+ CONSTRAINT "ui_instances_domain_ref_json_ck" CHECK(json_valid("ui_instances"."domain_ref_json") AND json_type("ui_instances"."domain_ref_json") = 'object'),
23
+ CONSTRAINT "ui_instances_state_json_ck" CHECK(json_valid("ui_instances"."state_json") AND json_type("ui_instances"."state_json") = 'object'),
24
+ CONSTRAINT "ui_instances_revision_ck" CHECK("ui_instances"."revision" >= 0),
25
+ CONSTRAINT "ui_instances_status_ck" CHECK("ui_instances"."status" IN ('active', 'completed', 'expired', 'revoked', 'unsupported')),
26
+ CONSTRAINT "ui_instances_granted_capabilities_json_ck" CHECK(json_valid("ui_instances"."granted_capabilities_json") AND json_type("ui_instances"."granted_capabilities_json") = 'array'),
27
+ CONSTRAINT "ui_instances_created_at_ck" CHECK("ui_instances"."created_at" >= 0),
28
+ CONSTRAINT "ui_instances_updated_at_ck" CHECK("ui_instances"."updated_at" >= "ui_instances"."created_at")
29
+ );
30
+ --> statement-breakpoint
31
+ CREATE INDEX IF NOT EXISTS `ui_instances_workspace_thread_idx` ON `ui_instances` (`workspace_id`,`thread_id`);--> statement-breakpoint
32
+ CREATE TABLE IF NOT EXISTS `ui_action_receipts` (
33
+ `id` text PRIMARY KEY NOT NULL,
34
+ `instance_id` text NOT NULL,
35
+ `request_id` text NOT NULL,
36
+ `action_id` text NOT NULL,
37
+ `actor_json` text NOT NULL,
38
+ `source` text NOT NULL,
39
+ `expected_revision` integer NOT NULL,
40
+ `before_revision` integer NOT NULL,
41
+ `after_revision` integer,
42
+ `status` text NOT NULL,
43
+ `outcome` text,
44
+ `input_json` text NOT NULL,
45
+ `result_json` text,
46
+ `error_code` text,
47
+ `requested_at` integer NOT NULL,
48
+ `settled_at` integer,
49
+ CONSTRAINT "ui_action_receipts_id_ck" CHECK(length("ui_action_receipts"."id") BETWEEN 5 AND 128),
50
+ CONSTRAINT "ui_action_receipts_instance_id_ck" CHECK(length("ui_action_receipts"."instance_id") BETWEEN 1 AND 128),
51
+ CONSTRAINT "ui_action_receipts_request_id_ck" CHECK(length("ui_action_receipts"."request_id") BETWEEN 1 AND 128),
52
+ CONSTRAINT "ui_action_receipts_action_id_ck" CHECK(length("ui_action_receipts"."action_id") BETWEEN 1 AND 128),
53
+ CONSTRAINT "ui_action_receipts_actor_json_ck" CHECK(json_valid("ui_action_receipts"."actor_json") AND json_type("ui_action_receipts"."actor_json") = 'object'),
54
+ CONSTRAINT "ui_action_receipts_source_ck" CHECK("ui_action_receipts"."source" IN ('ui_host', 'cli', 'system')),
55
+ CONSTRAINT "ui_action_receipts_expected_revision_ck" CHECK("ui_action_receipts"."expected_revision" >= 0),
56
+ CONSTRAINT "ui_action_receipts_before_revision_ck" CHECK("ui_action_receipts"."before_revision" >= 0),
57
+ CONSTRAINT "ui_action_receipts_after_revision_ck" CHECK("ui_action_receipts"."after_revision" IS NULL OR "ui_action_receipts"."after_revision" >= 0),
58
+ CONSTRAINT "ui_action_receipts_status_ck" CHECK("ui_action_receipts"."status" IN ('requested', 'settled')),
59
+ CONSTRAINT "ui_action_receipts_outcome_ck" CHECK("ui_action_receipts"."outcome" IS NULL OR "ui_action_receipts"."outcome" IN ('succeeded', 'rejected', 'failed', 'conflicted', 'expired')),
60
+ CONSTRAINT "ui_action_receipts_input_json_ck" CHECK(json_valid("ui_action_receipts"."input_json") AND json_type("ui_action_receipts"."input_json") = 'object'),
61
+ CONSTRAINT "ui_action_receipts_result_json_ck" CHECK("ui_action_receipts"."result_json" IS NULL OR (json_valid("ui_action_receipts"."result_json") AND json_type("ui_action_receipts"."result_json") = 'object')),
62
+ CONSTRAINT "ui_action_receipts_requested_at_ck" CHECK("ui_action_receipts"."requested_at" >= 0),
63
+ CONSTRAINT "ui_action_receipts_settled_at_ck" CHECK("ui_action_receipts"."settled_at" IS NULL OR "ui_action_receipts"."settled_at" >= "ui_action_receipts"."requested_at"),
64
+ CONSTRAINT "ui_action_receipts_status_transition_ck" CHECK((
65
+ ("ui_action_receipts"."status" = 'requested' AND "ui_action_receipts"."after_revision" IS NULL AND "ui_action_receipts"."outcome" IS NULL AND "ui_action_receipts"."settled_at" IS NULL)
66
+ OR
67
+ ("ui_action_receipts"."status" = 'settled' AND "ui_action_receipts"."after_revision" IS NOT NULL AND "ui_action_receipts"."outcome" IS NOT NULL AND "ui_action_receipts"."settled_at" IS NOT NULL)
68
+ ))
69
+ );
70
+ --> statement-breakpoint
71
+ CREATE UNIQUE INDEX IF NOT EXISTS `ui_action_receipts_instance_request_uq` ON `ui_action_receipts` (`instance_id`,`request_id`);--> statement-breakpoint
72
+ CREATE INDEX IF NOT EXISTS `ui_action_receipts_instance_id_idx` ON `ui_action_receipts` (`instance_id`);--> statement-breakpoint
73
+ CREATE INDEX IF NOT EXISTS `ui_action_receipts_requested_at_idx` ON `ui_action_receipts` (`requested_at`);
@@ -0,0 +1,71 @@
1
+ CREATE TABLE IF NOT EXISTS `project_criteria` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `project_id` text NOT NULL,
4
+ `position` integer NOT NULL,
5
+ `statement` text NOT NULL,
6
+ `verify_status` text DEFAULT 'pending' NOT NULL,
7
+ `verified_at` text,
8
+ `evidence_json` text DEFAULT '[]' NOT NULL,
9
+ `created_at` text NOT NULL,
10
+ `updated_at` text NOT NULL,
11
+ FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
12
+ CONSTRAINT "project_criteria_id_ck" CHECK("project_criteria"."id" GLOB 'pjc_*'),
13
+ CONSTRAINT "project_criteria_verify_status_ck" CHECK("project_criteria"."verify_status" IN ('pending', 'verifying', 'passed', 'failed')),
14
+ CONSTRAINT "project_criteria_evidence_json_ck" CHECK(json_valid("project_criteria"."evidence_json") AND json_type("project_criteria"."evidence_json") = 'array')
15
+ );
16
+ --> statement-breakpoint
17
+ CREATE UNIQUE INDEX IF NOT EXISTS `project_criteria_project_position_uq` ON `project_criteria` (`project_id`,`position`);--> statement-breakpoint
18
+ CREATE TABLE IF NOT EXISTS `project_events` (
19
+ `id` text PRIMARY KEY NOT NULL,
20
+ `project_id` text NOT NULL,
21
+ `seq` integer NOT NULL,
22
+ `actor_type` text NOT NULL,
23
+ `actor_id` text,
24
+ `type` text NOT NULL,
25
+ `title` text NOT NULL,
26
+ `summary` text,
27
+ `payload_json` text DEFAULT '{}' NOT NULL,
28
+ `created_at` text NOT NULL,
29
+ FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE restrict,
30
+ CONSTRAINT "project_events_id_ck" CHECK("project_events"."id" GLOB 'pje_*'),
31
+ CONSTRAINT "project_events_actor_type_ck" CHECK("project_events"."actor_type" IN ('user', 'project_manager', 'system')),
32
+ CONSTRAINT "project_events_payload_json_ck" CHECK(json_valid("project_events"."payload_json") AND json_type("project_events"."payload_json") = 'object')
33
+ );
34
+ --> statement-breakpoint
35
+ CREATE UNIQUE INDEX IF NOT EXISTS `project_events_project_seq_uq` ON `project_events` (`project_id`,`seq`);--> statement-breakpoint
36
+ CREATE TABLE IF NOT EXISTS `projects` (
37
+ `id` text PRIMARY KEY NOT NULL,
38
+ `workspace_id` text NOT NULL,
39
+ `name` text NOT NULL,
40
+ `outcome` text NOT NULL,
41
+ `due_date` text,
42
+ `scene` text DEFAULT 'planning' NOT NULL,
43
+ `stages_json` text NOT NULL,
44
+ `stage_index` integer NOT NULL,
45
+ `created_at` text NOT NULL,
46
+ `updated_at` text NOT NULL,
47
+ `accepted_at` text,
48
+ `archived_at` text,
49
+ CONSTRAINT "projects_id_ck" CHECK("projects"."id" GLOB 'prj_*'),
50
+ CONSTRAINT "projects_scene_ck" CHECK("projects"."scene" IN ('planning', 'plan_review', 'running', 'human_wait', 'limit_wait', 'failed_stop', 'analyzing_feedback', 'replanned', 'awaiting_review', 'accepted')),
51
+ CONSTRAINT "projects_stages_json_ck" CHECK(json_valid("projects"."stages_json") AND json_type("projects"."stages_json") = 'array'),
52
+ CONSTRAINT "projects_stage_index_ck" CHECK("projects"."stage_index" >= 1)
53
+ );
54
+ --> statement-breakpoint
55
+ CREATE TRIGGER IF NOT EXISTS project_events_no_update
56
+ BEFORE UPDATE ON project_events
57
+ BEGIN
58
+ SELECT RAISE(ABORT, 'project_events is append-only');
59
+ END;
60
+ --> statement-breakpoint
61
+ CREATE TRIGGER IF NOT EXISTS project_events_no_delete
62
+ BEFORE DELETE ON project_events
63
+ BEGIN
64
+ SELECT RAISE(ABORT, 'project_events is append-only');
65
+ END;
66
+ --> statement-breakpoint
67
+ UPDATE tasks
68
+ SET project_id = NULL
69
+ WHERE id = 'task_1773657200590_gu3z335oy'
70
+ AND project_id = '9c899c8b05f8d8d8d1df6d904c0a7fa8e5c95f1cb81602423c1e508f8b77d1dc'
71
+ AND workspace_id = '9c899c8b05f8d8d8d1df6d904c0a7fa8e5c95f1cb81602423c1e508f8b77d1dc';
@@ -0,0 +1,14 @@
1
+ CREATE TABLE IF NOT EXISTS `task_log_events` (
2
+ `id` integer PRIMARY KEY NOT NULL,
3
+ `task_id` text NOT NULL,
4
+ `seq` integer NOT NULL,
5
+ `timestamp` text NOT NULL,
6
+ `level` text NOT NULL,
7
+ `message` text NOT NULL,
8
+ FOREIGN KEY (`task_id`) REFERENCES `tasks`(`id`) ON UPDATE no action ON DELETE cascade
9
+ );
10
+ --> statement-breakpoint
11
+ CREATE UNIQUE INDEX IF NOT EXISTS `task_log_events_task_seq_uq` ON `task_log_events` (`task_id`,`seq`);--> statement-breakpoint
12
+ ALTER TABLE `tasks` ADD `log_storage` text DEFAULT 'blob' NOT NULL CONSTRAINT "tasks_log_storage_ck" CHECK("tasks"."log_storage" IN ('blob', 'events'));--> statement-breakpoint
13
+ ALTER TABLE `tasks` ADD `last_log_seq` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
14
+ ALTER TABLE `tasks` ADD `log_revision` integer DEFAULT 0 NOT NULL;
@@ -0,0 +1,29 @@
1
+ -- Normalize the exact historical default without carrying translated labels
2
+ -- into the SDK-owned persistence contract.
3
+ UPDATE projects
4
+ SET stages_json = '[{"key":"outcome_plan","labelOverride":null},{"key":"contract_check","labelOverride":null},{"key":"feature_link","labelOverride":null},{"key":"use_verify","labelOverride":null},{"key":"result_review","labelOverride":null}]'
5
+ WHERE json(stages_json) = json('[{"key":"outcome_plan","label":"결과·계획"},{"key":"contract_check","label":"계약 확인"},{"key":"feature_link","label":"기능 연결"},{"key":"use_verify","label":"사용 검증"},{"key":"result_review","label":"결과 확인"}]');
6
+ --> statement-breakpoint
7
+ -- Custom and mixed arrays retain their label text under the new property name.
8
+ UPDATE projects
9
+ SET stages_json = (
10
+ SELECT json_group_array(
11
+ CASE
12
+ WHEN json_type(value, '$.label') IS NULL THEN json(value)
13
+ ELSE json_set(
14
+ json_remove(json(value), '$.label'),
15
+ '$.labelOverride',
16
+ json(json_quote(json_extract(value, '$.label')))
17
+ )
18
+ END
19
+ )
20
+ FROM json_each(projects.stages_json)
21
+ )
22
+ WHERE json_valid(stages_json)
23
+ AND json_type(stages_json) = 'array'
24
+ AND json(stages_json) <> json('[{"key":"outcome_plan","label":"결과·계획"},{"key":"contract_check","label":"계약 확인"},{"key":"feature_link","label":"기능 연결"},{"key":"use_verify","label":"사용 검증"},{"key":"result_review","label":"결과 확인"}]')
25
+ AND EXISTS (
26
+ SELECT 1
27
+ FROM json_each(projects.stages_json)
28
+ WHERE json_type(value, '$.label') IS NOT NULL
29
+ );
@@ -0,0 +1,33 @@
1
+ CREATE TABLE IF NOT EXISTS `common_codes` (
2
+ `code_group` text NOT NULL,
3
+ `code` text NOT NULL,
4
+ `code_name` text,
5
+ `color` text DEFAULT 'gray' NOT NULL,
6
+ `sort_order` integer NOT NULL,
7
+ `workspace_id` text,
8
+ `is_builtin` integer DEFAULT 0 NOT NULL,
9
+ `is_archived` integer DEFAULT 0 NOT NULL,
10
+ `metadata` text,
11
+ `created_at` text NOT NULL,
12
+ `updated_at` text NOT NULL,
13
+ PRIMARY KEY(`code_group`, `code`),
14
+ CONSTRAINT "common_codes_metadata_ck" CHECK("common_codes"."metadata" IS NULL OR (json_valid("common_codes"."metadata") AND json_type("common_codes"."metadata") = 'object')),
15
+ CONSTRAINT "common_codes_builtin_ck" CHECK("common_codes"."is_builtin" IN (0, 1)),
16
+ CONSTRAINT "common_codes_archived_ck" CHECK("common_codes"."is_archived" IN (0, 1))
17
+ );
18
+ --> statement-breakpoint
19
+ CREATE INDEX IF NOT EXISTS `idx_common_codes_group_archived_order` ON `common_codes` (`code_group`,`is_archived`,`sort_order`);--> statement-breakpoint
20
+ INSERT OR IGNORE INTO `common_codes`
21
+ (`code_group`, `code`, `code_name`, `color`, `sort_order`, `workspace_id`, `is_builtin`, `is_archived`, `metadata`, `created_at`, `updated_at`)
22
+ VALUES
23
+ ('THREAD_PRIORITY', 'urgent', NULL, 'red', 0, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
24
+ ('THREAD_PRIORITY', 'high', NULL, 'orange', 1, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
25
+ ('THREAD_PRIORITY', 'medium', NULL, 'blue', 2, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
26
+ ('THREAD_PRIORITY', 'low', NULL, 'gray', 3, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
27
+ ('THREAD_STATE', 'investigating', NULL, 'violet', 0, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
28
+ ('THREAD_STATE', 'in-progress', NULL, 'blue', 1, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
29
+ ('THREAD_STATE', 'in-review', NULL, 'amber', 2, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
30
+ ('THREAD_STATE', 'on-hold', NULL, 'gray', 3, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
31
+ ('THREAD_STATE', 'done', NULL, 'green', 4, NULL, 1, 0, NULL, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));--> statement-breakpoint
32
+ CREATE INDEX IF NOT EXISTS `idx_threads_workspace_thread_state` ON `threads` (`workspace_id`, json_extract(metadata, '$.threadTitle.state'));--> statement-breakpoint
33
+ CREATE INDEX IF NOT EXISTS `idx_threads_workspace_thread_priority` ON `threads` (`workspace_id`, json_extract(metadata, '$.threadTitle.priority'));
@@ -0,0 +1,74 @@
1
+ -- Backfill the indexed thread_id before command-based legacy lookups are removed.
2
+ -- The command format stores a thread id as the next whitespace-delimited value
3
+ -- after --thread=. Quoted values are normalized because some older launchers
4
+ -- persisted argv strings with quotes. A prompt can also contain a documented
5
+ -- --thread= example, so the final marker is the actual CLI option appended by
6
+ -- the launcher.
7
+ WITH RECURSIVE thread_markers(id, command, marker_pos) AS (
8
+ SELECT
9
+ id,
10
+ command,
11
+ instr(command, '--thread=') AS marker_pos
12
+ FROM tasks
13
+ WHERE thread_id IS NULL
14
+ AND command IS NOT NULL
15
+ AND instr(command, '--thread=') > 0
16
+
17
+ UNION ALL
18
+
19
+ SELECT
20
+ id,
21
+ command,
22
+ marker_pos
23
+ + length('--thread=')
24
+ - 1
25
+ + instr(substr(command, marker_pos + length('--thread=')), '--thread=')
26
+ FROM thread_markers
27
+ WHERE instr(substr(command, marker_pos + length('--thread=')), '--thread=') > 0
28
+ ), last_markers AS (
29
+ SELECT id, command, MAX(marker_pos) AS marker_pos
30
+ FROM thread_markers
31
+ GROUP BY id, command
32
+ ), legacy_threads AS (
33
+ SELECT
34
+ id,
35
+ substr(command, marker_pos + length('--thread=')) AS tail
36
+ FROM last_markers
37
+ ), extracted AS (
38
+ SELECT
39
+ id,
40
+ trim(
41
+ CASE
42
+ WHEN instr(replace(replace(replace(tail, char(9), ' '), char(10), ' '), char(13), ' '), ' ') > 0
43
+ THEN substr(
44
+ replace(replace(replace(tail, char(9), ' '), char(10), ' '), char(13), ' '),
45
+ 1,
46
+ instr(replace(replace(replace(tail, char(9), ' '), char(10), ' '), char(13), ' '), ' ') - 1
47
+ )
48
+ ELSE replace(replace(replace(tail, char(9), ' '), char(10), ' '), char(13), ' ')
49
+ END
50
+ ) AS raw_thread_id
51
+ FROM legacy_threads
52
+ ), normalized AS (
53
+ SELECT
54
+ id,
55
+ CASE
56
+ WHEN substr(raw_thread_id, 1, 1) IN ('"', char(39))
57
+ AND substr(raw_thread_id, -1, 1) = substr(raw_thread_id, 1, 1)
58
+ THEN substr(raw_thread_id, 2, length(raw_thread_id) - 2)
59
+ ELSE raw_thread_id
60
+ END AS thread_id
61
+ FROM extracted
62
+ )
63
+ UPDATE tasks
64
+ SET thread_id = (
65
+ SELECT normalized.thread_id
66
+ FROM normalized
67
+ WHERE normalized.id = tasks.id
68
+ )
69
+ WHERE tasks.thread_id IS NULL
70
+ AND tasks.id IN (
71
+ SELECT id
72
+ FROM normalized
73
+ WHERE length(thread_id) > 0
74
+ );
@@ -0,0 +1,55 @@
1
+ CREATE TABLE IF NOT EXISTS `thread_events` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `thread_id` text NOT NULL,
4
+ `seq` integer NOT NULL,
5
+ `type` text NOT NULL,
6
+ `actor_type` text NOT NULL,
7
+ `payload_json` text NOT NULL,
8
+ `created_at` text NOT NULL,
9
+ `actor_id` text,
10
+ `task_id` text,
11
+ `highlight_id` text,
12
+ FOREIGN KEY (`thread_id`) REFERENCES `threads`(`id`) ON UPDATE no action ON DELETE cascade,
13
+ CONSTRAINT "thread_events_type_ck" CHECK("thread_events"."type" IN ('thread.snapshot.imported', 'thread.title.changed', 'thread.priority.changed', 'thread.state.changed', 'thread.highlight.created', 'thread.highlight.updated', 'thread.highlight.deleted')),
14
+ CONSTRAINT "thread_events_actor_type_ck" CHECK("thread_events"."actor_type" IN ('user', 'agent', 'system')),
15
+ CONSTRAINT "thread_events_payload_json_ck" CHECK(json_valid("thread_events"."payload_json") AND json_type("thread_events"."payload_json") = 'object')
16
+ );
17
+ --> statement-breakpoint
18
+ CREATE INDEX IF NOT EXISTS `idx_thread_events_thread_seq` ON `thread_events` (`thread_id`,`seq`);--> statement-breakpoint
19
+ CREATE INDEX IF NOT EXISTS `idx_thread_events_thread_created` ON `thread_events` (`thread_id`,`created_at`);--> statement-breakpoint
20
+ CREATE UNIQUE INDEX IF NOT EXISTS `thread_events_thread_seq_uq` ON `thread_events` (`thread_id`,`seq`);--> statement-breakpoint
21
+ CREATE TABLE IF NOT EXISTS `thread_highlights` (
22
+ `id` text PRIMARY KEY NOT NULL,
23
+ `thread_id` text NOT NULL,
24
+ `source` text NOT NULL,
25
+ `kind` text NOT NULL,
26
+ `status` text DEFAULT 'active' NOT NULL,
27
+ `text` text,
28
+ `covers_from_task_id` text NOT NULL,
29
+ `covers_to_task_id` text NOT NULL,
30
+ `revision` integer DEFAULT 0 NOT NULL,
31
+ `created_at` text NOT NULL,
32
+ `updated_at` text NOT NULL,
33
+ `agent_id` text,
34
+ `source_task_id` text,
35
+ `detector_version` text,
36
+ `content_fingerprint` text,
37
+ `dismissed_at` text,
38
+ FOREIGN KEY (`thread_id`) REFERENCES `threads`(`id`) ON UPDATE no action ON DELETE cascade,
39
+ CONSTRAINT "thread_highlights_source_ck" CHECK("thread_highlights"."source" IN ('auto', 'manual')),
40
+ CONSTRAINT "thread_highlights_kind_ck" CHECK("thread_highlights"."kind" IN ('decision', 'judgment', 'issue', 'result', 'pivot')),
41
+ CONSTRAINT "thread_highlights_status_ck" CHECK("thread_highlights"."status" IN ('active', 'dismissed')),
42
+ CONSTRAINT "thread_highlights_active_text_ck" CHECK("thread_highlights"."status" = 'dismissed' OR ("thread_highlights"."text" IS NOT NULL AND length("thread_highlights"."text") > 0)),
43
+ CONSTRAINT "thread_highlights_covers_ck" CHECK(length("thread_highlights"."covers_from_task_id") > 0 AND length("thread_highlights"."covers_to_task_id") > 0),
44
+ CONSTRAINT "thread_highlights_auto_key_ck" CHECK("thread_highlights"."source" = 'manual' OR ("thread_highlights"."source" = 'auto' AND "thread_highlights"."source_task_id" IS NOT NULL AND "thread_highlights"."detector_version" IS NOT NULL AND "thread_highlights"."content_fingerprint" IS NOT NULL AND length("thread_highlights"."source_task_id") > 0 AND length("thread_highlights"."detector_version") > 0 AND length("thread_highlights"."content_fingerprint") > 0))
45
+ );
46
+ --> statement-breakpoint
47
+ CREATE INDEX IF NOT EXISTS `idx_thread_highlights_thread_status_created` ON `thread_highlights` (`thread_id`,`status`,`created_at`);--> statement-breakpoint
48
+ CREATE INDEX IF NOT EXISTS `idx_thread_highlights_thread_covers` ON `thread_highlights` (`thread_id`,`covers_from_task_id`,`covers_to_task_id`);--> statement-breakpoint
49
+ CREATE UNIQUE INDEX IF NOT EXISTS `thread_highlights_auto_idempotency_uq` ON `thread_highlights` (`thread_id`,`source_task_id`,`detector_version`,`content_fingerprint`) WHERE "thread_highlights"."source" = 'auto';
50
+ --> statement-breakpoint
51
+ CREATE TRIGGER IF NOT EXISTS thread_events_no_update
52
+ BEFORE UPDATE ON thread_events
53
+ BEGIN
54
+ SELECT RAISE(ABORT, 'thread_events is append-only');
55
+ END;