fix(progress): 停压 task_progress 参数修进度还原 + 进度区移到对话区顶部
问题1(进度不对): 上下文压缩把旧 task_progress tool_call 参数换成
{"_compacted":true,"step_id":"sX"} 这种像合法调用的标记, 既毒化模型让它
照抄出残废 update_step(丢 step.status)入库, 又让前端 applyProgressAction
读不到 args.step → 步骤永停 pending。修复: task_progress 参数一律不压缩。
问题2(没像 codex 在顶部): 删掉每条消息卡内联进度块, 进度统一只在对话区
顶部单一 dock 实时显示(钉顶不滚); 全部完成时折叠成一行摘要。prompt/tool
描述改为跑完标 completed 而非 clear, 留住全绿收尾。
校验: unittest test_context_compaction/test_task_progress_tool 12 过;
node --test frontend_task_progress 2 过。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2136fdd306
commit
824f746571
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。
|
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。
|
||||||
|
|
||||||
最后更新:2026-06-08(task_progress 进度工具 + 前端固定进度区 + 静态资源 no-cache)
|
最后更新:2026-06-08(进度还原修复:停压 task_progress 参数 + 进度区移到对话区顶部 + 完成态折叠)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
### 2026-06-08
|
### 2026-06-08
|
||||||
|
|
||||||
|
- **修进度还原错乱 + 进度区移到对话区顶部(codex 式)**:根因(查 DB 实锤)= 上下文压缩把旧 `task_progress` tool_call 参数换成 `{"_compacted":true,"step_id":"sX"}` 这种"看着像合法调用"的标记,① 毒化模型让它后续照抄出残废 `update_step`(丢了 `step.status`)并入库,② 残废格式前端 `applyProgressAction` 读不到 `args.step` → s4/s5 永停 pending → 进度显示不对。修复:`context.py` 对 `task_progress` 参数**一律不压缩**(参数本就小,压缩省不了几个 token 却两头坏事);旧的 `_compact_task_progress_arguments` 整个删除。**进度展示重构**:删掉每条消息卡内联进度块(`renderProgressHtml`/`renderProgressInto` 移除),进度统一只在**对话区顶部**单一 `#task-progress-dock`(从 composer 上方移到 `chat-stream` 之上、`flex-shrink:0` 钉顶不滚)实时显示;**完成态折叠**——全部步骤 completed 时 dock 自动收成一行 `✓ 全部完成 · N/N 步`(`<details>` 点开看清单)。prompt + tool 描述改为"跑完把最后一步标 `completed`、不要 `clear`",留住全绿收尾。校验:`python -m unittest tests.test_context_compaction tests.test_task_progress_tool`(12 过,改写 `test_keeps_old_task_progress_arguments_intact` 断言参数原样保留);`node --test tests/frontend_task_progress.test.mjs`(2 过)。
|
||||||
- **修登录无反应(`$ is not defined`)+ 补 favicon 消 404**:`newtask.js` 用了 DOM 简写 `$`(`dom.js` 导出的 `getElementById`)却漏 import,模块加载到顶层 `$("hd-new").onclick` 即抛 `ReferenceError: $ is not defined`,中断 newtask 全部绑定及其 import 的 auth/chat 链路 → 点登录无反应。补 `import { $ } from "./dom.js"` 与其余模块对齐。另在 `dev.html` `<head>` 加内联 SVG data-URI `<link rel="icon">`(蓝底白机器人),浏览器不再请求根 `/favicon.ico`,消掉 404;选内联 SVG 而非新增 `.ico` 文件 / 服务端路由,零新增文件零 app.py 改动。
|
- **修登录无反应(`$ is not defined`)+ 补 favicon 消 404**:`newtask.js` 用了 DOM 简写 `$`(`dom.js` 导出的 `getElementById`)却漏 import,模块加载到顶层 `$("hd-new").onclick` 即抛 `ReferenceError: $ is not defined`,中断 newtask 全部绑定及其 import 的 auth/chat 链路 → 点登录无反应。补 `import { $ } from "./dom.js"` 与其余模块对齐。另在 `dev.html` `<head>` 加内联 SVG data-URI `<link rel="icon">`(蓝底白机器人),浏览器不再请求根 `/favicon.ico`,消掉 404;选内联 SVG 而非新增 `.ico` 文件 / 服务端路由,零新增文件零 app.py 改动。
|
||||||
- **新增 Codex 式 `task_progress` 进度工具 + Web 固定进度区**:`TaskProgressTool` 默认注册到 agent,支持 `set_plan/update_step/clear`,返回极短 UI-only 结果;上下文压缩对旧 `task_progress` tool_call/result 做专门折叠,避免进度历史长期占 prompt。前端新增 `progress.js` 做 task 级进度状态合并,修复 `update_step` 只带 `{id,status}` 时因缺标题不显示的问题;当前进度显示从助手消息内提升到 `#task-progress-dock`(对话流下方、输入框上方),历史消息内仍保留进度块作记录。system prompt + coding/ppt/proposal/analyze skill 加轻量使用约定,要求只在多步骤关键阶段少量更新。**部署侧补静态资源 no-cache**:`NoCacheStaticFiles` 替换默认 `StaticFiles`,让浏览器重新校验 `/static/*.js` 等资源,避免前端修复已部署但旧 `chat.js` 仍被缓存导致看不到进度区。校验:`pytest tests/test_context_compaction.py tests/test_task_progress_tool.py tests/test_executor_docker.py tests/test_static_vendor.py -v` 相关集通过;`node --test tests/frontend_task_progress.test.mjs` 2 过;`node --check web/static/js/chat.js web/static/js/progress.js` 过。
|
- **新增 Codex 式 `task_progress` 进度工具 + Web 固定进度区**:`TaskProgressTool` 默认注册到 agent,支持 `set_plan/update_step/clear`,返回极短 UI-only 结果;上下文压缩对旧 `task_progress` tool_call/result 做专门折叠,避免进度历史长期占 prompt。前端新增 `progress.js` 做 task 级进度状态合并,修复 `update_step` 只带 `{id,status}` 时因缺标题不显示的问题;当前进度显示从助手消息内提升到 `#task-progress-dock`(对话流下方、输入框上方),历史消息内仍保留进度块作记录。system prompt + coding/ppt/proposal/analyze skill 加轻量使用约定,要求只在多步骤关键阶段少量更新。**部署侧补静态资源 no-cache**:`NoCacheStaticFiles` 替换默认 `StaticFiles`,让浏览器重新校验 `/static/*.js` 等资源,避免前端修复已部署但旧 `chat.js` 仍被缓存导致看不到进度区。校验:`pytest tests/test_context_compaction.py tests/test_task_progress_tool.py tests/test_executor_docker.py tests/test_static_vendor.py -v` 相关集通过;`node --test tests/frontend_task_progress.test.mjs` 2 过;`node --check web/static/js/chat.js web/static/js/progress.js` 过。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,35 +45,13 @@ def _message_chars(msg: dict[str, Any]) -> int:
|
||||||
return len(str(msg))
|
return len(str(msg))
|
||||||
|
|
||||||
|
|
||||||
def _compact_task_progress_arguments(raw: Any) -> tuple[Any, bool]:
|
|
||||||
if not isinstance(raw, str):
|
|
||||||
return raw, False
|
|
||||||
try:
|
|
||||||
parsed = json.loads(raw)
|
|
||||||
except Exception:
|
|
||||||
parsed = {}
|
|
||||||
if not isinstance(parsed, dict):
|
|
||||||
parsed = {}
|
|
||||||
marker: dict[str, Any] = {
|
|
||||||
"_compacted": True,
|
|
||||||
"tool": "task_progress",
|
|
||||||
"note": "old UI progress details omitted from context",
|
|
||||||
}
|
|
||||||
action = parsed.get("action")
|
|
||||||
if isinstance(action, str) and action:
|
|
||||||
marker["action"] = action
|
|
||||||
steps = parsed.get("steps")
|
|
||||||
if isinstance(steps, list):
|
|
||||||
marker["step_count"] = len(steps)
|
|
||||||
step = parsed.get("step")
|
|
||||||
if isinstance(step, dict) and isinstance(step.get("id"), str):
|
|
||||||
marker["step_id"] = step["id"]
|
|
||||||
return json.dumps(marker, ensure_ascii=False), True
|
|
||||||
|
|
||||||
|
|
||||||
def _compact_tool_call_arguments(raw: Any, max_chars: int, tool_name: str = "") -> tuple[Any, bool]:
|
def _compact_tool_call_arguments(raw: Any, max_chars: int, tool_name: str = "") -> tuple[Any, bool]:
|
||||||
|
# task_progress 参数本就很小(3-7 个短步骤),压缩省的 token 微乎其微,但把它换成
|
||||||
|
# `{"_compacted":true,"step_id":...}` 这种"看起来像合法调用"的标记会:① 毒化模型,
|
||||||
|
# 让它照葫芦画瓢生成残废的 update_step(丢了 step.status)入库;② 残废格式前端
|
||||||
|
# applyProgressAction 读不到 args.step → 进度还原错乱。故 task_progress 一律不压缩参数。
|
||||||
if tool_name == "task_progress":
|
if tool_name == "task_progress":
|
||||||
return _compact_task_progress_arguments(raw)
|
return raw, False
|
||||||
if not isinstance(raw, str) or len(raw) <= max_chars:
|
if not isinstance(raw, str) or len(raw) <= max_chars:
|
||||||
return raw, False
|
return raw, False
|
||||||
marker: dict[str, Any] = {
|
marker: dict[str, Any] = {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
- 多步骤任务开始后,用 `task_progress(action="set_plan", steps=[...])` 发布一份简短计划。
|
- 多步骤任务开始后,用 `task_progress(action="set_plan", steps=[...])` 发布一份简短计划。
|
||||||
- 步骤标题面向用户,写“理解需求 / 实现功能 / 运行测试 / 输出结果”这类阶段,不要写每个文件读写或每次 shell 命令。
|
- 步骤标题面向用户,写“理解需求 / 实现功能 / 运行测试 / 输出结果”这类阶段,不要写每个文件读写或每次 shell 命令。
|
||||||
- 只在关键状态变化时用 `update_step`:当前步骤进入 `in_progress`,完成后改 `completed`。不要高频更新,不要把普通 tool 调用都转成进度步骤。
|
- 只在关键状态变化时用 `update_step`:当前步骤进入 `in_progress`,完成后改 `completed`。不要高频更新,不要把普通 tool 调用都转成进度步骤。
|
||||||
|
- 任务全部做完时,把最后一步标成 `completed`(让用户在顶部进度面板看到"全绿"收尾),**不要用 `clear`**;`clear` 只在计划被推翻、不再相关时才用。
|
||||||
- 简单问答、单次文件读取、很小的改动不需要调用 `task_progress`。
|
- 简单问答、单次文件读取、很小的改动不需要调用 `task_progress`。
|
||||||
|
|
||||||
## 媒体生成工具(按需可用,未配置 ARK_API_KEY 时该工具不会出现)
|
## 媒体生成工具(按需可用,未配置 ARK_API_KEY 时该工具不会出现)
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,9 @@ class ContextCompactionTests(unittest.TestCase):
|
||||||
self.assertNotIn("A" * 100, tc["function"]["arguments"])
|
self.assertNotIn("A" * 100, tc["function"]["arguments"])
|
||||||
self.assertEqual(stats["compacted_tool_call_arguments"], 1)
|
self.assertEqual(stats["compacted_tool_call_arguments"], 1)
|
||||||
|
|
||||||
def test_compacts_old_task_progress_arguments_to_plan_marker(self) -> None:
|
def test_keeps_old_task_progress_arguments_intact(self) -> None:
|
||||||
|
# task_progress 参数本就很小,且压成 `{"_compacted":...,"step_id":...}` 这种"像合法调用"
|
||||||
|
# 的标记会毒化模型 + 毁掉前端进度还原。故旧的 task_progress 调用参数必须原样保留。
|
||||||
args = json.dumps({
|
args = json.dumps({
|
||||||
"action": "set_plan",
|
"action": "set_plan",
|
||||||
"steps": [
|
"steps": [
|
||||||
|
|
@ -173,13 +175,11 @@ class ContextCompactionTests(unittest.TestCase):
|
||||||
|
|
||||||
prepared, stats = prepare_messages_with_stats(messages)
|
prepared, stats = prepare_messages_with_stats(messages)
|
||||||
|
|
||||||
compacted_args = json.loads(prepared[1]["tool_calls"][0]["function"]["arguments"])
|
kept_args = json.loads(prepared[1]["tool_calls"][0]["function"]["arguments"])
|
||||||
self.assertTrue(compacted_args["_compacted"])
|
self.assertNotIn("_compacted", kept_args)
|
||||||
self.assertEqual(compacted_args["tool"], "task_progress")
|
self.assertEqual(kept_args["action"], "set_plan")
|
||||||
self.assertEqual(compacted_args["action"], "set_plan")
|
self.assertEqual(kept_args["steps"][0]["title"], "理解需求")
|
||||||
self.assertEqual(compacted_args["step_count"], 2)
|
self.assertEqual(stats["compacted_tool_call_arguments"], 0)
|
||||||
self.assertNotIn("理解需求", prepared[1]["tool_calls"][0]["function"]["arguments"])
|
|
||||||
self.assertEqual(stats["compacted_tool_call_arguments"], 1)
|
|
||||||
|
|
||||||
def test_old_task_progress_tool_result_uses_tiny_marker(self) -> None:
|
def test_old_task_progress_tool_result_uses_tiny_marker(self) -> None:
|
||||||
messages = [
|
messages = [
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,9 @@ class TaskProgressTool(Tool):
|
||||||
description = (
|
description = (
|
||||||
"Publish or update a concise user-visible progress checklist for the current task. "
|
"Publish or update a concise user-visible progress checklist for the current task. "
|
||||||
"Use only for meaningful multi-step work: set the plan once, update a step when it "
|
"Use only for meaningful multi-step work: set the plan once, update a step when it "
|
||||||
"starts or completes, and clear it only when it is no longer relevant. This is a UI "
|
"starts or completes, and when all work is done mark the final step completed (do NOT "
|
||||||
"progress signal, not a work product."
|
"clear). Use clear only when the plan is no longer relevant. This is a UI progress "
|
||||||
|
"signal, not a work product."
|
||||||
)
|
)
|
||||||
parameters = {
|
parameters = {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
||||||
|
|
@ -445,9 +445,17 @@
|
||||||
border: 1px solid var(--border-soft); border-radius: var(--r-md);
|
border: 1px solid var(--border-soft); border-radius: var(--r-md);
|
||||||
background: #fafafa; font-size: 12px;
|
background: #fafafa; font-size: 12px;
|
||||||
}
|
}
|
||||||
.task-progress .tp-title {
|
.task-progress > .tp-summary {
|
||||||
margin-bottom: 5px; color: var(--muted); font-family: var(--mono); font-size: 11px;
|
cursor: pointer; list-style: none; color: var(--muted);
|
||||||
|
font-family: var(--mono); font-size: 11px; user-select: none;
|
||||||
}
|
}
|
||||||
|
.task-progress > .tp-summary::-webkit-details-marker { display: none; }
|
||||||
|
.task-progress > .tp-summary::before {
|
||||||
|
content: "▸"; display: inline-block; margin-right: 5px; transition: transform .12s;
|
||||||
|
}
|
||||||
|
.task-progress[open] > .tp-summary::before { transform: rotate(90deg); }
|
||||||
|
.task-progress > .tp-summary.tp-done { color: var(--c-green); }
|
||||||
|
.task-progress[open] > .tp-summary { margin-bottom: 5px; }
|
||||||
.task-progress .tp-list { display: grid; gap: 4px; }
|
.task-progress .tp-list { display: grid; gap: 4px; }
|
||||||
.task-progress .tp-step {
|
.task-progress .tp-step {
|
||||||
display: grid; grid-template-columns: 18px minmax(0, 1fr);
|
display: grid; grid-template-columns: 18px minmax(0, 1fr);
|
||||||
|
|
@ -469,7 +477,7 @@
|
||||||
.task-progress .tp-step.completed .tp-text { color: var(--muted); text-decoration: line-through; }
|
.task-progress .tp-step.completed .tp-text { color: var(--muted); text-decoration: line-through; }
|
||||||
#task-progress-dock {
|
#task-progress-dock {
|
||||||
flex-shrink: 0; display: none;
|
flex-shrink: 0; display: none;
|
||||||
padding: 8px 12px; border-top: 1px solid var(--border);
|
padding: 8px 12px; border-bottom: 1px solid var(--border);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
#task-progress-dock.show { display: block; }
|
#task-progress-dock.show { display: block; }
|
||||||
|
|
@ -1040,8 +1048,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="chat-meta"><span class="muted">(未选中任务)</span></div>
|
<div id="chat-meta"><span class="muted">(未选中任务)</span></div>
|
||||||
<div id="wd-concurrent-warn" style="display:none;"></div>
|
<div id="wd-concurrent-warn" style="display:none;"></div>
|
||||||
<div id="chat-stream"><div class="empty">请在左侧选一个任务</div></div>
|
|
||||||
<div id="task-progress-dock"></div>
|
<div id="task-progress-dock"></div>
|
||||||
|
<div id="chat-stream"><div class="empty">请在左侧选一个任务</div></div>
|
||||||
<form id="chat-form" style="display:none;">
|
<form id="chat-form" style="display:none;">
|
||||||
<textarea id="chat-input" placeholder="输入消息…(Enter 发送,Shift+Enter 换行,Ctrl+V 可粘贴文件)"></textarea>
|
<textarea id="chat-input" placeholder="输入消息…(Enter 发送,Shift+Enter 换行,Ctrl+V 可粘贴文件)"></textarea>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,7 @@ function renderLiveRunIfVisible() {
|
||||||
const wrap = $("chat-stream");
|
const wrap = $("chat-stream");
|
||||||
const card = run.card || createLiveAssistantCard(run);
|
const card = run.card || createLiveAssistantCard(run);
|
||||||
if (run.body && run.acc) run.body.innerHTML = renderMd(run.acc);
|
if (run.body && run.acc) run.body.innerHTML = renderMd(run.acc);
|
||||||
renderProgressInto(card, run.progressSteps || []);
|
renderTaskProgressDock(run.progressSteps || []);
|
||||||
if (card.parentElement !== wrap) wrap.appendChild(card);
|
if (card.parentElement !== wrap) wrap.appendChild(card);
|
||||||
wrap.scrollTop = wrap.scrollHeight;
|
wrap.scrollTop = wrap.scrollHeight;
|
||||||
setActionMode(run.cancelling ? "cancelling" : "streaming");
|
setActionMode(run.cancelling ? "cancelling" : "streaming");
|
||||||
|
|
@ -449,32 +449,8 @@ function setRunHint(run, text) {
|
||||||
if (state.taskId === run.taskId) $("chat-hint").textContent = text;
|
if (state.taskId === run.taskId) $("chat-hint").textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderProgressHtml(steps) {
|
// 进度只在对话区顶部的单一 dock 里渲染(codex 式钉顶面板),不再内联进每条消息卡。
|
||||||
if (!Array.isArray(steps) || !steps.length) return "";
|
// 进行中:展开实时显示 pending/in_progress/completed;全部完成:折叠成一行摘要,点开看清单。
|
||||||
const mark = (status) => status === "completed" ? "✓" : (status === "in_progress" ? "…" : "");
|
|
||||||
const rows = steps.map((s) => `
|
|
||||||
<div class="tp-step ${escapeHtml(s.status)}">
|
|
||||||
<span class="tp-mark">${escapeHtml(mark(s.status))}</span>
|
|
||||||
<span class="tp-text">${escapeHtml(s.title)}</span>
|
|
||||||
</div>
|
|
||||||
`).join("");
|
|
||||||
return `<div class="task-progress"><div class="tp-title">进度</div><div class="tp-list">${rows}</div></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderProgressInto(card, steps) {
|
|
||||||
let el = card.querySelector(":scope > .task-progress");
|
|
||||||
if (!Array.isArray(steps) || !steps.length) {
|
|
||||||
if (el) el.remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const html = renderProgressHtml(steps);
|
|
||||||
if (el) {
|
|
||||||
el.outerHTML = html;
|
|
||||||
} else {
|
|
||||||
card.insertAdjacentHTML("beforeend", html);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderTaskProgressDock(steps) {
|
function renderTaskProgressDock(steps) {
|
||||||
const dock = $("task-progress-dock");
|
const dock = $("task-progress-dock");
|
||||||
if (!dock) return;
|
if (!dock) return;
|
||||||
|
|
@ -483,7 +459,21 @@ function renderTaskProgressDock(steps) {
|
||||||
dock.classList.remove("show");
|
dock.classList.remove("show");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dock.innerHTML = renderProgressHtml(steps);
|
const total = steps.length;
|
||||||
|
const done = steps.filter(s => s.status === "completed").length;
|
||||||
|
const allDone = done === total;
|
||||||
|
const mark = (status) => status === "completed" ? "✓" : (status === "in_progress" ? "…" : "");
|
||||||
|
const rows = steps.map((s) => `
|
||||||
|
<div class="tp-step ${escapeHtml(s.status)}">
|
||||||
|
<span class="tp-mark">${escapeHtml(mark(s.status))}</span>
|
||||||
|
<span class="tp-text">${escapeHtml(s.title)}</span>
|
||||||
|
</div>
|
||||||
|
`).join("");
|
||||||
|
const summary = allDone
|
||||||
|
? `<summary class="tp-summary tp-done">✓ 全部完成 · ${done}/${total} 步</summary>`
|
||||||
|
: `<summary class="tp-summary">进度 · ${done}/${total} 步</summary>`;
|
||||||
|
const openAttr = allDone ? "" : " open"; // 全完成默认折叠,其余展开
|
||||||
|
dock.innerHTML = `<details class="task-progress"${openAttr}>${summary}<div class="tp-list">${rows}</div></details>`;
|
||||||
dock.classList.add("show");
|
dock.classList.add("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -588,7 +578,7 @@ function renderMessages(msgs) {
|
||||||
${renderArtifactBarHtml(rels, isProducer)}
|
${renderArtifactBarHtml(rels, isProducer)}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
if (progressResult.sawProgress) html += renderProgressHtml(currentProgressSteps);
|
// 进度不再内联进消息卡 —— 累积值在循环末统一喂顶部 dock(见 setTaskProgress)
|
||||||
}
|
}
|
||||||
card.innerHTML = html;
|
card.innerHTML = html;
|
||||||
highlightIn(card);
|
highlightIn(card);
|
||||||
|
|
@ -1004,7 +994,6 @@ function handleSseEvent(ev, asstCard, ctx) {
|
||||||
if (fn === "task_progress") {
|
if (fn === "task_progress") {
|
||||||
ctx.progressSteps = applyProgressAction(ctx.progressSteps || [], args);
|
ctx.progressSteps = applyProgressAction(ctx.progressSteps || [], args);
|
||||||
setTaskProgress(ctx.taskId, ctx.progressSteps);
|
setTaskProgress(ctx.taskId, ctx.progressSteps);
|
||||||
renderProgressInto(asstCard, ctx.progressSteps);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const label = toolActivityLabel(fn, args);
|
const label = toolActivityLabel(fn, args);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue