fix: 遵守仓库硬约束(禁止修改 DSH 源码)——tasks.output 改为会话事件回放
- 回退对 harness peek 缝隙的依赖:输出查看不再调用注册表,改为从属主 会话事件日志回放 task_output 的 tool/call+tool/result 对(模型已读内容) - read:false 时面板提示「模型尚未读取」;'(no new output)' 增量与错误 结果过滤;截断上限不变;kill 走注册表原生 API(缺席时 503) - AGENTS.md 新增 §0 硬约束:禁止修改 DSH 源码、挂载只走 cordis.patch.yml + profile 机制;设计文档同步实施偏差 - 测试:tasks-routes 重写为回放语义(拼接/过滤/read 标志/截断/503), 视图新增 read:false 提示用例;全量 369 通过
This commit is contained in:
+14
-11
@@ -359,13 +359,13 @@ function CatalogRows({
|
||||
|
||||
/**
|
||||
* The shared output dock of the tasks section: ONE pane at the bottom of the
|
||||
* sidebar body (sticky, terminal-like) shows the SELECTED task's FULL
|
||||
* accumulated output via the host's NON-CONSUMING peek (`tasks.output`),
|
||||
* refreshed every {@link TASK_POLL_MS} while the task runs and the page is
|
||||
* visible. The model's own `task_output` cursor is never touched, so
|
||||
* watching a stream cannot steal the agent's bytes. A single dock — not a
|
||||
* panel per row — keeps the task list compact and stable when many tasks
|
||||
* are running.
|
||||
* sidebar body (sticky, terminal-like) shows the SELECTED task's output as
|
||||
* the MODEL has read it so far (replayed from the owner session's event
|
||||
* log), refreshed every {@link TASK_POLL_MS} while the task runs and the
|
||||
* page is visible. The model's `task_output` cursor is never touched — the
|
||||
* pane can never steal the agent's bytes, and it stays empty until the
|
||||
* agent reads the task. A single dock — not a panel per row — keeps the
|
||||
* task list compact and stable when many tasks are running.
|
||||
*/
|
||||
function TaskOutputPane(props: {
|
||||
ownerSessionId: string
|
||||
@@ -437,7 +437,9 @@ function TaskOutputPane(props: {
|
||||
<>
|
||||
{state.text.length > 0
|
||||
? <pre ref={preRef} className={css.tasksPanePre}>{state.text}</pre>
|
||||
: <div className={css.tasksPaneHint}>{t('taskNoOutput')}</div>}
|
||||
: state.read
|
||||
? <div className={css.tasksPaneHint}>{t('taskNoOutput')}</div>
|
||||
: <div className={css.tasksPaneHint}>{t('taskNotReadYet')}</div>}
|
||||
{state.truncated && <div className={css.tasksPaneHint}>{t('taskOutputTruncated')}</div>}
|
||||
</>
|
||||
)}
|
||||
@@ -448,9 +450,10 @@ function TaskOutputPane(props: {
|
||||
/**
|
||||
* The background-task section of the Subagent page: every task of the whole
|
||||
* current tree (main agent + subagents, owner-labeled), fed by the harness
|
||||
* `session/tasks` push mirror. Clicking a row feeds its output to the shared
|
||||
* bottom dock (non-consuming peek); live rows carry a two-click-confirm
|
||||
* kill button. Renders nothing while the tree has no tasks.
|
||||
* `session/tasks` push mirror. Clicking a row feeds its model-read output to
|
||||
* the shared bottom dock (event replay — never the model's cursor); live
|
||||
* rows carry a two-click-confirm kill button. Renders nothing while the
|
||||
* tree has no tasks.
|
||||
*/
|
||||
function TasksSection(props: {
|
||||
byId: SidebarSessionList['byId']
|
||||
|
||||
+12
-8
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
import { encodeHtmlUrl } from '../html-route.ts'
|
||||
import type { BrowserProbeResult } from './browser.ts'
|
||||
import type { SidebarTaskStatus } from '../context-types.ts'
|
||||
|
||||
/** One wire failure. */
|
||||
export class SidebarApiError extends Error {
|
||||
@@ -61,13 +60,19 @@ export interface FsTextResult { kind: 'text'; content: string; truncated: boolea
|
||||
* `head` carries the first bytes (base64) for viewer detect sniffing. */
|
||||
export interface FsBinaryResult { kind: 'binary'; size: number; truncated: boolean; head: string }
|
||||
|
||||
/** One tasks.output response: the peeked full text (capped) plus the wire status. */
|
||||
/**
|
||||
* One tasks.output response: the output the MODEL has read so far for the
|
||||
* task (replayed from the owner session's event log — the model's
|
||||
* task_output cursor is never touched, so the pane can never steal the
|
||||
* agent's bytes). `read` is false until the model actually called
|
||||
* task_output for the task.
|
||||
*/
|
||||
export interface TaskOutputResult {
|
||||
text: string
|
||||
/** True when the host capped the text at its output limit. */
|
||||
truncated: boolean
|
||||
status: SidebarTaskStatus
|
||||
detail?: string
|
||||
/** Whether the model has read the task at least once. */
|
||||
read: boolean
|
||||
}
|
||||
|
||||
async function call<T>(method: string, payload: Record<string, unknown>, signal?: AbortSignal): Promise<T> {
|
||||
@@ -156,10 +161,9 @@ export const api = {
|
||||
agentPtyClose: (uuid: string) =>
|
||||
call<{ ok: true }>('agent-pty.close', { uuid }),
|
||||
/**
|
||||
* Full accumulated output of one background task (the host's NON-CONSUMING
|
||||
* peek — the model's task_output cursor and report bit are untouched, so a
|
||||
* human watching a stream never steals the agent's bytes). The scope MUST
|
||||
* be the task's OWNER session (the tasks registry fences by session id).
|
||||
* The output the model has read so far for one background task (replayed
|
||||
* from the owner session's event log — never the model's task_output
|
||||
* cursor). The scope MUST be the task's OWNER session.
|
||||
*/
|
||||
taskOutput: (scope: SessionScope, id: string, signal?: AbortSignal) =>
|
||||
call<TaskOutputResult>('tasks.output', scopePayload(scope, { id }), signal),
|
||||
|
||||
@@ -194,6 +194,7 @@ const zh = {
|
||||
taskViewOutput: '查看输出',
|
||||
taskHideOutput: '收起输出',
|
||||
taskNoOutput: '暂无输出',
|
||||
taskNotReadYet: '模型尚未读取该任务的输出,读取后会显示在这里',
|
||||
taskOutputTruncated: '输出过长,已截断显示',
|
||||
taskOutputError: '输出读取失败',
|
||||
taskKill: '终止',
|
||||
@@ -390,6 +391,7 @@ const en: Record<keyof typeof zh, string> = {
|
||||
taskViewOutput: 'View output',
|
||||
taskHideOutput: 'Hide output',
|
||||
taskNoOutput: 'No output yet',
|
||||
taskNotReadYet: 'The model has not read this task\'s output yet; it will appear here once read',
|
||||
taskOutputTruncated: 'Output truncated',
|
||||
taskOutputError: 'Failed to read output',
|
||||
taskKill: 'Kill',
|
||||
|
||||
Reference in New Issue
Block a user