Local File V4 path-free(原 wire v5,现归 Desktop 协议 v3)
族名仍是 V4,独立的 Local File 协议版本已删除
Local File 的模块族与源码目录仍叫 V4(local-files-v4 / local-file-v4);主题 id local-file-v5 沿用旧名。钉点 Agent e2adc9a3c325 / Desktop 8eecd12081db 起(Agent ADR-0090、Desktop ADR-0087),LOCAL_FILES_V4_PROTOCOL_VERSION = 5、capability local-files-v5、schema identity local-file-v5 与全部 Local File feature token 都已删除:两端只剩 DESKTOP_PROTOCOL_VERSION = 3(Desktop 侧为生成副本),版本 3 蕴含旧 feature。Agent 是否给模型 V4 工具只看会话载荷里有没有合法的 localFileCapability(localFileV4Admitted);Desktop 端只有 win32 缺 native 后端时才把能力置为不可用(native_backend_unavailable),V4 读不再受托管 Python 探针影响。Local File V4 也是唯一模型可见的文件能力:readFile / writeFile / listDirectory、FileGrant、Transient grant 等旧路径已删除。绝对路径只留在 Electron Main 的 AuthorizedFolder / SelectedFileHandle / Save Destination 底座;出 Main 的 wire 使用 relativePath / attachmentId / saveDestinationId 等 path-free 字段。
对话级附件由 Main 的 assembleSubmittedLocalFileAttachments 组装:Renderer 只提交 path-free Draft intents(attachmentId + displayName + kind),Main 填入 authority id 与 localProjection,组装结果不含绝对路径。Agent 侧 listForConversation(ADR-0079)按对话、installation、identity scope 取每个 attachmentId 的最新 relation。请求与回执账本在 Agent(local_file_operations),Desktop 执行与本机权威底座分离。Local Executor 的本机原语现只剩 readSkillFile / runSkillScript / Skill 管理(见 local-executor),不是本模块。
历史清仓:migration 114(对话级附件列)再 115(在途 v4 ledger cutover);当时仅广告 v4 的桌面端收到 426 LOCAL_FILE_PROTOCOL_UPGRADE_REQUIRED。该码与 localFileProtocolUpgradeRequired 判定已在本钉点删除:不是 v3 的 Desktop 现在统一收到 426 DESKTOP_UPGRADE_REQUIRED(见 trust-boundaries / desktop-runtime;ADR-0093 起 chat POST 没带协议头时,只有携带 localFilesV4 / assembleSubmittedLocalFileAttachments 等本机执行载荷才 426)。Desktop write-state-v4.json(文件版本 3)钉点 8eecd12081db 起存于账号作用域加密信封 LocalSecureEnvelope,旧明文只作迁移来源(见 storage-planes);解析 legacy v1/v2 条目时丢弃不可迁移项并诊断 LOCAL_FILE_WRITE_STATE_LEGACY_DROPPED;protocol-4 回执分支已删除,记录里带 protocolFeatures 字段现在判为 corrupt(升级风险见 storage-planes,未核实)。
文件目标收窄:restrictFileTargets 不再过滤本 Run 内经 request_file_access 拿到的授权(附件查询把这类关系标为 run_request_grant)。Desktop 授权附件改用权威 id 作 attachmentId(文件夹 authorizedFolderId、文件 selectedFileHandleId),同一文件夹不再积累多条关系。
aida-agent·packages/agent/src/routing/local-file-targets.tsL77–88 ·runRequestGrant·e2adc9a3c325aida-agent·packages/agent/src/session/local-file-message-attachment.tsL191–193 ·run_request_grant·e2adc9a3c325aida-desktop·apps/desktop/electron/local-files-v4/file-access.tsL228–230 ·attachmentId: folder.authorizedFolderId·4346d79eb364aida-desktop·apps/desktop/electron/local-files-v4/native-file-backend.tsL159–161 ·nativeFileBackendRequiredForPlatform·4346d79eb364
原件上传(2026-10-12 钉点)
原件上传能力原由 capability local-files-v5-original-content-v1 广告,本钉点该 capability 已删除(v3 蕴含);保留路由 POST /api/local-file/operations/:operationId/original-content:上限 20MB(LOCAL_FILE_ORIGINAL_CONTENT_MAX_BYTES,Gateway bodyLimit 超限 413 LOCAL_FILE_ORIGINAL_CONTENT_OVERSIZE),恢复路径上限 15MB(LOCAL_FILE_ORIGINAL_RESUME_MAX_BYTES);请求须 X-AIDA-Protocol: 3(原 X-Local-File-Protocol-Version: 5)、X-Local-File-Request-Digest、X-Local-File-Content-Sha256 等元数据头与 application/octet-stream,否则 400 LOCAL_FILE_ORIGINAL_CONTENT_METADATA_INVALID。原件落 local_file_operations.original_content(迁移 166);Desktop Main 由 LocalFileOriginalUplink 上传。chat POST 的 Runtime 提交门分别处理 work-v1、skill-v2 与 skillExecutionPolicy 的匹配,原 Work 身份仍是 work-v1;非支持的 wire 或 policy 不匹配返回不同的 426。RUNTIME_WORK_PROFILE_UNSUPPORTED 的 HTTP 状态依具体路径而定(connectorRead 组合限制为 409),不能一律写为 426;见 跨仓协议地图。
aida-agent·packages/tool-client/src/types/local-files-v4.tsL5–17 ·X-AIDA-Protocol·e2adc9a3c325aida-agent·packages/agent/src/gateway/api-local-file-v4.tsL347–376 ·original-content·e2adc9a3c325aida-agent·packages/agent/src/gateway/http.tsL1683–1684 ·LOCAL_FILE_ORIGINAL_CONTENT_MAX_BYTES·e2adc9a3c325aida-desktop·apps/desktop/shared/generated/local-files-v4.tsL6–18 ·X-AIDA-Protocol·4346d79eb364aida-desktop·apps/desktop/electron/main.tsL1315 ·LocalFileOriginalUplink·4346d79eb364
跨仓协议对齐与清仓(视图 wire-drift)
视图 wire-drift 是 v4→v5 对齐 + 清仓的历史快照,不是「仍有开放 drift」的告警图,也不再描述现行协议:本钉点下表中三个常量都已删除,由 DESKTOP_PROTOCOL_VERSION = 3 取代。它留在 local-file-v5 主题下作追溯,不单独拆成 topic。
| 字段 | Desktop a96b7a4 |
Agent b8d4167 |
|---|---|---|
| 源文件 | apps/desktop/shared/generated/local-files-v4.ts L5–23 |
packages/tool-client/src/types/local-files-v4.ts L4–21 |
LOCAL_FILES_V4_PROTOCOL_VERSION |
5 | 5 |
LOCAL_FILES_V4_CAPABILITY |
local-files-v5 |
local-files-v5 |
LOCAL_FILE_V4_SCHEMA_IDENTITY |
local-file-v5 |
local-file-v5 |
(表中为当时的钉点 Desktop a96b7a4 / Agent b8d4167。)图:local-file-wire-drift.architecture.json。
426 / cutover 部署序(视图 cutover-deploy,历史)
以下是 v4→v5 清仓时的顺序(ADR-0079 Consequences),已执行完毕,仅作追溯;LOCAL_FILE_PROTOCOL_UPGRADE_REQUIRED 现只存在于历史迁移 115 中。
- Agent:migration 114(前置 schema identity = interaction-protocol-v2 / mig 113)→ 115(前置 = conversation-attachments)。
- 窗口期:仅广告
localFileProtocolVersions ∋ 4且不含 5 的 Desktop,在 chat POST、/validate-local-executor(及 preview 同源门)收到426 LOCAL_FILE_PROTOCOL_UPGRADE_REQUIRED,文案「请更新桌面端后重试」——整轮对话硬门,不是「仅本地文件工具不可用」。 - mig 115:将在途
requested且request.protocolVersion = 4的 Operation 标为expired;等待该 Operation 的 suspended Run 记为failed(error_code = LOCAL_FILE_PROTOCOL_UPGRADE_REQUIRED),不依赖 TTL drain;并剥离历史绝对路径键。 - Desktop:Velopack 启动更新到 V5;之后 path-free wire + 对话级附件可用。
图:local-file-cutover-deploy.sequence.json(catalog 视图 cutover-deploy)。
现行 Gateway 426 协议硬门(Desktop 协议 v3 的 DESKTOP_UPGRADE_REQUIRED、Runtime Execution、Interaction Protocol)见 desktop-runtime NOTES「Gateway 协议硬门族」。
谁是材料权威:Agent 账本 ≠ 本机事实(步进图 A 补记)
- Agent 侧
localProjection不是能力判定:local_file_message_attachments行是有作用域的材料引用;localProjection(available | unavailable | revoked)是 Desktop 附加时的历史快照,Agent 源码注释明确「不得作为当前能力判定;Desktop Main 每次操作前复核 live authority」。 - Main 复核三件事(每次 Operation 投递都做):
- 授权文件夹:
requireFolder→V4AuthorizedFolderStore.requireActive,不存在 / 已撤销(AUTHORIZED_FOLDER_REVOKED)/ 根身份(dev+ino)变化即拒绝; - 路径解析:
resolveWithinRoot只接受 folder-relative POSIX 路径,越界(path outside root)拒绝;出 Main 的结果再经toWireRelativePath转回相对路径; - 版本 / 绑定:
capabilityFingerprint与本机不符 →capability_unavailable(冻结 protocol features 的检查随 feature token 删除);desktopExecutorInstanceId代际变化 →capability_unsupported;同一operationId不同requestDigest→conflict;写操作另校验expectedsha256(文件版本)。
- 授权文件夹:
- 回执落账是 CAS:Agent
terminalizeRequested只在status='requested' AND request_digest=期望值 AND desktop_executor_instance_id 一致时更新,迟到或错绑定回执不改账本。 - 读法:Agent 拥有「请求与回执」账本(谁要了什么、结果是什么);Main 拥有「此刻能否碰这块磁盘」的事实。两者不可互相替代。
目录身份摘要、参数纠错窗口与工作台澄清(钉点 Agent e2adc9a3c325 / Desktop 8eecd12081db)
directoryIdentityDigest(28a6ec920/9fdc200c):列目录回执可带被列目录本身(含空目录)的身份摘要,只能与原列目录的精确路径、actor、Root 和文件夹绑定一起用作改动版本;子项不能代表空父目录,缺失时不猜。发布顺序:先 Agent 后 Desktop(旧的严格 Agent 会拒绝该字段),不需要数据库迁移。- 参数纠错窗口(
3b4efa961/ee4a516be):change_files的参数纠错在用户正式拒绝或fs/changeOperation 已准入后结束,之后由真实效应回执决定;部分 / 未知效应仍被执行阻断,不获得重放额度。 - 工作台正式澄清(
d1ef96997):modify / create 任务经正式澄清后,一次语义复核判断剩余交付是否只在聊天中回答;证据须逐字来自用户输入,判为只聊天时必需工具收窄为 list / search / read 之一,不能换任务、扩大材料或产生新授权;判不清返回可重试的FILE_DELIVERY_REVIEW_UNRESOLVED。 - 发布顺序核对:Desktop 列目录无条件带上
directoryIdentityDigest,不按 Agent 版本协商;Agent28a6ec920之前的列目录 schema 是.strict()且无此字段,旧 Agent 会拒绝。git 上 Agent 先合入(2026-10-21 04:34:48 EDT,3a52e997d),Desktop 12 秒后合入(a45a26e5),Desktop v0.1.62(2026-10-21 05:39 EDT 打 tag)起带该字段、v0.1.61 不带;Agent 仓库没有对应发布 tag,部署先后未核。证据:aida-desktop·apps/desktop/electron/local-files-v4/query-executor.tsL104–144 ·directoryIdentityDigest·4346d79eb364;aida-agent·packages/agent/src/runtime/README.mdL149–151 ·older strict Agent·e2adc9a3c325 - 工作台澄清判例:单元测试用桩 LLM 固定返回值,覆盖只聊天收窄为单一读工具、仍要另存不放宽、各类无据或格式错返回
FILE_DELIVERY_REVIEW_UNRESOLVED、旧回答不能证明最新限制、模型失败原样抛出;2026-10-21 在钉点副本中 12 / 12 通过。真实模型判例未跑。证据:aida-agent·packages/agent/tests/unit/gateway/workbench-file-clarification.test.tsL123–155 ·FILE_DELIVERY_REVIEW_UNRESOLVED·e2adc9a3c325 - 证据:
aida-agent·packages/tool-client/src/types/local-files-v4.tsL1247–1251 ·directoryIdentityDigest·e2adc9a3c325;aida-agent·packages/agent/src/runtime/README.mdL144–151 ·directoryIdentityDigest·e2adc9a3c325;aida-agent·packages/agent/src/session/local-file-receipt-facts.tsL346–349 ·directoryIdentityDigest·e2adc9a3c325;aida-desktop·apps/desktop/electron/local-files-v4/query-executor.tsL104–144 ·directoryIdentityDigest·4346d79eb364;aida-agent·packages/agent/src/orchestrator/drive-react-loop.tsL699–715 ·An admitted Operation ends parameter correction·e2adc9a3c325;aida-agent·packages/agent/src/gateway/workbench-file-clarification.tsL31–64 ·reviewWorkbenchFileDelivery·e2adc9a3c325;aida-agent·packages/agent/src/gateway/workbench-file-clarification.tsL74–96 ·resolveWorkbenchFileClarification·e2adc9a3c325
证据与范围
-
aida-desktop·apps/desktop/shared/generated/protocol-version.tsL1–8 ·DESKTOP_PROTOCOL_VERSION·4346d79eb364 -
aida-desktop·docs/adr/0087-local-capability-convergence-and-protocol-v3.mdL22–32 ·Protocol v3·4346d79eb364 -
aida-desktop·apps/desktop/electron/local-files-v4/assemble-submitted-attachments.tsL49–79 ·assembleSubmittedLocalFileAttachments·4346d79eb364 -
aida-desktop·apps/desktop/shared/ipc-contract.tsL483–506 ·localFilesV4/assembleSubmittedLocalFileAttachments·4346d79eb364 -
aida-desktop·apps/desktop/electron/local-files-v4/capability.tsL9–11 ·assembleLocalFileV4Capability·4346d79eb364 -
aida-desktop·apps/desktop/electron/local-files-v4/write-state-persistence.tsL730–746 ·LOCAL_FILE_WRITE_STATE_LEGACY_DROPPED·4346d79eb364 -
aida-desktop·apps/desktop/electron/update/velopack-updater.tsL35–65 ·installVelopackStartup·4346d79eb364 -
aida-agent·packages/tool-client/src/types/protocol-version.tsL1–7 ·DESKTOP_PROTOCOL_VERSION·e2adc9a3c325 -
aida-agent·packages/agent/src/session/local-file-message-attachment.tsL126–158 ·listForConversation·e2adc9a3c325 -
aida-agent·docs/adr/0079-local-file-v5-conversation-attachments.mdL1–49 ·ADR-0079·e2adc9a3c325 -
aida-agent·packages/agent/migrations/114_local_file_v5_conversation_attachments.sqlL1–40 ·114·e2adc9a3c325 -
aida-agent·packages/agent/migrations/115_local_file_v5_ledger_cutover.sqlL1–120 ·115·e2adc9a3c325 -
aida-agent·packages/agent/src/runtime/local/local-file-v4-negotiation.tsL26–52 ·localFileV4Admitted·e2adc9a3c325 -
aida-agent·packages/agent/src/gateway/chat-routes.tsL520–524 ·desktopPayloadVersionUnsupported·e2adc9a3c325 -
aida-agent·packages/agent/src/session/local-file-operation-store.tsL214–245 ·LocalFileOperationStore·e2adc9a3c325 -
aida-agent·packages/agent/src/session/local-file-message-attachment.tsL99–102 ·historical client snapshot·e2adc9a3c325 -
aida-agent·packages/agent/src/session/local-file-operation-store.tsL369–503 ·terminalizeRequested·e2adc9a3c325 -
aida-desktop·apps/desktop/electron/local-files-v4/executor.tsL85–165 ·executeDelivery·4346d79eb364 -
aida-desktop·apps/desktop/electron/local-files-v4/executor.tsL220–243 ·executeFresh·4346d79eb364 -
aida-desktop·apps/desktop/electron/local-files-v4/authorized-folder-authority.tsL437–477 ·requireActive·4346d79eb364 -
aida-desktop·apps/desktop/electron/local-files-v4/relative-wire.tsL23–58 ·resolveWithinRoot·4346d79eb364
结论来自固定版本的静态源码与合同阅读;实现存在、默认开关和生产启用分别判断。本站代码证据面板列出声明状态与具体限制。
Runtime 提交 wire 与原 Work identity
chat POST 的 Runtime 提交门接受缺省/空值(无 policy)、runtime-work-v1(无 policy),以及 runtime-work-skill-v2 且携带合法 skillExecutionPolicy;其他非空头为 RUNTIME_EXECUTION_PROTOCOL_UNSUPPORTED,头与 policy 不匹配为 RUNTIME_WORK_SKILL_POLICY_MISMATCH,均返回 426。原 Work binding 仍是 runtime-work-v1;取消提交、补充材料等路由单独判定,见协议地图。
证据:提交门 与 实际 chat POST 调用。
条件产物不可变重建与 Carrier 终态封口门(视图 conditional-artifact-gate)
当路由决策包含 conditionalArtifactRead 时,模型不能仅凭输出一段文字声称「已处理完文件」就把 Run 置为 completed。finishRuntimeWorkCarrierInTx(run-work-carrier-terminal.ts L11–91)在 outcome === 'completed' 时强制调用 readConditionalArtifactCompletionInTx(run-work-conditional-artifact.ts L49–157),在原 Root/claim fence 下从底层不可变事实表重建完成证明(见视图 conditional-artifact-gate):
- 前置门禁与能力快照核验(L52–84):
- 校验
root.cancel_intent === null、无hasRuntimeWorkEvidenceConflict、无hasPendingWorkMaterialAdmission; - 通过
PgDesktopSkillSnapshotRegistry.resolveExact核对target.producer.skillSource.identity处于active,且admittedSkillPluginTools中存在唯一匹配的conditionalArtifactOutput声明; - 查
LocalFileMessageAttachmentStore.listForConversation核对target.scope引用的每一个attachmentId与selectedFileId都真实绑定在当前会话与frozenLocalExecutionScope下。
- 校验
- 6 表关联重建插件回执与消息链(L86–143):
- 执行
continuation_requests c⋈pending_interactions p⋈agent_runs r⋈runtime_invocations i⋈interaction_receipts receipt⋈messages m (role='tool')六表联合查询; - 按
work_step_ref(model:<round>:tool:0)严格排序,逐条核对:assertWorkPluginSource签名一致、m.metadata.receiptId === receipt.id、m.metadata.responseDigest === canonicalJsonSha256(p.response)、以及assertSettlementFact领域事实已登记; - 收集所有
reply.artifacts的{ artifactRef, sha256 }(若同一artifactId出现不同sha256直接抛RUNTIME_WORK_CONDITIONAL_ARTIFACT_INVALID)。
- 执行
- 完成度双分支判定(L144–156):
- 确认该
rootRunId下已没有任何status = 'pending'的continuation_requests; - 加载完整运行消息历史并调
projectWorkFileMessagesInTx投影文件消息; - 独立原件读取要求:
sourceReadSatisfied = !target.independentSourceRead || attachmentReadsComplete(messages, target.sourceReadAttachmentIds); - 条件产物满足判定:
- 分支 A(
conditionNotMet):最新状态为not_applicable且artifacts.size === 0; - 分支 B(已产出产物):
artifacts.size > 0且最新状态为produced | not_applicable,同时每一个产出的 artifact 都通过了artifactReadsComplete(messages, [...artifacts.values()])读取核验;
- 分支 A(
- 若
!completion?.satisfied,finishRuntimeWorkCarrierInTx直接抛RUNTIME_WORK_CONDITIONAL_ARTIFACT_UNSATISFIED阻断虚假结案。
- 确认该