فهرست منبع

feat(presets): omit workflow from PTC mode

fz 1 ماه پیش
والد
کامیت
0cdcc9c3c5

+ 6 - 0
.agents/notes/implemented/simplification/2026-09-01-ptc-omits-workflow-tool.i18n.yaml

@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# side as of the last confirmed-consistent state. Both languages carry equal authority;
+# after editing either side, bring the other along and re-record with:
+#   pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-09-01-ptc-omits-workflow-tool.md
+2026-09-01-ptc-omits-workflow-tool.md: 7453e06187690fe7fa1eec157ffad8b474c95cf8
+2026-09-01-ptc-omits-workflow-tool.zh.md: 71f64c11048d85c562432cdd51b3b717966fea29

+ 29 - 0
.agents/notes/implemented/simplification/2026-09-01-ptc-omits-workflow-tool.md

@@ -0,0 +1,29 @@
+# Agent Note: PTC preset omits the general workflow tool
+
+Status: implemented
+
+English | [中文](2026-09-01-ptc-omits-workflow-tool.zh.md)
+
+## Problem
+
+The shipped Web `ptc` preset exposed the general `workflow` tool through its generated SDK. PTC mode already makes `run_code` the model-authored composition interface, so `workflow` added a second orchestration language with different execution semantics. The preset description also claimed complete parity with Standard mode and could not state this intentional difference.
+
+## Decision
+
+The shipped Web `ptc` preset disables its `tool-workflow` row. Its generated PTC mode SDK therefore omits the `workflow` binding, while the model-facing wire contract remains the single `run_code` tool.
+
+The preset retains `workflow-worker-thread` in its isolated workflow realm because `tool-ralph` consumes the same engine. `ralph` remains available through the PTC mode SDK. The Standard and Creator presets continue to expose `workflow`, and a user-authored preset may mount the tool explicitly.
+
+The workflow package and its durable Session event types remain installed. Existing workflow records continue to render; this default composition change only prevents new top-level workflow calls from agents using the shipped `ptc` preset.
+
+## Alternatives considered
+
+**Disable the complete workflow realm in PTC mode.** Rejected because that also removes the provider required by `ralph`, even though Ralph's fixed fresh-agent loop is not a second model-authored workflow language.
+
+**Hide `workflow` only in the generated SDK.** Rejected because presentation-only filtering would leave executable tool lookup and the declared preset composition out of agreement. Disabling the consumer row removes the binding from registration, lookup, and presentation together.
+
+**Keep `workflow` until `run_code` has feature parity.** Rejected because the shipped PTC default is intended to make `run_code` its composition interface. Users who require declarative workflow semantics can select Standard mode or an explicit custom preset while capability gaps are evaluated independently.
+
+## Consequences
+
+The PTC picker description states the exception instead of promising full Standard parity. A real Web Loader composition test pins the `run_code` wire catalog, the absent `workflow` SDK binding, and the retained `ralph` binding. The keyless recorded Web PTC session owns the assembled prompt evidence, while preset tests pin that Standard and Creator remain unchanged.

+ 29 - 0
.agents/notes/implemented/simplification/2026-09-01-ptc-omits-workflow-tool.zh.md

@@ -0,0 +1,29 @@
+# Agent Note: PTC preset 不提供通用 workflow 工具
+
+Status: implemented
+
+[English](2026-09-01-ptc-omits-workflow-tool.md) | 中文
+
+## 问题
+
+Web 端随附的 `ptc` preset 会通过生成的 SDK 提供通用 `workflow` 工具。PTC mode 已经把 `run_code` 作为模型编写的组合接口,因此 `workflow` 又增加了一套执行语义不同的编排语言。preset 描述还声称与标准模式能力完全相同,无法说明这个有意的差异。
+
+## 决策
+
+Web 端随附的 `ptc` preset 禁用自身的 `tool-workflow` 配置项。因此,它生成的 PTC mode SDK 不包含 `workflow` 绑定,模型可见的协议约定仍然只有一个 `run_code` 工具。
+
+preset 在隔离的 workflow realm 中保留 `workflow-worker-thread`,因为 `tool-ralph` 使用同一个引擎。PTC mode SDK 继续提供 `ralph`。标准模式与创造模式继续提供 `workflow`,用户自定义 preset 也可以显式挂载该工具。
+
+workflow 包及其持久 Session 事件类型仍然随产品安装。现有 workflow 记录继续正常渲染;这次默认组合变更只会阻止使用随附 `ptc` preset 的 agent 发起新的顶层 workflow 调用。
+
+## 曾考虑的替代方案
+
+**在 PTC mode 中禁用整个 workflow realm。** 不予采用,因为这也会移除 `ralph` 所需的 provider,而 Ralph 固定的全新 agent 循环不是第二套由模型编写的 workflow 语言。
+
+**只在生成的 SDK 中隐藏 `workflow`。** 不予采用,因为只修改呈现会导致可执行工具查找与 preset 声明的组合不一致。禁用 consumer 配置项会同时从注册、查找与呈现中移除该绑定。
+
+**等到 `run_code` 功能完全对等后再移除 `workflow`。** 不予采用,因为随附的 PTC 默认模式就是要以 `run_code` 作为组合接口。在独立评估能力缺口期间,需要声明式 workflow 语义的用户可以选择标准模式或显式自定义 preset。
+
+## 后果
+
+PTC 选择器描述会说明这一例外,不再承诺与标准模式完全对等。真实 Web Loader 组合测试固定 `run_code` 协议工具清单、缺失的 `workflow` SDK 绑定和保留的 `ralph` 绑定。无密钥的 Web PTC 录制会话负责组装提示词证据,preset 测试则固定标准模式与创造模式保持不变。

+ 1 - 1
apps/web/tests/expected/agent-preset-authoring/created.expected.md

@@ -33,7 +33,7 @@
         - text: 复制
     - listitem:
       - 'button "设为默认: PTC 模式"':
-        - text: PTC 模式 内置 具备标准模式的全部能力,并通过 PTC 模式 SDK 呈现工具,让模型用一个 TypeScript 程序组合多步操作。
+        - text: PTC 模式 内置 功能完整的编码 Agent,但默认不提供 workflow 工具;其他工具通过 PTC 模式 SDK 呈现,让模型用一个 TypeScript 程序组合多步操作。
         - code: ptc
       - 'button "查看: PTC 模式"':
         - img

+ 1 - 1
apps/web/tests/expected/agent-preset-authoring/damaged.expected.md

@@ -33,7 +33,7 @@
         - text: 复制
     - listitem:
       - 'button "设为默认: PTC 模式"':
-        - text: PTC 模式 内置 具备标准模式的全部能力,并通过 PTC 模式 SDK 呈现工具,让模型用一个 TypeScript 程序组合多步操作。
+        - text: PTC 模式 内置 功能完整的编码 Agent,但默认不提供 workflow 工具;其他工具通过 PTC 模式 SDK 呈现,让模型用一个 TypeScript 程序组合多步操作。
         - code: ptc
       - 'button "查看: PTC 模式"':
         - img

+ 1 - 1
apps/web/tests/expected/agent-preset-authoring/section.expected.md

@@ -33,7 +33,7 @@
         - text: 复制
     - listitem:
       - 'button "设为默认: PTC 模式"':
-        - text: PTC 模式 内置 具备标准模式的全部能力,并通过 PTC 模式 SDK 呈现工具,让模型用一个 TypeScript 程序组合多步操作。
+        - text: PTC 模式 内置 功能完整的编码 Agent,但默认不提供 workflow 工具;其他工具通过 PTC 模式 SDK 呈现,让模型用一个 TypeScript 程序组合多步操作。
         - code: ptc
       - 'button "查看: PTC 模式"':
         - img

+ 1 - 1
apps/web/tests/expected/agent-preset-selection/menu.expected.md

@@ -2,7 +2,7 @@
   - menuitem "Standard mode Full coding agent with file editing, shell, file and web search, skills, planning, goals, subagents, and workflows.":
     - text: Standard mode Full coding agent with file editing, shell, file and web search, skills, planning, goals, subagents, and workflows.
     - img
-  - menuitem "PTC mode All Standard mode capabilities, with tools exposed through the PTC mode SDK so the model can combine multi-step operations in one TypeScript program."
+  - menuitem "PTC mode Full coding agent without the workflow tool; other tools are exposed through the PTC mode SDK so the model can combine multi-step operations in one TypeScript program."
   - menuitem "Minimal mode Two-tool coding agent with persistent bash and str_replace_editor."
   - menuitem "Creator mode Built for creating custom agent presets, with all Standard mode capabilities plus runtime inspection, plugin experiments, and preset-authoring guidance."
   - menuitem "Refusing mode Resolves, then refuses to start."

+ 1 - 1
apps/web/tests/ptc-round.e2e.ts

@@ -30,7 +30,7 @@ describe('web e2e: PTC mode round renders nested sub-calls', () => {
 
   beforeAll(async () => {
     scaffold = await launchWebScaffold({
-      toolsMode: 'ptc',
+      agentPresets: { roots: [], default: 'ptc' },
       compareReplaySession: true,
       ...(MODE === 'record' ? {} : { replayFixture: FIXTURE, paceMs: 15 }),
     })

+ 20 - 2
apps/web/tests/shipped-composition.e2e.ts

@@ -10,8 +10,8 @@ import { afterEach, expect, it } from 'vitest'
 import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import { canonicalPath, writableRoots } from '@deepseek-ai/dsh-sandbox'
 import { SessionId } from '@deepseek-ai/dsh-session'
-// Empty type imports carry the tools/sandboxPolicy/approval Context merges.
-import type {} from '@deepseek-ai/dsh-tools'
+// These imports carry the tools/sandboxPolicy/approval Context merges.
+import { RUN_CODE_NAME } from '@deepseek-ai/dsh-tools'
 import type {} from '@deepseek-ai/dsh-sandbox-policy'
 import type {} from '@deepseek-ai/dsh-user-approval'
 import type {} from '@deepseek-ai/dsh-permission-presets'
@@ -191,6 +191,24 @@ it('assembles the shipped Web transport, catalog, guidance, and defaults', async
   }
 }, 120_000)
 
+it('ships PTC with run_code but without the general workflow SDK binding', async () => {
+  scaffold = await launchWebScaffold({ deepSeekMissingCredential: true })
+  const ctx = scaffold.ctx
+  const handle = await ctx.agents.create({
+    sessionId: SessionId('shipped-ptc-composition'),
+    setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'ptc').then(() => undefined),
+  })
+  try {
+    const assembly = await ctx.systemPrompt.assemble({ scope: handle.agent })
+    expect(assembly.tools.map(tool => tool.name)).toEqual([RUN_CODE_NAME])
+    const sdk = assembly.sections.find(section => section.name === 'tools:sdk')?.text ?? ''
+    expect(sdk).toContain('  ralph: {')
+    expect(sdk).not.toContain('  workflow: {')
+  } finally {
+    await handle.dispose()
+  }
+}, 120_000)
+
 it('lets a preset producer reach the background-job registry', async () => {
   scaffold = await launchWebScaffold()
   const ctx = scaffold.ctx

+ 2 - 2
packages/client/ui-agent-preset/src/client/locales.ts

@@ -36,7 +36,7 @@ export const en: Record<AgentPresetSettingsKey, string> = {
     'Full coding agent with file editing, shell, file and web search, skills, planning, goals, subagents, and workflows.',
   presetPtcName: 'PTC mode',
   presetPtcDescription:
-    'All Standard mode capabilities, with tools exposed through the PTC mode SDK so the model can combine multi-step operations in one TypeScript program.',
+    'Full coding agent without the workflow tool; other tools are exposed through the PTC mode SDK so the model can combine multi-step operations in one TypeScript program.',
   presetMinimalName: 'Minimal mode',
   presetMinimalDescription:
     'Two-tool coding agent with persistent bash and str_replace_editor.',
@@ -96,7 +96,7 @@ export const zh: Record<AgentPresetSettingsKey, string> = {
   presetStandardName: '标准模式',
   presetStandardDescription: '功能完整的编码 Agent,支持文件编辑、Shell、文件与网页检索、Skills、计划、目标、子代理和工作流。',
   presetPtcName: 'PTC 模式',
-  presetPtcDescription: '具备标准模式的全部能力,并通过 PTC 模式 SDK 呈现工具,让模型用一个 TypeScript 程序组合多步操作。',
+  presetPtcDescription: '功能完整的编码 Agent,但默认不提供 workflow 工具;其他工具通过 PTC 模式 SDK 呈现,让模型用一个 TypeScript 程序组合多步操作。',
   presetMinimalName: '极简模式',
   presetMinimalDescription: '仅提供持久 bash 与 str_replace_editor 的双工具编码 Agent。',
   presetCordisName: '创造模式',

+ 7 - 4
packages/preset/agent-presets/presets/ptc/agent.cordis.yml

@@ -1,9 +1,9 @@
 # The `ptc` agent preset: the standard coding agent, presented as PTC mode.
 #
-# Everything in `standard` is here unchanged. What is added is the `tool-presentation`
-# row: instead of one tool call per action, the model writes a TypeScript
-# program against a generated SDK and `run_code` executes it, so a sequence
-# that would be five round trips becomes one.
+# Most of `standard` is here unchanged. The deliberate exception is the
+# general-purpose `workflow` tool: PTC mode uses `run_code` as its model-authored
+# composition surface. The `tool-presentation` row turns the remaining registry
+# into a generated SDK, so a sequence that would be five round trips becomes one.
 #
 # The registry itself stays on the host plane — the agent loop's scheduler and
 # the API proxy's presenters are its consumers — so what this preset owns is
@@ -232,6 +232,9 @@
 
     - id: tool-workflow
       name: '@deepseek-ai/dsh-tool-workflow'
+      # Keep the engine above for `ralph`, but do not publish a second
+      # model-authored orchestration surface beside `run_code` in PTC mode.
+      disabled: true
 
     - id: tool-ralph
       name: '@deepseek-ai/dsh-tool-ralph'

+ 1 - 1
packages/preset/agent-presets/presets/ptc/preset.yml

@@ -1,3 +1,3 @@
 name: PTC 模式
-description: 具备标准模式的全部能力,并通过 PTC 模式 SDK 呈现工具,让模型用一个 TypeScript 程序组合多步操作。
+description: 功能完整的编码 Agent,但默认不提供 workflow 工具;其他工具通过 PTC 模式 SDK 呈现,让模型用一个 TypeScript 程序组合多步操作。
 order: 2

+ 40 - 3
packages/preset/agent-presets/tests/shipped-root.spec.ts

@@ -53,6 +53,34 @@ async function roster(config: Partial<Config> = {}): Promise<Context> {
   return ctx
 }
 
+interface ShippedEntry {
+  id?: unknown
+  disabled?: unknown
+  config?: unknown
+}
+
+/** Find one entry through the shipped composition's nested groups. */
+function findEntry(entries: unknown[], id: string): ShippedEntry | undefined {
+  for (const entry of entries) {
+    if (typeof entry !== 'object' || entry === null) continue
+    const candidate = entry as ShippedEntry
+    if (candidate.id === id) return candidate
+    if (Array.isArray(candidate.config)) {
+      const nested = findEntry(candidate.config, id)
+      if (nested !== undefined) return nested
+    }
+  }
+  return undefined
+}
+
+/** Read and validate one shipped preset's Cordis entry list. */
+async function shippedEntries(id: string): Promise<unknown[]> {
+  const source = await readFile(join(SHIPPED_PRESET_ROOT, id, 'agent.cordis.yml'), 'utf8')
+  const entries: unknown = yaml.load(source, { schema: entryListSchema })
+  if (!Array.isArray(entries)) throw new TypeError(`${id} preset must contain a Cordis entry list`)
+  return entries.map((entry: unknown) => entry)
+}
+
 describe('the shipped preset root', () => {
   it('supplies the built-in presets from a bare roster, healthy and system-trusted', async () => {
     const ctx = await roster({ includeUserRoot: false })
@@ -99,9 +127,7 @@ describe('the shipped preset root', () => {
 
   it('enables web_fetch in each tool-bearing Web app preset', async () => {
     for (const id of ['cordis', 'ptc', 'standard']) {
-      const source = await readFile(join(SHIPPED_PRESET_ROOT, id, 'agent.cordis.yml'), 'utf8')
-      const entries: unknown = yaml.load(source, { schema: entryListSchema })
-      if (!Array.isArray(entries)) throw new TypeError(`${id} preset must contain a Cordis entry list`)
+      const entries = await shippedEntries(id)
       const toolWeb: unknown = entries.find((entry: unknown) =>
         typeof entry === 'object' && entry !== null && 'id' in entry && entry.id === 'tool-web')
       if (typeof toolWeb !== 'object' || toolWeb === null || !('config' in toolWeb)
@@ -111,4 +137,15 @@ describe('the shipped preset root', () => {
       expect(toolWeb.config.fetch, id).toBe(true)
     }
   })
+
+  it('omits the general workflow tool only from PTC while retaining Ralph infrastructure', async () => {
+    const ptc = await shippedEntries('ptc')
+    expect(findEntry(ptc, 'tool-workflow')?.disabled).toBe(true)
+    expect(findEntry(ptc, 'workflow-worker-thread')?.disabled).not.toBe(true)
+    expect(findEntry(ptc, 'tool-ralph')?.disabled).not.toBe(true)
+
+    for (const id of ['standard', 'cordis']) {
+      expect(findEntry(await shippedEntries(id), 'tool-workflow')?.disabled, id).not.toBe(true)
+    }
+  })
 })

+ 1 - 1
snapshots/web/ptc-round/session.jsonl

@@ -1,4 +1,4 @@
-{"type":"session","version":0,"id":"{{session:1}}","createdAt":1787736414061,"cwd":"{{cwd}}","agentPreset":"standard"}
+{"type":"session","version":0,"id":"{{session:1}}","createdAt":1787736414061,"cwd":"{{cwd}}","agentPreset":"ptc"}
 {"type":"permission/preset","data":{"preset":"workspace-write"}}
 {"type":"sandbox/mode","data":{"mode":"workspace-write"}}
 {"type":"approval/policy","data":{"policy":"ask"}}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 2
snapshots/web/ptc-round/system-prompt.expected.md


+ 1 - 1
snapshots/web/ptc-round/ui.expected.md

@@ -2,7 +2,7 @@
   - navigation "Session hierarchy":
     - 'button "Using ONE run_code program: run" [disabled]'
   - img
-  - text: Standard mode
+  - text: PTC mode
   - button "Session log":
     - text: Session log
     - img

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است