dsh-taskboard 0.6.6 → 0.6.7
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 +301 -294
- package/lib/client.js +937 -931
- package/lib/host/archive-sessions.js +30 -0
- package/lib/host/archive-sessions.js.map +1 -0
- package/lib/host/execution.js +3 -0
- package/lib/host/execution.js.map +1 -1
- package/lib/host/locale.js +17 -0
- package/lib/host/locale.js.map +1 -0
- package/lib/host/routes.js +45 -6
- package/lib/host/routes.js.map +1 -1
- package/lib/host/scheduler.js +2 -0
- package/lib/host/scheduler.js.map +1 -1
- package/lib/host/session-sync.js +4 -1
- package/lib/host/session-sync.js.map +1 -1
- package/lib/host/templates.js +11 -75
- package/lib/host/templates.js.map +1 -1
- package/lib/host/tools.js +17 -4
- package/lib/host/tools.js.map +1 -1
- package/lib/shared/api.js.map +1 -1
- package/lib/shared/builtin-templates.js +155 -0
- package/lib/shared/builtin-templates.js.map +1 -0
- package/lib/shared/protocol.js +39 -2
- package/lib/shared/protocol.js.map +1 -1
- package/package.json +90 -90
- package/src/client/api.ts +5 -1
- package/src/client/board/TaskBoard.tsx +16 -11
- package/src/client/board/TaskDetail.tsx +116 -10
- package/src/client/board/TemplateManager.tsx +22 -10
- package/src/client/controller.ts +23 -3
- package/src/client/i18n/en.ts +18 -0
- package/src/client/i18n/templates.ts +25 -0
- package/src/client/i18n/zh.ts +18 -0
- package/src/client/styles.ts +1 -1
- package/src/host/archive-sessions.ts +18 -0
- package/src/host/execution.ts +4 -1
- package/src/host/locale.ts +44 -0
- package/src/host/routes.ts +40 -7
- package/src/host/scheduler.ts +2 -0
- package/src/host/session-sync.ts +4 -1
- package/src/host/templates.ts +8 -59
- package/src/host/tools.ts +20 -7
- package/src/shared/api.ts +6 -3
- package/src/shared/builtin-templates.ts +153 -0
- package/src/shared/protocol.ts +64 -0
- package/src/shared/version.ts +9 -9
package/lib/host/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","names":["v"],"sources":["../../src/host/tools.ts"],"sourcesContent":["/**\n * The ten `taskboard_*` agent tools. All writes require a calling agent\n * session (ownership audit), carry optimistic-version checks, and enforce\n * the protocol gates in CODE, not in prompt text:\n *\n * - `move → done` is rejected for agent callers (user confirmation only)\n * - `move todo → in_progress` requires the calling session's workspace to\n * match the task's project (claim boundary)\n * - taking over a task held by another session is rejected\n * - `delete` for agent callers only sets the soft-delete marker\n * - checklist items may be added/checked by agents, but checking never\n * completes the task (done stays user-only)\n *\n * OUTPUT CONTRACT (lesson: registry `createSuccessResult` renders\n * `output.render(args, value)` into `result.content`, and the loop feeds\n * exactly that content to the model — the raw JSON `value` never reaches\n * the model): render() IS the model-facing tool result. Every render must\n * carry the complete facts an agent needs to act (ids, versions, statuses);\n * a \"terse UI summary\" here starves the agent.\n *\n * @module dsh-taskboard/host/tools\n */\nimport type { WorkspaceRegistry } from '@deepseek-ai/dsh-workspace'\nimport { defineTool } from './sdk.ts'\nimport {\n MAX_CHECKLIST_ITEMS,\n asIsolation,\n asStatus,\n asUrgency,\n canTransition,\n checklistFromTexts,\n defaultIsolationOf,\n effectivePrompt,\n isClaim,\n isClaimedBy,\n newChecklistItemId,\n newCommentId,\n newTaskId,\n normalizeBody,\n normalizeExecution,\n normalizeExecutionReport,\n normalizeModel,\n normalizePrompt,\n normalizeTitle,\n summarize,\n syncClaim,\n type Actor,\n type ChecklistItem,\n type TaskLedger,\n type TaskModel,\n type TaskRecord,\n} from '../shared/protocol.ts'\nimport type { TaskStore } from './store.ts'\n\n/** Render side: one compact task line (id/status/version are load-bearing). */\nfunction taskLine(t: {\n id: string\n title: string\n status: string\n urgency: string\n version: number\n workspaceId: string\n blocked: boolean\n executionMode: string\n commentCount?: number\n lastExecutionOutcome?: string\n checklist?: { done: number; total: number }\n trashed?: boolean\n}): string {\n const parts = [\n `- ${t.id} [${t.status}] v${t.version} · ${t.urgency} · 项目 ${t.workspaceId}`,\n `「${t.title}」`,\n ]\n if (t.blocked) parts.push('·受阻')\n if (t.executionMode === 'scheduled') parts.push('·定时')\n if (t.commentCount !== undefined && t.commentCount > 0) parts.push(`·评论${t.commentCount}`)\n if (t.checklist !== undefined && t.checklist.total > 0) parts.push(`·清单${t.checklist.done}/${t.checklist.total}`)\n if (t.lastExecutionOutcome !== undefined) parts.push(`·上次执行${t.lastExecutionOutcome}`)\n if (t.trashed === true) parts.push('·已删')\n return parts.join(' ')\n}\n\n/** Render side: the full task detail block (everything an executor needs). */\nfunction taskDetail(t: TaskRecord & { effectivePrompt?: string }): string {\n const lines: string[] = [\n `任务 ${t.id} 「${t.title}」`,\n `状态: ${t.status} (v${t.version}) · 紧急度: ${t.urgency} · 项目: ${t.workspaceId}${t.blocked ? ' · 受阻' : ''}`,\n `执行方式: ${t.execution.mode}${t.execution.cron !== undefined ? ` cron=${t.execution.cron}` : ''}`,\n `隔离: ${t.isolation === 'none' ? '关闭(原目录执行)' : 'Git Worktree'}${t.branch !== undefined ? `(分支 ${t.branch})` : ''}${t.branches !== undefined ? `(多仓库镜像 ${Object.keys(t.branches).length + (t.branch !== undefined ? 1 : 0)} 个仓库)` : ''}`,\n ]\n const holder = isClaimedBy(t)\n if (holder !== undefined) lines.push(`认领: agent ${String(holder).slice(0, 24)}(持有期间其他会话不可移动)`)\n if (t.execution.nextRunAt !== undefined) lines.push(`下次触发: ${new Date(t.execution.nextRunAt).toISOString()}`)\n if (t.model !== undefined) lines.push(`固定模型: ${t.model.provider}/${t.model.model}${t.model.reasoningEffort !== undefined ? ` (思考强度: ${t.model.reasoningEffort})` : ''}`)\n if (t.presetId !== undefined) lines.push(`执行模式: ${t.presetId}(未指定时为部署默认 preset)`)\n lines.push(`描述: ${t.description.length > 0 ? t.description : '(无)'}`)\n lines.push(`执行 Prompt: ${t.effectivePrompt ?? effectivePrompt(t)}`)\n if (t.checklist !== undefined && t.checklist.length > 0) {\n const done = t.checklist.filter(i => i.checked).length\n lines.push(`验收清单 (${done}/${t.checklist.length}):`)\n for (const [index, item] of t.checklist.entries()) {\n const mark = item.checked ? '☑' : '☐'\n const who = item.checkedBy === undefined ? '' : item.checkedBy === 'user' ? ' ·用户勾选' : ` ·agent ${String(item.checkedBy).slice(0, 24)}勾选`\n const note = item.note !== undefined ? ` ·证据: ${item.note}` : ''\n // Carry the checklist item id so `taskboard_checklist check/uncheck` can\n // address it without guessing — a terse render starves the agent (render\n // is fed to the model as result.content). Mirrors the index+id carried\n // by the taskboard_checklist tool output.\n lines.push(` ${mark} [${index + 1}] ${item.text}${who}${note} id=${item.id}`)\n }\n }\n if (t.comments.length > 0) {\n lines.push(`评论 (${t.comments.length}):`)\n for (const c of t.comments) {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n lines.push(` - [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`)\n }\n } else {\n lines.push('评论: 无')\n }\n if (t.executions.length > 0) {\n lines.push(`执行记录 (${t.executions.length}):`)\n for (const e of t.executions) {\n const at = e.startedAt !== undefined ? new Date(e.startedAt).toISOString() : '?'\n const err = e.error !== undefined ? ` 错误: ${e.error}` : ''\n const report = e.report !== undefined ? ' [已交报告]' : ''\n lines.push(` - [${e.trigger} ${at}] ${e.outcome}${report}${err}`)\n }\n } else {\n lines.push('执行记录: 无')\n }\n const updatedBy = t.updatedBy.kind === 'agent' ? `agent ${String(t.updatedBy.sessionId).slice(0, 24)}` : t.updatedBy.kind === 'system' ? 'system' : 'user'\n lines.push(`更新: ${new Date(t.updatedAt).toISOString()} 由 ${updatedBy}`)\n return lines.join('\\n')\n}\n\n/** Stable error codes surfaced at the head of tool error messages. */\nexport const ERR = {\n notFound: 'not_found',\n versionConflict: 'version_conflict',\n workspaceMismatch: 'workspace_mismatch',\n invalidTransition: 'invalid_transition',\n forbidden: 'forbidden',\n requiresAgent: 'unauthorized_actor',\n invalidInput: 'invalid_input',\n} as const\n\n/** Tool failure: an Error whose message starts with a stable code. The code\n * is also carried structurally so the routes layer can map failures without\n * re-parsing messages (review P2). */\nexport class ToolError extends Error {\n constructor(readonly code: string, detail: string) {\n super(`Error: ${code}: ${detail}`)\n }\n}\n\n/** The workspace face the tools need (narrow for tests). */\nexport interface WorkspaceFace {\n /** Resolve the workspace owning a canonical cwd, if any. */\n resolveByPath(path: string): Promise<{ id: string } | undefined>\n /** Get a workspace by id. */\n get(id: string): { id: string; path: string; title: string } | undefined\n /** List all workspaces. */\n list(): Array<{ id: string; path: string; title: string }>\n}\n\n/** Adapt the real registry to the narrow face. */\nexport function workspaceFace(registry: WorkspaceRegistry): WorkspaceFace {\n // Explicit field mapping: Workspace entities expose path/title as prototype\n // getters, which JSON.stringify skips (own enumerable properties only).\n return {\n resolveByPath: async (path) => {\n const ws = await registry.resolveByPath(path as never)\n return ws === undefined ? undefined : { id: ws.id }\n },\n get: id => {\n const ws = registry.get(id as never)\n return ws === undefined ? undefined : { id: ws.id, path: ws.path, title: ws.title }\n },\n list: () => registry.list().map(ws => ({ id: ws.id, path: ws.path, title: ws.title })),\n }\n}\n\n/** Everything the tool set needs. */\nexport interface ToolDeps {\n store: TaskStore\n workspaces: WorkspaceFace\n /** Current epoch ms (injectable for tests). */\n now: () => number\n /**\n * Registered model provider routes (from the host llm runtime), for\n * advisory validation of pinned models; undefined = runtime unavailable,\n * in which case only the structural check applies.\n */\n modelProviders?: () => string[] | undefined\n}\n\n/** Validate a pinned model: structural check always, provider route when known. */\nfunction checkModel(deps: ToolDeps, raw: unknown): TaskModel {\n const model = normalizeModel(raw)\n const providers = deps.modelProviders?.()\n if (providers !== undefined && !providers.includes(model.provider)) {\n throw new ToolError(ERR.invalidInput, `model provider \"${model.provider}\" has no registered route (available: ${providers.join(', ')})`)\n }\n return model\n}\n\n/** Resolve the calling agent's actor and session id. */\nfunction caller(exec: ToolRunContext): { actor: Actor & { kind: 'agent' }; sessionId: string } {\n if (!exec.agent) throw new ToolError(ERR.requiresAgent, 'taskboard tools require a calling agent session')\n const sessionId = exec.agent.id\n return { actor: { kind: 'agent', sessionId }, sessionId }\n}\n\n/** The calling session's workspace id (undefined when unaffiliated). */\nasync function callerWorkspace(deps: ToolDeps, exec: ToolRunContext): Promise<string | undefined> {\n const cwd = exec.agent?.session.header.cwd\n if (typeof cwd !== 'string' || cwd.length === 0) return undefined\n const ws = await deps.workspaces.resolveByPath(cwd)\n return ws?.id\n}\n\n/** Guard: version match. */\nfunction versionGuard(task: TaskRecord, ifVersion: number | undefined): void {\n if (ifVersion === undefined) {\n throw new ToolError(ERR.versionConflict, 'this write requires ifVersion; read the task first')\n }\n if (ifVersion !== task.version) {\n throw new ToolError(ERR.versionConflict, `stale version ${ifVersion} (current ${task.version}); re-read the task and retry once`)\n }\n}\n\n/**\n * Find a live (non-trashed) task INSIDE a mutator (R1: every guard must run\n * on the fresh draft the serial queue hands us, never on a pre-read clone —\n * a pre-read can pass its version check and then blind-overwrite a task that\n * changed while the caller awaited). Throws not_found for missing/trashed.\n */\nfunction liveTaskAt(ledger: TaskLedger, id: string): { index: number; task: TaskRecord } {\n const index = ledger.tasks.findIndex(t => t.id === id)\n if (index < 0) throw new ToolError(ERR.notFound, `no task ${id}`)\n const task = ledger.tasks[index]!\n if (task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return { index, task }\n}\n\n/** Re-throw with a stable code; non-ToolErrors become invalid_input. */\nfunction fail(error: unknown): never {\n if (error instanceof ToolError) throw error\n const message = error instanceof Error ? error.message : String(error)\n throw new ToolError(ERR.invalidInput, message)\n}\n\n/** Loose json output schema shared by every taskboard tool. */\nconst JSON_OUT = { type: 'json' } as const\n\n/** Deep-JSON a value for a json-rooted tool output (spread results lose implicit index signatures). */\nfunction json<T>(value: T): Record<string, unknown> {\n return JSON.parse(JSON.stringify(value)) as Record<string, unknown>\n}\n\n/** The exec context face the tools read (agent identity + session cwd). */\nexport interface ToolRunContext {\n agent?: { id: string; session: { header: { cwd?: string } } }\n}\n\n/** Registry-like context face (tests stub this). */\nexport interface ToolContextFace {\n tools: { register(tool: { name: string }): unknown }\n}\n\n/**\n * Register all ten tools.\n * @param ctx - a context exposing `tools.register`.\n * @param deps - store + workspaces + clock.\n * @returns dispose functions, one per tool.\n */\nexport function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Array<() => void> {\n const disposers: Array<() => void> = []\n const { store, workspaces } = deps\n\n // Env-gated tool-call tracing (ATB_TRACE=1) — evidence for protocol E2E.\n const register = (tool: { name: string; execute?: unknown }) => {\n if (process.env.ATB_TRACE === '1' && typeof tool.execute === 'function') {\n const orig = tool.execute as (args: unknown, exec: unknown) => Promise<unknown>\n tool.execute = async (args: unknown, exec: unknown) => {\n console.error(`[atb ▶] ${tool.name}`, JSON.stringify(args).slice(0, 300))\n try {\n const result = await orig(args, exec)\n console.error(`[atb ✓] ${tool.name}`, JSON.stringify(result).slice(0, 300))\n return result\n } catch (error) {\n console.error(`[atb ✗] ${tool.name}`, String(error).slice(0, 400))\n throw error\n }\n }\n }\n return ctx.tools.register(tool as { name: string })\n }\n\n // ------------------------------------------------------------------ list\n disposers.push(register(defineTool({\n name: 'taskboard_list',\n description:\n 'List task-board tasks. Filter by project (workspaceId), status, or urgency. '\n + 'Returns compact summaries (id, title, status, urgency, version, claim owner). '\n + 'Check this before starting work to find claimable todo tasks in your project.',\n parameters: {\n workspaceId: { type: 'string', description: 'Filter by project (DSH workspace id).' },\n status: { type: 'string', description: 'Filter by exact status (backlog/todo/in_progress/in_review/done/canceled/archived).' },\n urgency: { type: 'string', description: 'Filter by urgency (urgent/normal/relaxed).' },\n includeTrashed: { type: 'boolean', description: 'Include soft-deleted tasks (default false).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { revision?: number; tasks?: Array<Record<string, unknown>> }\n const tasks = v.tasks ?? []\n const head = `任务 ${tasks.length} 条(台账 rev ${v.revision ?? '?'})`\n if (tasks.length === 0) return [{ type: 'text', text: `${head}:无匹配任务。` }]\n return [{ type: 'text', text: [head, ...tasks.map(t => taskLine(t as never))].join('\\n') }]\n },\n },\n async execute(args) {\n try {\n const a = args as { workspaceId?: string; status?: string; urgency?: string; includeTrashed?: boolean }\n const tasks = store.snapshot().tasks.filter(t =>\n (a.workspaceId === undefined || t.workspaceId === a.workspaceId)\n && (a.status === undefined || t.status === a.status)\n && (a.urgency === undefined || t.urgency === a.urgency)\n && (a.includeTrashed === true || t.trashedAt === undefined))\n return json({ revision: store.snapshot().revision, tasks: tasks.map(summarize) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------- get\n disposers.push(register(defineTool({\n name: 'taskboard_get',\n description:\n 'Read one task in full: description, prompt, project, urgency, status, comments, executions, version. '\n + 'Read this (and the comments) BEFORE claiming or starting work on a task.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id from the board.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: TaskRecord & { effectivePrompt?: string } }\n return [{ type: 'text', text: v.task === undefined ? '任务不存在。' : taskDetail(v.task) }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const { id } = args\n const task = store.get(id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return json({ task: { ...task, effectivePrompt: effectivePrompt(task) } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- create\n disposers.push(register(defineTool({\n name: 'taskboard_create',\n description:\n 'Create a task on the board. Required: title, workspaceId (project), urgency (urgent/normal/relaxed). '\n + 'Optional: description, prompt (sent to a fresh session on execution), status (default todo), '\n + 'execution mode (claim|scheduled + cron), model {provider, model} to pin executions to a model. '\n + 'Do not track trivial requests as tasks.',\n parameters: {\n title: { type: 'string', required: true, description: 'Short imperative line (1..200 chars).' },\n workspaceId: { type: 'string', required: true, description: 'Project (DSH workspace id) this task belongs to.' },\n urgency: { type: 'string', required: true, description: 'urgent (red) | normal (purple) | relaxed (blue).' },\n description: { type: 'string', description: 'What the task involves (plain text).' },\n prompt: { type: 'string', description: 'Extra execution instructions; the session receives title+description+this prompt.' },\n status: { type: 'string', description: 'Initial status; default todo. backlog = not approved for execution.' },\n execution: {\n type: 'object',\n additionalProperties: false,\n description: 'Execution config: { mode: \"claim\" } (default) or { mode: \"scheduled\", cron: \"m h dom mon dow\" }.',\n properties: {\n mode: { type: 'string', description: 'claim | scheduled.' },\n cron: { type: 'string', description: 'Five-field cron expression (scheduled only).' },\n },\n },\n model: {\n type: 'object',\n additionalProperties: false,\n description: 'Pin executions to one configured model: { provider, model, reasoningEffort? }. Omit to use the default model.',\n properties: {\n provider: { type: 'string', description: 'Provider route id.' },\n model: { type: 'string', description: 'Provider-owned model id.' },\n reasoningEffort: { type: 'string', description: 'Optional thinking intensity / reasoning effort (e.g. low, medium, high).' },\n },\n },\n isolation: {\n type: 'string',\n description: 'Code isolation for executions: \"worktree\" (each run gets a fresh git worktree on branch task/<标题>+<taskId>) or \"none\" (run in the project directory, zero git interaction). Omitted → the board default (看板设置 → 默认执行隔离; factory default \"none\").',\n },\n presetId: {\n type: 'string',\n description: 'Agent preset the execution session is composed from (its tool set / persona); default = the deployment default preset. Optional.',\n },\n checklist: {\n type: 'array',\n description: `Acceptance checklist (DoD) item texts (≤${MAX_CHECKLIST_ITEMS} × 200 chars); agents check them off at handoff, the user reviews.`,\n items: { type: 'string' },\n },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '创建失败。' : `已创建任务 ${t.id} [${t.status}] v${t.version}。写入前先 taskboard_get 读取。` }]\n },\n },\n async execute(args: {\n title: string\n workspaceId: string\n urgency: string\n status?: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string }\n model?: { provider?: string; model?: string }\n isolation?: string\n presetId?: string\n checklist?: string[]\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const title = normalizeTitle(args.title)\n if (workspaces.get(args.workspaceId) === undefined) {\n throw new ToolError(ERR.notFound, `unknown workspaceId ${args.workspaceId}`)\n }\n const urgency = asUrgency(args.urgency)\n const status = args.status === undefined ? 'todo' as const : asStatus(args.status)\n if (status !== 'backlog' && status !== 'todo') {\n throw new ToolError(ERR.invalidTransition, 'a new task must start as backlog or todo (in_progress requires claiming the task)')\n }\n const execution = normalizeExecution(args.execution ?? {}, deps.now())\n const model = args.model !== undefined ? checkModel(deps, args.model) : undefined\n // 0.5.0: an omitted isolation is MATERIALIZED from the board setting\n // (看板设置) at creation, so later setting changes never rewrite\n // existing tasks.\n const isolation = args.isolation === undefined ? defaultIsolationOf(store.snapshot().settings) : asIsolation(args.isolation)\n const presetId = args.presetId?.trim() || undefined\n // T9: match the GUI create route — trim and drop blank lines instead\n // of failing the whole call over one empty string.\n const checklistTexts = args.checklist?.map(c => c.trim()).filter(c => c.length > 0)\n const checklist = checklistTexts !== undefined && checklistTexts.length > 0 ? checklistFromTexts(checklistTexts) : undefined\n const now = deps.now()\n const task: TaskRecord = {\n id: newTaskId(),\n title,\n description: (args.description ?? '').trim(),\n prompt: normalizePrompt(args.prompt),\n workspaceId: args.workspaceId,\n urgency,\n status,\n blocked: false,\n execution,\n model,\n isolation,\n ...(presetId !== undefined ? { presetId } : {}),\n ...(checklist !== undefined ? { checklist } : {}),\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: actor,\n updatedBy: actor,\n comments: [],\n executions: [],\n }\n await store.mutate('task-created', ledger => {\n ledger.tasks.push(task)\n return [task]\n })\n return json({ task: summarize(task) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- update\n disposers.push(register(defineTool({\n name: 'taskboard_update',\n description:\n 'Update a task\\'s title/description/prompt/urgency/blocked. Requires ifVersion (read first). '\n + 'The model and execution config are read-only through this tool (they belong to the task owner/user).',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'The task version you read; the write fails on mismatch.' },\n title: { type: 'string', description: 'New title.' },\n description: { type: 'string', description: 'New description.' },\n prompt: { type: 'string', description: 'New execution prompt.' },\n urgency: { type: 'string', description: 'urgent | normal | relaxed.' },\n blocked: { type: 'boolean', description: 'Blocked marker (work cannot continue right now).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '更新失败。' : `已更新任务 ${t.id},当前 v${t.version} [${t.status}]。` }]\n },\n },\n async execute(args: {\n id: string\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n // R1: lookup + version guard + write run inside the serial-queue\n // mutation, on the fresh draft — a pre-read clone could pass its\n // version check and then blind-overwrite a concurrent writer.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n if (args.title !== undefined) next.title = normalizeTitle(args.title)\n if (args.description !== undefined) next.description = args.description.trim()\n if (args.prompt !== undefined) next.prompt = normalizePrompt(args.prompt)\n if (args.urgency !== undefined) next.urgency = asUrgency(args.urgency)\n if (args.blocked !== undefined) next.blocked = args.blocked\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n ledger.tasks[index] = next\n return [next]\n })\n return json({ task: summarize(next!) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------ move\n disposers.push(register(defineTool({\n name: 'taskboard_move',\n description:\n 'Move a task between statuses (requires ifVersion). Claim = todo→in_progress (only a session '\n + 'inside the task\\'s project may claim; never take over a task held by another session). '\n + 'After implementing and self-verifying: comment, then in_progress→in_review. '\n + 'You can NEVER move a task to done — that requires explicit user confirmation.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n status: { type: 'string', required: true, description: 'Target status.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '移动失败。' : `任务 ${t.id} 已移到 ${t.status},当前 v${t.version}。` }]\n },\n },\n async execute(args: { id: string; status: string; ifVersion: number }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const to = asStatus(args.status)\n // Claim boundary (policy gate): resolving the caller's workspace is\n // async, so it happens BEFORE the mutation — the comparison runs INSIDE\n // against the FRESH task (stronger than the old stale-clone compare).\n const callerWsId = to === 'in_progress'\n ? await callerWorkspace(deps, exec as ToolRunContext)\n : undefined\n // R1: every state guard + the write itself run inside the mutation.\n let next: TaskRecord | undefined\n await store.mutate('task-moved', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n\n // Code-level gate: agents never complete a task.\n if (to === 'done') {\n throw new ToolError(ERR.forbidden, 'moving a task to done requires explicit user confirmation (GUI); agents cannot do it')\n }\n if (!canTransition(task.status, to)) {\n throw new ToolError(ERR.invalidTransition, `illegal transition ${task.status} → ${to}`)\n }\n // Exclusive hold: while a task is in_progress under a session\n // (explicit claimedBy — an agent claim or a live execution), no other\n // session may move it (that would be a takeover).\n if (task.status === 'in_progress' && task.claimedBy !== undefined && task.claimedBy !== actor.sessionId) {\n throw new ToolError(ERR.forbidden, `task is held by session ${task.claimedBy}; never take over another session's claim`)\n }\n // Claim boundary: the calling session must belong to the task's project.\n if (isClaim(task.status, to) && callerWsId !== task.workspaceId) {\n throw new ToolError(ERR.workspaceMismatch, 'only a session inside this task\\'s project may claim it')\n }\n next = structuredClone(task)\n next.status = to\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n if (isClaim(task.status, to)) next.blocked = false\n // Record the holder on a claim; every move out of in_progress releases it.\n syncClaim(next, to, deps.now(), isClaim(task.status, to) ? actor.sessionId : undefined)\n ledger.tasks[index] = next\n return [next]\n })\n return json({ task: summarize(next!) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ----------------------------------------------------------- comment_add\n disposers.push(register(defineTool({\n name: 'taskboard_comment_add',\n description:\n 'Append a progress/report comment to a task. When handing off to review, the comment should cover: '\n + 'what changed, how it was verified, outcome, and remaining risks.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n body: { type: 'string', required: true, description: 'Comment text (1..4000 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comment?: { id?: string }; task?: { id?: string; version?: number; status?: string } }\n const c = v.comment\n const t = v.task\n if (c === undefined || t === undefined) return [{ type: 'text', text: '评论失败。' }]\n // The comment bumped the version — echo it so the agent can chain the\n // next write (e.g. move → in_review) WITHOUT re-reading.\n return [{\n type: 'text',\n text: `评论 ${c.id} 已添加;任务 ${t.id} 当前 v${t.version} [${t.status}](后续写操作用此版本号).`,\n }]\n },\n },\n async execute(args: { id: string; body: string }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const comment = {\n id: newCommentId(),\n body: normalizeBody(args.body),\n version: 1,\n createdAt: deps.now(),\n threadId: sessionId,\n }\n // R1: find + append inside the mutation (no ifVersion by design —\n // comments are append-only — but the write must not clobber a task\n // that changed while we were queued).\n let next: TaskRecord | undefined\n await store.mutate('comment-added', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n next.comments.push(comment)\n next.version = task.version + 1\n next.updatedAt = deps.now()\n ledger.tasks[index] = next\n return [next]\n })\n return json({ comment, task: { id: next!.id, version: next!.version, status: next!.status } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- comments\n disposers.push(register(defineTool({\n name: 'taskboard_comments',\n description: 'List a task\\'s comments, oldest first. Read them before deciding to start work.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comments?: unknown[] }\n const list = v.comments as Array<{ body: string; createdAt: number; threadId?: string }> | undefined\n if (list === undefined || list.length === 0) return [{ type: 'text', text: '无评论。' }]\n const lines = list.map(c => {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n return `- [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`\n })\n return [{ type: 'text', text: `评论 ${list.length} 条:\\n${lines.join('\\n')}` }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n return json({ comments: task.comments })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- delete\n disposers.push(register(defineTool({\n name: 'taskboard_delete',\n description:\n 'Soft-delete a task (marks it trashed; the user confirms the purge in the GUI). '\n + 'Requires ifVersion. Prefer canceled/archived over delete unless the task was a mistake.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { trashed?: boolean }\n return [{ type: 'text', text: v.trashed === true ? '任务已标记删除(等待用户在 GUI 清除)。' : '删除失败。' }]\n },\n },\n async execute(args: { id: string; ifVersion: number }, exec: unknown) {\n try {\n caller(exec as ToolRunContext)\n // R1: guards inside the mutation. S5: a running execution keeps\n // writing to the task (report, settlement) — refuse the soft-delete\n // until it is cancelled or settled. T8: clear claim residue.\n let next: TaskRecord | undefined\n await store.mutate('task-deleted', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.executions.some(e => e.outcome === 'running')) {\n throw new ToolError(ERR.invalidInput, '任务有正在运行的执行(先在 GUI 取消或等它结束再删除)')\n }\n next = structuredClone(task)\n next.trashedAt = deps.now()\n next.version = task.version + 1\n delete next.claimedBy\n delete next.claimedAt\n next.blocked = false\n ledger.tasks[index] = next\n return [next]\n })\n return { trashed: true }\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- checklist\n disposers.push(register(defineTool({\n name: 'taskboard_checklist',\n description:\n `Manage the task's acceptance checklist (DoD). Actions: \"add\" (append item texts, ≤10 per call), `\n + '\"check\" (mark an item done, with an optional evidence note), \"uncheck\" (reopen an item). '\n + 'Checking items NEVER completes the task — done stays a user-only action. Requires ifVersion.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n action: { type: 'string', required: true, description: 'add | check | uncheck.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n items: {\n type: 'array',\n description: 'Item texts to append (action=add only; 1..10 per call, 200 chars each).',\n items: { type: 'string' },\n },\n itemId: { type: 'string', description: 'The checklist item id (action=check/uncheck).' },\n note: { type: 'string', description: 'Evidence note recorded with the check (≤400 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; version?: number }; checklist?: Array<Record<string, unknown>>; done?: number; total?: number }\n if (v.task === undefined || v.checklist === undefined) return [{ type: 'text', text: '清单操作失败。' }]\n const lines = v.checklist.map((i, index) => `${i.checked === true ? '☑' : '☐'} [${index + 1}] ${String(i.text)}${i.note !== undefined ? `(证据: ${String(i.note)})` : ''} id=${String(i.id)}`)\n return [{\n type: 'text',\n text: `任务 ${v.task.id} 验收清单 ${v.done ?? 0}/${v.total ?? 0} 已完成,当前 v${v.task.version}:\\n${lines.join('\\n')}`,\n }]\n },\n },\n async execute(args: {\n id: string\n action: string\n ifVersion: number\n items?: string[]\n itemId?: string\n note?: string\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n // R1: guards + the checklist edit itself run inside the mutation.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n const checklist: ChecklistItem[] = next.checklist === undefined ? [] : [...next.checklist]\n\n if (args.action === 'add') {\n const texts = args.items ?? []\n if (texts.length === 0 || texts.length > 10) {\n throw new ToolError(ERR.invalidInput, 'items must carry 1..10 texts per add call')\n }\n if (checklist.length + texts.length > MAX_CHECKLIST_ITEMS) {\n throw new ToolError(ERR.invalidInput, `checklist may hold at most ${MAX_CHECKLIST_ITEMS} items (currently ${checklist.length})`)\n }\n checklist.push(...checklistFromTexts(texts))\n } else if (args.action === 'check') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for check')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n const note = args.note !== undefined && args.note.trim().length > 0 ? args.note.trim().slice(0, 400) : undefined\n item.checked = true\n item.checkedBy = actor.sessionId\n item.checkedAt = deps.now()\n if (note !== undefined) item.note = note\n } else if (args.action === 'uncheck') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for uncheck')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n item.checked = false\n delete item.checkedBy\n delete item.checkedAt\n delete item.note\n } else {\n throw new ToolError(ERR.invalidInput, `action must be add | check | uncheck (got \"${args.action}\")`)\n }\n\n if (checklist.length > 0) next.checklist = checklist\n else delete next.checklist\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n ledger.tasks[index] = next\n return [next]\n })\n const progress = next!.checklist !== undefined\n ? { done: next!.checklist.filter(i => i.checked).length, total: next!.checklist.length }\n : { done: 0, total: 0 }\n return json({ task: { id: next!.id, version: next!.version }, checklist: next!.checklist ?? [], ...progress })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------- execution report\n disposers.push(register(defineTool({\n name: 'taskboard_execution_report',\n description:\n 'Submit the structured execution report for the task you are currently executing (summary / changed '\n + 'files / how you verified / artifacts / remaining risk). Submit BEFORE moving the task to in_review; '\n + 'a later submission overwrites the previous report. If your run already settled, you may back-submit '\n + 'onto your latest succeeded execution while you still hold the task. Commits and diffs are host-collected.',\n parameters: {\n summary: { type: 'string', required: true, description: 'What was done (1..2000 chars).' },\n changedFiles: {\n type: 'array',\n description: 'Files you changed (paths, ≤50 × 300 chars).',\n items: { type: 'string' },\n },\n checks: {\n type: 'array',\n description: 'How the work was verified (e.g. test commands + outcomes, ≤50 entries).',\n items: { type: 'string' },\n },\n artifacts: {\n type: 'array',\n description: 'Artifacts worth reviewing (build outputs, screenshots, docs, ≤30 entries).',\n items: { type: 'string' },\n },\n risk: { type: 'string', description: 'Known remaining risks or follow-ups (≤2000 chars, optional).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { taskId?: string; executionId?: string; report?: { summary?: string } }\n if (v.taskId === undefined || v.report === undefined) return [{ type: 'text', text: '报告提交失败。' }]\n return [{\n type: 'text',\n text: `执行报告已记录到任务 ${v.taskId}(执行 ${v.executionId}):${v.report.summary?.slice(0, 120) ?? ''}\\n`\n + '接下来:taskboard_comment_add 留交接评论,然后 taskboard_move 移至待验收 in_review。',\n }]\n },\n },\n async execute(args: {\n summary: string\n changedFiles?: string[]\n checks?: string[]\n artifacts?: string[]\n risk?: string\n }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const report = normalizeExecutionReport(args)\n // Path 1 (unchanged): attach to the RUNNING execution this session\n // owns — reports ride the live run, so the agent never needs ids.\n let taskId: string | undefined\n let executionId: string | undefined\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n const execution = task.executions.find(e => e.sessionId === sessionId && e.outcome === 'running')\n if (execution !== undefined) {\n execution.report = report\n taskId = task.id\n executionId = execution.id\n return [task]\n }\n }\n return undefined\n })\n // Path 2 (review follow-up, P2): back-submit onto a session-owned\n // SETTLED execution — the main conversation claims tasks directly and\n // has no live run. Allowed when the session holds the task or owns\n // its latest successful execution; never touches anyone else's runs.\n if (taskId === undefined || executionId === undefined) {\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n if (task.trashedAt !== undefined || task.status === 'archived') continue\n const last = task.executions[task.executions.length - 1]\n const owned = last !== undefined && last.sessionId === sessionId && last.outcome === 'succeeded'\n const holds = task.claimedBy === sessionId && last !== undefined && last.sessionId === sessionId\n if (!owned && !holds) continue\n last!.report = report\n taskId = task.id\n executionId = last!.id\n return [task]\n }\n return undefined\n })\n }\n if (taskId === undefined || executionId === undefined) {\n throw new ToolError(ERR.forbidden, 'no running execution and no settled execution of yours to report on — reports attach to your running execution, or back-submit onto your latest succeeded one while you hold the task')\n }\n return json({ taskId, executionId, report })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n return disposers\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tools.js","names":["v"],"sources":["../../src/host/tools.ts"],"sourcesContent":["import type { SessionArchiveResult } from '../shared/api.ts'\nimport { archiveTaskSessions } from './archive-sessions.ts'\n/**\n * The ten `taskboard_*` agent tools. All writes require a calling agent\n * session (ownership audit), carry optimistic-version checks, and enforce\n * the protocol gates in CODE, not in prompt text:\n *\n * - `move → done` is rejected for agent callers (user confirmation only)\n * - `move todo → in_progress` requires the calling session's workspace to\n * match the task's project (claim boundary)\n * - taking over a task held by another session is rejected\n * - `delete` for agent callers only sets the soft-delete marker\n * - checklist items may be added/checked by agents, but checking never\n * completes the task (done stays user-only)\n *\n * OUTPUT CONTRACT (lesson: registry `createSuccessResult` renders\n * `output.render(args, value)` into `result.content`, and the loop feeds\n * exactly that content to the model — the raw JSON `value` never reaches\n * the model): render() IS the model-facing tool result. Every render must\n * carry the complete facts an agent needs to act (ids, versions, statuses);\n * a \"terse UI summary\" here starves the agent.\n *\n * @module dsh-taskboard/host/tools\n */\nimport type { WorkspaceRegistry } from '@deepseek-ai/dsh-workspace'\nimport { defineTool } from './sdk.ts'\nimport {\n MAX_CHECKLIST_ITEMS,\n asIsolation,\n asStatus,\n asUrgency,\n canTransition,\n checklistFromTexts,\n defaultIsolationOf,\n effectivePrompt,\n isClaim,\n isClaimedBy,\n newChecklistItemId,\n newCommentId,\n newTaskId,\n normalizeBody,\n normalizeExecution,\n normalizeExecutionReport,\n normalizeModel,\n normalizePrompt,\n normalizeTitle,\n summarize,\n syncClaim,\n type Actor,\n type ChecklistItem,\n type TaskLedger,\n type TaskModel,\n type TaskRecord,\n} from '../shared/protocol.ts'\nimport type { TaskStore } from './store.ts'\n\n/** Render side: one compact task line (id/status/version are load-bearing). */\nfunction taskLine(t: {\n id: string\n title: string\n status: string\n urgency: string\n version: number\n workspaceId: string\n blocked: boolean\n executionMode: string\n commentCount?: number\n lastExecutionOutcome?: string\n checklist?: { done: number; total: number }\n trashed?: boolean\n}): string {\n const parts = [\n `- ${t.id} [${t.status}] v${t.version} · ${t.urgency} · 项目 ${t.workspaceId}`,\n `「${t.title}」`,\n ]\n if (t.blocked) parts.push('·受阻')\n if (t.executionMode === 'scheduled') parts.push('·定时')\n if (t.commentCount !== undefined && t.commentCount > 0) parts.push(`·评论${t.commentCount}`)\n if (t.checklist !== undefined && t.checklist.total > 0) parts.push(`·清单${t.checklist.done}/${t.checklist.total}`)\n if (t.lastExecutionOutcome !== undefined) parts.push(`·上次执行${t.lastExecutionOutcome}`)\n if (t.trashed === true) parts.push('·已删')\n return parts.join(' ')\n}\n\n/** Render side: the full task detail block (everything an executor needs). */\nfunction taskDetail(t: TaskRecord & { effectivePrompt?: string }): string {\n const lines: string[] = [\n `任务 ${t.id} 「${t.title}」`,\n `状态: ${t.status} (v${t.version}) · 紧急度: ${t.urgency} · 项目: ${t.workspaceId}${t.blocked ? ' · 受阻' : ''}`,\n `执行方式: ${t.execution.mode}${t.execution.cron !== undefined ? ` cron=${t.execution.cron}` : ''}`,\n `隔离: ${t.isolation === 'none' ? '关闭(原目录执行)' : 'Git Worktree'}${t.branch !== undefined ? `(分支 ${t.branch})` : ''}${t.branches !== undefined ? `(多仓库镜像 ${Object.keys(t.branches).length + (t.branch !== undefined ? 1 : 0)} 个仓库)` : ''}`,\n ]\n const holder = isClaimedBy(t)\n if (holder !== undefined) lines.push(`认领: agent ${String(holder).slice(0, 24)}(持有期间其他会话不可移动)`)\n if (t.execution.nextRunAt !== undefined) lines.push(`下次触发: ${new Date(t.execution.nextRunAt).toISOString()}`)\n if (t.model !== undefined) lines.push(`固定模型: ${t.model.provider}/${t.model.model}${t.model.reasoningEffort !== undefined ? ` (思考强度: ${t.model.reasoningEffort})` : ''}`)\n if (t.presetId !== undefined) lines.push(`执行模式: ${t.presetId}(未指定时为部署默认 preset)`)\n lines.push(`描述: ${t.description.length > 0 ? t.description : '(无)'}`)\n lines.push(`执行 Prompt: ${t.effectivePrompt ?? effectivePrompt(t)}`)\n if (t.checklist !== undefined && t.checklist.length > 0) {\n const done = t.checklist.filter(i => i.checked).length\n lines.push(`验收清单 (${done}/${t.checklist.length}):`)\n for (const [index, item] of t.checklist.entries()) {\n const mark = item.checked ? '☑' : '☐'\n const who = item.checkedBy === undefined ? '' : item.checkedBy === 'user' ? ' ·用户勾选' : ` ·agent ${String(item.checkedBy).slice(0, 24)}勾选`\n const note = item.note !== undefined ? ` ·证据: ${item.note}` : ''\n // Carry the checklist item id so `taskboard_checklist check/uncheck` can\n // address it without guessing — a terse render starves the agent (render\n // is fed to the model as result.content). Mirrors the index+id carried\n // by the taskboard_checklist tool output.\n lines.push(` ${mark} [${index + 1}] ${item.text}${who}${note} id=${item.id}`)\n }\n }\n if (t.comments.length > 0) {\n lines.push(`评论 (${t.comments.length}):`)\n for (const c of t.comments) {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n lines.push(` - [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`)\n }\n } else {\n lines.push('评论: 无')\n }\n if (t.executions.length > 0) {\n lines.push(`执行记录 (${t.executions.length}):`)\n for (const e of t.executions) {\n const at = e.startedAt !== undefined ? new Date(e.startedAt).toISOString() : '?'\n const err = e.error !== undefined ? ` 错误: ${e.error}` : ''\n const report = e.report !== undefined ? ' [已交报告]' : ''\n lines.push(` - [${e.trigger} ${at}] ${e.outcome}${report}${err}`)\n }\n } else {\n lines.push('执行记录: 无')\n }\n const updatedBy = t.updatedBy.kind === 'agent' ? `agent ${String(t.updatedBy.sessionId).slice(0, 24)}` : t.updatedBy.kind === 'system' ? 'system' : 'user'\n lines.push(`更新: ${new Date(t.updatedAt).toISOString()} 由 ${updatedBy}`)\n return lines.join('\\n')\n}\n\n/** Stable error codes surfaced at the head of tool error messages. */\nexport const ERR = {\n notFound: 'not_found',\n versionConflict: 'version_conflict',\n workspaceMismatch: 'workspace_mismatch',\n invalidTransition: 'invalid_transition',\n forbidden: 'forbidden',\n requiresAgent: 'unauthorized_actor',\n invalidInput: 'invalid_input',\n} as const\n\n/** Tool failure: an Error whose message starts with a stable code. The code\n * is also carried structurally so the routes layer can map failures without\n * re-parsing messages (review P2). */\nexport class ToolError extends Error {\n constructor(readonly code: string, detail: string) {\n super(`Error: ${code}: ${detail}`)\n }\n}\n\n/** The workspace face the tools need (narrow for tests). */\nexport interface WorkspaceFace {\n /** Resolve the workspace owning a canonical cwd, if any. */\n resolveByPath(path: string): Promise<{ id: string } | undefined>\n /** Get a workspace by id. */\n get(id: string): { id: string; path: string; title: string } | undefined\n /** List all workspaces. */\n list(): Array<{ id: string; path: string; title: string }>\n /** Archive one session durably (when supported by runtime workspaceRegistry). */\n archiveSession?(sessionId: string): Promise<void>\n}\n\n/** Adapt the real registry to the narrow face. */\nexport function workspaceFace(registry: WorkspaceRegistry): WorkspaceFace {\n // Explicit field mapping: Workspace entities expose path/title as prototype\n // getters, which JSON.stringify skips (own enumerable properties only).\n return {\n resolveByPath: async (path) => {\n const ws = await registry.resolveByPath(path as never)\n return ws === undefined ? undefined : { id: ws.id }\n },\n get: id => {\n const ws = registry.get(id as never)\n return ws === undefined ? undefined : { id: ws.id, path: ws.path, title: ws.title }\n },\n list: () => registry.list().map(ws => ({ id: ws.id, path: ws.path, title: ws.title })),\n ...(typeof registry.archiveSession === 'function'\n ? { archiveSession: (sessionId: string) => registry.archiveSession(sessionId as Parameters<WorkspaceRegistry['archiveSession']>[0]) }\n : {}),\n }\n}\n\n/** Everything the tool set needs. */\nexport interface ToolDeps {\n store: TaskStore\n workspaces: WorkspaceFace\n /** Current epoch ms (injectable for tests). */\n now: () => number\n /**\n * Registered model provider routes (from the host llm runtime), for\n * advisory validation of pinned models; undefined = runtime unavailable,\n * in which case only the structural check applies.\n */\n modelProviders?: () => string[] | undefined\n}\n\n/** Validate a pinned model: structural check always, provider route when known. */\nfunction checkModel(deps: ToolDeps, raw: unknown): TaskModel {\n const model = normalizeModel(raw)\n const providers = deps.modelProviders?.()\n if (providers !== undefined && !providers.includes(model.provider)) {\n throw new ToolError(ERR.invalidInput, `model provider \"${model.provider}\" has no registered route (available: ${providers.join(', ')})`)\n }\n return model\n}\n\n/** Resolve the calling agent's actor and session id. */\nfunction caller(exec: ToolRunContext): { actor: Actor & { kind: 'agent' }; sessionId: string } {\n if (!exec.agent) throw new ToolError(ERR.requiresAgent, 'taskboard tools require a calling agent session')\n const sessionId = exec.agent.id\n return { actor: { kind: 'agent', sessionId }, sessionId }\n}\n\n/** The calling session's workspace id (undefined when unaffiliated). */\nasync function callerWorkspace(deps: ToolDeps, exec: ToolRunContext): Promise<string | undefined> {\n const cwd = exec.agent?.session.header.cwd\n if (typeof cwd !== 'string' || cwd.length === 0) return undefined\n const ws = await deps.workspaces.resolveByPath(cwd)\n return ws?.id\n}\n\n/** Guard: version match. */\nfunction versionGuard(task: TaskRecord, ifVersion: number | undefined): void {\n if (ifVersion === undefined) {\n throw new ToolError(ERR.versionConflict, 'this write requires ifVersion; read the task first')\n }\n if (ifVersion !== task.version) {\n throw new ToolError(ERR.versionConflict, `stale version ${ifVersion} (current ${task.version}); re-read the task and retry once`)\n }\n}\n\n/**\n * Find a live (non-trashed) task INSIDE a mutator (R1: every guard must run\n * on the fresh draft the serial queue hands us, never on a pre-read clone —\n * a pre-read can pass its version check and then blind-overwrite a task that\n * changed while the caller awaited). Throws not_found for missing/trashed.\n */\nfunction liveTaskAt(ledger: TaskLedger, id: string): { index: number; task: TaskRecord } {\n const index = ledger.tasks.findIndex(t => t.id === id)\n if (index < 0) throw new ToolError(ERR.notFound, `no task ${id}`)\n const task = ledger.tasks[index]!\n if (task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return { index, task }\n}\n\n/** Re-throw with a stable code; non-ToolErrors become invalid_input. */\nfunction fail(error: unknown): never {\n if (error instanceof ToolError) throw error\n const message = error instanceof Error ? error.message : String(error)\n throw new ToolError(ERR.invalidInput, message)\n}\n\n/** Loose json output schema shared by every taskboard tool. */\nconst JSON_OUT = { type: 'json' } as const\n\n/** Deep-JSON a value for a json-rooted tool output (spread results lose implicit index signatures). */\nfunction json<T>(value: T): Record<string, unknown> {\n return JSON.parse(JSON.stringify(value)) as Record<string, unknown>\n}\n\n/** The exec context face the tools read (agent identity + session cwd). */\nexport interface ToolRunContext {\n agent?: { id: string; session: { header: { cwd?: string } } }\n}\n\n/** Registry-like context face (tests stub this). */\nexport interface ToolContextFace {\n tools: { register(tool: { name: string }): unknown }\n}\n\n/**\n * Register all ten tools.\n * @param ctx - a context exposing `tools.register`.\n * @param deps - store + workspaces + clock.\n * @returns dispose functions, one per tool.\n */\nexport function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Array<() => void> {\n const disposers: Array<() => void> = []\n const { store, workspaces } = deps\n\n // Env-gated tool-call tracing (ATB_TRACE=1) — evidence for protocol E2E.\n const register = (tool: { name: string; execute?: unknown }) => {\n if (process.env.ATB_TRACE === '1' && typeof tool.execute === 'function') {\n const orig = tool.execute as (args: unknown, exec: unknown) => Promise<unknown>\n tool.execute = async (args: unknown, exec: unknown) => {\n console.error(`[atb ▶] ${tool.name}`, JSON.stringify(args).slice(0, 300))\n try {\n const result = await orig(args, exec)\n console.error(`[atb ✓] ${tool.name}`, JSON.stringify(result).slice(0, 300))\n return result\n } catch (error) {\n console.error(`[atb ✗] ${tool.name}`, String(error).slice(0, 400))\n throw error\n }\n }\n }\n return ctx.tools.register(tool as { name: string })\n }\n\n // ------------------------------------------------------------------ list\n disposers.push(register(defineTool({\n name: 'taskboard_list',\n description:\n 'List task-board tasks. Filter by project (workspaceId), status, or urgency. '\n + 'Returns compact summaries (id, title, status, urgency, version, claim owner). '\n + 'Check this before starting work to find claimable todo tasks in your project.',\n parameters: {\n workspaceId: { type: 'string', description: 'Filter by project (DSH workspace id).' },\n status: { type: 'string', description: 'Filter by exact status (backlog/todo/in_progress/in_review/done/canceled/archived).' },\n urgency: { type: 'string', description: 'Filter by urgency (urgent/normal/relaxed).' },\n includeTrashed: { type: 'boolean', description: 'Include soft-deleted tasks (default false).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { revision?: number; tasks?: Array<Record<string, unknown>> }\n const tasks = v.tasks ?? []\n const head = `任务 ${tasks.length} 条(台账 rev ${v.revision ?? '?'})`\n if (tasks.length === 0) return [{ type: 'text', text: `${head}:无匹配任务。` }]\n return [{ type: 'text', text: [head, ...tasks.map(t => taskLine(t as never))].join('\\n') }]\n },\n },\n async execute(args) {\n try {\n const a = args as { workspaceId?: string; status?: string; urgency?: string; includeTrashed?: boolean }\n const tasks = store.snapshot().tasks.filter(t =>\n (a.workspaceId === undefined || t.workspaceId === a.workspaceId)\n && (a.status === undefined || t.status === a.status)\n && (a.urgency === undefined || t.urgency === a.urgency)\n && (a.includeTrashed === true || t.trashedAt === undefined))\n return json({ revision: store.snapshot().revision, tasks: tasks.map(summarize) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------- get\n disposers.push(register(defineTool({\n name: 'taskboard_get',\n description:\n 'Read one task in full: description, prompt, project, urgency, status, comments, executions, version. '\n + 'Read this (and the comments) BEFORE claiming or starting work on a task.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id from the board.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: TaskRecord & { effectivePrompt?: string } }\n return [{ type: 'text', text: v.task === undefined ? '任务不存在。' : taskDetail(v.task) }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const { id } = args\n const task = store.get(id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return json({ task: { ...task, effectivePrompt: effectivePrompt(task) } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- create\n disposers.push(register(defineTool({\n name: 'taskboard_create',\n description:\n 'Create a task on the board. Required: title, workspaceId (project), urgency (urgent/normal/relaxed). '\n + 'Optional: description, prompt (sent to a fresh session on execution), status (default todo), '\n + 'execution mode (claim|scheduled + cron), model {provider, model} to pin executions to a model. '\n + 'Do not track trivial requests as tasks.',\n parameters: {\n title: { type: 'string', required: true, description: 'Short imperative line (1..200 chars).' },\n workspaceId: { type: 'string', required: true, description: 'Project (DSH workspace id) this task belongs to.' },\n urgency: { type: 'string', required: true, description: 'urgent (red) | normal (purple) | relaxed (blue).' },\n description: { type: 'string', description: 'What the task involves (plain text).' },\n prompt: { type: 'string', description: 'Extra execution instructions; the session receives title+description+this prompt.' },\n status: { type: 'string', description: 'Initial status; default todo. backlog = not approved for execution.' },\n execution: {\n type: 'object',\n additionalProperties: false,\n description: 'Execution config: { mode: \"claim\" } (default) or { mode: \"scheduled\", cron: \"m h dom mon dow\" }.',\n properties: {\n mode: { type: 'string', description: 'claim | scheduled.' },\n cron: { type: 'string', description: 'Five-field cron expression (scheduled only).' },\n },\n },\n model: {\n type: 'object',\n additionalProperties: false,\n description: 'Pin executions to one configured model: { provider, model, reasoningEffort? }. Omit to use the default model.',\n properties: {\n provider: { type: 'string', description: 'Provider route id.' },\n model: { type: 'string', description: 'Provider-owned model id.' },\n reasoningEffort: { type: 'string', description: 'Optional thinking intensity / reasoning effort (e.g. low, medium, high).' },\n },\n },\n isolation: {\n type: 'string',\n description: 'Code isolation for executions: \"worktree\" (each run gets a fresh git worktree on branch task/<标题>+<taskId>) or \"none\" (run in the project directory, zero git interaction). Omitted → the board default (看板设置 → 默认执行隔离; factory default \"none\").',\n },\n presetId: {\n type: 'string',\n description: 'Agent preset the execution session is composed from (its tool set / persona); default = the deployment default preset. Optional.',\n },\n checklist: {\n type: 'array',\n description: `Acceptance checklist (DoD) item texts (≤${MAX_CHECKLIST_ITEMS} × 200 chars); agents check them off at handoff, the user reviews.`,\n items: { type: 'string' },\n },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '创建失败。' : `已创建任务 ${t.id} [${t.status}] v${t.version}。写入前先 taskboard_get 读取。` }]\n },\n },\n async execute(args: {\n title: string\n workspaceId: string\n urgency: string\n status?: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string }\n model?: { provider?: string; model?: string }\n isolation?: string\n presetId?: string\n checklist?: string[]\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const title = normalizeTitle(args.title)\n if (workspaces.get(args.workspaceId) === undefined) {\n throw new ToolError(ERR.notFound, `unknown workspaceId ${args.workspaceId}`)\n }\n const urgency = asUrgency(args.urgency)\n const status = args.status === undefined ? 'todo' as const : asStatus(args.status)\n if (status !== 'backlog' && status !== 'todo') {\n throw new ToolError(ERR.invalidTransition, 'a new task must start as backlog or todo (in_progress requires claiming the task)')\n }\n const execution = normalizeExecution(args.execution ?? {}, deps.now())\n const model = args.model !== undefined ? checkModel(deps, args.model) : undefined\n // 0.5.0: an omitted isolation is MATERIALIZED from the board setting\n // (看板设置) at creation, so later setting changes never rewrite\n // existing tasks.\n const isolation = args.isolation === undefined ? defaultIsolationOf(store.snapshot().settings) : asIsolation(args.isolation)\n const presetId = args.presetId?.trim() || undefined\n // T9: match the GUI create route — trim and drop blank lines instead\n // of failing the whole call over one empty string.\n const checklistTexts = args.checklist?.map(c => c.trim()).filter(c => c.length > 0)\n const checklist = checklistTexts !== undefined && checklistTexts.length > 0 ? checklistFromTexts(checklistTexts) : undefined\n const now = deps.now()\n const task: TaskRecord = {\n id: newTaskId(),\n title,\n description: (args.description ?? '').trim(),\n prompt: normalizePrompt(args.prompt),\n workspaceId: args.workspaceId,\n urgency,\n status,\n blocked: false,\n execution,\n model,\n isolation,\n ...(presetId !== undefined ? { presetId } : {}),\n ...(checklist !== undefined ? { checklist } : {}),\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: actor,\n updatedBy: actor,\n comments: [],\n executions: [],\n }\n await store.mutate('task-created', ledger => {\n ledger.tasks.push(task)\n return [task]\n })\n return json({ task: summarize(task) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- update\n disposers.push(register(defineTool({\n name: 'taskboard_update',\n description:\n 'Update a task\\'s title/description/prompt/urgency/blocked. Requires ifVersion (read first). '\n + 'The model and execution config are read-only through this tool (they belong to the task owner/user).',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'The task version you read; the write fails on mismatch.' },\n title: { type: 'string', description: 'New title.' },\n description: { type: 'string', description: 'New description.' },\n prompt: { type: 'string', description: 'New execution prompt.' },\n urgency: { type: 'string', description: 'urgent | normal | relaxed.' },\n blocked: { type: 'boolean', description: 'Blocked marker (work cannot continue right now).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '更新失败。' : `已更新任务 ${t.id},当前 v${t.version} [${t.status}]。` }]\n },\n },\n async execute(args: {\n id: string\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n // R1: lookup + version guard + write run inside the serial-queue\n // mutation, on the fresh draft — a pre-read clone could pass its\n // version check and then blind-overwrite a concurrent writer.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n if (args.title !== undefined) next.title = normalizeTitle(args.title)\n if (args.description !== undefined) next.description = args.description.trim()\n if (args.prompt !== undefined) next.prompt = normalizePrompt(args.prompt)\n if (args.urgency !== undefined) next.urgency = asUrgency(args.urgency)\n if (args.blocked !== undefined) next.blocked = args.blocked\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n ledger.tasks[index] = next\n return [next]\n })\n return json({ task: summarize(next!) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------ move\n disposers.push(register(defineTool({\n name: 'taskboard_move',\n description:\n 'Move a task between statuses (requires ifVersion). Claim = todo→in_progress (only a session '\n + 'inside the task\\'s project may claim; never take over a task held by another session). '\n + 'After implementing and self-verifying: comment, then in_progress→in_review. '\n + 'You can NEVER move a task to done — that requires explicit user confirmation.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n status: { type: 'string', required: true, description: 'Target status.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n archiveSessions: { type: 'boolean', description: 'When moving to archived: whether to archive associated execution sessions as well. Defaults to false.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '移动失败。' : `任务 ${t.id} 已移到 ${t.status},当前 v${t.version}。${v.sessionArchive === undefined ? '' : ` 会话归档结果:${JSON.stringify(v.sessionArchive)}`}` }]\n },\n },\n async execute(args: { id: string; status: string; ifVersion: number; archiveSessions?: boolean }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const to = asStatus(args.status)\n // Claim boundary (policy gate): resolving the caller's workspace is\n // async, so it happens BEFORE the mutation — the comparison runs INSIDE\n // against the FRESH task (stronger than the old stale-clone compare).\n const callerWsId = to === 'in_progress'\n ? await callerWorkspace(deps, exec as ToolRunContext)\n : undefined\n // R1: every state guard + the write itself run inside the mutation.\n let next: TaskRecord | undefined\n let beforeTask: TaskRecord | undefined\n await store.mutate('task-moved', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n beforeTask = task\n\n // Code-level gate: agents never complete a task.\n if (to === 'done') {\n throw new ToolError(ERR.forbidden, 'moving a task to done requires explicit user confirmation (GUI); agents cannot do it')\n }\n if (!canTransition(task.status, to)) {\n throw new ToolError(ERR.invalidTransition, `illegal transition ${task.status} → ${to}`)\n }\n // Exclusive hold: while a task is in_progress under a session\n // (explicit claimedBy — an agent claim or a live execution), no other\n // session may move it (that would be a takeover).\n if (task.status === 'in_progress' && task.claimedBy !== undefined && task.claimedBy !== actor.sessionId) {\n throw new ToolError(ERR.forbidden, `task is held by session ${task.claimedBy}; never take over another session's claim`)\n }\n // Claim boundary: the calling session must belong to the task's project.\n if (isClaim(task.status, to) && callerWsId !== task.workspaceId) {\n throw new ToolError(ERR.workspaceMismatch, 'only a session inside this task\\'s project may claim it')\n }\n next = structuredClone(task)\n next.status = to\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n if (isClaim(task.status, to)) next.blocked = false\n // Record the holder on a claim; every move out of in_progress releases it.\n syncClaim(next, to, deps.now(), isClaim(task.status, to) ? actor.sessionId : undefined)\n ledger.tasks[index] = next\n return [next]\n })\n const sessionArchive = to === 'archived' && args.archiveSessions === true\n ? await archiveTaskSessions(beforeTask ?? next!, deps.workspaces.archiveSession)\n : undefined\n return json({ task: summarize(next!), ...(sessionArchive !== undefined ? { sessionArchive } : {}) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ----------------------------------------------------------- comment_add\n disposers.push(register(defineTool({\n name: 'taskboard_comment_add',\n description:\n 'Append a progress/report comment to a task. When handing off to review, the comment should cover: '\n + 'what changed, how it was verified, outcome, and remaining risks.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n body: { type: 'string', required: true, description: 'Comment text (1..4000 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comment?: { id?: string }; task?: { id?: string; version?: number; status?: string } }\n const c = v.comment\n const t = v.task\n if (c === undefined || t === undefined) return [{ type: 'text', text: '评论失败。' }]\n // The comment bumped the version — echo it so the agent can chain the\n // next write (e.g. move → in_review) WITHOUT re-reading.\n return [{\n type: 'text',\n text: `评论 ${c.id} 已添加;任务 ${t.id} 当前 v${t.version} [${t.status}](后续写操作用此版本号).`,\n }]\n },\n },\n async execute(args: { id: string; body: string }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const comment = {\n id: newCommentId(),\n body: normalizeBody(args.body),\n version: 1,\n createdAt: deps.now(),\n threadId: sessionId,\n }\n // R1: find + append inside the mutation (no ifVersion by design —\n // comments are append-only — but the write must not clobber a task\n // that changed while we were queued).\n let next: TaskRecord | undefined\n await store.mutate('comment-added', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n next.comments.push(comment)\n next.version = task.version + 1\n next.updatedAt = deps.now()\n ledger.tasks[index] = next\n return [next]\n })\n return json({ comment, task: { id: next!.id, version: next!.version, status: next!.status } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- comments\n disposers.push(register(defineTool({\n name: 'taskboard_comments',\n description: 'List a task\\'s comments, oldest first. Read them before deciding to start work.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comments?: unknown[] }\n const list = v.comments as Array<{ body: string; createdAt: number; threadId?: string }> | undefined\n if (list === undefined || list.length === 0) return [{ type: 'text', text: '无评论。' }]\n const lines = list.map(c => {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n return `- [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`\n })\n return [{ type: 'text', text: `评论 ${list.length} 条:\\n${lines.join('\\n')}` }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n return json({ comments: task.comments })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- delete\n disposers.push(register(defineTool({\n name: 'taskboard_delete',\n description:\n 'Soft-delete a task (marks it trashed; the user confirms the purge in the GUI). '\n + 'Requires ifVersion. Prefer canceled/archived over delete unless the task was a mistake.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { trashed?: boolean }\n return [{ type: 'text', text: v.trashed === true ? '任务已标记删除(等待用户在 GUI 清除)。' : '删除失败。' }]\n },\n },\n async execute(args: { id: string; ifVersion: number }, exec: unknown) {\n try {\n caller(exec as ToolRunContext)\n // R1: guards inside the mutation. S5: a running execution keeps\n // writing to the task (report, settlement) — refuse the soft-delete\n // until it is cancelled or settled. T8: clear claim residue.\n let next: TaskRecord | undefined\n await store.mutate('task-deleted', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.executions.some(e => e.outcome === 'running')) {\n throw new ToolError(ERR.invalidInput, '任务有正在运行的执行(先在 GUI 取消或等它结束再删除)')\n }\n next = structuredClone(task)\n next.trashedAt = deps.now()\n next.version = task.version + 1\n delete next.claimedBy\n delete next.claimedAt\n next.blocked = false\n ledger.tasks[index] = next\n return [next]\n })\n return { trashed: true }\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- checklist\n disposers.push(register(defineTool({\n name: 'taskboard_checklist',\n description:\n `Manage the task's acceptance checklist (DoD). Actions: \"add\" (append item texts, ≤10 per call), `\n + '\"check\" (mark an item done, with an optional evidence note), \"uncheck\" (reopen an item). '\n + 'Checking items NEVER completes the task — done stays a user-only action. Requires ifVersion.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n action: { type: 'string', required: true, description: 'add | check | uncheck.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n items: {\n type: 'array',\n description: 'Item texts to append (action=add only; 1..10 per call, 200 chars each).',\n items: { type: 'string' },\n },\n itemId: { type: 'string', description: 'The checklist item id (action=check/uncheck).' },\n note: { type: 'string', description: 'Evidence note recorded with the check (≤400 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; version?: number }; checklist?: Array<Record<string, unknown>>; done?: number; total?: number }\n if (v.task === undefined || v.checklist === undefined) return [{ type: 'text', text: '清单操作失败。' }]\n const lines = v.checklist.map((i, index) => `${i.checked === true ? '☑' : '☐'} [${index + 1}] ${String(i.text)}${i.note !== undefined ? `(证据: ${String(i.note)})` : ''} id=${String(i.id)}`)\n return [{\n type: 'text',\n text: `任务 ${v.task.id} 验收清单 ${v.done ?? 0}/${v.total ?? 0} 已完成,当前 v${v.task.version}:\\n${lines.join('\\n')}`,\n }]\n },\n },\n async execute(args: {\n id: string\n action: string\n ifVersion: number\n items?: string[]\n itemId?: string\n note?: string\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n // R1: guards + the checklist edit itself run inside the mutation.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n const checklist: ChecklistItem[] = next.checklist === undefined ? [] : [...next.checklist]\n\n if (args.action === 'add') {\n const texts = args.items ?? []\n if (texts.length === 0 || texts.length > 10) {\n throw new ToolError(ERR.invalidInput, 'items must carry 1..10 texts per add call')\n }\n if (checklist.length + texts.length > MAX_CHECKLIST_ITEMS) {\n throw new ToolError(ERR.invalidInput, `checklist may hold at most ${MAX_CHECKLIST_ITEMS} items (currently ${checklist.length})`)\n }\n checklist.push(...checklistFromTexts(texts))\n } else if (args.action === 'check') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for check')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n const note = args.note !== undefined && args.note.trim().length > 0 ? args.note.trim().slice(0, 400) : undefined\n item.checked = true\n item.checkedBy = actor.sessionId\n item.checkedAt = deps.now()\n if (note !== undefined) item.note = note\n } else if (args.action === 'uncheck') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for uncheck')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n item.checked = false\n delete item.checkedBy\n delete item.checkedAt\n delete item.note\n } else {\n throw new ToolError(ERR.invalidInput, `action must be add | check | uncheck (got \"${args.action}\")`)\n }\n\n if (checklist.length > 0) next.checklist = checklist\n else delete next.checklist\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n ledger.tasks[index] = next\n return [next]\n })\n const progress = next!.checklist !== undefined\n ? { done: next!.checklist.filter(i => i.checked).length, total: next!.checklist.length }\n : { done: 0, total: 0 }\n return json({ task: { id: next!.id, version: next!.version }, checklist: next!.checklist ?? [], ...progress })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------- execution report\n disposers.push(register(defineTool({\n name: 'taskboard_execution_report',\n description:\n 'Submit the structured execution report for the task you are currently executing (summary / changed '\n + 'files / how you verified / artifacts / remaining risk). Submit BEFORE moving the task to in_review; '\n + 'a later submission overwrites the previous report. If your run already settled, you may back-submit '\n + 'onto your latest succeeded execution while you still hold the task. Commits and diffs are host-collected.',\n parameters: {\n summary: { type: 'string', required: true, description: 'What was done (1..2000 chars).' },\n changedFiles: {\n type: 'array',\n description: 'Files you changed (paths, ≤50 × 300 chars).',\n items: { type: 'string' },\n },\n checks: {\n type: 'array',\n description: 'How the work was verified (e.g. test commands + outcomes, ≤50 entries).',\n items: { type: 'string' },\n },\n artifacts: {\n type: 'array',\n description: 'Artifacts worth reviewing (build outputs, screenshots, docs, ≤30 entries).',\n items: { type: 'string' },\n },\n risk: { type: 'string', description: 'Known remaining risks or follow-ups (≤2000 chars, optional).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { taskId?: string; executionId?: string; report?: { summary?: string } }\n if (v.taskId === undefined || v.report === undefined) return [{ type: 'text', text: '报告提交失败。' }]\n return [{\n type: 'text',\n text: `执行报告已记录到任务 ${v.taskId}(执行 ${v.executionId}):${v.report.summary?.slice(0, 120) ?? ''}\\n`\n + '接下来:taskboard_comment_add 留交接评论,然后 taskboard_move 移至待验收 in_review。',\n }]\n },\n },\n async execute(args: {\n summary: string\n changedFiles?: string[]\n checks?: string[]\n artifacts?: string[]\n risk?: string\n }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const report = normalizeExecutionReport(args)\n // Path 1 (unchanged): attach to the RUNNING execution this session\n // owns — reports ride the live run, so the agent never needs ids.\n let taskId: string | undefined\n let executionId: string | undefined\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n const execution = task.executions.find(e => e.sessionId === sessionId && e.outcome === 'running')\n if (execution !== undefined) {\n execution.report = report\n taskId = task.id\n executionId = execution.id\n return [task]\n }\n }\n return undefined\n })\n // Path 2 (review follow-up, P2): back-submit onto a session-owned\n // SETTLED execution — the main conversation claims tasks directly and\n // has no live run. Allowed when the session holds the task or owns\n // its latest successful execution; never touches anyone else's runs.\n if (taskId === undefined || executionId === undefined) {\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n if (task.trashedAt !== undefined || task.status === 'archived') continue\n const last = task.executions[task.executions.length - 1]\n const owned = last !== undefined && last.sessionId === sessionId && last.outcome === 'succeeded'\n const holds = task.claimedBy === sessionId && last !== undefined && last.sessionId === sessionId\n if (!owned && !holds) continue\n last!.report = report\n taskId = task.id\n executionId = last!.id\n return [task]\n }\n return undefined\n })\n }\n if (taskId === undefined || executionId === undefined) {\n throw new ToolError(ERR.forbidden, 'no running execution and no settled execution of yours to report on — reports attach to your running execution, or back-submit onto your latest succeeded one while you hold the task')\n }\n return json({ taskId, executionId, report })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n return disposers\n}\n"],"mappings":"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"}
|
package/lib/shared/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","names":[],"sources":["../../src/shared/api.ts"],"sourcesContent":["/**\n * Wire contract for the /taskboard host routes: the JSON envelope,\n * request/response shapes, and SSE event payloads shared by the host routes\n * and the browser client.\n *\n * @module dsh-taskboard/shared/api\n */\nimport type { BoardSettings, TaskLedger, TaskModel, TaskRecord, TaskSummary } from './protocol.ts'\n\nexport type { TaskModel, TaskRecord }\n\n/** Route prefix on the shared DSH webserver (same origin as the GUI). */\nexport const ROUTE_PREFIX = '/dsh-taskboard'\n\n/** SSE stream path (exact route; longest-prefix wins keep it disjoint). */\nexport const SSE_PATH = '/dsh-taskboard/events'\n\n/** Stable error codes (mirror the tool-level codes plus HTTP mapping). */\nexport type ApiErrorCode =\n | 'invalid_input'\n | 'not_found'\n | 'version_conflict'\n | 'invalid_transition'\n | 'forbidden'\n | 'internal'\n\n/** Success envelope. */\nexport type ApiOk<T> = { ok: true; value: T }\n\n/** Failure envelope. */\nexport type ApiFail = { ok: false; error: { code: ApiErrorCode; message: string } }\n\n/** The envelope either way. */\nexport type ApiResult<T> = ApiOk<T> | ApiFail\n\n// ---------------------------------------------------------------------------\n// payloads\n// ---------------------------------------------------------------------------\n\n/** Full-state response (the reconnect baseline after an SSE gap). */\nexport type StateResponse = TaskLedger\n\n/**\n * Workspace listing for the UI pickers. `repoCount` (0.6.3): how many repos a\n * task mirror of this workspace would cover (root repo + nested) — the form's\n * worktree option shows the mirror badge when it exceeds 1.\n */\nexport type WorkspaceView = { id: string; path: string; title: string; sessionCount: number; gitAvailable?: boolean; repoCount?: number }\n\n/** Create-task request body (actor is always the GUI user). */\nexport type CreateTaskBody = {\n title: string\n workspaceId: string\n urgency: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel\n /** Code isolation for executions ('worktree' | 'none'); omitted = default. */\n isolation?: string\n /** Agent preset for execution sessions; omitted = deployment default. */\n presetId?: string\n /** Execution permission preset ('workspace-write' | 'read-only' | 'danger-full-access'); omitted = default. */\n permission?: string\n /** Acceptance checklist item texts (host mints ids, all unchecked). */\n checklist?: string[]\n}\n\n/** Update-task request body (ifVersion mandatory). */\nexport type UpdateTaskBody = {\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n /** Rebind the task to another project (GUI owner surface only). */\n workspaceId?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel | null\n /** Change isolation; locked once the task has execution history. */\n isolation?: string\n /** Change the execution preset (takes effect on the next run). */\n presetId?: string | null\n /** Change the execution permission (0.5.5; 'workspace-write' | 'read-only' | 'danger-full-access'). */\n permission?: string | null\n /** Replace the whole checklist (GUI owner surface); null clears it. */\n checklist?: unknown\n}\n\n/** Move-task request body (ifVersion mandatory; the user MAY move to done). */\nexport type MoveTaskBody = { ifVersion: number; status: string }\n\n/**\n * Quick-reject request body (card ✗ button): move back to todo plus an\n * optional user comment, committed as ONE ledger mutation so a failed move\n * can never strand an orphan comment.\n */\nexport type RejectTaskBody = { ifVersion: number; body?: string }\n\n/** Comment request body. */\nexport type CommentBody = { body: string }\n\n/** Delete request body (purge=true physically removes a trashed task). */\nexport type DeleteTaskBody = { ifVersion?: number; purge?: boolean }\n\n/** Run request body; `reuse: true` = 续跑 (keep a live worktree as-is). */\nexport type RunTaskBody = { reuse?: boolean }\n\n/** One repo's merge outcome in a multi-repo merge (0.6.3; `repo: ''` = the workspace root repo). */\nexport type MergeRepoResult = {\n repo: string\n branch: string\n outcome: 'merged' | 'noop' | 'failed'\n /** Failure reason (verbatim git message) when outcome = 'failed'. */\n error?: string\n}\n\n/**\n * Merge outcome. Legacy single-repo tasks keep the flat shape; multi-repo\n * mirror tasks (0.6.3) additionally return per-repo results — merges run\n * sequentially and a failed repo does not block the others (plan §4.5).\n */\nexport type MergeBranchResponse = {\n merged: boolean\n noop?: boolean\n /** The merged task branch (legacy single-repo shape; multi-repo responses omit it). */\n branch?: string\n /** Present only on multi-repo mirror merges (0.6.3). */\n results?: MergeRepoResult[]\n}\n\n/** Remove a task's worktree; optionally delete its branch too. */\nexport type WorktreeRemoveBody = { deleteBranch?: boolean }\n\n/** One orphan worktree directory (exists on disk, owned by no live task). */\nexport type OrphanWorktree = { workspaceId: string; workspacePath: string; taskId: string; path: string }\n\n/** A git-enabled workspace whose .gitignore does not cover the worktree dir. */\nexport type GitignoreSuggestion = { workspaceId: string; workspacePath: string }\n\n/** Health-diagnostics response (⚙ panel). */\nexport type DiagnosticsResponse = {\n revision: number\n tasks: number\n /** Executions currently marked `running`. */\n staleRunning: number\n /** Worktree directories whose task no longer exists in the ledger. */\n orphanWorktrees: OrphanWorktree[]\n /** Git workspaces whose .gitignore does not ignore the worktree dir. */\n gitIgnoreSuggestions: GitignoreSuggestion[]\n}\n\n/** Fields a task template may prefill (0.4.0). */\nexport type TaskTemplateSpec = {\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel\n isolation?: string\n presetId?: string\n /** Execution permission preset (0.5.5). */\n permission?: string\n /** Checklist item texts (host mints ids at create time). */\n checklist?: string[]\n}\n\n/** One reusable task template (0.4.0). */\nexport type TaskTemplate = {\n id: string\n name: string\n task: TaskTemplateSpec\n /** Seeded built-in templates (kept on load, deletable like any other). */\n builtin?: boolean\n createdAt: number\n updatedAt: number\n}\n\n/** Templates listing response. */\nexport type TemplatesResponse = { templates: TaskTemplate[] }\n\n/** Board-settings response (0.5.0; absent fields follow factory defaults). */\nexport type SettingsResponse = BoardSettings\n\n/** Update-board-settings request body (0.5.0; whole-object replace semantics). */\nexport type UpdateSettingsBody = {\n /** Default code isolation for NEW tasks ('worktree' | 'none'). */\n defaultIsolation?: string\n /** Automatically capture external workspace sessions into the taskboard. */\n syncExternalSessions?: boolean\n /** Default permission preset for NEW tasks ('workspace-write' | 'read-only' | 'danger-full-access'). */\n defaultPermission?: string\n}\n\n/** Prompt completion item for skills and slash commands (0.5.5). */\nexport type PromptCompletionItem = {\n name: string\n kind: 'skill' | 'command'\n description?: string\n hint?: string\n}\n\n/** Prompt completions response (0.5.5). */\nexport type PromptCompletionsResponse = {\n commands: PromptCompletionItem[]\n skills: PromptCompletionItem[]\n}\n\n/** Model item in catalog (0.5.5). */\nexport type CatalogModelItem = {\n provider: string\n model: string\n name?: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n}\n\n/** Preset item in catalog (0.5.5). */\nexport type CatalogPresetItem = {\n id: string\n name?: string\n}\n\n/** Model and preset catalog response (0.5.5). */\nexport type ModelCatalogResponse = {\n models: CatalogModelItem[]\n presets: CatalogPresetItem[]\n defaultPresetId?: string\n}\n\n/** Import dry-run response (0.4.0): every task classified, nothing written. */\nexport type ImportPreviewResponse = {\n plan: {\n create: Array<{ id: string; title: string; status: string }>\n overwrite: Array<{ id: string; title: string; status: string }>\n invalid: Array<{ id?: string; reason: string }>\n }\n}\n\n/** Import commit response. */\nexport type ImportCommitResponse = {\n mode: 'merge' | 'replace'\n created: number\n overwritten: number\n replacedTotal?: number\n /** The backup file written BEFORE a replace wiped the live ledger. */\n backupFile?: string\n}\n\n/** Diff-viewer response (0.4.0). */\nexport type DiffResponse = { diff: string; truncated: boolean }\n\n/** One task (full record) response. */\nexport type TaskResponse = TaskRecord\n\n/** Summary response used by list-ish endpoints. */\nexport type SummaryResponse = { tasks: TaskSummary[] }\n\n// ---------------------------------------------------------------------------\n// SSE\n// ---------------------------------------------------------------------------\n\n/** Change frame pushed on every committed ledger mutation. */\nexport type ChangeEvent = {\n revision: number\n kind: 'task-created' | 'task-updated' | 'task-moved' | 'task-deleted' | 'comment-added' | 'execution-recorded' | 'settings-updated' | 'ledger-replaced'\n tasks: TaskSummary[]\n}"],"mappings":";;AAYA,MAAa,eAAe;;AAG5B,MAAa,WAAW"}
|
|
1
|
+
{"version":3,"file":"api.js","names":[],"sources":["../../src/shared/api.ts"],"sourcesContent":["/**\n * Wire contract for the /taskboard host routes: the JSON envelope,\n * request/response shapes, and SSE event payloads shared by the host routes\n * and the browser client.\n *\n * @module dsh-taskboard/shared/api\n */\nimport type { BoardSettings, TaskLedger, TaskModel, TaskRecord, TaskSummary } from './protocol.ts'\n\nexport type { TaskModel, TaskRecord }\n\n/** Route prefix on the shared DSH webserver (same origin as the GUI). */\nexport const ROUTE_PREFIX = '/dsh-taskboard'\n\n/** SSE stream path (exact route; longest-prefix wins keep it disjoint). */\nexport const SSE_PATH = '/dsh-taskboard/events'\n\n/** Stable error codes (mirror the tool-level codes plus HTTP mapping). */\nexport type ApiErrorCode =\n | 'invalid_input'\n | 'not_found'\n | 'version_conflict'\n | 'invalid_transition'\n | 'forbidden'\n | 'internal'\n\n/** Success envelope. */\nexport type ApiOk<T> = { ok: true; value: T }\n\n/** Failure envelope. */\nexport type ApiFail = { ok: false; error: { code: ApiErrorCode; message: string } }\n\n/** The envelope either way. */\nexport type ApiResult<T> = ApiOk<T> | ApiFail\n\n// ---------------------------------------------------------------------------\n// payloads\n// ---------------------------------------------------------------------------\n\n/** Full-state response (the reconnect baseline after an SSE gap). */\nexport type StateResponse = TaskLedger & { capabilities?: { archiveSessions: boolean } }\n\n/**\n * Workspace listing for the UI pickers. `repoCount` (0.6.3): how many repos a\n * task mirror of this workspace would cover (root repo + nested) — the form's\n * worktree option shows the mirror badge when it exceeds 1.\n */\nexport type WorkspaceView = { id: string; path: string; title: string; sessionCount: number; gitAvailable?: boolean; repoCount?: number }\n\n/** Create-task request body (actor is always the GUI user). */\nexport type CreateTaskBody = {\n title: string\n workspaceId: string\n urgency: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel\n /** Code isolation for executions ('worktree' | 'none'); omitted = default. */\n isolation?: string\n /** Agent preset for execution sessions; omitted = deployment default. */\n presetId?: string\n /** Execution permission preset ('workspace-write' | 'read-only' | 'danger-full-access'); omitted = default. */\n permission?: string\n /** Acceptance checklist item texts (host mints ids, all unchecked). */\n checklist?: string[]\n}\n\n/** Update-task request body (ifVersion mandatory). */\nexport type UpdateTaskBody = {\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n /** Rebind the task to another project (GUI owner surface only). */\n workspaceId?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel | null\n /** Change isolation; locked once the task has execution history. */\n isolation?: string\n /** Change the execution preset (takes effect on the next run). */\n presetId?: string | null\n /** Change the execution permission (0.5.5; 'workspace-write' | 'read-only' | 'danger-full-access'). */\n permission?: string | null\n /** Replace the whole checklist (GUI owner surface); null clears it. */\n checklist?: unknown\n}\n\n/** Move-task request body (ifVersion mandatory; the user MAY move to done). */\nexport type MoveTaskBody = { ifVersion: number; status: string; archiveSessions?: boolean }\n\nexport type SessionArchiveResult = { archived: string[]; failed: Array<{ sessionId: string; error: string }>; unsupported: string[] }\nexport type MoveTaskResponse = TaskSummary & { sessionArchive?: SessionArchiveResult }\n\n/**\n * Quick-reject request body (card ✗ button): move back to todo plus an\n * optional user comment, committed as ONE ledger mutation so a failed move\n * can never strand an orphan comment.\n */\nexport type RejectTaskBody = { ifVersion: number; body?: string }\n\n/** Comment request body. */\nexport type CommentBody = { body: string }\n\n/** Delete request body (purge=true physically removes a trashed task). */\nexport type DeleteTaskBody = { ifVersion?: number; purge?: boolean }\n\n/** Run request body; `reuse: true` = 续跑 (keep a live worktree as-is). */\nexport type RunTaskBody = { reuse?: boolean }\n\n/** One repo's merge outcome in a multi-repo merge (0.6.3; `repo: ''` = the workspace root repo). */\nexport type MergeRepoResult = {\n repo: string\n branch: string\n outcome: 'merged' | 'noop' | 'failed'\n /** Failure reason (verbatim git message) when outcome = 'failed'. */\n error?: string\n}\n\n/**\n * Merge outcome. Legacy single-repo tasks keep the flat shape; multi-repo\n * mirror tasks (0.6.3) additionally return per-repo results — merges run\n * sequentially and a failed repo does not block the others (plan §4.5).\n */\nexport type MergeBranchResponse = {\n merged: boolean\n noop?: boolean\n /** The merged task branch (legacy single-repo shape; multi-repo responses omit it). */\n branch?: string\n /** Present only on multi-repo mirror merges (0.6.3). */\n results?: MergeRepoResult[]\n}\n\n/** Remove a task's worktree; optionally delete its branch too. */\nexport type WorktreeRemoveBody = { deleteBranch?: boolean }\n\n/** One orphan worktree directory (exists on disk, owned by no live task). */\nexport type OrphanWorktree = { workspaceId: string; workspacePath: string; taskId: string; path: string }\n\n/** A git-enabled workspace whose .gitignore does not cover the worktree dir. */\nexport type GitignoreSuggestion = { workspaceId: string; workspacePath: string }\n\n/** Health-diagnostics response (⚙ panel). */\nexport type DiagnosticsResponse = {\n revision: number\n tasks: number\n /** Executions currently marked `running`. */\n staleRunning: number\n /** Worktree directories whose task no longer exists in the ledger. */\n orphanWorktrees: OrphanWorktree[]\n /** Git workspaces whose .gitignore does not ignore the worktree dir. */\n gitIgnoreSuggestions: GitignoreSuggestion[]\n}\n\n/** Fields a task template may prefill (0.4.0). */\nexport type TaskTemplateSpec = {\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel\n isolation?: string\n presetId?: string\n /** Execution permission preset (0.5.5). */\n permission?: string\n /** Checklist item texts (host mints ids at create time). */\n checklist?: string[]\n}\n\n/** One reusable task template (0.4.0). */\nexport type TaskTemplate = {\n id: string\n name: string\n task: TaskTemplateSpec\n /** Seeded built-in templates (kept on load, deletable like any other). */\n builtin?: boolean\n createdAt: number\n updatedAt: number\n}\n\n/** Templates listing response. */\nexport type TemplatesResponse = { templates: TaskTemplate[] }\n\n/** Board-settings response (0.5.0; absent fields follow factory defaults). */\nexport type SettingsResponse = BoardSettings\n\n/** Update-board-settings request body (0.5.0; whole-object replace semantics). */\nexport type UpdateSettingsBody = {\n /** Default code isolation for NEW tasks ('worktree' | 'none'). */\n defaultIsolation?: string\n /** Automatically capture external workspace sessions into the taskboard. */\n syncExternalSessions?: boolean\n /** Default permission preset for NEW tasks ('workspace-write' | 'read-only' | 'danger-full-access'). */\n defaultPermission?: string\n}\n\n/** Prompt completion item for skills and slash commands (0.5.5). */\nexport type PromptCompletionItem = {\n name: string\n kind: 'skill' | 'command'\n description?: string\n hint?: string\n}\n\n/** Prompt completions response (0.5.5). */\nexport type PromptCompletionsResponse = {\n commands: PromptCompletionItem[]\n skills: PromptCompletionItem[]\n}\n\n/** Model item in catalog (0.5.5). */\nexport type CatalogModelItem = {\n provider: string\n model: string\n name?: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n}\n\n/** Preset item in catalog (0.5.5). */\nexport type CatalogPresetItem = {\n id: string\n name?: string\n}\n\n/** Model and preset catalog response (0.5.5). */\nexport type ModelCatalogResponse = {\n models: CatalogModelItem[]\n presets: CatalogPresetItem[]\n defaultPresetId?: string\n}\n\n/** Import dry-run response (0.4.0): every task classified, nothing written. */\nexport type ImportPreviewResponse = {\n plan: {\n create: Array<{ id: string; title: string; status: string }>\n overwrite: Array<{ id: string; title: string; status: string }>\n invalid: Array<{ id?: string; reason: string }>\n }\n}\n\n/** Import commit response. */\nexport type ImportCommitResponse = {\n mode: 'merge' | 'replace'\n created: number\n overwritten: number\n replacedTotal?: number\n /** The backup file written BEFORE a replace wiped the live ledger. */\n backupFile?: string\n}\n\n/** Diff-viewer response (0.4.0). */\nexport type DiffResponse = { diff: string; truncated: boolean }\n\n/** One task (full record) response. */\nexport type TaskResponse = TaskRecord\n\n/** Summary response used by list-ish endpoints. */\nexport type SummaryResponse = { tasks: TaskSummary[] }\n\n// ---------------------------------------------------------------------------\n// SSE\n// ---------------------------------------------------------------------------\n\n/** Change frame pushed on every committed ledger mutation. */\nexport type ChangeEvent = {\n revision: number\n kind: 'task-created' | 'task-updated' | 'task-moved' | 'task-deleted' | 'comment-added' | 'execution-recorded' | 'settings-updated' | 'ledger-replaced'\n tasks: TaskSummary[]\n}\n"],"mappings":";;AAYA,MAAa,eAAe;;AAG5B,MAAa,WAAW"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
//#region src/shared/builtin-templates.ts
|
|
2
|
+
/** Stable ids of the factory templates (persisted in the side file). */
|
|
3
|
+
const BUILTIN_TEMPLATE_IDS = [
|
|
4
|
+
"tpl-feature",
|
|
5
|
+
"tpl-bugfix",
|
|
6
|
+
"tpl-release",
|
|
7
|
+
"tpl-patrol"
|
|
8
|
+
];
|
|
9
|
+
/** Built-in template content per locale (name + prefilled task spec). */
|
|
10
|
+
const BUILTIN_TEMPLATE_CONTENT = {
|
|
11
|
+
zh: {
|
|
12
|
+
"tpl-feature": {
|
|
13
|
+
name: "新增功能",
|
|
14
|
+
task: {
|
|
15
|
+
title: "新增:",
|
|
16
|
+
prompt: [
|
|
17
|
+
"实现以上新功能并按序交接:",
|
|
18
|
+
"1. 明确需求边界与验收标准,列出实现要点",
|
|
19
|
+
"2. 实现功能(含类型定义与错误处理)",
|
|
20
|
+
"3. 补充测试(单测/回归)",
|
|
21
|
+
"4. 运行相关测试套件确认通过"
|
|
22
|
+
].join("\n"),
|
|
23
|
+
urgency: "normal",
|
|
24
|
+
checklist: [
|
|
25
|
+
"实现要点已明确(需求边界与验收标准)",
|
|
26
|
+
"功能已实现并补充测试",
|
|
27
|
+
"相关测试套件通过"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"tpl-bugfix": {
|
|
32
|
+
name: "Bug 修复",
|
|
33
|
+
task: {
|
|
34
|
+
title: "修复:",
|
|
35
|
+
prompt: [
|
|
36
|
+
"修复以上问题并按序交接:",
|
|
37
|
+
"1. 复现问题(写最小复现步骤或测试)",
|
|
38
|
+
"2. 定位根因,说明为什么会发生",
|
|
39
|
+
"3. 修复并补回归测试",
|
|
40
|
+
"4. 运行相关测试套件确认无回归"
|
|
41
|
+
].join("\n"),
|
|
42
|
+
urgency: "urgent",
|
|
43
|
+
checklist: [
|
|
44
|
+
"已复现并定位根因",
|
|
45
|
+
"修复已提交到任务分支",
|
|
46
|
+
"回归测试通过"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"tpl-release": {
|
|
51
|
+
name: "发布检查",
|
|
52
|
+
task: {
|
|
53
|
+
title: "发布:",
|
|
54
|
+
prompt: "执行发布流程:版本号更新、构建、测试、变更记录,完成后按序交接(不要实际推送/发布,等用户确认)。",
|
|
55
|
+
urgency: "normal",
|
|
56
|
+
checklist: [
|
|
57
|
+
"版本号已更新(package.json 与版本常量同步)",
|
|
58
|
+
"构建通过",
|
|
59
|
+
"全部测试通过",
|
|
60
|
+
"变更记录已写"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"tpl-patrol": {
|
|
65
|
+
name: "例行巡检",
|
|
66
|
+
task: {
|
|
67
|
+
title: "巡检:",
|
|
68
|
+
prompt: [
|
|
69
|
+
"例行巡检:检查依赖更新、失败测试、明显代码问题与未处理的告警。",
|
|
70
|
+
"发现的问题逐条列出(严重度/位置/建议),小问题直接修复,大问题只报告不动手。",
|
|
71
|
+
"输出巡检摘要(用 {{lastComments}} 可回看上次巡检结论)。"
|
|
72
|
+
].join("\n"),
|
|
73
|
+
urgency: "relaxed",
|
|
74
|
+
execution: {
|
|
75
|
+
mode: "scheduled",
|
|
76
|
+
cron: "0 9 * * 1"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
en: {
|
|
82
|
+
"tpl-feature": {
|
|
83
|
+
name: "New feature",
|
|
84
|
+
task: {
|
|
85
|
+
title: "New feature:",
|
|
86
|
+
prompt: [
|
|
87
|
+
"Implement the new feature above and hand off in order:",
|
|
88
|
+
"1. Clarify the requirement boundaries and acceptance criteria; list the implementation points",
|
|
89
|
+
"2. Implement the feature (including type definitions and error handling)",
|
|
90
|
+
"3. Add tests (unit / regression)",
|
|
91
|
+
"4. Run the relevant test suites and confirm they pass"
|
|
92
|
+
].join("\n"),
|
|
93
|
+
urgency: "normal",
|
|
94
|
+
checklist: [
|
|
95
|
+
"Implementation points clarified (requirement boundaries and acceptance criteria)",
|
|
96
|
+
"Feature implemented with tests added",
|
|
97
|
+
"Relevant test suites pass"
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"tpl-bugfix": {
|
|
102
|
+
name: "Bug fix",
|
|
103
|
+
task: {
|
|
104
|
+
title: "Fix:",
|
|
105
|
+
prompt: [
|
|
106
|
+
"Fix the issue above and hand off in order:",
|
|
107
|
+
"1. Reproduce the issue (write minimal reproduction steps or a test)",
|
|
108
|
+
"2. Locate the root cause and explain why it happens",
|
|
109
|
+
"3. Fix it and add regression tests",
|
|
110
|
+
"4. Run the relevant test suites and confirm there are no regressions"
|
|
111
|
+
].join("\n"),
|
|
112
|
+
urgency: "urgent",
|
|
113
|
+
checklist: [
|
|
114
|
+
"Issue reproduced and root cause located",
|
|
115
|
+
"Fix committed to the task branch",
|
|
116
|
+
"Regression tests pass"
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"tpl-release": {
|
|
121
|
+
name: "Release check",
|
|
122
|
+
task: {
|
|
123
|
+
title: "Release:",
|
|
124
|
+
prompt: "Run the release process: bump the version, build, test, and update the changelog, then hand off in order (do not actually push / publish — wait for user confirmation).",
|
|
125
|
+
urgency: "normal",
|
|
126
|
+
checklist: [
|
|
127
|
+
"Version bumped (package.json and version constants in sync)",
|
|
128
|
+
"Build passes",
|
|
129
|
+
"All tests pass",
|
|
130
|
+
"Changelog written"
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"tpl-patrol": {
|
|
135
|
+
name: "Routine patrol",
|
|
136
|
+
task: {
|
|
137
|
+
title: "Patrol:",
|
|
138
|
+
prompt: [
|
|
139
|
+
"Routine patrol: check dependency updates, failing tests, obvious code issues, and unhandled alerts.",
|
|
140
|
+
"List each finding (severity / location / suggestion); fix small issues directly, and only report (do not touch) large ones.",
|
|
141
|
+
"Output a patrol summary (use {{lastComments}} to review the last patrol’s conclusions)."
|
|
142
|
+
].join("\n"),
|
|
143
|
+
urgency: "relaxed",
|
|
144
|
+
execution: {
|
|
145
|
+
mode: "scheduled",
|
|
146
|
+
cron: "0 9 * * 1"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
//#endregion
|
|
153
|
+
export { BUILTIN_TEMPLATE_CONTENT, BUILTIN_TEMPLATE_IDS };
|
|
154
|
+
|
|
155
|
+
//# sourceMappingURL=builtin-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin-templates.js","names":[],"sources":["../../src/shared/builtin-templates.ts"],"sourcesContent":["/**\n * Built-in task-template content (0.6.4): the four factory templates seeded\n * into the template side file, in both shipped locales. The host seeds the zh\n * copy as the persisted fallback (the side file is plain data); the client\n * resolves the ACTIVE locale's copy at render / prefill time via\n * {@link builtinTemplateContent}, so an English GUI shows English template\n * text without ever re-seeding the side file.\n *\n * Kept in shared/ (not the client i18n dictionaries) because the content is\n * structured task DATA — a checklist is an array and the prompt is multi-line\n * — rather than UI-chrome strings, and because the host needs the same seed\n * content to write the side file.\n *\n * @module dsh-taskboard/shared/builtin-templates\n */\nimport type { TaskTemplateSpec } from './api.ts'\n\n/** Shipped built-in template locales (mirrors the client i18n LocaleId). */\nexport type BuiltinTemplateLocale = 'zh' | 'en'\n\n/** One built-in template's localized name + task spec. */\nexport interface BuiltinTemplateContent {\n name: string\n task: TaskTemplateSpec\n}\n\n/** Stable ids of the factory templates (persisted in the side file). */\nexport const BUILTIN_TEMPLATE_IDS = ['tpl-feature', 'tpl-bugfix', 'tpl-release', 'tpl-patrol'] as const\n\nexport type BuiltinTemplateId = (typeof BUILTIN_TEMPLATE_IDS)[number]\n\n/** Built-in template content per locale (name + prefilled task spec). */\nexport const BUILTIN_TEMPLATE_CONTENT: Readonly<Record<BuiltinTemplateLocale, Readonly<Record<BuiltinTemplateId, BuiltinTemplateContent>>>> = {\n zh: {\n 'tpl-feature': {\n name: '新增功能',\n task: {\n title: '新增:',\n prompt: [\n '实现以上新功能并按序交接:',\n '1. 明确需求边界与验收标准,列出实现要点',\n '2. 实现功能(含类型定义与错误处理)',\n '3. 补充测试(单测/回归)',\n '4. 运行相关测试套件确认通过',\n ].join('\\n'),\n urgency: 'normal',\n checklist: ['实现要点已明确(需求边界与验收标准)', '功能已实现并补充测试', '相关测试套件通过'],\n },\n },\n 'tpl-bugfix': {\n name: 'Bug 修复',\n task: {\n title: '修复:',\n prompt: [\n '修复以上问题并按序交接:',\n '1. 复现问题(写最小复现步骤或测试)',\n '2. 定位根因,说明为什么会发生',\n '3. 修复并补回归测试',\n '4. 运行相关测试套件确认无回归',\n ].join('\\n'),\n urgency: 'urgent',\n checklist: ['已复现并定位根因', '修复已提交到任务分支', '回归测试通过'],\n },\n },\n 'tpl-release': {\n name: '发布检查',\n task: {\n title: '发布:',\n prompt: '执行发布流程:版本号更新、构建、测试、变更记录,完成后按序交接(不要实际推送/发布,等用户确认)。',\n urgency: 'normal',\n checklist: ['版本号已更新(package.json 与版本常量同步)', '构建通过', '全部测试通过', '变更记录已写'],\n },\n },\n 'tpl-patrol': {\n name: '例行巡检',\n task: {\n title: '巡检:',\n prompt: [\n '例行巡检:检查依赖更新、失败测试、明显代码问题与未处理的告警。',\n '发现的问题逐条列出(严重度/位置/建议),小问题直接修复,大问题只报告不动手。',\n '输出巡检摘要(用 {{lastComments}} 可回看上次巡检结论)。',\n ].join('\\n'),\n urgency: 'relaxed',\n execution: { mode: 'scheduled', cron: '0 9 * * 1' },\n },\n },\n },\n en: {\n 'tpl-feature': {\n name: 'New feature',\n task: {\n title: 'New feature:',\n prompt: [\n 'Implement the new feature above and hand off in order:',\n '1. Clarify the requirement boundaries and acceptance criteria; list the implementation points',\n '2. Implement the feature (including type definitions and error handling)',\n '3. Add tests (unit / regression)',\n '4. Run the relevant test suites and confirm they pass',\n ].join('\\n'),\n urgency: 'normal',\n checklist: ['Implementation points clarified (requirement boundaries and acceptance criteria)', 'Feature implemented with tests added', 'Relevant test suites pass'],\n },\n },\n 'tpl-bugfix': {\n name: 'Bug fix',\n task: {\n title: 'Fix:',\n prompt: [\n 'Fix the issue above and hand off in order:',\n '1. Reproduce the issue (write minimal reproduction steps or a test)',\n '2. Locate the root cause and explain why it happens',\n '3. Fix it and add regression tests',\n '4. Run the relevant test suites and confirm there are no regressions',\n ].join('\\n'),\n urgency: 'urgent',\n checklist: ['Issue reproduced and root cause located', 'Fix committed to the task branch', 'Regression tests pass'],\n },\n },\n 'tpl-release': {\n name: 'Release check',\n task: {\n title: 'Release:',\n prompt: 'Run the release process: bump the version, build, test, and update the changelog, then hand off in order (do not actually push / publish — wait for user confirmation).',\n urgency: 'normal',\n checklist: ['Version bumped (package.json and version constants in sync)', 'Build passes', 'All tests pass', 'Changelog written'],\n },\n },\n 'tpl-patrol': {\n name: 'Routine patrol',\n task: {\n title: 'Patrol:',\n prompt: [\n 'Routine patrol: check dependency updates, failing tests, obvious code issues, and unhandled alerts.',\n 'List each finding (severity / location / suggestion); fix small issues directly, and only report (do not touch) large ones.',\n 'Output a patrol summary (use {{lastComments}} to review the last patrol\\u2019s conclusions).',\n ].join('\\n'),\n urgency: 'relaxed',\n execution: { mode: 'scheduled', cron: '0 9 * * 1' },\n },\n },\n },\n}\n\n/**\n * Resolve one built-in template's localized content.\n * @param id - the template id.\n * @param locale - the requested locale.\n * @returns the content, or undefined when `id` is not a built-in template id.\n */\nexport function builtinTemplateContent(id: string, locale: BuiltinTemplateLocale): BuiltinTemplateContent | undefined {\n if (!(BUILTIN_TEMPLATE_IDS as readonly string[]).includes(id)) return undefined\n return BUILTIN_TEMPLATE_CONTENT[locale][id as BuiltinTemplateId]\n}\n"],"mappings":";;AA2BA,MAAa,uBAAuB;CAAC;CAAe;CAAc;CAAe;AAAY;;AAK7F,MAAa,2BAAiI;CAC5I,IAAI;EACF,eAAe;GACb,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAC;KAAsB;KAAc;IAAU;GAC5D;EACF;EACA,cAAc;GACZ,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAC;KAAY;KAAc;IAAQ;GAChD;EACF;EACA,eAAe;GACb,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,WAAW;KAAC;KAAgC;KAAQ;KAAU;IAAQ;GACxE;EACF;EACA,cAAc;GACZ,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAE,MAAM;KAAa,MAAM;IAAY;GACpD;EACF;CACF;CACA,IAAI;EACF,eAAe;GACb,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAC;KAAoF;KAAwC;IAA2B;GACrK;EACF;EACA,cAAc;GACZ,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAC;KAA2C;KAAoC;IAAuB;GACpH;EACF;EACA,eAAe;GACb,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,WAAW;KAAC;KAA+D;KAAgB;KAAkB;IAAmB;GAClI;EACF;EACA,cAAc;GACZ,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAE,MAAM;KAAa,MAAM;IAAY;GACpD;EACF;CACF;AACF"}
|
package/lib/shared/protocol.js
CHANGED
|
@@ -363,6 +363,30 @@ function syncClaim(task, to, now, holder) {
|
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
365
|
/**
|
|
366
|
+
* Collect unique execution session IDs associated with a task:
|
|
367
|
+
* - executions with a non-empty `sessionId`
|
|
368
|
+
* Creator and claim sessions may serve other tasks and are never included.
|
|
369
|
+
* @param task - the task record to inspect.
|
|
370
|
+
* @returns an array of distinct session IDs in stable discovery order.
|
|
371
|
+
*/
|
|
372
|
+
function taskAssociatedSessionIds(task) {
|
|
373
|
+
const seen = /* @__PURE__ */ new Set();
|
|
374
|
+
const result = [];
|
|
375
|
+
const push = (raw) => {
|
|
376
|
+
if (typeof raw === "string") {
|
|
377
|
+
const trimmed = raw.trim();
|
|
378
|
+
if (trimmed.length > 0 && !seen.has(trimmed)) {
|
|
379
|
+
seen.add(trimmed);
|
|
380
|
+
result.push(trimmed);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
if (Array.isArray(task.executions)) {
|
|
385
|
+
for (const ex of task.executions) if (ex !== null && typeof ex === "object") push(ex.sessionId);
|
|
386
|
+
}
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
366
390
|
* Validate and normalize a pinned model: `{ provider, model, reasoningEffort? }`,
|
|
367
391
|
* provider and model must be non-empty trimmed strings.
|
|
368
392
|
* @param raw - the raw input.
|
|
@@ -579,7 +603,20 @@ function validateImportedTask(raw, now) {
|
|
|
579
603
|
body,
|
|
580
604
|
version: numOr(ce, "version", 1),
|
|
581
605
|
createdAt: numOr(ce, "createdAt", now),
|
|
582
|
-
...typeof ce.threadId === "string" ? { threadId: ce.threadId } : {}
|
|
606
|
+
...typeof ce.threadId === "string" ? { threadId: ce.threadId } : {},
|
|
607
|
+
...typeof ce.systemKey === "string" && /^sys\.[A-Za-z0-9]+$/.test(ce.systemKey) && ce.systemKey.length <= 100 ? {
|
|
608
|
+
systemKey: ce.systemKey,
|
|
609
|
+
...typeof ce.systemParams === "object" && ce.systemParams !== null && !Array.isArray(ce.systemParams) ? { systemParams: Object.fromEntries(Object.entries(ce.systemParams).filter(([key, value]) => key.length <= 100 && typeof value === "string" && value.length <= 4e3).slice(0, 20)) } : {},
|
|
610
|
+
...Array.isArray(ce.systemRows) ? { systemRows: ce.systemRows.filter((row) => typeof row === "object" && row !== null && typeof row.repo === "string" && (row.repo === "" || isValidRelRepoPath(row.repo)) && [
|
|
611
|
+
"merged",
|
|
612
|
+
"noop",
|
|
613
|
+
"failed"
|
|
614
|
+
].includes(row.outcome) && (row.error === void 0 || typeof row.error === "string")).slice(0, 8).map((row) => ({
|
|
615
|
+
repo: row.repo,
|
|
616
|
+
outcome: row.outcome,
|
|
617
|
+
...row.error !== void 0 ? { error: row.error.slice(0, 4e3) } : {}
|
|
618
|
+
})) } : {}
|
|
619
|
+
} : {}
|
|
583
620
|
});
|
|
584
621
|
}
|
|
585
622
|
else return fail("comments must be an array");
|
|
@@ -743,6 +780,6 @@ function summarize(task) {
|
|
|
743
780
|
};
|
|
744
781
|
}
|
|
745
782
|
//#endregion
|
|
746
|
-
export { ALL_STATUSES, DEFAULT_ISOLATION, DEFAULT_PERMISSION, MAIN_STATUSES, SECONDARY_STATUSES, URGENCIES, asBoardSettings, asIsolation, asPermission, asStatus, asUrgency, canTransition, checklistFromTexts, checklistProgress, defaultIsolationOf, defaultPermissionOf, defaultSyncExternalSessionsOf, effectiveIsolation, effectivePrompt, emptyLedger, isClaim, isClaimedBy, isPlausibleTaskRecord, isValidRelRepoPath, isValidTaskId, newChecklistItemId, newCommentId, newExecutionId, newTaskId, nextCronTime, normalizeBody, normalizeBranchesMap, normalizeChecklist, normalizeChecklistText, normalizeExecution, normalizeExecutionReport, normalizeModel, normalizePrompt, normalizeRepoEvidence, normalizeTitle, parseCron, pruneExecutions, summarize, syncClaim, validateImportedTask, validateLedgerImport };
|
|
783
|
+
export { ALL_STATUSES, DEFAULT_ISOLATION, DEFAULT_PERMISSION, MAIN_STATUSES, SECONDARY_STATUSES, URGENCIES, asBoardSettings, asIsolation, asPermission, asStatus, asUrgency, canTransition, checklistFromTexts, checklistProgress, defaultIsolationOf, defaultPermissionOf, defaultSyncExternalSessionsOf, effectiveIsolation, effectivePrompt, emptyLedger, isClaim, isClaimedBy, isPlausibleTaskRecord, isValidRelRepoPath, isValidTaskId, newChecklistItemId, newCommentId, newExecutionId, newTaskId, nextCronTime, normalizeBody, normalizeBranchesMap, normalizeChecklist, normalizeChecklistText, normalizeExecution, normalizeExecutionReport, normalizeModel, normalizePrompt, normalizeRepoEvidence, normalizeTitle, parseCron, pruneExecutions, summarize, syncClaim, taskAssociatedSessionIds, validateImportedTask, validateLedgerImport };
|
|
747
784
|
|
|
748
785
|
//# sourceMappingURL=protocol.js.map
|