verybot 0.1.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.
- package/README.md +167 -0
- package/dist/aliases/store.d.ts +21 -0
- package/dist/aliases/store.js +148 -0
- package/dist/aliases/types.d.ts +6 -0
- package/dist/aliases/types.js +1 -0
- package/dist/brain/agent-registry.d.ts +96 -0
- package/dist/brain/agent-registry.js +141 -0
- package/dist/brain/agent.d.ts +167 -0
- package/dist/brain/agent.js +932 -0
- package/dist/brain/channel-store.d.ts +27 -0
- package/dist/brain/channel-store.js +78 -0
- package/dist/brain/compaction.d.ts +37 -0
- package/dist/brain/compaction.js +214 -0
- package/dist/brain/context.d.ts +43 -0
- package/dist/brain/context.js +139 -0
- package/dist/brain/delegation-store.d.ts +33 -0
- package/dist/brain/delegation-store.js +106 -0
- package/dist/brain/loop.d.ts +24 -0
- package/dist/brain/loop.js +318 -0
- package/dist/brain/mcp-adapter.d.ts +43 -0
- package/dist/brain/mcp-adapter.js +244 -0
- package/dist/brain/memory-extractor.d.ts +26 -0
- package/dist/brain/memory-extractor.js +82 -0
- package/dist/brain/providers.d.ts +14 -0
- package/dist/brain/providers.js +85 -0
- package/dist/brain/queue.d.ts +18 -0
- package/dist/brain/queue.js +111 -0
- package/dist/brain/run-tools.d.ts +50 -0
- package/dist/brain/run-tools.js +136 -0
- package/dist/brain/session-key.d.ts +23 -0
- package/dist/brain/session-key.js +41 -0
- package/dist/brain/session-state.d.ts +36 -0
- package/dist/brain/session-state.js +51 -0
- package/dist/brain/session-store.d.ts +50 -0
- package/dist/brain/session-store.js +207 -0
- package/dist/brain/session.d.ts +32 -0
- package/dist/brain/session.js +75 -0
- package/dist/brain/task-subscriber.d.ts +56 -0
- package/dist/brain/task-subscriber.js +317 -0
- package/dist/brain/user-content.d.ts +16 -0
- package/dist/brain/user-content.js +32 -0
- package/dist/brain/utils.d.ts +4 -0
- package/dist/brain/utils.js +26 -0
- package/dist/brain/worker-coordinator.d.ts +25 -0
- package/dist/brain/worker-coordinator.js +83 -0
- package/dist/channels/commands.d.ts +50 -0
- package/dist/channels/commands.js +132 -0
- package/dist/channels/discord/channel.d.ts +29 -0
- package/dist/channels/discord/channel.js +159 -0
- package/dist/channels/discord/markdown.d.ts +19 -0
- package/dist/channels/discord/markdown.js +62 -0
- package/dist/channels/manager.d.ts +29 -0
- package/dist/channels/manager.js +100 -0
- package/dist/channels/slack/channel.d.ts +37 -0
- package/dist/channels/slack/channel.js +227 -0
- package/dist/channels/slack/markdown.d.ts +19 -0
- package/dist/channels/slack/markdown.js +62 -0
- package/dist/channels/specs.d.ts +32 -0
- package/dist/channels/specs.js +99 -0
- package/dist/channels/telegram/channel.d.ts +29 -0
- package/dist/channels/telegram/channel.js +182 -0
- package/dist/channels/telegram/markdown.d.ts +17 -0
- package/dist/channels/telegram/markdown.js +66 -0
- package/dist/channels/types.d.ts +26 -0
- package/dist/channels/types.js +1 -0
- package/dist/channels/whatsapp/channel.d.ts +34 -0
- package/dist/channels/whatsapp/channel.js +276 -0
- package/dist/channels/whatsapp/markdown.d.ts +20 -0
- package/dist/channels/whatsapp/markdown.js +51 -0
- package/dist/cli/claude-login.d.ts +5 -0
- package/dist/cli/claude-login.js +47 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +78 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.js +96 -0
- package/dist/computer/browser/actions.d.ts +31 -0
- package/dist/computer/browser/actions.js +148 -0
- package/dist/computer/browser/context-manager.d.ts +28 -0
- package/dist/computer/browser/context-manager.js +78 -0
- package/dist/computer/browser/manager.d.ts +91 -0
- package/dist/computer/browser/manager.js +344 -0
- package/dist/computer/browser/profile-badge.d.ts +13 -0
- package/dist/computer/browser/profile-badge.js +67 -0
- package/dist/computer/browser/screenshot.d.ts +5 -0
- package/dist/computer/browser/screenshot.js +21 -0
- package/dist/computer/browser/snapshot.d.ts +30 -0
- package/dist/computer/browser/snapshot.js +242 -0
- package/dist/computer/browser/tools.d.ts +5 -0
- package/dist/computer/browser/tools.js +167 -0
- package/dist/computer/browser/types.d.ts +26 -0
- package/dist/computer/browser/types.js +1 -0
- package/dist/computer/desktop/adapter.d.ts +25 -0
- package/dist/computer/desktop/adapter.js +11 -0
- package/dist/computer/desktop/macos.d.ts +24 -0
- package/dist/computer/desktop/macos.js +223 -0
- package/dist/computer/desktop/tools.d.ts +25 -0
- package/dist/computer/desktop/tools.js +114 -0
- package/dist/config/agent-config.d.ts +55 -0
- package/dist/config/agent-config.js +16 -0
- package/dist/config/model-catalog.d.ts +22 -0
- package/dist/config/model-catalog.js +112 -0
- package/dist/config/model-spec.d.ts +8 -0
- package/dist/config/model-spec.js +66 -0
- package/dist/config/store.d.ts +25 -0
- package/dist/config/store.js +143 -0
- package/dist/config.d.ts +110 -0
- package/dist/config.js +259 -0
- package/dist/control-ui/assets/index-Cbl7G5Sc.css +1 -0
- package/dist/control-ui/assets/index-Cu1P4C62.js +266 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
- package/dist/control-ui/index.html +14 -0
- package/dist/control-ui/vite.svg +1 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +11 -0
- package/dist/gateway/broadcast.d.ts +5 -0
- package/dist/gateway/broadcast.js +33 -0
- package/dist/gateway/methods/aliases.d.ts +17 -0
- package/dist/gateway/methods/aliases.js +22 -0
- package/dist/gateway/methods/chat.d.ts +33 -0
- package/dist/gateway/methods/chat.js +37 -0
- package/dist/gateway/methods/config.d.ts +14 -0
- package/dist/gateway/methods/config.js +24 -0
- package/dist/gateway/methods/models.d.ts +10 -0
- package/dist/gateway/methods/models.js +14 -0
- package/dist/gateway/methods/playbooks.d.ts +45 -0
- package/dist/gateway/methods/playbooks.js +488 -0
- package/dist/gateway/methods/prompt-templates.d.ts +27 -0
- package/dist/gateway/methods/prompt-templates.js +106 -0
- package/dist/gateway/methods/scheduler.d.ts +62 -0
- package/dist/gateway/methods/scheduler.js +129 -0
- package/dist/gateway/methods/sessions.d.ts +44 -0
- package/dist/gateway/methods/sessions.js +111 -0
- package/dist/gateway/methods/system.d.ts +12 -0
- package/dist/gateway/methods/system.js +39 -0
- package/dist/gateway/methods/tasks.d.ts +40 -0
- package/dist/gateway/methods/tasks.js +151 -0
- package/dist/gateway/methods/teams.d.ts +69 -0
- package/dist/gateway/methods/teams.js +376 -0
- package/dist/gateway/methods/tools.d.ts +6 -0
- package/dist/gateway/methods/tools.js +7 -0
- package/dist/gateway/methods/whatsapp.d.ts +19 -0
- package/dist/gateway/methods/whatsapp.js +35 -0
- package/dist/gateway/rpc.d.ts +38 -0
- package/dist/gateway/rpc.js +79 -0
- package/dist/gateway/server.d.ts +9 -0
- package/dist/gateway/server.js +137 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +254 -0
- package/dist/integrations/github.d.ts +7 -0
- package/dist/integrations/github.js +133 -0
- package/dist/integrations/mcp.d.ts +7 -0
- package/dist/integrations/mcp.js +106 -0
- package/dist/integrations/registry.d.ts +47 -0
- package/dist/integrations/registry.js +332 -0
- package/dist/integrations/scanner.d.ts +10 -0
- package/dist/integrations/scanner.js +122 -0
- package/dist/integrations/twitter.d.ts +10 -0
- package/dist/integrations/twitter.js +120 -0
- package/dist/integrations/types.d.ts +72 -0
- package/dist/integrations/types.js +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +104 -0
- package/dist/markdown/chunk.d.ts +9 -0
- package/dist/markdown/chunk.js +52 -0
- package/dist/markdown/ir.d.ts +37 -0
- package/dist/markdown/ir.js +529 -0
- package/dist/markdown/render.d.ts +22 -0
- package/dist/markdown/render.js +148 -0
- package/dist/markdown/table-render.d.ts +43 -0
- package/dist/markdown/table-render.js +219 -0
- package/dist/markdown/tables.d.ts +17 -0
- package/dist/markdown/tables.js +27 -0
- package/dist/memory/embedding.d.ts +16 -0
- package/dist/memory/embedding.js +66 -0
- package/dist/memory/explicit.d.ts +16 -0
- package/dist/memory/explicit.js +29 -0
- package/dist/memory/extractor.d.ts +13 -0
- package/dist/memory/extractor.js +82 -0
- package/dist/memory/search.d.ts +15 -0
- package/dist/memory/search.js +57 -0
- package/dist/memory/session-learning.d.ts +23 -0
- package/dist/memory/session-learning.js +55 -0
- package/dist/memory/store.d.ts +36 -0
- package/dist/memory/store.js +334 -0
- package/dist/memory/types.d.ts +9 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +28 -0
- package/dist/paths.js +48 -0
- package/dist/prompt-templates/builtins/index.d.ts +4 -0
- package/dist/prompt-templates/builtins/index.js +5 -0
- package/dist/prompt-templates/builtins/planner.d.ts +4 -0
- package/dist/prompt-templates/builtins/planner.js +77 -0
- package/dist/prompt-templates/store.d.ts +45 -0
- package/dist/prompt-templates/store.js +224 -0
- package/dist/prompt-templates/types.d.ts +10 -0
- package/dist/prompt-templates/types.js +1 -0
- package/dist/scheduler/connected-channels.d.ts +24 -0
- package/dist/scheduler/connected-channels.js +57 -0
- package/dist/scheduler/scheduler.d.ts +22 -0
- package/dist/scheduler/scheduler.js +132 -0
- package/dist/scheduler/store.d.ts +27 -0
- package/dist/scheduler/store.js +205 -0
- package/dist/scheduler/types.d.ts +29 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/security/command-validator.d.ts +22 -0
- package/dist/security/command-validator.js +160 -0
- package/dist/security/docker-sandbox.d.ts +48 -0
- package/dist/security/docker-sandbox.js +218 -0
- package/dist/security/env-filter.d.ts +8 -0
- package/dist/security/env-filter.js +41 -0
- package/dist/skills/loader.d.ts +33 -0
- package/dist/skills/loader.js +132 -0
- package/dist/skills/prompt.d.ts +6 -0
- package/dist/skills/prompt.js +17 -0
- package/dist/skills/read-tool.d.ts +7 -0
- package/dist/skills/read-tool.js +24 -0
- package/dist/skills/scanner.d.ts +6 -0
- package/dist/skills/scanner.js +73 -0
- package/dist/skills/types.d.ts +15 -0
- package/dist/skills/types.js +1 -0
- package/dist/tasks/inline-attachment-content.d.ts +9 -0
- package/dist/tasks/inline-attachment-content.js +64 -0
- package/dist/tasks/store.d.ts +112 -0
- package/dist/tasks/store.js +519 -0
- package/dist/tasks/types.d.ts +129 -0
- package/dist/tasks/types.js +80 -0
- package/dist/teams/status-config.d.ts +8 -0
- package/dist/teams/status-config.js +40 -0
- package/dist/teams/store.d.ts +111 -0
- package/dist/teams/store.js +671 -0
- package/dist/teams/types.d.ts +30 -0
- package/dist/teams/types.js +1 -0
- package/dist/tools/bash.d.ts +18 -0
- package/dist/tools/bash.js +64 -0
- package/dist/tools/channel-history.d.ts +10 -0
- package/dist/tools/channel-history.js +43 -0
- package/dist/tools/delegate.d.ts +20 -0
- package/dist/tools/delegate.js +299 -0
- package/dist/tools/fs.d.ts +4 -0
- package/dist/tools/fs.js +335 -0
- package/dist/tools/integration-toggle.d.ts +14 -0
- package/dist/tools/integration-toggle.js +47 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/memory.js +59 -0
- package/dist/tools/prompt-templates.d.ts +7 -0
- package/dist/tools/prompt-templates.js +133 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +9 -0
- package/dist/tools/schedule.d.ts +8 -0
- package/dist/tools/schedule.js +219 -0
- package/dist/tools/speak.d.ts +10 -0
- package/dist/tools/speak.js +56 -0
- package/dist/tools/tasks.d.ts +67 -0
- package/dist/tools/tasks.js +288 -0
- package/dist/tools/teams.d.ts +22 -0
- package/dist/tools/teams.js +470 -0
- package/dist/tools/web-fetch.d.ts +3 -0
- package/dist/tools/web-fetch.js +22 -0
- package/dist/tts/edge.d.ts +10 -0
- package/dist/tts/edge.js +60 -0
- package/dist/tts/speak.d.ts +12 -0
- package/dist/tts/speak.js +81 -0
- package/dist/tts/transcribe.d.ts +5 -0
- package/dist/tts/transcribe.js +40 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +22 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +13 -0
- package/package.json +102 -0
- package/verybot.js +2 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { mkdirSync } from "fs";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import Database from "better-sqlite3";
|
|
5
|
+
import { logger } from "../logger.js";
|
|
6
|
+
import { DEFAULT_TEAM_ID } from "../config/agent-config.js";
|
|
7
|
+
const DEFAULT_TASK_ACTOR = "system";
|
|
8
|
+
const HUMAN_TASK_ACTOR = "user";
|
|
9
|
+
const EMPTY_STATUS_POSITION = 0;
|
|
10
|
+
/**
|
|
11
|
+
* SQLite-backed persistence for team tasks.
|
|
12
|
+
* Shares the same DB file as MemoryStore, ScheduleStore, etc.
|
|
13
|
+
*/
|
|
14
|
+
export class TaskStore {
|
|
15
|
+
db;
|
|
16
|
+
constructor(db) {
|
|
17
|
+
this.db = db;
|
|
18
|
+
}
|
|
19
|
+
static async create(dbPath) {
|
|
20
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
21
|
+
const db = new Database(dbPath);
|
|
22
|
+
db.pragma("journal_mode = WAL");
|
|
23
|
+
const store = new TaskStore(db);
|
|
24
|
+
store.createSchema();
|
|
25
|
+
store.migrate();
|
|
26
|
+
return store;
|
|
27
|
+
}
|
|
28
|
+
createSchema() {
|
|
29
|
+
this.db.exec(`
|
|
30
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
31
|
+
id TEXT PRIMARY KEY,
|
|
32
|
+
team_id TEXT,
|
|
33
|
+
title TEXT NOT NULL,
|
|
34
|
+
description TEXT,
|
|
35
|
+
status TEXT NOT NULL DEFAULT 'todo',
|
|
36
|
+
assignee TEXT,
|
|
37
|
+
priority TEXT NOT NULL DEFAULT 'medium',
|
|
38
|
+
position INTEGER NOT NULL DEFAULT 0,
|
|
39
|
+
needs_human_review INTEGER NOT NULL DEFAULT 0,
|
|
40
|
+
last_processed_by TEXT,
|
|
41
|
+
last_processed_for_updated_at INTEGER,
|
|
42
|
+
updated_by TEXT,
|
|
43
|
+
created_at INTEGER NOT NULL,
|
|
44
|
+
updated_at INTEGER NOT NULL
|
|
45
|
+
);
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_team ON tasks(team_id);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee);
|
|
49
|
+
|
|
50
|
+
CREATE TABLE IF NOT EXISTS task_comments (
|
|
51
|
+
id TEXT PRIMARY KEY,
|
|
52
|
+
task_id TEXT NOT NULL,
|
|
53
|
+
content TEXT NOT NULL,
|
|
54
|
+
created_by TEXT NOT NULL,
|
|
55
|
+
updated_by TEXT NOT NULL,
|
|
56
|
+
created_at INTEGER NOT NULL,
|
|
57
|
+
updated_at INTEGER NOT NULL
|
|
58
|
+
);
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_task_comments_task ON task_comments(task_id);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_task_comments_created_at ON task_comments(created_at);
|
|
61
|
+
|
|
62
|
+
CREATE TABLE IF NOT EXISTS task_counters (
|
|
63
|
+
key TEXT PRIMARY KEY,
|
|
64
|
+
value INTEGER NOT NULL
|
|
65
|
+
);
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
/** Add position column for existing databases that lack it. */
|
|
69
|
+
migrate() {
|
|
70
|
+
const columns = this.db.pragma("table_info(tasks)");
|
|
71
|
+
const hasPosition = columns.some((c) => c.name === "position");
|
|
72
|
+
if (!hasPosition) {
|
|
73
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN position INTEGER NOT NULL DEFAULT 0");
|
|
74
|
+
// Backfill: assign positions based on created_at DESC per status
|
|
75
|
+
for (const status of ["backlog", "todo", "plan", "in_progress", "done", "archived"]) {
|
|
76
|
+
const rows = this.db
|
|
77
|
+
.prepare("SELECT id FROM tasks WHERE status = ? ORDER BY created_at DESC")
|
|
78
|
+
.all(status);
|
|
79
|
+
const stmt = this.db.prepare("UPDATE tasks SET position = ? WHERE id = ?");
|
|
80
|
+
rows.forEach((row, i) => stmt.run(i, row.id));
|
|
81
|
+
}
|
|
82
|
+
logger.info("Migrated tasks table: added position column");
|
|
83
|
+
}
|
|
84
|
+
const hasAttachments = columns.some((c) => c.name === "attachments");
|
|
85
|
+
if (!hasAttachments) {
|
|
86
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN attachments TEXT DEFAULT '[]'");
|
|
87
|
+
logger.info("Migrated tasks table: added attachments column");
|
|
88
|
+
}
|
|
89
|
+
const hasNeedsHumanReview = columns.some((c) => c.name === "needs_human_review");
|
|
90
|
+
if (!hasNeedsHumanReview) {
|
|
91
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN needs_human_review INTEGER NOT NULL DEFAULT 0");
|
|
92
|
+
logger.info("Migrated tasks table: added needs_human_review column");
|
|
93
|
+
}
|
|
94
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_human_review ON tasks(needs_human_review)");
|
|
95
|
+
// Backfill NULL team_id → "default"
|
|
96
|
+
const nullCount = this.db
|
|
97
|
+
.prepare(`SELECT COUNT(*) AS n FROM tasks WHERE team_id IS NULL`)
|
|
98
|
+
.get();
|
|
99
|
+
if (nullCount.n > 0) {
|
|
100
|
+
this.db.prepare(`UPDATE tasks SET team_id = ? WHERE team_id IS NULL`).run(DEFAULT_TEAM_ID);
|
|
101
|
+
logger.info(`Migrated ${nullCount.n} tasks: set NULL team_id to '${DEFAULT_TEAM_ID}'`);
|
|
102
|
+
}
|
|
103
|
+
// Add claimed_by / claimed_at columns for pull-based task subscriptions
|
|
104
|
+
const hasClaimedBy = columns.some((c) => c.name === "claimed_by");
|
|
105
|
+
if (!hasClaimedBy) {
|
|
106
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN claimed_by TEXT");
|
|
107
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN claimed_at INTEGER");
|
|
108
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_claimed ON tasks(claimed_by)");
|
|
109
|
+
logger.info("Migrated tasks table: added claimed_by, claimed_at columns");
|
|
110
|
+
}
|
|
111
|
+
// Add actor audit field for task updates.
|
|
112
|
+
const hasUpdatedBy = columns.some((c) => c.name === "updated_by");
|
|
113
|
+
if (!hasUpdatedBy) {
|
|
114
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN updated_by TEXT");
|
|
115
|
+
this.db.prepare("UPDATE tasks SET updated_by = ? WHERE updated_by IS NULL").run("legacy");
|
|
116
|
+
logger.info("Migrated tasks table: added updated_by column");
|
|
117
|
+
}
|
|
118
|
+
// Add processed-marker fields for subscription dedupe.
|
|
119
|
+
const hasLastProcessedBy = columns.some((c) => c.name === "last_processed_by");
|
|
120
|
+
if (!hasLastProcessedBy) {
|
|
121
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN last_processed_by TEXT");
|
|
122
|
+
logger.info("Migrated tasks table: added last_processed_by column");
|
|
123
|
+
}
|
|
124
|
+
const hasLastProcessedForUpdatedAt = columns.some((c) => c.name === "last_processed_for_updated_at");
|
|
125
|
+
if (!hasLastProcessedForUpdatedAt) {
|
|
126
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN last_processed_for_updated_at INTEGER");
|
|
127
|
+
logger.info("Migrated tasks table: added last_processed_for_updated_at column");
|
|
128
|
+
}
|
|
129
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_last_processed ON tasks(last_processed_by, last_processed_for_updated_at)");
|
|
130
|
+
this.ensureTaskIdCounter();
|
|
131
|
+
}
|
|
132
|
+
/** Initialize or bump task ID counter based on highest numeric task id in DB. */
|
|
133
|
+
ensureTaskIdCounter() {
|
|
134
|
+
const row = this.db
|
|
135
|
+
.prepare(`SELECT COALESCE(MAX(CAST(id AS INTEGER)), 0) AS max_id
|
|
136
|
+
FROM tasks
|
|
137
|
+
WHERE id <> '' AND id NOT GLOB '*[^0-9]*'`)
|
|
138
|
+
.get();
|
|
139
|
+
const nextId = Number(row.max_id) + 1;
|
|
140
|
+
const current = this.db
|
|
141
|
+
.prepare("SELECT value FROM task_counters WHERE key = 'task_id'")
|
|
142
|
+
.get();
|
|
143
|
+
if (!current) {
|
|
144
|
+
this.db
|
|
145
|
+
.prepare("INSERT INTO task_counters (key, value) VALUES ('task_id', ?)")
|
|
146
|
+
.run(nextId);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (current.value < nextId) {
|
|
150
|
+
this.db
|
|
151
|
+
.prepare("UPDATE task_counters SET value = ? WHERE key = 'task_id'")
|
|
152
|
+
.run(nextId);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Allocate next task ID as an incrementing number persisted in SQLite. */
|
|
156
|
+
nextTaskId() {
|
|
157
|
+
const next = this.db.transaction(() => {
|
|
158
|
+
const current = this.db
|
|
159
|
+
.prepare("SELECT value FROM task_counters WHERE key = 'task_id'")
|
|
160
|
+
.get();
|
|
161
|
+
const value = current?.value ?? 1;
|
|
162
|
+
if (!current) {
|
|
163
|
+
this.db
|
|
164
|
+
.prepare("INSERT INTO task_counters (key, value) VALUES ('task_id', ?)")
|
|
165
|
+
.run(value + 1);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
this.db
|
|
169
|
+
.prepare("UPDATE task_counters SET value = ? WHERE key = 'task_id'")
|
|
170
|
+
.run(value + 1);
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
})();
|
|
174
|
+
return String(next);
|
|
175
|
+
}
|
|
176
|
+
/** Get the next position value for a given status (appends to end). */
|
|
177
|
+
nextPosition(status) {
|
|
178
|
+
const row = this.db
|
|
179
|
+
.prepare("SELECT COALESCE(MAX(position), -1) + 1 AS next FROM tasks WHERE status = ?")
|
|
180
|
+
.get(status);
|
|
181
|
+
return row.next;
|
|
182
|
+
}
|
|
183
|
+
/** Get the next position value for a given status (prepends to top). */
|
|
184
|
+
topPosition(status) {
|
|
185
|
+
const row = this.db
|
|
186
|
+
.prepare("SELECT COALESCE(MIN(position), ?) - 1 AS next FROM tasks WHERE status = ?")
|
|
187
|
+
.get(EMPTY_STATUS_POSITION + 1, status);
|
|
188
|
+
return row.next;
|
|
189
|
+
}
|
|
190
|
+
create(input, options = {}) {
|
|
191
|
+
const now = Date.now();
|
|
192
|
+
const status = input.status ?? "todo";
|
|
193
|
+
const updatedBy = options.updatedBy ?? DEFAULT_TASK_ACTOR;
|
|
194
|
+
const task = {
|
|
195
|
+
id: this.nextTaskId(),
|
|
196
|
+
teamId: input.teamId || DEFAULT_TEAM_ID,
|
|
197
|
+
title: input.title,
|
|
198
|
+
description: input.description ?? null,
|
|
199
|
+
status,
|
|
200
|
+
assignee: input.assignee ?? null,
|
|
201
|
+
priority: input.priority ?? "medium",
|
|
202
|
+
position: this.nextPosition(status),
|
|
203
|
+
needsHumanReview: input.needsHumanReview ?? false,
|
|
204
|
+
attachments: input.attachments ?? [],
|
|
205
|
+
claimedBy: null,
|
|
206
|
+
claimedAt: null,
|
|
207
|
+
updatedBy,
|
|
208
|
+
createdAt: now,
|
|
209
|
+
updatedAt: now,
|
|
210
|
+
};
|
|
211
|
+
this.db
|
|
212
|
+
.prepare(`INSERT INTO tasks (id, team_id, title, description, status, assignee, priority, position, needs_human_review, attachments, claimed_by, claimed_at, updated_by, created_at, updated_at)
|
|
213
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
214
|
+
.run(task.id, task.teamId, task.title, task.description, task.status, task.assignee, task.priority, task.position, task.needsHumanReview ? 1 : 0, JSON.stringify(task.attachments), null, null, task.updatedBy, task.createdAt, task.updatedAt);
|
|
215
|
+
return task;
|
|
216
|
+
}
|
|
217
|
+
update(id, input, options = {}) {
|
|
218
|
+
const existing = this.getById(id);
|
|
219
|
+
if (!existing)
|
|
220
|
+
return null;
|
|
221
|
+
// When status changes, assign position at top of destination column
|
|
222
|
+
const statusChanged = input.status !== undefined && input.status !== existing.status;
|
|
223
|
+
const newPosition = statusChanged ? this.topPosition(input.status) : existing.position;
|
|
224
|
+
// Default behavior: clear claim on status changes so a new subscriber can pick it up.
|
|
225
|
+
// Strict subscription flows can opt out and release claim explicitly after run completion.
|
|
226
|
+
const clearClaimOnStatusChange = options.clearClaimOnStatusChange ?? true;
|
|
227
|
+
const clearClaim = statusChanged && clearClaimOnStatusChange;
|
|
228
|
+
const updatedBy = options.updatedBy ?? DEFAULT_TASK_ACTOR;
|
|
229
|
+
const isHumanUpdate = updatedBy === HUMAN_TASK_ACTOR;
|
|
230
|
+
const needsHumanReview = input.needsHumanReview !== undefined
|
|
231
|
+
? input.needsHumanReview
|
|
232
|
+
: isHumanUpdate
|
|
233
|
+
? false
|
|
234
|
+
: existing.needsHumanReview;
|
|
235
|
+
const updated = {
|
|
236
|
+
...existing,
|
|
237
|
+
...(input.title !== undefined && { title: input.title }),
|
|
238
|
+
...(input.description !== undefined && { description: input.description }),
|
|
239
|
+
...(input.status !== undefined && { status: input.status }),
|
|
240
|
+
...(input.assignee !== undefined && { assignee: input.assignee }),
|
|
241
|
+
...(input.priority !== undefined && { priority: input.priority }),
|
|
242
|
+
...(input.teamId !== undefined && { teamId: input.teamId || DEFAULT_TEAM_ID }),
|
|
243
|
+
...(input.attachments !== undefined && { attachments: input.attachments }),
|
|
244
|
+
needsHumanReview,
|
|
245
|
+
...(clearClaim && { claimedBy: null, claimedAt: null }),
|
|
246
|
+
updatedBy,
|
|
247
|
+
position: newPosition,
|
|
248
|
+
updatedAt: Date.now(),
|
|
249
|
+
};
|
|
250
|
+
this.db
|
|
251
|
+
.prepare(`UPDATE tasks SET team_id = ?, title = ?, description = ?, status = ?, assignee = ?, priority = ?, position = ?, needs_human_review = ?, attachments = ?, claimed_by = ?, claimed_at = ?, updated_by = ?, updated_at = ?
|
|
252
|
+
WHERE id = ?`)
|
|
253
|
+
.run(updated.teamId, updated.title, updated.description, updated.status, updated.assignee, updated.priority, updated.position, updated.needsHumanReview ? 1 : 0, JSON.stringify(updated.attachments), updated.claimedBy, updated.claimedAt, updated.updatedBy, updated.updatedAt, id);
|
|
254
|
+
return updated;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Bulk-reorder tasks within a status column. Sets positions 0, 1, 2, ...
|
|
258
|
+
* Reordering is a board-layout concern and must not mutate task freshness fields
|
|
259
|
+
* (for example updated_at), otherwise subscription workers re-run unchanged tasks.
|
|
260
|
+
*/
|
|
261
|
+
reorder(status, orderedIds) {
|
|
262
|
+
const stmt = this.db.prepare("UPDATE tasks SET position = ? WHERE id = ? AND status = ?");
|
|
263
|
+
const tx = this.db.transaction(() => {
|
|
264
|
+
orderedIds.forEach((id, i) => stmt.run(i, id, status));
|
|
265
|
+
});
|
|
266
|
+
tx();
|
|
267
|
+
}
|
|
268
|
+
delete(id) {
|
|
269
|
+
const info = this.db.transaction(() => {
|
|
270
|
+
this.db.prepare("DELETE FROM task_comments WHERE task_id = ?").run(id);
|
|
271
|
+
return this.db.prepare("DELETE FROM tasks WHERE id = ?").run(id);
|
|
272
|
+
})();
|
|
273
|
+
return info.changes > 0;
|
|
274
|
+
}
|
|
275
|
+
getById(id) {
|
|
276
|
+
const row = this.db.prepare("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
277
|
+
return row ? toTask(row) : null;
|
|
278
|
+
}
|
|
279
|
+
list(filter = {}) {
|
|
280
|
+
const conditions = [];
|
|
281
|
+
const params = [];
|
|
282
|
+
if (filter.teamId) {
|
|
283
|
+
conditions.push("team_id = ?");
|
|
284
|
+
params.push(filter.teamId);
|
|
285
|
+
}
|
|
286
|
+
if (filter.status) {
|
|
287
|
+
conditions.push("status = ?");
|
|
288
|
+
params.push(filter.status);
|
|
289
|
+
}
|
|
290
|
+
else if (!filter.includeArchived) {
|
|
291
|
+
conditions.push("status != 'archived'");
|
|
292
|
+
}
|
|
293
|
+
if (filter.assignee) {
|
|
294
|
+
conditions.push("assignee = ?");
|
|
295
|
+
params.push(filter.assignee);
|
|
296
|
+
}
|
|
297
|
+
if (filter.needsHumanReview !== undefined) {
|
|
298
|
+
conditions.push("needs_human_review = ?");
|
|
299
|
+
params.push(filter.needsHumanReview ? 1 : 0);
|
|
300
|
+
}
|
|
301
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
302
|
+
const rows = this.db
|
|
303
|
+
.prepare(`SELECT * FROM tasks ${where} ORDER BY position ASC`)
|
|
304
|
+
.all(...params);
|
|
305
|
+
return rows.map(toTask);
|
|
306
|
+
}
|
|
307
|
+
listComments(taskId) {
|
|
308
|
+
const rows = this.db
|
|
309
|
+
.prepare("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at ASC, id ASC")
|
|
310
|
+
.all(taskId);
|
|
311
|
+
return rows.map(toTaskComment);
|
|
312
|
+
}
|
|
313
|
+
addComment(taskId, content, options = {}) {
|
|
314
|
+
const task = this.getById(taskId);
|
|
315
|
+
if (!task)
|
|
316
|
+
return null;
|
|
317
|
+
const now = Date.now();
|
|
318
|
+
const actor = options.actor ?? DEFAULT_TASK_ACTOR;
|
|
319
|
+
const normalizedContent = content.trim();
|
|
320
|
+
const comment = {
|
|
321
|
+
id: randomUUID(),
|
|
322
|
+
taskId,
|
|
323
|
+
content: normalizedContent,
|
|
324
|
+
createdBy: actor,
|
|
325
|
+
updatedBy: actor,
|
|
326
|
+
createdAt: now,
|
|
327
|
+
updatedAt: now,
|
|
328
|
+
};
|
|
329
|
+
this.db.transaction(() => {
|
|
330
|
+
this.db
|
|
331
|
+
.prepare(`INSERT INTO task_comments (id, task_id, content, created_by, updated_by, created_at, updated_at)
|
|
332
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
333
|
+
.run(comment.id, comment.taskId, comment.content, comment.createdBy, comment.updatedBy, comment.createdAt, comment.updatedAt);
|
|
334
|
+
this.touchTaskAudit(taskId, actor, now);
|
|
335
|
+
})();
|
|
336
|
+
return comment;
|
|
337
|
+
}
|
|
338
|
+
updateComment(id, content, options = {}) {
|
|
339
|
+
const existing = this.getCommentById(id);
|
|
340
|
+
if (!existing)
|
|
341
|
+
return null;
|
|
342
|
+
const now = Date.now();
|
|
343
|
+
const actor = options.actor ?? DEFAULT_TASK_ACTOR;
|
|
344
|
+
const normalizedContent = content.trim();
|
|
345
|
+
const updated = {
|
|
346
|
+
...existing,
|
|
347
|
+
content: normalizedContent,
|
|
348
|
+
updatedBy: actor,
|
|
349
|
+
updatedAt: now,
|
|
350
|
+
};
|
|
351
|
+
this.db.transaction(() => {
|
|
352
|
+
this.db
|
|
353
|
+
.prepare("UPDATE task_comments SET content = ?, updated_by = ?, updated_at = ? WHERE id = ?")
|
|
354
|
+
.run(updated.content, updated.updatedBy, updated.updatedAt, id);
|
|
355
|
+
this.touchTaskAudit(existing.taskId, actor, now);
|
|
356
|
+
})();
|
|
357
|
+
return updated;
|
|
358
|
+
}
|
|
359
|
+
deleteComment(id, options = {}) {
|
|
360
|
+
const existing = this.getCommentById(id);
|
|
361
|
+
if (!existing)
|
|
362
|
+
return null;
|
|
363
|
+
const now = Date.now();
|
|
364
|
+
const actor = options.actor ?? DEFAULT_TASK_ACTOR;
|
|
365
|
+
const info = this.db.transaction(() => {
|
|
366
|
+
const deleteInfo = this.db.prepare("DELETE FROM task_comments WHERE id = ?").run(id);
|
|
367
|
+
if (deleteInfo.changes > 0) {
|
|
368
|
+
this.touchTaskAudit(existing.taskId, actor, now);
|
|
369
|
+
}
|
|
370
|
+
return deleteInfo;
|
|
371
|
+
})();
|
|
372
|
+
return info.changes > 0 ? existing : null;
|
|
373
|
+
}
|
|
374
|
+
/** Bulk-archive all tasks currently in the provided completion status, optionally scoped to a team. */
|
|
375
|
+
archiveDone(teamId, doneStatus = "done", updatedBy = DEFAULT_TASK_ACTOR) {
|
|
376
|
+
const STATUS_KEY_RE = /^\w+$/;
|
|
377
|
+
if (!STATUS_KEY_RE.test(doneStatus)) {
|
|
378
|
+
throw new Error(`Invalid status key: ${doneStatus}`);
|
|
379
|
+
}
|
|
380
|
+
const now = Date.now();
|
|
381
|
+
if (teamId) {
|
|
382
|
+
const info = this.db
|
|
383
|
+
.prepare("UPDATE tasks SET status = 'archived', updated_by = ?, updated_at = ? WHERE status = ? AND team_id = ?")
|
|
384
|
+
.run(updatedBy, now, doneStatus, teamId);
|
|
385
|
+
return info.changes;
|
|
386
|
+
}
|
|
387
|
+
const info = this.db
|
|
388
|
+
.prepare("UPDATE tasks SET status = 'archived', updated_by = ?, updated_at = ? WHERE status = ?")
|
|
389
|
+
.run(updatedBy, now, doneStatus);
|
|
390
|
+
return info.changes;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Atomically claim the oldest unclaimed task matching the given status for an agent.
|
|
394
|
+
* Respects assignee: assigned tasks only go to that agent, unassigned to any subscriber.
|
|
395
|
+
*/
|
|
396
|
+
claimTask(agentId, status) {
|
|
397
|
+
return this.db.transaction(() => {
|
|
398
|
+
const row = this.db.prepare(`SELECT * FROM tasks
|
|
399
|
+
WHERE status = ? AND claimed_by IS NULL AND needs_human_review = 0
|
|
400
|
+
AND (assignee IS NULL OR assignee = ?)
|
|
401
|
+
AND (
|
|
402
|
+
last_processed_by IS NULL
|
|
403
|
+
OR last_processed_for_updated_at IS NULL
|
|
404
|
+
OR last_processed_by != ?
|
|
405
|
+
OR last_processed_for_updated_at != updated_at
|
|
406
|
+
)
|
|
407
|
+
ORDER BY
|
|
408
|
+
CASE WHEN assignee = ? THEN 0 ELSE 1 END,
|
|
409
|
+
created_at ASC
|
|
410
|
+
LIMIT 1`).get(status, agentId, agentId, agentId);
|
|
411
|
+
if (!row)
|
|
412
|
+
return null;
|
|
413
|
+
const now = Date.now();
|
|
414
|
+
this.db.prepare("UPDATE tasks SET claimed_by = ?, claimed_at = ? WHERE id = ?").run(agentId, now, row.id);
|
|
415
|
+
const task = toTask(row);
|
|
416
|
+
task.claimedBy = agentId;
|
|
417
|
+
task.claimedAt = now;
|
|
418
|
+
return task;
|
|
419
|
+
})();
|
|
420
|
+
}
|
|
421
|
+
/** Atomically claim a specific task by ID for an agent. Returns the task if claimed, null if already taken. */
|
|
422
|
+
claimTaskById(taskId, agentId) {
|
|
423
|
+
const now = Date.now();
|
|
424
|
+
const info = this.db.prepare("UPDATE tasks SET claimed_by = ?, claimed_at = ? WHERE id = ? AND claimed_by IS NULL AND needs_human_review = 0").run(agentId, now, taskId);
|
|
425
|
+
if (info.changes === 0)
|
|
426
|
+
return null;
|
|
427
|
+
return this.getById(taskId);
|
|
428
|
+
}
|
|
429
|
+
/** Release a claimed task so it can be retried. */
|
|
430
|
+
releaseTask(taskId) {
|
|
431
|
+
this.db.prepare("UPDATE tasks SET claimed_by = NULL, claimed_at = NULL WHERE id = ?").run(taskId);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Release claim only when held by the provided agent id.
|
|
435
|
+
* Returns true when a claim was released.
|
|
436
|
+
*/
|
|
437
|
+
releaseTaskIfClaimedBy(taskId, agentId) {
|
|
438
|
+
const info = this.db.prepare("UPDATE tasks SET claimed_by = NULL, claimed_at = NULL WHERE id = ? AND claimed_by = ?").run(taskId, agentId);
|
|
439
|
+
return info.changes > 0;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Mark a claimed task as processed by the given agent for the current task version,
|
|
443
|
+
* then release the claim atomically.
|
|
444
|
+
* Returns true when the task was finalized by the expected claim owner.
|
|
445
|
+
*/
|
|
446
|
+
finalizeClaimedTaskRun(taskId, agentId) {
|
|
447
|
+
const info = this.db.prepare(`UPDATE tasks
|
|
448
|
+
SET last_processed_by = ?,
|
|
449
|
+
last_processed_for_updated_at = updated_at,
|
|
450
|
+
claimed_by = NULL,
|
|
451
|
+
claimed_at = NULL
|
|
452
|
+
WHERE id = ? AND claimed_by = ?`).run(agentId, taskId, agentId);
|
|
453
|
+
return info.changes > 0;
|
|
454
|
+
}
|
|
455
|
+
/** Release claims older than the given cutoff timestamp. Returns count of released claims. */
|
|
456
|
+
cleanupStaleClaims(cutoffTimestamp) {
|
|
457
|
+
const info = this.db.prepare("UPDATE tasks SET claimed_by = NULL, claimed_at = NULL WHERE claimed_by IS NOT NULL AND claimed_at < ?").run(cutoffTimestamp);
|
|
458
|
+
return info.changes;
|
|
459
|
+
}
|
|
460
|
+
close() {
|
|
461
|
+
this.db.close();
|
|
462
|
+
logger.info("Task store closed");
|
|
463
|
+
}
|
|
464
|
+
getCommentById(id) {
|
|
465
|
+
const row = this.db.prepare("SELECT * FROM task_comments WHERE id = ?").get(id);
|
|
466
|
+
return row ? toTaskComment(row) : null;
|
|
467
|
+
}
|
|
468
|
+
touchTaskAudit(taskId, updatedBy, updatedAt) {
|
|
469
|
+
if (updatedBy === HUMAN_TASK_ACTOR) {
|
|
470
|
+
this.db
|
|
471
|
+
.prepare("UPDATE tasks SET updated_by = ?, updated_at = ?, needs_human_review = 0 WHERE id = ?")
|
|
472
|
+
.run(updatedBy, updatedAt, taskId);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
this.db
|
|
476
|
+
.prepare("UPDATE tasks SET updated_by = ?, updated_at = ? WHERE id = ?")
|
|
477
|
+
.run(updatedBy, updatedAt, taskId);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function parseAttachments(raw) {
|
|
481
|
+
if (typeof raw !== "string" || !raw)
|
|
482
|
+
return [];
|
|
483
|
+
try {
|
|
484
|
+
return JSON.parse(raw);
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
return [];
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function toTask(row) {
|
|
491
|
+
return {
|
|
492
|
+
id: row.id,
|
|
493
|
+
teamId: row.team_id || DEFAULT_TEAM_ID,
|
|
494
|
+
title: row.title,
|
|
495
|
+
description: row.description ?? null,
|
|
496
|
+
status: row.status,
|
|
497
|
+
assignee: row.assignee ?? null,
|
|
498
|
+
priority: row.priority,
|
|
499
|
+
position: row.position ?? 0,
|
|
500
|
+
attachments: parseAttachments(row.attachments),
|
|
501
|
+
needsHumanReview: Number(row.needs_human_review ?? 0) === 1,
|
|
502
|
+
claimedBy: row.claimed_by ?? null,
|
|
503
|
+
claimedAt: row.claimed_at ?? null,
|
|
504
|
+
updatedBy: row.updated_by ?? null,
|
|
505
|
+
createdAt: row.created_at,
|
|
506
|
+
updatedAt: row.updated_at,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function toTaskComment(row) {
|
|
510
|
+
return {
|
|
511
|
+
id: row.id,
|
|
512
|
+
taskId: row.task_id,
|
|
513
|
+
content: row.content,
|
|
514
|
+
createdBy: row.created_by,
|
|
515
|
+
updatedBy: row.updated_by,
|
|
516
|
+
createdAt: row.created_at,
|
|
517
|
+
updatedAt: row.updated_at,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Max upload file size: 10 MB */
|
|
3
|
+
export declare const MAX_FILE_SIZE: number;
|
|
4
|
+
export declare const ALLOWED_IMAGE_TYPES: Set<string>;
|
|
5
|
+
export declare const EXT_FOR_MIME: Record<string, string>;
|
|
6
|
+
/** Legacy list of default status keys — used only for migration backfills. */
|
|
7
|
+
export declare const LEGACY_STATUS_KEYS: readonly ["backlog", "todo", "plan", "in_progress", "done", "archived"];
|
|
8
|
+
/** Status is now a free-form string to support per-team custom statuses. */
|
|
9
|
+
export type TaskStatus = string;
|
|
10
|
+
export declare const TASK_PRIORITIES: readonly ["low", "medium", "high"];
|
|
11
|
+
export type TaskPriority = (typeof TASK_PRIORITIES)[number];
|
|
12
|
+
import type { TaskStatusConfig } from "../config/agent-config.js";
|
|
13
|
+
export type { TaskStatusConfig } from "../config/agent-config.js";
|
|
14
|
+
/** Default statuses used when a team has no custom config. */
|
|
15
|
+
export declare const DEFAULT_TASK_STATUSES: TaskStatusConfig[];
|
|
16
|
+
export interface TaskAttachment {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
type: string;
|
|
20
|
+
size: number;
|
|
21
|
+
createdAt: number;
|
|
22
|
+
}
|
|
23
|
+
export interface TaskComment {
|
|
24
|
+
id: string;
|
|
25
|
+
taskId: string;
|
|
26
|
+
content: string;
|
|
27
|
+
createdBy: string;
|
|
28
|
+
updatedBy: string;
|
|
29
|
+
createdAt: number;
|
|
30
|
+
updatedAt: number;
|
|
31
|
+
}
|
|
32
|
+
export interface Task {
|
|
33
|
+
id: string;
|
|
34
|
+
teamId: string;
|
|
35
|
+
title: string;
|
|
36
|
+
description: string | null;
|
|
37
|
+
status: TaskStatus;
|
|
38
|
+
assignee: string | null;
|
|
39
|
+
priority: TaskPriority;
|
|
40
|
+
position: number;
|
|
41
|
+
attachments: TaskAttachment[];
|
|
42
|
+
/** True when the task is waiting on a human to review or act. */
|
|
43
|
+
needsHumanReview: boolean;
|
|
44
|
+
/** Agent ID that claimed this task for pull-based execution. */
|
|
45
|
+
claimedBy: string | null;
|
|
46
|
+
/** Timestamp when the task was claimed. */
|
|
47
|
+
claimedAt: number | null;
|
|
48
|
+
/** Last actor that updated this task (user, orchestrator, worker, system). */
|
|
49
|
+
updatedBy: string | null;
|
|
50
|
+
createdAt: number;
|
|
51
|
+
updatedAt: number;
|
|
52
|
+
}
|
|
53
|
+
export declare const MAX_TASK_COMMENT_LENGTH = 4000;
|
|
54
|
+
export declare const CreateTaskSchema: z.ZodObject<{
|
|
55
|
+
title: z.ZodString;
|
|
56
|
+
description: z.ZodOptional<z.ZodString>;
|
|
57
|
+
teamId: z.ZodOptional<z.ZodString>;
|
|
58
|
+
assignee: z.ZodOptional<z.ZodString>;
|
|
59
|
+
priority: z.ZodDefault<z.ZodEnum<{
|
|
60
|
+
low: "low";
|
|
61
|
+
medium: "medium";
|
|
62
|
+
high: "high";
|
|
63
|
+
}>>;
|
|
64
|
+
status: z.ZodDefault<z.ZodString>;
|
|
65
|
+
needsHumanReview: z.ZodDefault<z.ZodBoolean>;
|
|
66
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
67
|
+
id: z.ZodString;
|
|
68
|
+
name: z.ZodString;
|
|
69
|
+
type: z.ZodEnum<{
|
|
70
|
+
"image/png": "image/png";
|
|
71
|
+
"image/jpeg": "image/jpeg";
|
|
72
|
+
"image/gif": "image/gif";
|
|
73
|
+
"image/webp": "image/webp";
|
|
74
|
+
}>;
|
|
75
|
+
size: z.ZodNumber;
|
|
76
|
+
createdAt: z.ZodNumber;
|
|
77
|
+
}, z.core.$strip>>>;
|
|
78
|
+
}, z.core.$strip>;
|
|
79
|
+
export declare const UpdateTaskSchema: z.ZodObject<{
|
|
80
|
+
id: z.ZodString;
|
|
81
|
+
title: z.ZodOptional<z.ZodString>;
|
|
82
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
83
|
+
status: z.ZodOptional<z.ZodString>;
|
|
84
|
+
assignee: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
85
|
+
priority: z.ZodOptional<z.ZodEnum<{
|
|
86
|
+
low: "low";
|
|
87
|
+
medium: "medium";
|
|
88
|
+
high: "high";
|
|
89
|
+
}>>;
|
|
90
|
+
teamId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
91
|
+
needsHumanReview: z.ZodOptional<z.ZodBoolean>;
|
|
92
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
93
|
+
id: z.ZodString;
|
|
94
|
+
name: z.ZodString;
|
|
95
|
+
type: z.ZodEnum<{
|
|
96
|
+
"image/png": "image/png";
|
|
97
|
+
"image/jpeg": "image/jpeg";
|
|
98
|
+
"image/gif": "image/gif";
|
|
99
|
+
"image/webp": "image/webp";
|
|
100
|
+
}>;
|
|
101
|
+
size: z.ZodNumber;
|
|
102
|
+
createdAt: z.ZodNumber;
|
|
103
|
+
}, z.core.$strip>>>;
|
|
104
|
+
}, z.core.$strip>;
|
|
105
|
+
export declare const ListTasksSchema: z.ZodObject<{
|
|
106
|
+
teamId: z.ZodOptional<z.ZodString>;
|
|
107
|
+
status: z.ZodOptional<z.ZodString>;
|
|
108
|
+
assignee: z.ZodOptional<z.ZodString>;
|
|
109
|
+
needsHumanReview: z.ZodOptional<z.ZodBoolean>;
|
|
110
|
+
includeArchived: z.ZodOptional<z.ZodBoolean>;
|
|
111
|
+
}, z.core.$strip>;
|
|
112
|
+
export declare const ReorderTasksSchema: z.ZodObject<{
|
|
113
|
+
status: z.ZodString;
|
|
114
|
+
orderedIds: z.ZodArray<z.ZodString>;
|
|
115
|
+
}, z.core.$strip>;
|
|
116
|
+
export declare const ListTaskCommentsSchema: z.ZodObject<{
|
|
117
|
+
taskId: z.ZodString;
|
|
118
|
+
}, z.core.$strip>;
|
|
119
|
+
export declare const AddTaskCommentSchema: z.ZodObject<{
|
|
120
|
+
taskId: z.ZodString;
|
|
121
|
+
content: z.ZodString;
|
|
122
|
+
}, z.core.$strip>;
|
|
123
|
+
export declare const UpdateTaskCommentSchema: z.ZodObject<{
|
|
124
|
+
id: z.ZodString;
|
|
125
|
+
content: z.ZodString;
|
|
126
|
+
}, z.core.$strip>;
|
|
127
|
+
export declare const DeleteTaskCommentSchema: z.ZodObject<{
|
|
128
|
+
id: z.ZodString;
|
|
129
|
+
}, z.core.$strip>;
|