Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1393-workflow-settings-prompt.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Settings → Workflows can now set each node's **system prompt** in edit mode, the way the sidebar flowchart already did, so a run Bot's prompt is configurable where the graph is drawn (#1393).
23 changes: 23 additions & 0 deletions core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ var ZH = {
"Use this": "用这个",
"Finish editing": "完成",
"Drag a Bot here": "把 Bot 拖到图上",
"System prompt": "系统提示词",
"Parallel branch, then join": "并行分支 · 全部通过后汇合",
"This workflow has no flowchart yet.": "这个工作流还没有流程图。",
Queued: "排队中",
Expand Down Expand Up @@ -4228,6 +4229,9 @@ button.bt-set-row:hover{background:var(--bt-hover)}
.bt-wf-canvas{position:relative;min-height:440px;overflow:auto;border:1px solid var(--bt-line);border-radius:12px;background:var(--bt-main);padding:12px 8px 20px}
.bt-wf-palette{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;align-items:center}
.bt-wf-palette button{height:28px;padding:0 10px;border-radius:14px;border:1px solid var(--bt-line);background:var(--bt-card);color:inherit;font:inherit;font-size:12px;cursor:pointer}
.bt-wf-prompts{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px;margin:12px 4px 0}
.bt-wf-prompt{display:flex;flex-direction:column;gap:4px;font-size:12px;min-width:0}
.bt-wf-prompt textarea{width:100%;box-sizing:border-box;border-radius:8px;border:1px solid var(--bt-line);background:var(--bt-card);color:inherit;font:inherit;font-size:12px;padding:6px 8px;resize:vertical}
.bt-wf-plane{position:relative;width:0;margin:16px auto 0}
.bt-wf-wires{position:absolute;left:0;top:0;overflow:visible;pointer-events:none}
.bt-wf-wires path{fill:none;stroke:var(--bt-ink-3);stroke-width:1.6}
Expand Down Expand Up @@ -14486,6 +14490,25 @@ function GraphCanvas({ graph, editing, roster, onCommit }) {
}
}, bot.name))
) : null,
// A node's system prompt is the run Bot's prompt (plugins/workflow
// nodeConfig). The sidebar panel has this field, so the Settings editor
// writes the same graph instead of only drawing it (#1393).
editing ? (0, import_react34.createElement)("div", { className: "bt-wf-prompts" }, nodes.map((node) => (0, import_react34.createElement)(
"label",
{ key: node.id, className: "bt-wf-prompt" },
(0, import_react34.createElement)("span", null, `${node.name} · ${t("System prompt")}`),
(0, import_react34.createElement)("textarea", {
"data-prompt": node.id,
rows: 3,
defaultValue: node.prompt || "",
onPointerDown: (event) => event.stopPropagation?.(),
onBlur: (event) => {
const prompt = event.target?.value ?? "";
if (prompt === (node.prompt || "")) return;
onCommit?.({ nodes: nodes.map((item) => item.id === node.id ? { ...item, prompt } : item), edges });
}
})
))) : null,
(0, import_react34.createElement)(
"div",
{ className: "bt-wf-plane", "data-plane": "1", style: { height } },
Expand Down
1 change: 1 addition & 0 deletions core/src/client/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const ZH = {
'Use this': '用这个',
'Finish editing': '完成',
'Drag a Bot here': '把 Bot 拖到图上',
'System prompt': '系统提示词',
'Parallel branch, then join': '并行分支 · 全部通过后汇合',
'This workflow has no flowchart yet.': '这个工作流还没有流程图。',
Queued: '排队中',
Expand Down
3 changes: 3 additions & 0 deletions core/src/client/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,9 @@ button.bt-set-row:hover{background:var(--bt-hover)}
.bt-wf-canvas{position:relative;min-height:440px;overflow:auto;border:1px solid var(--bt-line);border-radius:12px;background:var(--bt-main);padding:12px 8px 20px}
.bt-wf-palette{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;align-items:center}
.bt-wf-palette button{height:28px;padding:0 10px;border-radius:14px;border:1px solid var(--bt-line);background:var(--bt-card);color:inherit;font:inherit;font-size:12px;cursor:pointer}
.bt-wf-prompts{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px;margin:12px 4px 0}
.bt-wf-prompt{display:flex;flex-direction:column;gap:4px;font-size:12px;min-width:0}
.bt-wf-prompt textarea{width:100%;box-sizing:border-box;border-radius:8px;border:1px solid var(--bt-line);background:var(--bt-card);color:inherit;font:inherit;font-size:12px;padding:6px 8px;resize:vertical}
.bt-wf-plane{position:relative;width:0;margin:16px auto 0}
.bt-wf-wires{position:absolute;left:0;top:0;overflow:visible;pointer-events:none}
.bt-wf-wires path{fill:none;stroke:var(--bt-ink-3);stroke-width:1.6}
Expand Down
16 changes: 16 additions & 0 deletions core/src/client/workflows.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,22 @@ function GraphCanvas({ graph, editing, roster, onCommit }) {
key: bot.id, type: 'button', 'data-bot': bot.id,
onPointerDown: (event) => { event.preventDefault?.(); gesture.current = { kind: 'add', bot } },
}, bot.name))) : null,
// A node's system prompt is the run Bot's prompt (plugins/workflow
// nodeConfig). The sidebar panel has this field, so the Settings editor
// writes the same graph instead of only drawing it (#1393).
editing ? h('div', { className: 'bt-wf-prompts' }, nodes.map((node) => h('label', { key: node.id, className: 'bt-wf-prompt' },
h('span', null, `${node.name} · ${t('System prompt')}`),
h('textarea', {
'data-prompt': node.id,
rows: 3,
defaultValue: node.prompt || '',
onPointerDown: (event) => event.stopPropagation?.(),
onBlur: (event) => {
const prompt = event.target?.value ?? ''
if (prompt === (node.prompt || '')) return
onCommit?.({ nodes: nodes.map((item) => (item.id === node.id ? { ...item, prompt } : item)), edges })
},
})))) : null,
h('div', { className: 'bt-wf-plane', 'data-plane': '1', style: { height } },
h('svg', {
className: 'bt-wf-wires', 'aria-hidden': true,
Expand Down
41 changes: 41 additions & 0 deletions core/test/workflows-page.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,44 @@ test('a drag on the open flowchart commits the moved graph', () => {
assert.equal(commits[0].nodes.find((node) => node.id === 'coder').x, 20)
assert.equal(commits[0].nodes.find((node) => node.id === 'coder').y, 138)
})

// #1393: a node's system prompt is the prompt of that node's run Bot, and the
// Settings editor writes it the way the sidebar flowchart does — same graph,
// with the edges kept.
test('edit mode sets a node system prompt and the commit keeps the edges', () => {
const withPrompt = {
...own,
nodes: own.nodes.map((node) => (node.id === 'coder' ? { ...node, prompt: 'Write the fix.' } : node)),
}
const mine = { ...pack, workflows: [factory, withPrompt] }

const readOnly = renderView({ phase: 'ready', pack: mine, opened: { source: 'own', id: 'wf-mine' }, error: '' })
assert.equal(all(readOnly).some((entry) => entry.type === 'textarea'), false)

const commits = []
const tree = renderView({
phase: 'ready', pack: mine, opened: { source: 'own', id: 'wf-mine' }, editing: true, error: '',
roster: { bots: [] },
onCommit: (graph) => commits.push(graph),
})
const fields = all(tree).filter((entry) => entry.type === 'textarea')
assert.deepEqual(fields.map((entry) => entry.props['data-prompt']).sort(), ['coder', 'planner'])
const coder = fields.find((entry) => entry.props['data-prompt'] === 'coder')
assert.equal(coder.props.defaultValue, 'Write the fix.')
const planner = fields.find((entry) => entry.props['data-prompt'] === 'planner')
assert.equal(planner.props.defaultValue, '')
assert.match(text(tree), /Coder · System prompt/)

// An untouched field commits nothing, so a click-through cannot rewrite a graph.
planner.props.onBlur({ target: { value: '' } })
coder.props.onBlur({ target: { value: 'Write the fix.' } })
assert.equal(commits.length, 0)

planner.props.onBlur({ target: { value: 'Plan the work.' } })
assert.equal(commits.length, 1)
assert.equal(commits[0].nodes.find((node) => node.id === 'planner').prompt, 'Plan the work.')
assert.equal(commits[0].nodes.find((node) => node.id === 'coder').prompt, 'Write the fix.')
assert.equal(commits[0].nodes.find((node) => node.id === 'planner').x, 0)
assert.deepEqual(commits[0].edges, own.edges)
assert.equal(DICTIONARY.zh['System prompt'], '系统提示词')
})
2 changes: 1 addition & 1 deletion docs/subsystems/core-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ The overlay (`flybot-slots.js` `FlyBotApprovals`) is the fallback, not a second
### New-chat menu

- The + menu is a popover anchored next to the sidebar **+** (#195, #173): its `.bt-pane` carries `data-menu` and stays see-through, the menu is a 280px card at the top left with no title row, and a press on the pane outside the card (or Escape) closes it. New chat, New group chat, New Bot and plugin rows open their full pane as before.
- Settings → **Workflows** (`src/client/workflows.js`, #1364) sits right after **Scheduled tasks**, beside the other automation page, so the page list's existing order and the plugin-slot rule (a `flybot.settings.page` entry is listed after Connectors) are unchanged. It is not a second row in the account menu: the menu keeps its one name per destination. The page reads `@flybot/workflow` through `call('workflow/state')`: **Factory presets** are the template catalog (a drawable graph, so a pipeline can be opened before it is copied) and **Yours** are the workflows saved on this machine whose ids are not those templates. Opening one draws the flowchart; a saved workflow can be dragged (move a node, draw an edge, drop a local Bot, remove a node) and the picture is saved with `workflow/graph`. **Use this** copies a factory template with `workflow/create`. A read that rejects or never returns settles as an error with **Retry**; an empty store says there are no workflows. When that read is `bot/not-found`, or the host says `Unknown FlyBot endpoint workflow/…`, the page says the Workflow plugin is not installed or not enabled (a default install includes `@flybot/workflow`) in the interface language, and **Retry** stays (#1381). Any other failure still shows its own reason. The page does not teach `workflows/<id>.json`. The definition engine that still stores those files stays on the host and is not this page. Tests: `core/test/workflows-page.test.js`.
- Settings → **Workflows** (`src/client/workflows.js`, #1364) sits right after **Scheduled tasks**, beside the other automation page, so the page list's existing order and the plugin-slot rule (a `flybot.settings.page` entry is listed after Connectors) are unchanged. It is not a second row in the account menu: the menu keeps its one name per destination. The page reads `@flybot/workflow` through `call('workflow/state')`: **Factory presets** are the template catalog (a drawable graph, so a pipeline can be opened before it is copied) and **Yours** are the workflows saved on this machine whose ids are not those templates. Opening one draws the flowchart; a saved workflow can be dragged (move a node, draw an edge, drop a local Bot, remove a node) and each node's **system prompt** can be typed in a field beside the canvas — that prompt is what the node's run Bot receives — and the picture is saved with `workflow/graph` (#1393). **Use this** copies a factory template with `workflow/create`. A read that rejects or never returns settles as an error with **Retry**; an empty store says there are no workflows. When that read is `bot/not-found`, or the host says `Unknown FlyBot endpoint workflow/…`, the page says the Workflow plugin is not installed or not enabled (a default install includes `@flybot/workflow`) in the interface language, and **Retry** stays (#1381). Any other failure still shows its own reason. The page does not teach `workflows/<id>.json`. The definition engine that still stores those files stays on the host and is not this page. Tests: `core/test/workflows-page.test.js`.
- The sidebar footer button (`ConnectApps` in `sidebar.js`, label **Connect apps** / 连接应用) opens Settings > Connectors through `actions.openConnectors()`, the same call and the same words as the account menu's **Connect apps** row. The shell's plugin panel stays reachable in the Bot surface through the account menu's **Plugins** row (`actions.selectPanel('plugins')`, 插件), without switching to classic Agent mode.
- One name per destination in the account menu and command palette (#392, #393, #394): **Settings** opens the Settings dialog (General), since **Bot settings** / **Edit Bot** open one Bot; **Scheduled tasks** opens Settings → Scheduled tasks, the name Settings and a Bot's details use (DSH's own bundle stays "Automation tasks" in the Plugins hints); and **Switch to classic Agent mode** is the only way to classic Agent mode, so Settings → General is Appearance only.
- Create Bot starts on the Main Bot's model (#230): `startModel()` (`bot-draft.js`) picks the Main Bot's chosen model, else the one it last ran on, when it is served; then `roster.defaultModel`, then the first served model. Only served models are picked, so a provider without a credential is never preselected.
Expand Down
3 changes: 2 additions & 1 deletion patches/ds-bot/0001-core-fixes.patch
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ diff --git a/src/client/feedback.js b/src/client/feedback.js
diff --git a/src/client/i18n.js b/src/client/i18n.js
--- a/src/client/i18n.js
+++ b/src/client/i18n.js
@@ -5,6 +5,49 @@
@@ -5,6 +5,50 @@
export const NAMESPACE = 'ds-bot'

const ZH = {
Expand All @@ -601,6 +601,7 @@ diff --git a/src/client/i18n.js b/src/client/i18n.js
+ 'Use this': '用这个',
+ 'Finish editing': '完成',
+ 'Drag a Bot here': '把 Bot 拖到图上',
+ 'System prompt': '系统提示词',
+ 'Parallel branch, then join': '并行分支 · 全部通过后汇合',
+ 'This workflow has no flowchart yet.': '这个工作流还没有流程图。',
+ Queued: '排队中',
Expand Down
5 changes: 4 additions & 1 deletion patches/ds-bot/0003-flybot-api-client.patch
Original file line number Diff line number Diff line change
Expand Up @@ -2754,7 +2754,7 @@ diff --git a/src/client/styles.js b/src/client/styles.js
/* Usage while another page is open: out of the flow at Usage's own width, so it keeps
its layout, and not rendered. */
.bt-settings-usage[data-off]{position:absolute;top:0;left:198px;width:calc(min(1100px,calc(100vw - 48px)) - 198px);height:100%;box-sizing:border-box;content-visibility:hidden;pointer-events:none}
@@ -470,13 +470,55 @@ button.bt-set-row:hover{background:var(--bt-hover)}
@@ -470,13 +470,58 @@ button.bt-set-row:hover{background:var(--bt-hover)}
.bt-set-card>.bt-set-row+.bt-set-row{border-top:.5px solid var(--bt-line)}
.bt-set-row:first-child{border-top-left-radius:12px;border-top-right-radius:12px}
.bt-set-row:last-child{border-bottom-left-radius:12px;border-bottom-right-radius:12px}
Expand Down Expand Up @@ -2784,6 +2784,9 @@ diff --git a/src/client/styles.js b/src/client/styles.js
+.bt-wf-canvas{position:relative;min-height:440px;overflow:auto;border:1px solid var(--bt-line);border-radius:12px;background:var(--bt-main);padding:12px 8px 20px}
+.bt-wf-palette{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;align-items:center}
+.bt-wf-palette button{height:28px;padding:0 10px;border-radius:14px;border:1px solid var(--bt-line);background:var(--bt-card);color:inherit;font:inherit;font-size:12px;cursor:pointer}
+.bt-wf-prompts{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px;margin:12px 4px 0}
+.bt-wf-prompt{display:flex;flex-direction:column;gap:4px;font-size:12px;min-width:0}
+.bt-wf-prompt textarea{width:100%;box-sizing:border-box;border-radius:8px;border:1px solid var(--bt-line);background:var(--bt-card);color:inherit;font:inherit;font-size:12px;padding:6px 8px;resize:vertical}
+.bt-wf-plane{position:relative;width:0;margin:16px auto 0}
+.bt-wf-wires{position:absolute;left:0;top:0;overflow:visible;pointer-events:none}
+.bt-wf-wires path{fill:none;stroke:var(--bt-ink-3);stroke-width:1.6}
Expand Down
Loading
Loading