Commit Graph

20 Commits

Author SHA1 Message Date
wangjia 74be3ecec1 feat(agent): L3 记忆注入——子任务带父任务拆解意图 + 兄弟状态
- types.TaskContext + Task.context(daemon 装配、不持久化)
- store.taskContextOf(taskId):父 plan + 同层兄弟概览(标题/状态/复杂度,排除自身)
- orchestrator.claimOne:executor 子任务填充 context 下发(planner 跑在父任务上不需要)
- runner.buildPrompt 注入「## 拆解背景」段(顺序:规范→背景→任务内容)
- 仅 DB 读,不做 git/RAG 检索(成本控制)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:32:21 +08:00
wangjia 73f80d27c9 feat(agent): 接通 plan 自动放行(autoApprovePlan)+ 项目配置可写
之前 auto_approve_plan/auto_approve_exec/checks 列与 Project 类型已存在,但
PatchProjectInput 不含它们 → 无法启用(dead config)。本次接通:
- PatchProjectInput + patchProject 支持 autoApprovePlan/autoApproveExec/checks
- API PATCH /projects 透传三字段
- ingest decompose-result:auto-approved + autoApprovePlan + 全 easy + 子任务≤5
  → store.decide(accept) 跳过 plan_review 直达 decomposed(默认关)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:13:18 +08:00
wangjia ecea1ae9b6 feat(agent): L1 记忆注入——重试任务带上次失败原因喂回 prompt
- store.lastRunErrorOf(taskId):取最近一次失败 run 的 error(任意 kind)
- orchestrator.claimOne:写 job.json 时填充 task.lastRunError 下发 worker
- runner 三处 prompt(executor/planner/conflict)注入「## 上次失败原因」段(截断 2000 字符)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:09:06 +08:00
wangjia 6c9b05e575 feat: 项目级成本预算系统(按模型×token 精确计费 + 超额自动暂停)
精确到项目级的成本/预算护栏,成本按「模型 × token 用量」折算(⑧ ★ 新特性)。

定价(单源):
- src/model/pricing.ts:MODEL_PRICES(opus/sonnet/haiku,USD/1M token,分
  input/output/cacheRead/cacheWrite)+ computeCost/addUsage,前缀容错、未知模型记 0

token 捕获链(worker → daemon):
- cc.ts:从 SDK result 消息抓 usage,跨 resume/回退累计;CCResult.usage
- runner/reviewer:结果对象带 usage + modelUsed
- protocol.ts:新增 'usage' outbox 记录;pipeline 每次 CC(executor/复审/planner/
  conflict)后 emit usage(worker 侧写文件,不碰 DB)
- ingest.ts:'usage' → store.setRunUsage(累加、按模型折算)→ enforceBudget

存储 + 护栏:
- schema/db.ts:runs.usage/cost_usd、projects.budget_usd/budget_period(幂等迁移)
- store.ts:setRunUsage(累加)、projectSpend、costSummary(按项目/模型/总计)、
  enforceBudget(超额 → 置 paused + 广播 budget.exceeded)
- 编排器天然停领:orchestrator 既有「跳过 paused 项目」即生效,无需改

API:
- GET /api/usage:并入成本明细 {session,weekly,cost:{period,total,byProject,byModel}},?period/?project
- GET /api/health:{ok,db,inflight,at}
- PATCH /api/projects:支持 budgetUsd/budgetPeriod

前端:
- adapt.js:agentSummary 用真实成本/token;adaptProject 透传预算字段
- app.jsx:budget.exceeded → 告警 toast
- ConfigPanel:预算 $ + 周期(day|month)受控输入

验证:typecheck 干净;206 测试通过(含新增 budget.test.ts 4 例:定价折算/累加/
costSummary/enforceBudget);前端 build + 截图确认预算输入渲染、0 错误。
注:生效需合并后重启 daemon(迁移幂等加列、向后兼容旧库)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:29:44 +08:00
wangjia f8900de2f7 feat(daemon+store+api): verdict硬闸+执行期锁+conflict优先+schema
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 12:27:48 +08:00
wangjia 8392944877 feat(planner): 编排器把 analyzing/speccing 也当可执行——派 planner run 自动拆解/写方案
修复"hard/medium 任务建后永远停在 analyzing/speccing"的缺口:编排器现在对 analyzing(hard)
派 planner-decompose、对 speccing(medium) 派 planner-spec(auto-approved 下;auto-easy 仍只执行 easy),
planner 只读跑 CC(无 worktree/无写)产出 → daemon 落库 + 自动提交到 plan_review/spec_review 闸(仍人审)。

- protocol: JobSpec.runKind + spec-result/decompose-result 两型 outbox + DecomposeResult
- runner: runPlanner(只读 Read/Glob/Grep 跑在 repo,opus 档)+ buildPlannerPrompt
- pipeline: runKind 分支 + parseDecompose(取末尾 fenced JSON,非法→failed)
- orchestrator: claimable 纳入 analyzing/speccing + 排除在途;并发/in-flight 从 countExecuting 泛化为
  inflightTaskIds(有 started run 的任务,executor+planner 通用);claimOne 按状态分 executor/planner
  (planner 不转状态);reap 泛化(死 planner→failPlanAttempt)
- ingest: ingestAll 覆盖所有 started run;spec-result→setSpec+spec_review;decompose-result→setPlan+建子任务+plan_review;
  failed 按 kind 分流(planner→failPlanAttempt 退避留态、超限→needs_attention)
- store: inflightTaskIds / liveRunsWithTask / failPlanAttempt;reconcile 泛化到所有 started run
- status: analyzing/speccing 加 →needs_attention(planner 失败超限升级)
- models: planner 角色(opus);status: RunKind 已含 planner
- 测试 +12(pipeline 5 / ingest 3 / orchestrator 4),194 全绿

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:59:30 +08:00
maestro dcf610f6fd merge: maestro/tsk_oTFzOMeAU20D [tsk_oTFzOMeAU20D] 2026-06-13 15:51:31 +08:00
wangjia d0b807460e feat(executor): worker OS 级沙箱 + 资源限额(sandbox-exec / ulimit)[tsk_oTFzOMeAU20D]
进程隔离之上再加一层 OS 级硬隔离,默认关闭,便于先在一个项目灰度:

- 新增 src/executor/sandbox.ts:
  - macOS sandbox-exec 写围栏 profile(allow default 基线 + deny file-write* 收口
    + 逐条放行 worktree/主仓.git/node_modules/runs/transcripts/tmp/工具缓存)。
    不 deny default 全围栏——合法 git/npm/go/node 需读海量系统路径,写才是破坏向量。
  - ulimit 资源上限(CPU 时间/虚拟内存/打开文件数/单文件大小),经 sh -c 下发,
    exec 链保证 pid 不变(daemon 记录的 workerPid 仍是真 worker)。
  - 全部由 env 解析(MAESTRO_SANDBOX*),网络默认放行(SDK 连 Anthropic)。
- orchestrator.defaultSpawnWorker 接入:开启时把命令包成
  sh -c 'ulimit…; exec sandbox-exec -f profile <原命令>';关闭时行为零变化。
  startOrchestrator 启动时打印生效策略,每个 run 的 profile 落 runs/<runId>/sandbox.sb。
- README 增补环境变量与「worker 沙箱」说明。
- 新增 test/sandbox.test.ts(18 例:config 解析 / profile 生成 / ulimit / 包装 / 预建目录)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:08:18 +08:00
wangjia a365aaf06a feat(cleanup): exec_review 合并后回收 runs/<runId>/ 工作目录 + 定期扫尾 [tsk_6uziW1Dg-ftQ]
每个 run 在 ~/.maestro/runs/<runId>/ 留下 job.json/outbox.ndjson/heartbeat,
合并(exec_review accept)后不再需要,需回收避免无限堆积。

- protocol.ts:新增 removeRunDir / isPlainRunId(幂等删 runDir,runId 白名单防越界)
- cleanup.ts(新):
  - cleanupTaskRunArtifacts:合并/接受后即时回收某任务全部 run 工作目录
  - sweepRunArtifacts:周期扫尾终态/孤儿 runDir 与(可选)转录,按保留天数
  - isRunCleanable:仅终态(done/cancelled)/孤儿可回收,活跃/在途一律保留
  - 双保险:任务状态判定 + heartbeat 新鲜则跳过,绝不误删活跃 run
  - 保留策略可配:MAESTRO_RUN_RETENTION_DAYS(默认3) /
    MAESTRO_TRANSCRIPT_RETENTION_DAYS(默认0=永久保留,便于排查)
- server.ts:exec_review accept 合并成功后即时清 runDir(与 removeWorktree 同处)
- index.ts:startCleanupLoop 定时扫尾(MAESTRO_CLEANUP_INTERVAL 秒,默认3600,0=关闭)
- test/cleanup.test.ts:终态清/在途留/孤儿清/保留期/heartbeat 兜底/转录开关/配置

测试:npm test → 164 passed;npm run typecheck → clean

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:02:23 +08:00
wangjia 0d45de4507 fix(worker): env 收敛改黑名单 + 不 detached —— 修 worker 'Not logged in'
cutover 实测:worker 起的 claude 报 Not logged in。根因:① 严格 env allowlist 把 macOS
keychain/Security 访问所需的会话变量(USER/LOGNAME/__CF_USER_TEXT_ENCODING/TMPDIR/XPC_* 等,
均非密钥)也滤掉了;本机鉴权走 keychain(无 API_KEY/凭证文件) → 读不到。
② detached(setsid)另起会话也不利于沿用 daemon 鉴权上下文。
修:workerEnv 改黑名单(会话变量透传保 keychain,仅剥 *_SECRET/*_TOKEN/*_KEY/AWS_/CF_ 等密钥,
ANTHROPIC_/CLAUDE_ 始终留);spawn 不 detached(同会话,unref 后孤儿仍被 init 收养存活)。
实测:smoke 任务 worker 真建文件+提交、双复审 approve、到 exec_review。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 13:26:17 +08:00
wangjia da7c93105c phase3(daemon): 监工 tick(spawn worker)+ ingest(outbox→DB)+ reaper + reconcile 接真判活
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 13:00:50 +08:00
wangjia 007dffac39 phase3(契约层): protocol.ts + 落盘状态 + store 失败策略/判活原语
多进程执行解耦的 Phase 0 共享契约(worker 与 daemon 都 import 它):
- executor/protocol.ts: JobSpec(daemon→worker 唯一输入) + OutboxRecord(worker→daemon) +
  文件 IO(job/outbox/heartbeat) + isWorkerAlive(心跳为主/boot 窗口 pid 兜底/防 pid 复用)
- schema+db+types+mappers: runs.worker_pid/last_seq、tasks.next_eligible_at(持久化退避)
- store: failTaskAttempt(失败/重试策略唯一落点) + setNextEligibleAt/setWorkerPid/setLastSeq +
  countExecuting + executingWithLatestExecutorRun + reconcileInterrupted(isAlive 注入,默认保守回收)

纯增量,140 测试全绿。worker.ts/pipeline.ts(worker 侧) 与 orchestrator/ingest/index(daemon 侧) 待接。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 12:51:09 +08:00
wangjia a00091329d merge: maestro/tsk_N5wO3Armums2 [重试与超时策略细化(可配 + 退避)]
# Conflicts:
#	src/api/server.ts
#	test/store.test.ts
2026-06-13 10:20:20 +08:00
wangjia 217eb6aa1f feat(merge): 自动合并失败时建最高优先级补救任务来完成合并
decide 端 exec_review accept→merge 冲突失败时,不再只是报错留在闸:
自动建一个 P0 easy 补救任务(operations 写明分支/目标/冲突详情与解冲突步骤),
自治项目会自动领取去解决。幂等:补救任务 id 记到原任务 result.mergeTaskId,
已有未结束补救任务则复用不重复建;结束(done/cancelled)后允许新建。

- model/types: TaskResult 加 mergeTaskId 字段
- mappers/orchestrator: 同步透传/补 null
- store.ensureMergeRemediationTask + store.test 覆盖(P0/easy/ready/operations/幂等/可重建)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 09:13:30 +08:00
wangjia c89d6d129c feat: 重试与超时策略细化(可配 + 退避)tsk_N5wO3Armums2
## 变更摘要

### 1. 项目级可配重试上限与超时
- `Project` 新增 `maxRetries`(默认 2)和 `timeoutMs`(默认 30min = 1800000ms)字段
- SQLite schema:`projects` 表补 `max_retries` / `timeout_ms` 列(带 DEFAULT 的轻量迁移)
- `createProject` / `patchProject` 支持设置 / 校验新字段(maxRetries>=0, timeoutMs>=1000)
- API `POST /api/projects` 与 `PATCH /api/projects/:id` 透传新字段
- `runner.ts` 将 `project.timeoutMs` 传给 `runClaude`,不再固定 30min

### 2. 失败重试指数退避
- 编排器内存维护 `backoffUntil` Map(taskId → nextRetryAt),daemon 重启后清空
- 退避公式:`min(30s * 2^(attempt-1), 10min)`,第 1 次 30s / 第 2 次 60s / ...
- `claimable()` 过滤退避冷却中的任务
- `nowMs` 注入点(默认 `Date.now`)使测试可快进时钟验证退避行为

### 3. needs_attention 一键重投
- `Task` 新增 `retryBaseline` 字段(默认 0):记录上次重投时的失败 run 基线
- `Store.requeueTask(taskId)`:设置基线 = 当前失败 run 数 → 转 `queued`(仅限 needs_attention)
- 编排器用净失败数(`allFailed - retryBaseline`)判断是否已耗尽重试次数
- API `POST /api/tasks/:id/requeue` + MCP `requeue_task` 工具

### 4. 测试
- 重命名 `MAX_RETRIES` → `DEFAULT_MAX_RETRIES`,新增导出 `computeBackoffMs`
- 新增测试(共 +18):maxRetries=1/0、退避时序(精确 ms 边界)、重投后基线重置
- 迁移测试扩展:验证旧库补列后 maxRetries/timeoutMs/retryBaseline 使用默认值

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 04:04:41 +08:00
wangjia 740d2c2637 feat: PR 审核流程 + 自动合并 + macOS 通知 + 订阅额度透传 + 归档详情
PR 流程(开发→提PR→CodeReview→安全审计→审核→通过即合并):
- reviewer 拆分为 code review 与安全审计两个独立 CC run(kind=reviewer/security),
  TaskResult 四字段(summary/verdict + securitySummary/securityVerdict),闸上两节报告两枚结论徽章
- executor/merge.ts: 通过即合并——临时 worktree 内 merge --no-ff,绝不碰用户工作区/不 push;
  冲突安全拒绝(报冲突文件);重复合并幂等;合并后回收执行 worktree + 删分支
- decide 路由 merge:false 逃生口 + 看板「仅通过」按钮(目标分支被工作区检出时用)

通知(daemon/notify.ts):
- macOS 原生通知: 进审核闸(复审建议拒绝标⚠)/连续失败需人工/合并完成
- 同任务同类型 60s 抑制、osascript 转义截断、MAESTRO_NOTIFY=0 关闭

订阅额度透传(daemon/usage.ts):
- OAuth usage API(与 Claude Code/claude-hud 同源),凭证 keychain→内存零泄漏
- 60s 成败双缓存+并发去重+5s 超时,失败降级 null
- GET /api/agents 顶层 usage 字段;Agent 面板显示 5h/周用量条+重置倒计时(>80%琥珀/>95%红)

看板与生命周期:
- 归档区: 深度1整树完成沉底,时间倒序分页(尺寸 chip 10/20/50/100 置底)
- 归档详情对话框: 全属性/执行历史与时长/审批记录/状态流转时间线(GET /api/tasks/:id/events)
- 容器收口: 已拆解 Hard 子任务全 done 自动 done(afterDone 逐级向上)
- 同步按钮收进配置面板;执行白名单扩测试命令(npm/go/shellcheck/make/pytest)
- Agent 面板显示调度模式与各复杂度模型;被依赖阻塞→被阻塞

测试: 74/74(新增 merge 6/notify 10/usage 7/容器收口/双复审适配)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 03:22:30 +08:00
wangjia f18db021c3 feat: Phase2 完整管线——score 调度 + 自动复审 + 模型分级 + 归档与详情
调度:
- model/scoring.ts: score = 自身分(P0=3/P1=2/P2=1) + 已完成依赖分(链条惯性)
  + 等待解锁的 blocked 任务分(解锁加权),编排器与 nextExecutable 同一打分
- createTask 校验 deps 存在且同项目(依赖图天然无环)
- daemon 重启中断自愈: executing 任务标 failed run 后重新入队(reconcileInterrupted)

执行管线:
- executor/cc.ts: 公共 headless CC 执行器(转录/超时/模型回退重试)
- executor/reviewer.ts: 执行后自动复审(只读 CC 审 diff),固定模板 summary
  (做了什么/怎么做/测试/CodeReview/安全Review/结论) + VERDICT 解析
- executor/models.ts: 按复杂度选模型(easy→sonnet/medium→opus/hard→fable5),
  env 可覆盖、project.model 最优先、不可用自动回退链
- runner: 测试/构建命令白名单(npm/go/shellcheck/make/pytest),prompt 要求实跑测试
- TaskResult 加 summary/verdict; RunKind 加 reviewer
- 容器收口: 已拆解 Hard 子任务全 done → 容器自动 done(afterDone 逐级向上)

看板:
- 五徽章组(待审批/待执行/执行中/被阻塞/总量,hover 展开,均不含已完成)
- 归档区: 深度1整树完成沉底,时间倒序分页(10/20/50/100 chip 选择)
- 归档详情对话框: 全属性/执行历史与时长/审批记录/状态流转时间线(含相关人或事)
- Agent 面板显示调度模式 + 各复杂度实际模型
- 结果闸展示复审 summary + 建议通过/拒绝徽章
- 筛选修复(组选与单选分离、已拆解移出进行中)、同步按钮收进配置面板、
  保存配置自动收起、预览全宽、被依赖阻塞→被阻塞
- API: GET /api/tasks/:id/events(任务级事件时间线)、/api/agents 带 scheduling/models

测试: 49/49(新增 scoring/复审/模型/容器收口/deps 校验/中断恢复)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 02:46:37 +08:00
wangjia fa472a06a7 feat: 同步引擎+Agent配置+依赖自动落位+看板大改(预览/md渲染/筛选/徽章组)
后端:
- src/sync/todo-sync.ts: todo.json 单向同步引擎(导入=首次同步,幂等,source_ref 映射,
  subs 复杂度修正为 easy,旧侧 done 历史事实优先 forceDone)
- 依赖自动落位:ready 意图按 deps 落位 blocked,依赖全 done 自动放行,
  daemon 启动 reconcileDeps 对账,手动绕过会弹回
- 新 API: PATCH projects/:id(autonomy/concurrency)、POST :id/sync、GET /api/agents、
  PATCH tasks/:id(title/priority/complexity 重置)
- daemon 定时同步(MAESTRO_SYNC_INTERVAL 默认 300s) + project.synced 事件
- priority 语义翻转: P0 最高/P1 默认/P2 最低,取值限 0..2,排序/映射/MCP/CLI 全跟进
- 静态服务发 no-cache 头(修浏览器吃旧 CSS/JS)
- schema 迁移: tasks.source_ref / projects.last_sync_at(ensureColumn 平滑升级旧库)

看板:
- 全屏预览模式(94vh 读完整方案+就地裁决,Esc/遮罩/裁决自动关闭)
- 产出 markdown 渲染为 HTML(零依赖渲染器,转义优先)
- 任务树筛选(复杂度/状态分组/关键字)+ 顶栏徽章组(待审批/可执行/执行中,hover 展开)
- 依赖可视化:详情 DEPS 区块 + 行内⛓等依赖 + 锚点跳转定位
- 按钮收敛:提交评审/编辑产出移除(CC 经 MCP 操作),界面只留用户动作
- Agent 执行面板 + 项目配置(并发/工作模式)+ 同步按钮

测试:21 个全过(新增 sync 幂等/迁移/patch/依赖落位/对账幂等)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:58:28 +08:00
wangjia c6e66baa19 feat: Phase1 完整交付——Web 看板(frontend-design) + MCP server(12 工具) + CLI/导入器
- web/: phosphor 调度台风格纯静态看板,审批闸内联 accept/reject(拒绝必填意见),WS 实时
- src/api/static.ts: 手写静态服务(路径穿越防护),daemon 接入
- src/mcp/: stdio MCP server,@modelcontextprotocol/sdk 1.29.0,12 工具薄封装 REST
- src/cli/: 零依赖 CLI(project/task/next/approvals/import-todo),旧 todo.json 导入器
- 实测: pangolin 18 条旧任务导入 45 条;端到端验证通过

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 21:23:08 +08:00
wangjia 3172718a94 feat: Phase1 核心——模型/状态机 + SQLite Store(守卫+审批闸) + REST/WS API + daemon
- src/model: 复杂度分级、16 状态状态机、实体类型
- src/store: better-sqlite3 接 schema,transition 受 canTransition 守卫,
  decide 审批闸(reject 必带改进意见),事件订阅广播,nextExecutable
- src/api: Fastify REST + ws 事件广播(/ws)
- src/daemon: maestrod 入口(env 配置,默认 ~/.maestro :4517)
- test: 9 个生命周期单测全过;typecheck/build 干净;REST+WS 端到端实跑验证

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 20:44:45 +08:00