English
Context
#778 adds interactive PTY control through WriteStdin. Its review confirmed the merge-blocking contract and identified four related, non-blocking follow-ups at the boundary between model intent, user authorization, and PTY driver guarantees.
This issue tracks only those four follow-ups. It does not propose growing #778 or reopening its accepted canonical-audit decisions.
Problems
1. Permission inspection is less complete than the authorized side effect
Activity rows intentionally use a bounded, redacted inputPreview. The same projection currently reaches the permission UI, including its "view full parameters" surface. A WriteStdin call may contain up to 64 KiB, so a meaningful suffix can be outside the 160-character preview.
For example, a long diagnostic prefix and a later destructive command can share the same visible preview. The user would be authorizing the complete input while only being able to inspect its prefix.
The activity projection should remain bounded and redacted. The permission surface should instead offer a deliberate full-input inspector that:
- displays the exact input as escaped text rather than interpreting terminal control sequences;
- shows the target
ref and resize, when present;
- keeps the compact preview as the default view.
2. Turn memory is not bound to terminal state
The permission scope now includes the complete ref + input + size, which prevents different inputs from sharing approval. It does not make repeated bytes equivalent across terminal states: approving y\r for one prompt may currently authorize the same bytes at a later, unrelated prompt on the same PTY.
The smallest safe direction is to disable turn-level permission memory for WriteStdin. An optimistic concurrency contract such as expectedRevision may be useful later, but it should be considered separately rather than required to close the immediate authorization gap.
3. input.applied claims more than the driver can observe
node-pty exposes write(data): void. A non-throwing call establishes that the driver accepted or queued the write request; it does not prove that the target process received or consumed every byte.
The operation result should therefore use queued, which describes the driver's asynchronous handoff without claiming delivery. Resize semantics are separate and need not be weakened because the resize API exposes a different observable contract.
4. ref validation is stricter at execution than at the schema and presentation boundaries
Runtime execution already parses the canonical maka://runtime/background-tasks/... resource format and rejects malformed refs. The public WriteStdin schema still accepts an unbounded string, and the human-facing projection copies that string without a bound.
The schema, projection, and runtime parser should share one bounded ref contract so malformed or oversized values fail before permission and cannot expand UI or telemetry payloads.
Proposed boundary
- Canonical tool-call history continues to retain exact model-generated arguments, including denied calls.
- Activity cards and ordinary transcript rows continue to use the bounded, redacted projection.
- Permission inspection can reveal the full exact input only through an explicit, safely escaped inspector.
WriteStdin does not reuse permission across terminal states through turn memory.
- Tool results describe only what the PTY driver can actually guarantee.
- The canonical runtime-resource parser remains the source of truth for valid refs and is reused at earlier boundaries where practical.
Implementation direction
- Restore the common meaning of
PermissionRequest.args: the exact invocation being authorized. WriteStdin should no longer replace it with the activity projection in the permission engine. The Desktop and TUI permission surfaces derive their bounded summary from those exact args; the expanded view renders the full input as inert escaped text. No second inspector payload is needed.
- Disable turn memory for
WriteStdin at the permission-policy boundary, and hide or disable the corresponding UI control. All clients should receive the same rule rather than relying on a TUI-only exception.
- Rename
operation.input.applied to operation.input.queued. A non-throwing node-pty.write() means the write entered the driver's asynchronous path, not that the child process received it.
- Reuse the canonical shell-run ref validator in the
WriteStdin schema, add a length bound derived from the canonical format, and bound the defensive human projection independently.
- Treat revision-bound control as a separate enhancement. It is not required to remove unsafe turn-memory reuse and should not expand this follow-up.
The permission-facing changes and runtime-contract changes can be delivered as separate, independently reviewable PRs under this issue.
Planned PR split
Acceptance criteria
- A user can inspect the complete exact input, plus the target ref and resize, before approving a
WriteStdin side effect.
- ANSI escapes and control characters in the full inspector are rendered as inert escaped text.
- Approving one
WriteStdin call does not silently authorize the same bytes at a later terminal state.
- A successful input operation reports that the input was queued, without claiming end-to-end delivery.
- Malformed and oversized refs are rejected by the tool schema and remain bounded in human-facing projections.
- The compact activity-card experience remains bounded and redacted.
Follow-up to #778.
简体中文
背景
#778 通过 WriteStdin 增加了交互式 PTY 控制。审查确认了阻断合并的契约已经落实,同时指出四项彼此相关、但不阻断本次合并的后续问题。它们都位于模型意图、用户授权与 PTY driver 保证之间的边界。
本 issue 只跟踪这四项后续工作,不建议继续扩大 #778,也不重新讨论其中已经确认的 canonical 审计决策。
问题
1. 权限检查展示的信息少于实际授权的副作用
Activity 行有意使用有界、脱敏的 inputPreview。目前同一投影也会进入权限 UI,包括“查看完整参数”界面。一次 WriteStdin 最多可以包含 64 KiB 输入,因此有意义的后缀可能位于 160 字预览之外。
例如,一段很长的诊断前缀之后可以跟随另一个具有破坏性的命令,而两者在权限框中只显示相同的前缀。用户实际上批准的是完整输入,却无法检查完整输入。
Activity 投影应继续保持有界和脱敏;权限界面则应提供一个需要用户主动展开的完整输入检查器,并满足:
- 把精确输入显示为转义后的普通文本,不解释终端控制序列;
- 显示目标
ref,以及存在时的 resize;
- 默认仍显示紧凑预览。
2. 回合内授权记忆没有绑定终端状态
权限 scope 已包含完整的 ref + input + size,因此不同输入不会再共享授权。但相同字节在不同终端状态下并不具有相同语义:为一个提示批准 y\r,目前可能使同一 PTY 在稍后另一个无关提示中自动放行相同字节。
最小且稳妥的方向是禁用 WriteStdin 的回合内授权记忆。将来可以单独讨论 expectedRevision 一类乐观并发契约,但不应把它作为关闭眼前授权缺口的前置条件。
3. input.applied 超出了 driver 能够观察到的保证
node-pty 暴露的是 write(data): void。调用未抛异常只能说明 driver 接受或排队了写入请求,不能证明目标进程已经收到或消费了每一个字节。
因此,操作结果应采用 queued,表达写入已经进入 driver 的异步路径,但不声称已经送达。Resize 的可观察契约不同,不需要因为输入语义的调整而一并弱化。
4. ref 在执行边界的验证严于 schema 与展示边界
Runtime 执行前已经解析 canonical maka://runtime/background-tasks/... 资源格式并拒绝畸形 ref,但公开的 WriteStdin schema 仍接受无界字符串,人类可见投影也会无界复制该字符串。
Schema、投影和 runtime parser 应共享同一个有界 ref 契约,使畸形或超长输入在进入权限流程前失败,并且不能放大 UI 或 telemetry payload。
建议边界
- Canonical 工具调用历史继续保留模型生成的精确参数,包括被拒绝的调用。
- Activity 卡片和普通 transcript 行继续使用有界、脱敏的投影。
- 只有用户主动展开权限检查器时,才以安全转义形式展示完整精确输入。
WriteStdin 不通过回合内授权记忆跨终端状态复用权限。
- 工具结果只描述 PTY driver 实际能够保证的事实。
- Canonical runtime-resource parser 继续作为合法 ref 的事实来源,并在合适的更早边界复用。
实现方向
- 恢复
PermissionRequest.args 的统一含义:它表示正在等待授权的精确调用。WriteStdin 不再由 permission engine 提前替换成 activity 投影。Desktop 与 TUI 权限界面从精确参数派生有界摘要,展开视图则把完整输入渲染为不可执行的转义文本;不需要新增第二套 inspector payload。
- 在 permission policy 边界统一禁用
WriteStdin 的 turn memory,并隐藏或禁用相应 UI 控件。所有客户端遵循同一规则,不加入只对 TUI 生效的例外。
- 将
operation.input.applied 重命名为 operation.input.queued。node-pty.write() 未抛异常只表示写入进入 driver 的异步路径,不表示子进程已经收到输入。
- 在
WriteStdin schema 中复用 canonical shell-run ref validator,按 canonical 格式推导长度上限,并让防御性的人类投影独立保持有界。
- 将 revision-bound control 作为独立增强处理。它不是消除不安全 turn-memory 复用的前置条件,也不应扩大本 follow-up。
权限侧修改与 runtime 契约修改可以在本 issue 下拆成两个彼此独立、易于审查的 PR。
计划的 PR 拆分
验收条件
- 用户在批准
WriteStdin 副作用前,可以检查完整精确输入、目标 ref 和 resize。
- 完整检查器把 ANSI escape 与控制字符显示为不可执行的转义文本。
- 批准一次
WriteStdin 不会在稍后的终端状态中静默放行相同字节。
- 成功结果只报告输入已进入队列,不声称已经端到端送达。
- 畸形和超长 ref 会被 tool schema 拒绝,并且在人类可见投影中始终有界。
- 紧凑 activity 卡片继续保持有界和脱敏。
#778 的后续跟进。
English
Context
#778 adds interactive PTY control through
WriteStdin. Its review confirmed the merge-blocking contract and identified four related, non-blocking follow-ups at the boundary between model intent, user authorization, and PTY driver guarantees.This issue tracks only those four follow-ups. It does not propose growing #778 or reopening its accepted canonical-audit decisions.
Problems
1. Permission inspection is less complete than the authorized side effect
Activity rows intentionally use a bounded, redacted
inputPreview. The same projection currently reaches the permission UI, including its "view full parameters" surface. AWriteStdincall may contain up to 64 KiB, so a meaningful suffix can be outside the 160-character preview.For example, a long diagnostic prefix and a later destructive command can share the same visible preview. The user would be authorizing the complete input while only being able to inspect its prefix.
The activity projection should remain bounded and redacted. The permission surface should instead offer a deliberate full-input inspector that:
refand resize, when present;2. Turn memory is not bound to terminal state
The permission scope now includes the complete
ref + input + size, which prevents different inputs from sharing approval. It does not make repeated bytes equivalent across terminal states: approvingy\rfor one prompt may currently authorize the same bytes at a later, unrelated prompt on the same PTY.The smallest safe direction is to disable turn-level permission memory for
WriteStdin. An optimistic concurrency contract such asexpectedRevisionmay be useful later, but it should be considered separately rather than required to close the immediate authorization gap.3.
input.appliedclaims more than the driver can observenode-ptyexposeswrite(data): void. A non-throwing call establishes that the driver accepted or queued the write request; it does not prove that the target process received or consumed every byte.The operation result should therefore use
queued, which describes the driver's asynchronous handoff without claiming delivery. Resize semantics are separate and need not be weakened because the resize API exposes a different observable contract.4.
refvalidation is stricter at execution than at the schema and presentation boundariesRuntime execution already parses the canonical
maka://runtime/background-tasks/...resource format and rejects malformed refs. The publicWriteStdinschema still accepts an unbounded string, and the human-facing projection copies that string without a bound.The schema, projection, and runtime parser should share one bounded ref contract so malformed or oversized values fail before permission and cannot expand UI or telemetry payloads.
Proposed boundary
WriteStdindoes not reuse permission across terminal states through turn memory.Implementation direction
PermissionRequest.args: the exact invocation being authorized.WriteStdinshould no longer replace it with the activity projection in the permission engine. The Desktop and TUI permission surfaces derive their bounded summary from those exact args; the expanded view renders the full input as inert escaped text. No second inspector payload is needed.WriteStdinat the permission-policy boundary, and hide or disable the corresponding UI control. All clients should receive the same rule rather than relying on a TUI-only exception.operation.input.appliedtooperation.input.queued. A non-throwingnode-pty.write()means the write entered the driver's asynchronous path, not that the child process received it.WriteStdinschema, add a length bound derived from the canonical format, and bound the defensive human projection independently.The permission-facing changes and runtime-contract changes can be delivered as separate, independently reviewable PRs under this issue.
Planned PR split
PermissionRequest.args, derive the bounded summary and full escaped inspector in Desktop/TUI, and disableWriteStdinturn memory at both policy and UI boundaries.appliedtoqueued, validate the canonical shell-run ref in the tool schema, and keep its defensive human projection bounded.Acceptance criteria
WriteStdinside effect.WriteStdincall does not silently authorize the same bytes at a later terminal state.Follow-up to #778.
简体中文
背景
#778 通过
WriteStdin增加了交互式 PTY 控制。审查确认了阻断合并的契约已经落实,同时指出四项彼此相关、但不阻断本次合并的后续问题。它们都位于模型意图、用户授权与 PTY driver 保证之间的边界。本 issue 只跟踪这四项后续工作,不建议继续扩大 #778,也不重新讨论其中已经确认的 canonical 审计决策。
问题
1. 权限检查展示的信息少于实际授权的副作用
Activity 行有意使用有界、脱敏的
inputPreview。目前同一投影也会进入权限 UI,包括“查看完整参数”界面。一次WriteStdin最多可以包含 64 KiB 输入,因此有意义的后缀可能位于 160 字预览之外。例如,一段很长的诊断前缀之后可以跟随另一个具有破坏性的命令,而两者在权限框中只显示相同的前缀。用户实际上批准的是完整输入,却无法检查完整输入。
Activity 投影应继续保持有界和脱敏;权限界面则应提供一个需要用户主动展开的完整输入检查器,并满足:
ref,以及存在时的 resize;2. 回合内授权记忆没有绑定终端状态
权限 scope 已包含完整的
ref + input + size,因此不同输入不会再共享授权。但相同字节在不同终端状态下并不具有相同语义:为一个提示批准y\r,目前可能使同一 PTY 在稍后另一个无关提示中自动放行相同字节。最小且稳妥的方向是禁用
WriteStdin的回合内授权记忆。将来可以单独讨论expectedRevision一类乐观并发契约,但不应把它作为关闭眼前授权缺口的前置条件。3.
input.applied超出了 driver 能够观察到的保证node-pty暴露的是write(data): void。调用未抛异常只能说明 driver 接受或排队了写入请求,不能证明目标进程已经收到或消费了每一个字节。因此,操作结果应采用
queued,表达写入已经进入 driver 的异步路径,但不声称已经送达。Resize 的可观察契约不同,不需要因为输入语义的调整而一并弱化。4.
ref在执行边界的验证严于 schema 与展示边界Runtime 执行前已经解析 canonical
maka://runtime/background-tasks/...资源格式并拒绝畸形 ref,但公开的WriteStdinschema 仍接受无界字符串,人类可见投影也会无界复制该字符串。Schema、投影和 runtime parser 应共享同一个有界 ref 契约,使畸形或超长输入在进入权限流程前失败,并且不能放大 UI 或 telemetry payload。
建议边界
WriteStdin不通过回合内授权记忆跨终端状态复用权限。实现方向
PermissionRequest.args的统一含义:它表示正在等待授权的精确调用。WriteStdin不再由 permission engine 提前替换成 activity 投影。Desktop 与 TUI 权限界面从精确参数派生有界摘要,展开视图则把完整输入渲染为不可执行的转义文本;不需要新增第二套 inspector payload。WriteStdin的 turn memory,并隐藏或禁用相应 UI 控件。所有客户端遵循同一规则,不加入只对 TUI 生效的例外。operation.input.applied重命名为operation.input.queued。node-pty.write()未抛异常只表示写入进入 driver 的异步路径,不表示子进程已经收到输入。WriteStdinschema 中复用 canonical shell-run ref validator,按 canonical 格式推导长度上限,并让防御性的人类投影独立保持有界。权限侧修改与 runtime 契约修改可以在本 issue 下拆成两个彼此独立、易于审查的 PR。
计划的 PR 拆分
PermissionRequest.args,在 Desktop/TUI 中派生有界摘要与完整转义检查视图,并在 policy 和 UI 两层禁用WriteStdinturn memory。applied重命名为queued,在 tool schema 中验证 canonical shell-run ref,并保持防御性的人类投影有界。验收条件
WriteStdin副作用前,可以检查完整精确输入、目标 ref 和 resize。WriteStdin不会在稍后的终端状态中静默放行相同字节。#778 的后续跟进。