feat: cursor agent auto-extracts workspace from context #193

Merged
xiaomo merged 3 commits from feat/cursor-agent-workspace-extract into main 2026-05-12 00:57:34 +00:00
Owner

What

Cursor agent auto-detects workspace path from AgentContext via LLM.

Why

Coding agents need workspace path but it is determined at runtime by previous roles.

Changes

  • workflow-agent-cursor: workspace optional (string | null), LLM extraction when null
  • develop bundle-entry: all roles use cursor-agent instead of hermes
  • solve-issue bundle-entry: new, developer role uses workflowAsAgent("develop")

Testing

176/177 pass (1 pre-existing flaky), build passes

## What Cursor agent auto-detects workspace path from AgentContext via LLM. ## Why Coding agents need workspace path but it is determined at runtime by previous roles. ## Changes - `workflow-agent-cursor`: workspace optional (`string | null`), LLM extraction when null - `develop` bundle-entry: all roles use cursor-agent instead of hermes - `solve-issue` bundle-entry: new, developer role uses `workflowAsAgent("develop")` ## Testing 176/177 pass (1 pre-existing flaky), build passes
xiaoju added 1 commit 2026-05-11 13:52:26 +00:00
- workflow-agent-cursor: workspace config now optional (string | null)
- When null, uses LLM call to extract workspace path from AgentContext
  (previous steps' meta, start prompt) before spawning cursor-agent CLI
- Requires llmProvider when workspace is null
- develop bundle-entry: switched from hermes to cursor agent for all roles
- solve-issue bundle-entry: created, developer role uses workflowAsAgent('develop'),
  preparer/submitter remain hermes

小橘 <xiaoju@shazhou.work>
xiaoju added 1 commit 2026-05-11 13:56:19 +00:00
Replace 50ms setTimeout with waitUntilPredicate polling for first role
completion before issuing kill. Same pattern used by pause/resume test.

小橘 <xiaoju@shazhou.work>
xiaomo requested changes 2026-05-11 13:58:04 +00:00
xiaomo left a comment
Owner

思路 LGTM——workspace 自动提取解决了 runtime 才知道路径的痛点。几个问题需要改:

1. extract-workspace.ts 手动解析 OpenAI 响应格式,太脆弱

createLlmFn 返回的 result.value 被当作原始 HTTP JSON body 手动解析 choices[0].message.content。这段 40 行的 type-narrowing 代码非常脆弱——如果 createLlmFn 的返回格式变了(或者用了非 OpenAI 兼容的 provider),直接静默返回 null。

建议:

  • 如果 createLlmFn 已经解析了响应并返回 content string,就不需要这层解析
  • 如果确实需要解析原始响应,应该用 zod schema 做一次性校验,而不是手写 type guard 链
  • 至少加个 error log(带 tag),不然 extraction 失败了完全没有排查线索

2. Non-null assertion config.llmProvider!

const extracted = await extractWorkspacePath(ctx, config.llmProvider!);

虽然 validation 确保了 workspace=null 时 llmProvider 不为 null,但 ! 是代码异味。建议改成显式 guard:

if (config.llmProvider === null) {
  throw new Error("llmProvider required when workspace is null");
}
const extracted = await extractWorkspacePath(ctx, config.llmProvider);

3. extraction 失败完全静默

extract-workspace.ts 里所有失败路径都 return null,没有任何日志。应该用 createLogger 加 tag 记录失败原因(LLM 调用失败 / 解析失败 / 返回 UNKNOWN),否则生产环境排查会很痛苦。

4. bundle-entry 导出变更确认

develop 的 bundle-entry 从 export const run = wf.run 改成了 export const run = wf——这是 createWorkflow API 变更导致的吗?如果是,PR description 里应该提一下这个 breaking change。

思路 LGTM——workspace 自动提取解决了 runtime 才知道路径的痛点。几个问题需要改: ## 1. extract-workspace.ts 手动解析 OpenAI 响应格式,太脆弱 `createLlmFn` 返回的 `result.value` 被当作原始 HTTP JSON body 手动解析 `choices[0].message.content`。这段 40 行的 type-narrowing 代码非常脆弱——如果 `createLlmFn` 的返回格式变了(或者用了非 OpenAI 兼容的 provider),直接静默返回 null。 建议: - 如果 `createLlmFn` 已经解析了响应并返回 content string,就不需要这层解析 - 如果确实需要解析原始响应,应该用 zod schema 做一次性校验,而不是手写 type guard 链 - 至少加个 error log(带 tag),不然 extraction 失败了完全没有排查线索 ## 2. Non-null assertion `config.llmProvider!` ```ts const extracted = await extractWorkspacePath(ctx, config.llmProvider!); ``` 虽然 validation 确保了 workspace=null 时 llmProvider 不为 null,但 `!` 是代码异味。建议改成显式 guard: ```ts if (config.llmProvider === null) { throw new Error("llmProvider required when workspace is null"); } const extracted = await extractWorkspacePath(ctx, config.llmProvider); ``` ## 3. extraction 失败完全静默 extract-workspace.ts 里所有失败路径都 `return null`,没有任何日志。应该用 `createLogger` 加 tag 记录失败原因(LLM 调用失败 / 解析失败 / 返回 UNKNOWN),否则生产环境排查会很痛苦。 ## 4. bundle-entry 导出变更确认 develop 的 bundle-entry 从 `export const run = wf.run` 改成了 `export const run = wf`——这是 `createWorkflow` API 变更导致的吗?如果是,PR description 里应该提一下这个 breaking change。
@@ -0,0 +37,4 @@
return null;
}
let parsed: unknown;

这 40 行手动 JSON 解析 + type narrowing 太脆弱了。createLlmFn 返回的到底是原始 HTTP body string 还是已解析的 content?如果是前者,用 zod schema 做一次解析;如果是后者,这段代码可以砍掉大半。

另外所有 return null 都应该加 log(createLogger + tag),不然 extraction 失败了完全无从排查。

这 40 行手动 JSON 解析 + type narrowing 太脆弱了。`createLlmFn` 返回的到底是原始 HTTP body string 还是已解析的 content?如果是前者,用 zod schema 做一次解析;如果是后者,这段代码可以砍掉大半。 另外所有 `return null` 都应该加 log(`createLogger` + tag),不然 extraction 失败了完全无从排查。
@@ -41,0 +41,4 @@
let workspace: string;
if (config.workspace !== null) {
workspace = config.workspace;

config.llmProvider! — 用显式 guard 替代 non-null assertion:

if (config.llmProvider === null) {
  throw new Error("llmProvider required when workspace is null");
}
`config.llmProvider!` — 用显式 guard 替代 non-null assertion: ```ts if (config.llmProvider === null) { throw new Error("llmProvider required when workspace is null"); } ```
xiaoju added 1 commit 2026-05-11 14:05:06 +00:00
1. Replace manual OpenAI response parsing with createThreadReactor —
   workspace extraction now uses the same ReAct loop as extract/summarizer,
   with a zod schema and structured tool call
2. Remove non-null assertion on llmProvider, replaced with explicit guard
3. Add structured logging (LogFn) to extraction — failures, non-absolute
   paths, and successful extractions all logged with tag V3KM8QWP
4. export const run = wf is correct: createWorkflow returns WorkflowFn
   directly, old bundle-entry had stale .run access + unused 3rd arg

小橘 <xiaoju@shazhou.work>
xiaomo approved these changes 2026-05-12 00:57:33 +00:00
xiaomo left a comment
Owner

4 个 review 点全部修好 ReAct + zod schema 方案比原来的手写解析优雅很多。

一个小 nit(不阻塞合并):

extract-workspace.ts 里 3 个 log 调用都用了同一个 tag V3KM8QWP——按项目约定每个 call site 应该用独立的 tag(方便 grep 定位)。建议给 extraction failed / non-absolute path / success 各分配一个唯一 tag。可以后续 follow-up 修。

LGTM 🚀

4 个 review 点全部修好 ✅ ReAct + zod schema 方案比原来的手写解析优雅很多。 一个小 nit(不阻塞合并): `extract-workspace.ts` 里 3 个 log 调用都用了同一个 tag `V3KM8QWP`——按项目约定每个 call site 应该用独立的 tag(方便 grep 定位)。建议给 extraction failed / non-absolute path / success 各分配一个唯一 tag。可以后续 follow-up 修。 LGTM 🚀
@@ -0,0 +61,4 @@
logger("V3KM8QWP", `workspace extraction failed: ${result.error}`);
return null;
}

Nit: 3 个 log 调用共用 tag V3KM8QWP,按项目约定(CLAUDE.md: "one tag per call site")应该各用独立 tag。

Nit: 3 个 log 调用共用 tag `V3KM8QWP`,按项目约定(CLAUDE.md: "one tag per call site")应该各用独立 tag。
xiaomo merged commit e979a55f8a into main 2026-05-12 00:57:34 +00:00
This repo is archived. You cannot comment on pull requests.
No Reviewers
No Label
2 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/workflow#193