@dsh-enhanced/plugin-control-plane 0.1.67 → 0.1.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +54 -20
  2. package/lib/cli.d.ts.map +1 -1
  3. package/lib/cli.js +66 -11
  4. package/lib/cli.js.map +1 -1
  5. package/lib/creation-capability-feedback.d.ts +16 -1
  6. package/lib/creation-capability-feedback.d.ts.map +1 -1
  7. package/lib/creation-capability-feedback.js +82 -5
  8. package/lib/creation-capability-feedback.js.map +1 -1
  9. package/lib/creation-capability-journal.d.ts.map +1 -1
  10. package/lib/creation-capability-journal.js +2 -0
  11. package/lib/creation-capability-journal.js.map +1 -1
  12. package/lib/creation-capability-runtime.d.ts.map +1 -1
  13. package/lib/creation-capability-runtime.js +2 -0
  14. package/lib/creation-capability-runtime.js.map +1 -1
  15. package/lib/creation-capability-types.d.ts +1 -0
  16. package/lib/creation-capability-types.d.ts.map +1 -1
  17. package/lib/linux-source-build.d.ts +13 -2
  18. package/lib/linux-source-build.d.ts.map +1 -1
  19. package/lib/linux-source-build.js.map +1 -1
  20. package/lib/live-qualification-authority.js +2 -2
  21. package/lib/live-qualification-authority.js.map +1 -1
  22. package/lib/owner-instruction-drain.d.ts +60 -0
  23. package/lib/owner-instruction-drain.d.ts.map +1 -0
  24. package/lib/owner-instruction-drain.js +101 -0
  25. package/lib/owner-instruction-drain.js.map +1 -0
  26. package/lib/revision-capability-feedback.d.ts +165 -0
  27. package/lib/revision-capability-feedback.d.ts.map +1 -0
  28. package/lib/revision-capability-feedback.js +316 -0
  29. package/lib/revision-capability-feedback.js.map +1 -0
  30. package/lib/revision-capability-journal.d.ts +23 -0
  31. package/lib/revision-capability-journal.d.ts.map +1 -1
  32. package/lib/revision-capability-journal.js +124 -15
  33. package/lib/revision-capability-journal.js.map +1 -1
  34. package/lib/revision-capability-runtime.d.ts +19 -1
  35. package/lib/revision-capability-runtime.d.ts.map +1 -1
  36. package/lib/revision-capability-runtime.js +80 -7
  37. package/lib/revision-capability-runtime.js.map +1 -1
  38. package/lib/revision-capability-types.d.ts +50 -4
  39. package/lib/revision-capability-types.d.ts.map +1 -1
  40. package/lib/service.d.ts +174 -3
  41. package/lib/service.d.ts.map +1 -1
  42. package/lib/service.js +732 -68
  43. package/lib/service.js.map +1 -1
  44. package/lib/source-adoption-authority.js +2 -2
  45. package/lib/source-adoption-authority.js.map +1 -1
  46. package/lib/source-approval-authority.js +3 -3
  47. package/lib/source-approval-authority.js.map +1 -1
  48. package/lib/source-build.d.ts +33 -53
  49. package/lib/source-build.d.ts.map +1 -1
  50. package/lib/source-build.js +28 -408
  51. package/lib/source-build.js.map +1 -1
  52. package/lib/source-job-types.d.ts +111 -4
  53. package/lib/source-job-types.d.ts.map +1 -1
  54. package/lib/source-jobs.d.ts +68 -4
  55. package/lib/source-jobs.d.ts.map +1 -1
  56. package/lib/source-jobs.js +296 -46
  57. package/lib/source-jobs.js.map +1 -1
  58. package/lib/source-release-authority.js +2 -2
  59. package/lib/source-release-authority.js.map +1 -1
  60. package/lib/source-revision.d.ts +29 -3
  61. package/lib/source-revision.d.ts.map +1 -1
  62. package/lib/source-revision.js +40 -9
  63. package/lib/source-revision.js.map +1 -1
  64. package/lib/source-workspace.d.ts +11 -4
  65. package/lib/source-workspace.d.ts.map +1 -1
  66. package/lib/source-workspace.js +51 -17
  67. package/lib/source-workspace.js.map +1 -1
  68. package/lib/sqlite.d.ts +6 -7
  69. package/lib/sqlite.d.ts.map +1 -1
  70. package/lib/sqlite.js +255 -17
  71. package/lib/sqlite.js.map +1 -1
  72. package/lib/store.d.ts +142 -3
  73. package/lib/store.d.ts.map +1 -1
  74. package/lib/store.js +732 -63
  75. package/lib/store.js.map +1 -1
  76. package/lib/systemd-host-authority.js +4 -4
  77. package/lib/systemd-host-authority.js.map +1 -1
  78. package/lib/task-observation-authority.js +2 -2
  79. package/lib/task-observation-authority.js.map +1 -1
  80. package/lib/version.d.ts +1 -1
  81. package/lib/version.js +1 -1
  82. package/lib/work-item-creation-contract.d.ts +3 -1
  83. package/lib/work-item-creation-contract.d.ts.map +1 -1
  84. package/lib/work-item-creation-contract.js +10 -1
  85. package/lib/work-item-creation-contract.js.map +1 -1
  86. package/package.json +11 -8
package/README.md CHANGED
@@ -211,26 +211,37 @@ schema 17 在成功启用前保存 `package.json`、`pnpm-lock.yaml`、`cordis.p
211
211
 
212
212
  ## 源码能力 lane 和边界
213
213
 
214
- 公开 `validateSourceBuildConfig` 只验证构建配置组件;完整部署仍须通过 `normalizeControlPlaneConfig`,Linux repository 后端必须同时配置持久 `sourceJobs`。Linux 的可选 `stateSeal: { path, sha256 }` 固定已初始化 runner 账本的外部 seal,并由运行时在写入前核验;它不会在配置校验期间初始化数据库。Docker/macOS 配置不接受此字段。
214
+ 公开 `validateSourceBuildConfig` 只验证原生构建配置组件;Docker 后端已删除,传入无 `backend` 的 Docker 时代形状会同步拒绝。完整部署仍须通过 `normalizeControlPlaneConfig`,Linux repository 后端必须同时配置持久 `sourceJobs`。Linux 的可选 `stateSeal: { path, sha256 }` 固定已初始化 runner 账本的外部 seal,并由运行时在写入前核验;它不会在配置校验期间初始化数据库。macOS seatbelt 配置不接受此字段。
215
215
 
216
216
  `source-plan` / `scaffold` 只在 owner 审批的 linked、clean worktree 和固定 generator digest 上生成插件并运行 `pnpm check`。local checks 使用临时 Git index 对 exact scope 计算 staged tree/patch digest,不污染工作树的真实 index。owner 必须在 checks 之后为 exact source digests、scope 和 release policy 签发独立 authorization,随后才能执行 `release-start`。
217
217
 
218
- `prepareModifySourcePlan` 是另一条待审批修改路径:它只接受现有非保护插件树内的有界文件集,并用临时 Git index 生成精确 tree。默认 Docker 后端以 `git archive` stdin 传入 owner 配置的镜像;显式 Linux 原生后端见下文。容器没有 Host bind mount、网络、特权或调用者环境,使用只读根、非 root UID、`cap-drop=ALL`、`no-new-privileges`、固定 CPU/内存/PID/tmpfs 限制和离线 `pnpm install --ignore-scripts`、`pnpm check`、`pnpm pack`。镜像必须由 registry manifest digest 或本地 image content ID 固定,并预热离线 pnpm store。控制面在持久化前重算 tree/patch digest;任意漂移、取消、超时或构建失败都会删除 worktree 而不创建计划。`.git`、`.gitattributes` 与 `.gitmodules` 不能通过该路径修改。
218
+ `prepareModifySourcePlan` 是另一条待审批修改路径:它只接受现有非保护插件树内的有界文件集,并用临时 Git index 生成精确 tree。源码检查只派发给绑定的持久原生后端(linux-native 或 macOS seatbelt):无 Host bind mount、网络、特权或调用者环境,使用只读根、非 root 执行身份、固定 CPU/内存/PID/临时目录限制和离线 `pnpm install --ignore-scripts`、`pnpm check`、`pnpm pack`。控制面在持久化前重算 tree/patch digest;任意漂移、取消、超时或构建失败都会删除 worktree 而不创建计划。`.git`、`.gitattributes` 与 `.gitmodules` 不能通过该路径修改。
219
219
 
220
220
  `inspectSource` 只从当前 `HEAD` 的 Git objects 读取非保护插件的已跟踪普通文本文件;它不读取工作树。调用者可先请求空 `paths` 获得有界 manifest,再将返回的 `baseCommit` 用作读取和 `prepareModifySourcePlan.expectedBaseCommit` 的精确绑定。HEAD 已移动、二进制/生成/隐藏路径、symlink、submodule 或越界路径都会被拒绝。清单最多 1024 条,内容最多 64 个文件、单文件 64 KiB、合计 256 KiB;返回真实 blob 大小并支持普通可执行文本文件。整个读取请求从服务入口起受 15 秒截止时间控制;取消会终止正在运行的 Git 子进程,异步权限检查的迟到结果不再生效,Fiber 卸载等待请求收尾。
221
221
 
222
- Host patch config 在启用修改准备前必须提供 `sourceBuild`,例如:
222
+ Host patch config 在启用修改准备前必须提供原生 `sourceBuild`,例如:
223
223
 
224
224
  ```yaml
225
225
  sourceBuild:
226
- dockerPath: /usr/bin/docker
227
- image: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
226
+ backend: linux-native
227
+ stateRoot: /private/owner/source-build
228
+ authorityDigest: sha256:<64-hex>
229
+ runtime: # 已验证的固定 Linux 工具链(Node/pnpm/系统工具)
230
+ schemaVersion: 1
231
+ # ...runtime roots、versions 等,由 Host 原生资源准备入口产出
232
+ cgroupRoots: # 当前 UID 委派的 cgroup v1 roots
233
+ expiresAt: 1800000000000
234
+ maxRuns: 63
235
+ maxTotalDurationMs: 600000
228
236
  timeoutMs: 180000
237
+ outputBytes: 65536
229
238
  memoryMiB: 512
230
239
  cpus: 1
231
240
  pidsLimit: 64
232
241
  workspaceMiB: 512
233
- outputBytes: 65536
242
+ temporaryMiB: 32
243
+ workspaceInodes: 32768
244
+ temporaryInodes: 32768
234
245
  ```
235
246
 
236
247
  可选 `sourceBuild.versioning: patch` 让 Host 在冻结检查树前,从基准 Git 提交生成当前插件的下一补丁版本,同时更新 `package.json` 与 `src/version.ts`。只支持稳定 `x.y.z`;模型不得提交这两个保留文件。生成的版本变化与源码一起接受离线检查、打包和摘要绑定,检查后再次核对文件及制品版本。持久作业冻结此配置,重启不会改用新的版本策略。
@@ -239,13 +250,7 @@ sourceBuild:
239
250
 
240
251
  同一基准的多个候选会得到相同下一版本,不提供覆盖已发布版本的权限。后续采用流程须串行推进获准源码基准;现有 registry/catalog 冲突检查继续拒绝版本复用。此选项只准备可区分版本的候选,自动发布和采用需显式启用下述有限授权与执行配置,不会为每次修复发布公共 npm 包。
241
252
 
242
- `sourceBuild.profile` defaults to `standard`, whose existing maximum build timeout is 4 minutes and whose `/tmp` tmpfs is fixed at 32 MiB. An owner may explicitly set `profile: repository` for a full repository `pnpm check`; only that profile permits a timeout up to 30 minutes, memory up to 16 GiB, 16 CPUs, 1024 PIDs, an 8 GiB workspace tmpfs and a 4 GiB `/tmp` tmpfs (default 2 GiB). The repository profile explicitly permits execution from both bounded tmpfs mounts, needed by native build tools and temporary executable test fixtures; the standard profile retains Docker’s default no-exec mounts. The repository profile fixes `CI=true` and sets `VITEST_MAX_WORKERS` from the frozen configured CPUs (floor, clamped to 1–4) inside the container. The caller cannot select a profile or increase these limits: its timeout is capped by the owner configuration. Cancellation, deadline expiry, output overflow, or Fiber disposal kills the preparation client, waits for archive/build processes, and proves named-container absence before any pending plan is stored. Interrupted builds are never automatically replayed; the optional durable Host lane below records their status and resource identity.
243
-
244
- `repositorySandbox: { seccompPath: /absolute/owner/path/source-builder-seccomp.json }` is a separate repository-only opt-in for the existing nested Bubblewrap integration tests. It requires the approved profile digest and Docker Server `29.4.1/linux/amd64`; other bytes or runtimes fail before candidate execution. It permits additional namespace/mount syscalls, removes Docker's masked/read-only system-path lists, and hides `/sys` behind an empty read-only tmpfs. This expands the outer container's `/proc` visibility and kernel surface; it is not equivalent to Docker's default policy. UID 65534, zero capabilities, no-new-privileges, offline execution, read-only root, and no Host bind mounts remain mandatory. See the [profile provenance and limits](../../scripts/isolation/README.md#nested-sandbox-profile). Omitting this option retains Docker's default system-path restrictions, including in repository mode.
245
-
246
- `dockerPath` 必须是 canonical、owner/root-owned 且不可被 group/world 写的可执行文件。镜像要包含 UID 65534 可执行的 Node、pnpm 和可用的离线 store;pnpm 的 cache 必须定位到可写 tmpfs;离线 store 由镜像中的只读种子复制到 workspace tmpfs,供 pnpm 11 写入其 SQLite 索引;未配置时 `canPrepareSource()` 返回 false,修改准备请求 fail closed。
247
-
248
- 完整仓库镜像的 owner 构建脚本见 [source builder](../../scripts/isolation/README.md)。它只传入依赖清单和 lockfile,在联网构建阶段预取依赖;候选源码进入容器时仍禁网、无 Host 挂载。`repository` profile 仅扩大 owner 配置的检查预算,不延长 Growth Driver 的 5 分钟授权。长检查可通过下述持久 Host 任务提交;入队不代表检查通过或源码已改进。
253
+ Linux 原生构建的超时上限为 30 分钟(仓库级 `pnpm check`),内存、CPU、PID、workspace 与临时目录配额均由上面的固定字段声明并在执行合同中冻结;调用方不能选择配置外的 profile 或提高限制,每次请求取调用方与配置 timeout 的较小值。取消、截止到期、输出溢出或 Fiber 卸载都会终止准备客户端并等待构建进程退出;中断的构建不会自动重放,持久 Host 任务(见下文)记录其状态与资源身份。
249
254
 
250
255
  Host-only `inspectSourceTargets({ repository, baseCommit? })` 返回当前受信源码基线与最多 128 个已提交、可修改插件的名称及可选简短描述,供当前来源的 Growth 选择读取目标。目录仅从 canonical Git 仓库的正规 `plugins/<name>/package.json` blob 形成;保护根、符号链接、submodule、脏工作树和未跟踪目录不进入结果,manifest 非法或超过限制会拒绝整次查询。每个 manifest 最多 16 KiB,合计最多 512 KiB,描述最多 512 UTF-8 字节;`baseCommit` 不匹配当前受信基线时拒绝。目录不授予源码修改、创建、审批或采用权限,后续源码读取与准备仍核对原有来源及基线。
251
256
 
@@ -275,7 +280,7 @@ Linux 创建准备可接通独立 `creationVerifications`,必须同时配置
275
280
 
276
281
  Linux 创建证书可接下述有限动态采用,构建和采用后端必须一致。linux-native 边界现已接通对已有插件的修改链:修订 v2 与配对回归 v2,以及修改计划的源码审批、发布与源码发布采用均消费同一份 schema 3 仓库准备 evidence,不再要求任何 Docker 命令证据;schema 2 macOS 单进程证据永远不能支撑全仓修改。无 Docker 全新安装默认链路及 pre-owner 基础资源迁移已接通;完整活 Home 的 Docker→native 升级仍未验收,不能由 pre-owner 迁移证据替代,进程内代码热加载也不在此链路内;配置后端不支持的组合(如 macOS 全仓修改、linux-native 缺持久 sourceJobs 或双 UID、跨采用链缺上游环节)仍会明确拒绝启动。动态工具采用不改变源码计划的 `pending-approval`,不能据此替换需要完整自迭代链的生产部署。
277
282
 
278
- ### 持久源码检查任务(Docker 可选,Linux 原生必需)
283
+ ### 持久源码检查任务(原生后端必需)
279
284
 
280
285
  Growth Driver 设置 `pluginSourceProposals.preparationMode: durable` 后,`plugin_source_prepare` 只提交任务,`plugin_source_job_status` 回读当前 owner 的状态。控制面还必须配置独立于模型回合的有限 Host 授权:
281
286
 
@@ -349,9 +354,9 @@ Growth Driver 同时启用 `pluginSourceProposals.allowCreation: true` 与 `prep
349
354
 
350
355
  Host 在私有 worktree 使用该基线的 `scripts/create-plugin.mjs`,生成 manifest、patch、许可证、版本和构建配置,登记目录行,并只为新包追加锁文件 importer。依赖只能复用基线 catalog 的唯一既有解析,不进行网络解析、不改变已有 importers/packages/snapshots。生成器以固定 Node 执行路径、清理后的环境及有限运行时限运行;这属于可信基线的 Host 子进程,临时目录本身不构成操作系统沙箱。Host 在构建前后重建生成器输出,复验保留文件、catalog、lock 与精确 Git 源码快照。
351
356
 
352
- `sourceBuild.backend: macos-seatbelt` 选择纯 macOS 创建构建:Host 校验冻结的 Node/TypeScript/Cordis 依赖树,按原创建或修订授权复核 worktree,把候选 TypeScript 当作数据交给固定编译器,在无网络、无子进程的系统沙箱内作语义类型检查并生成 JS/声明文件。编译器不执行候选脚本、测试、tsconfig 插件或已生成 JS。Host 将受限输出与固定 manifest、README、LICENSE、patch 打成确定性的不超过 512 KiB 的 tar.gz,留存 v2 证据中的运行时摘要、固定编译器摘要和实际三项检查。v2 不声明 `pnpm check` 或 CPU、内存、磁盘硬配额;v1 Docker 证据及 Linux 修改/发布路径保持原样。原生作业使用单独的持久资源身份;unknown 只有经原生执行器的精确关闭回执核对后才释放,不调用 Docker 清理,也不自动重派。
357
+ `sourceBuild.backend: macos-seatbelt` 选择纯 macOS 创建构建:Host 校验冻结的 Node/TypeScript/Cordis 依赖树,按原创建或修订授权复核 worktree,把候选 TypeScript 当作数据交给固定编译器,在无网络、无子进程的系统沙箱内作语义类型检查并生成 JS/声明文件。编译器不执行候选脚本、测试、tsconfig 插件或已生成 JS。Host 将受限输出与固定 manifest、README、LICENSE、patch 打成确定性的不超过 512 KiB 的 tar.gz,留存 v2 证据中的运行时摘要、固定编译器摘要和实际三项检查。v2 不声明 `pnpm check` 或 CPU、内存、磁盘硬配额;历史 v1 Docker 证据仅可从存储只读,不能支撑新的修改/发布路径。原生作业使用单独的持久资源身份;unknown 只有经原生执行器的精确关闭回执核对后才释放,不自动重派。
353
358
 
354
- Linux Docker 创建检查需要包含已验证基线锁文件的新构建镜像(运行 `node scripts/isolation/build-source-image.mjs`)。Host 复核实际归档的创建范围,容器核对原 Git 基线锁摘要与只读镜像 `/opt/dsh-source-baseline/pnpm-lock.yaml` 一致,才在 pnpm 11.7.0 的首次离线 frozen install 使用 `--trust-lockfile`。该选项跳过锁文件解析与供应链策略复核;已有解析已在镜像构建时通过策略验证,新 importer 不引入新解析。普通修改不使用此选项,旧镜像或不同基线均拒绝此创建检查。构建证据保留实际 Docker 参数及基线锁摘要。
359
+ Linux 创建检查由下文的 linux-native 完整源码准备承担:隔离仓库在首次离线 frozen install 前核对 Git 基线锁摘要与运行时只读锁文件一致才使用 `--trust-lockfile`。该选项跳过锁文件解析与供应链策略复核;已有解析已在固定运行时准备阶段通过策略验证,新 importer 不引入新解析。基线不匹配直接拒绝;构建证据(schema 3)保留执行合同、运行时与基线锁摘要。
355
360
 
356
361
  `creation.id` 对应的授权定义不可变,累计用量独立持久保存;更换 `sourceJobs.authorityId`、重启或修改构建配置不会重置同一创建授权。接受的失败/unknown 尝试仍消费创建额度,同一幂等请求不重复扣取。实际创建同时受 `maxSubmissions`、Policy 预算及全账本单 outstanding job 上限约束。
357
362
 
@@ -408,6 +413,16 @@ Host-only `inspectPreparedRevisionRegression` 提供精确父/候选不可变制
408
413
 
409
414
  主账本在入库和历史回读时独立核对 Linux 修订证书的构建运行时及完整准备证据摘要;回归证书须绑定精确候选证书和同族父证书,并匹配双方共享观察器的边界、运行时与准备回执。历史回读只核对保留的不可变元数据,不重建产物、不要求旧授权仍有效,也不授予新的执行权限。
410
415
 
416
+ ### 修订非劣收益门(P1-4,opt-in)
417
+
418
+ `sourceJobs.revision.benefit` 可选携带一个 `assistant-growth/revision-benefit-contract/v1` 合同的 64 位 digest;合同冻结同供应商、同预算与最小任务修订数,并为失败率、返工率、纠正率、延迟与成本五条指标各设非劣阈值。未配置该字段且 grant 未携带 benefit pin 时,修订采用维持上述 review-only 历史行为;配置删除而 grant 仍有 pin 时采用 fail closed。配置后(grant 同步 pin 合同 digest)默认采用严格语义:已验证修订还必须在主账本 `source_revision_benefit_outcomes`(schema 36,外键锁定 `source_revision_verifications`,只按记录时间单调更新)存在一条针对**同一合同 digest** 的 `non-inferior` 结果,否则 `revisionAdoptionReady` 为 false,候选保持 `pending-approval`。唯一例外是显式打开 `sourceJobs.allowReviewOnlyFirstAdoption`(默认 false):只允许一次 review-only 首次采用,并由下面的观察窗口终端回退兜底;配置删除 `revisionBenefit` 而 grant 保留 pin 时采用 fail closed。未配置合同且 grant 未携带 pin 时维持既有 review-only 行为。
419
+
420
+ 非劣评估(`@dsh-enhanced/assistant-growth-contract` 的 `evaluateRevisionBenefit`)只接受 Host 派生的窗口统计:按 canonical 任务修订去重,corrections/withdrawals 排除并单列;任一样本窗口低于最小修订数、合同过期,或任一窗口的成本/延迟不可测,结果只能是 `insufficient`,不产生收益结论;越过任一阈值返回带指标名的 `inferior`。部署关联与工具成功都不是收益证据,评估不能由成长模型自评。schema 2 的修订调用账本为每个 entry 调用保存 Host-only foreground 见证(task/inbox/session/binding/call 与参数摘要,不含任务文本);参数摘要与 Delivery 一样按原生 `JSON.parse`→`JSON.stringify` 归一化,不按 key 排序,保证多键乱序参数仍能归属。窗口固定为等长相邻的父 [adoptedAt-W, adoptedAt] 与候选 [adoptedAt, adoptedAt+W],W 受候选签名期限、已观测服务时间、父采用历史和一天上限约束;评估入口拒绝不等长/不相邻或统计窗口不匹配的数据。父窗口证据 = 父创建采用的已认证任务关联(切换前)∪ 修订账本中父版本调用(回滚后或链式修订父版本,两者按 entry 生命周期天然不相交);候选窗口只取修订账本中采用后的 foreground 调用。owner-instruction 修订没有 growth run,故不参与收益门。
421
+ 修订采用运行时此前在 `assertParent` 中把父 creation 证书的 `source` 与修订证书的 `source` 做 JSON 相等比较。父子证书的 `source` 语义不同、不可比:v1/v2 键集相同但值描述不同 run(创建 run vs 修订 source),v3 键集不同(`{instructionDigest, ownerDigest}`);精确父绑定由签名的 `certificate.parent.certificateDigest` 以及 artifact/plan/owner/schemaDigest 检查覆盖。该比较会错误拒绝真实创建父证书的采用,本次已删除该连词;shipped profile 不配置 `revisionCapabilities`,因此该缺陷此前是休眠代码。
422
+ 采用模式有两种,由 `sourceJobs.allowReviewOnlyFirstAdoption`(默认 false)显式选择:默认合同语义要求采用前已有同一合同 digest 的 `non-inferior` 结果;打开后仅允许**一次** review-only 首次采用,并在有界观察窗口到期仍无 `non-inferior`(无证据、`insufficient`、父窗口不可读)时由 `reconcileRevisionBenefits` 记录 `observation-expired-unverified` 的 owner 可读结果并用独立回滚授权 CAS 回退到精确父版本,不把候选静默挂到期限。观察窗口 deadline 与评估窗口使用**同一个 W**:`revisionBenefitObservationDeadline` 复用等长配对窗口,deadline = 候选窗口 end = adoptedAt + W;终态回退还要求 outcome 的 contractDigest 等于 pinned digest,异合同结果不触发回退。配置删除 `revisionBenefit` 而修订 grant 仍保留 benefit pin 时采用 fail closed。任何已记录的 `inferior`/`insufficient`/异合同结果都阻止后续(重新)采用。
423
+
424
+ 已知边界:① `reconcileRevisionBenefits` → 终态 → 真实 CAS 仍缺单条端到端 service 测试,两源 parent union 也缺 service 级测试(反馈层链路已有真实测试);② 采用后删除或旋转 `revisionBenefit` 时,已挂载候选的终态处理未覆盖,当前 fail closed 只作用于后续采用;③ `mergeRevisionBenefitWindows` 按 entry 生命周期不相交的假设合并两个来源,未按 Inbox 去重;④ source-jobs 全文件 readiness 在慢机器上存在既有 flake,与本补丁无关。
425
+
411
426
  ### 修订采用的稳定入口替换与精确父版本回退
412
427
 
413
428
  已动态采用的稳定工具入口可以被独立验收的修订版本替换,同时保留精确父版本回退。该路径默认关闭:未配置 `revisionCapabilities` 时,修订验收与回归门之后的计划仍停在 `pending-approval`,旧计划、旧配置和 `control.sqlite` 行为完全不变。启用须同时具备既有 `creationCapabilities`、`revisionVerifications` 和 `sourceJobs.revision`,且采用后端与创建采用 runner 一致:
@@ -475,9 +490,12 @@ creationCapabilities:
475
490
  retention:
476
491
  maximumLifetimeMs: 2592000000 # optional: 30 days after adoption
477
492
  runner:
493
+ backend: linux-native
478
494
  stateRoot: /private/owner/capability-runner
479
- image: sha256:<64-hex-plugin-verifier-image-id>
480
- dockerPath: /usr/bin/docker
495
+ runtime: # 完整 LinuxPluginBehaviorRuntime(含隔离运行时与观察器 sha256)
496
+ schemaVersion: 1
497
+ # ...由 Host 原生资源准备入口产出并冻结
498
+ cgroupRoots: # 当前 UID 委派的 cgroup v1 roots
481
499
  expiresAt: 1800000000000
482
500
  maxRuns: 63
483
501
  maxTotalDurationMs: 600000
@@ -489,7 +507,7 @@ creationCapabilities:
489
507
 
490
508
  Linux 采用使用显式 `runner.backend: linux-native`、签名时的完整 `LinuxPluginBehaviorRuntime`、当前 UID 的 `cgroupRoots` 及相同的有限 runner 字段,不配置镜像。仅接受 v3 创建证书;其四项执行绑定随完整证书摘要写入原采用回执,无需新建账本或更换回执协议。首次采用复核原成功构建收据;挂载、恢复及每次调用复核私有 `behavior.json`、签名基底/准备摘要和实际保留观察器。采用后的 v2 保留期不要求旧构建授权、原工具链或编译器继续存在,仍要求精确观察器可用以及当前 owner/任务来源有效。取消与关闭会等待这些检查结束;验收失败不挂载工具,未知调用不重派。Linux 可选校验模块仅在显式配置时加载,基础 Control Plane 不要求安装 Verifier。已有 Docker 账本不能改写为 Linux 账本;保权安装迁移仍是独立交付。
491
509
 
492
- Host 用真实 Cordis Fiber 动态注册受控 `evolved_<插件名>_<plan hash>_<序号>` 原生工具入口。候选代码在每次 discovery/invoke 中创建自己的 Cordis Context 并挂载:Docker 使用离线容器,Linux 使用原生父/候选隔离进程,macOS 使用 Seatbelt 受限单进程;Host 不导入候选代码。当前只支持原生 Tools/SystemPrompt 服务的有界纯工具插件;每次调用新建 candidate Context,不支持跨调用候选内存状态或任意 Host 服务桥接。Host 的包读取、临时制品、所选后端进程与隔离资源、私有 ledger 和密钥读取属于本授权;Linux 另读取固定只读运行时和准备回执。候选无法读取 Host 凭据、签名私钥或私有验收数据,无网络,不执行包安装脚本。各后端限制详见 [行为执行器](../assistant-verifier/README.md#新插件包的隔离行为观察)。
510
+ Host 用真实 Cordis Fiber 动态注册受控 `evolved_<插件名>_<plan hash>_<序号>` 原生工具入口。候选代码在每次 discovery/invoke 中创建自己的 Cordis Context 并挂载:Linux 使用原生父/候选隔离进程,macOS 使用 Seatbelt 受限单进程;Host 不导入候选代码。Docker 后端已删除,历史 Docker runner 形状只能从存储或 sealed-handoff 只读,无法构造行为执行器。当前只支持原生 Tools/SystemPrompt 服务的有界纯工具插件;每次调用新建 candidate Context,不支持跨调用候选内存状态或任意 Host 服务桥接。Host 的包读取、临时制品、所选后端进程与隔离资源、私有 ledger 和密钥读取属于本授权;Linux 另读取固定只读运行时和准备回执。候选无法读取 Host 凭据、签名私钥或私有验收数据,无网络,不执行包安装脚本。各后端限制详见 [行为执行器](../assistant-verifier/README.md#新插件包的隔离行为观察)。
493
511
 
494
512
  调用经过原生 ToolRuntime/Policy,须由 Delivery 证明当前注册的真实 Agent、Session 和 owner lineage;Policy 还须允许该 owner scope 下的准确工具别名或有限别名模式。输入、结果和累计调用均有界;同一 Agent Session/call id/参数的已完成调用回读缓存,unknown 不再派发。候选注释不进入 Host 描述,输出作为不可信数据。每次异步检查后、派发前和落账前重验真实调用者与 canonical 来源,撤权或纠正期间的迟到结果不能变成成功。
495
513
 
@@ -735,9 +753,25 @@ Host-only `recordOwnerTaskFailureGap(source)` 将经 Delivery 再验证的 foreg
735
753
 
736
754
  该 Host API 使用同批 `assistant-delivery` 与可选 `assistant-evaluation >=0.1.33` 的精确接口;不要求手工 gap 部署安装 Evaluation。它只授权来源绑定的候选准备,pending plan 的后续授权采用、发布与观测仍走独立控制链。
737
755
 
756
+ ### Owner 显式指令修订来源
757
+
758
+ Host-only `requestOwnerInstructionRevision(input)`(schema 37 `source_owner_instructions`)是与任务失败并列的第二条 `revise-created` 触发来源,让 owner 不依赖失败任务也能发起一次对已采用插件的修订。它在同一同步帧内:经 Delivery 重验当前 owner route,核对 parent 计划仍是已采用的同名插件,校验提交文件并冻结其规范 digest,写入带 TTL 的指令行(模型工具面不可达此接口),再用合成的 `owner-instruction:<id>` gap 入队一个修订作业。入队、重启恢复和每个 continuation 都重放同一组 fail-closed 校验:指令 digest、过期时间、plan/plugin 名、files digest、gap 归属与 owner continuation;指令与任务失败证据互斥,不允许混用。该类作业不携带 growth run,`validateSourceRevisionBinding` 仅在显式 `allowWithoutGrowthRun` 下接受,独立 revision 审查仍强制;回归 pin 与 P1-4 收益门不适用于无 growth run 的指令作业。指令对应的合成 gap 不出现在全局 `gaps()` 中。
759
+
760
+ schema 41 在 `source_owner_instructions` 上冻结该次修订的独立审查供应者与预算身份:新增 `review_supplier_json`(provider/model/reasoningEffort)与 `review_budget_json`(verifier 的 `RevisionAcceptanceAuthorityRef`、public key SHA-256、以及 verification/regression/adoption 三层外层窗口),并写入 `review_freeze_digest`(三段 canonical 自摘要)。冻结只在首次入队时解析一次;重试与重启只读回该行,绝不回退到活 owner route 或活 verifier 配置——供应者、authority、key digest、窗口任一不一致都抛错拒绝该作业(不会静默换模型)。该冻结是 write-once:没有 UPDATE/DELETE writer;v40 及以前写入的旧行三列保持 NULL,读取为“无冻结”,入队、重启续跑与审查前检查一律 fail closed,且不能回填;复用旧行 idempotency key 的请求会被判冲突,只能换新 idempotency key 重新提交指令。v40→v41 迁移只在原表上 ALTER 三列并原子写版本,旧指令行与既有作业 intent 原样保留;新作业 intent 另存 `reviewFreezeDigest`/`reviewWindow`,每次 continuation 逐项比对。独立审查使用冻结 supplier,审查预算身份由冻结 authority digest 与 verifier key digest 钉住;按次收窄审查预算需 verifier 请求/响应协议 v2。续跑自动化共享的外层 `continuationTimeoutMs` 仍取活配置窗口,它只是 continuation 的 wall-clock 上限:live 变小只会更早截断为 unknown,不会扩大内层已冻结的 authority/budget,per-job definition 与审查窗口仍用冻结值。
761
+
762
+ 结构化 CLI 入口分两层,使无 Delivery 的 CLI 进程不直接触发授权:`dsh-plugin-control source revision-enqueue --plan-id <id> --plugin-name <name> --files-dir <dir> --expected-base-commit <sha40> --idempotency-key <key> --ttl-ms <900000..86400000>` 用 `readScopedPluginFiles` 把目录安全读成候选文件(拒 symlink/越界/`.git`,沿用 64 文件/64 KiB/256 KiB 与 candidate authority 路径边界),再写入 schema 38 起的 `source_owner_instruction_requests` 表,保存为 `pending` 并打印 requestId。相同幂等键和内容返回原请求及原期限,改变内容会报冲突。原生调度器唤醒时只读检查队列,有待处理项时激活已有的 source continuation。实际认领、owner route 重验和入队使用该 continuation 的生产运行与预算。空队列暂停,不消耗运行额度。schema 40 用递增认领代次保护结算,在 `accepted` 行保存指令 ID 和已注册的作业 ID。进程若在结算前退出,五分钟后的孤儿认领可以重试,重用原期限及同一作业。过期或无法入队的请求记录有界失败码。独立验证继续使用同一 continuation,显式指令的自动采用留给后续验收。
763
+
764
+ 使用提交时返回的 `requestId`,从同一 owner-private ledger 查看历史状态:
765
+
766
+ ```sh
767
+ dsh-plugin-control source revision-show --request-id <request-id>
768
+ ```
769
+
770
+ 命令以只读连接打开当前 schema 的既有账本,可读取正在运行的服务已提交到 WAL 的状态。缺失或旧版账本会报错,命令无需写入或迁移账本。输出包含请求目标、状态、失败码、创建与过期时间,以及已记录的指令和作业 ID。候选内容损坏时仍可查看请求状态和失败码。只有请求、指令和作业的持久绑定相符时,`jobAssociation` 才为 `matched`,并返回作业状态、失败码和候选 plan ID。旧版 `accepted` 请求缺少收据,或关联行缺失、错配或损坏时,`jobAssociation` 为 `unknown`,`job` 为 `null`。`accepted` 表示请求曾被受理。当前授权、执行成功、独立验证和采用需要各自的证据。候选文件、源码和完整作业 intent 留在账本内。
771
+
738
772
  ## 权限
739
773
 
740
- - 插件 Host service:启用 `sourceApprovals` 时执行固定的 owner helper(可读其配置、私钥和私有审批账本),Host 只消费签名回执;读取 catalog/trust,写 owner-private SQLite/WAL;启用源码读取或准备时执行受限 Git,启用 `sourceJobs.baseline` 时还会从指定本地 bare 仓库导入对象并写入专用 `refs/dsh-source/` 引用;启用 `sourceJobs.creation` 时执行冻结基线的公共生成器并写临时模板目录、私有 worktree、目录行和新锁文件 importer;启用 Docker `sourceBuild` 时调用 owner 配置的离线容器构建,启用原生 macOS `sourceBuild` 时执行固定编译器及 Host 打包,读取冻结依赖树并写 owner-private 持久账本、工作目录和回执。模型不能选择可执行文件、网络或凭据;Host 不使用浏览器。
774
+ - 插件 Host service:启用 `sourceApprovals` 时执行固定的 owner helper(可读其配置、私钥和私有审批账本),Host 只消费签名回执;读取 catalog/trust,写 owner-private SQLite/WAL;启用源码读取或准备时执行受限 Git,启用 `sourceJobs.baseline` 时还会从指定本地 bare 仓库导入对象并写入专用 `refs/dsh-source/` 引用;启用 `sourceJobs.creation` 时执行冻结基线的公共生成器并写临时模板目录、私有 worktree、目录行和新锁文件 importer;启用 linux-native `sourceBuild` 时在 user namespace/cgroup/seccomp 边界内执行固定工具链的离线仓库构建,启用 macOS seatbelt `sourceBuild` 时执行固定编译器及 Host 打包,读取冻结依赖树并写 owner-private 持久账本、工作目录和回执。模型不能选择可执行文件、网络或凭据;Host 不使用浏览器。
741
775
  - owner CLI `activate`:读取/复制/rename/恢复 DSH profile,并执行固定 DSH executable。
742
776
  - owner CLI `probe`:执行固定 Host attestor,只有严格 allowlist 环境;不读取 attestation 私钥,不使用 shell或网络客户端。
743
777
  - owner CLI `scaffold`:仅在审批绑定的 linked worktree 中运行固定边界内的 `git` / `pnpm`。
package/lib/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,iBAAiB,EAA6C,MAAM,YAAY,CAAA;AAGzF,OAAO,EAAsB,qBAAqB,EAA+F,MAAM,uBAAuB,CAAA;AAC9K,OAAO,EACuB,KAAK,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAC/E,OAAO,KAAK,EACE,oBAAoB,EAC2F,MAAM,YAAY,CAAA;AAwB/I,OAAO,EAAE,qBAAqB,EAAE,CAAA;AA+uBhC;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,wBAAwB,CAAC;IAC3G,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA2BvE;AAuCD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,wBAAwB,CAAC;IAC1G,MAAM,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkGjG;AA0FD,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,wBAAwB,CAAC;IACvG,MAAM,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAE/I;AAqWD,wBAAsB,gBAAgB,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmClF"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,iBAAiB,EAAqE,MAAM,YAAY,CAAA;AAIjH,OAAO,EAAsB,qBAAqB,EAAsH,MAAM,uBAAuB,CAAA;AACrM,OAAO,EACuB,KAAK,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAC/E,OAAO,KAAK,EACE,oBAAoB,EAC2F,MAAM,YAAY,CAAA;AAwB/I,OAAO,EAAE,qBAAqB,EAAE,CAAA;AAqvBhC;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,wBAAwB,CAAC;IAC3G,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA2BvE;AAuCD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,wBAAwB,CAAC;IAC1G,MAAM,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkGjG;AA0FD,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,wBAAwB,CAAC;IACvG,MAAM,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAE/I;AA2YD,wBAAsB,gBAAgB,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqClF"}
package/lib/cli.js CHANGED
@@ -12,10 +12,11 @@ import { fetchRegistryArtifact, RegistryFetchError } from './registry-fetch.js';
12
12
  import { verifyApprovedPackagesInLockfile } from './lockfile.js';
13
13
  import { captureHostInputFiles, materializeHostInputFiles } from './host-inputs.js';
14
14
  import { Ed25519SourcePublishReconciliationAuthority, Ed25519SourceReleaseAuthority, Ed25519SourceReleaseAuthorizationAuthority, invokeSourcePublishReconciliationAdapter, invokeSourceReleaseAdapter, parseSourcePublishReconciliationReceipt, parseSourceReleaseAuthorization, parseSourceReleaseReceipt } from './release.js';
15
- import { ControlPlaneStore, controlPlaneDigest, expectedSourceRelease } from './store.js';
15
+ import { ControlPlaneStore, ControlPlaneStoreError, controlPlaneDigest, expectedSourceRelease } from './store.js';
16
16
  import { ControlPlaneCliError } from './errors.js';
17
+ import { validateSourceCreationFiles } from './source-creation.js';
17
18
  import { ControlledProcessError, executeControlledProcess } from './adapter-process.js';
18
- import { changedSourcePaths, checkedSourceSnapshot, gcPreparedModifyWorktrees, runLocalCommand, sourcePathAllowed, verifyPreparedSourceWorktree } from './source-workspace.js';
19
+ import { changedSourcePaths, checkedSourceSnapshot, gcPreparedModifyWorktrees, readScopedPluginFiles, runLocalCommand, sourcePathAllowed, verifyPreparedSourceWorktree } from './source-workspace.js';
19
20
  import { inheritedEnvironment, loadTrustConfig, openTrustedExecutable, resolveTrustKey, verifyOpenTrustedExecutable } from './trust.js';
20
21
  const pluginPattern = /^(?=.{1,64}$)[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
21
22
  const leaseMs = 30_000;
@@ -517,10 +518,6 @@ async function syncDirectory(path) {
517
518
  await directory.close();
518
519
  }
519
520
  }
520
- function descriptorReference(handle) {
521
- assertDescriptorFileSystem('local artifact activation');
522
- return `/proc/${process.pid}/fd/${handle.fd}`;
523
- }
524
521
  async function verifyActivationArtifactSnapshot(snapshot) {
525
522
  const before = await snapshot.handle.stat({ bigint: true });
526
523
  if (!before.isFile() || before.nlink > 1n || before.dev !== snapshot.device || before.ino !== snapshot.inode
@@ -598,8 +595,17 @@ async function snapshotLocalArtifact(plan, item) {
598
595
  // Shared terminus for both artifact source paths (a local file URL and a
599
596
  // downloaded registry object): the approved bytes are materialized into an
600
597
  // owner-private 0400 cache, then reopened through a pinned file descriptor so
601
- // the package manager only ever reads /proc/self/fd/N while the control plane
602
- // re-verifies the bytes after the executor returns.
598
+ // the control plane re-verifies the sealed inode after the executor returns.
599
+ //
600
+ // The package manager is given the stable cache path (not /proc/self/fd/N):
601
+ // pnpm's "file:" installer treats an extension-less descriptor path as a
602
+ // directory, so a descriptor URL can never install a local tarball. The
603
+ // TOCTOU boundary remains closed because (1) the pinned fd still proves the
604
+ // sealed inode was not modified while staging ran, and (2) after the executor
605
+ // returns verifyApprovedPackagesInLockfile compares the lockfile entry and the
606
+ // installed bytes against the approved integrity; replacing the path with
607
+ // different bytes makes that check fail closed. The cache directory is owner
608
+ // private and 0400, so another user cannot rewrite it in place.
603
609
  async function pinActivationArtifactBytes(plan, item, registry, bytes) {
604
610
  const cacheRoot = join(plan.target.dshHome, 'plugin-control', 'activation-artifacts');
605
611
  await mkdir(cacheRoot, { recursive: true, mode: 0o700 });
@@ -663,7 +669,9 @@ async function pinActivationArtifactBytes(plan, item, registry, bytes) {
663
669
  || !createHash('sha512').update(current).digest().equals(createHash('sha512').update(bytes).digest())) {
664
670
  throw new ControlPlaneCliError('ACTIVATION_BINDING', 'activation artifact cache does not retain the approved bytes');
665
671
  }
666
- const reference = pathToFileURL(descriptorReference(pinned)).href;
672
+ // Install from the stable .tgz cache path; the pinned descriptor remains
673
+ // the integrity witness and is re-verified after the executor returns.
674
+ const reference = pathToFileURL(destination).href;
667
675
  return { package: { ...item, registry: { ...registry, reference } }, handle: pinned, path: destination, device: metadata.dev,
668
676
  inode: metadata.ino, size: metadata.size, mtimeNs: metadata.mtimeNs, ctimeNs: metadata.ctimeNs,
669
677
  sha512: createHash('sha512').update(current).digest() };
@@ -1423,6 +1431,49 @@ async function sourceGc(argv) {
1423
1431
  store.close();
1424
1432
  }
1425
1433
  }
1434
+ /**
1435
+ * Records one structured owner-instruction revision request. This CLI process
1436
+ * has no Delivery access, so it only freezes the owner-supplied candidate files
1437
+ * and target into the schema-38 request table; the running service re-validates
1438
+ * the owner route inside its trust boundary before enqueueing. Files come from
1439
+ * a real directory (no symlinks/escapes) and are bounded by the same prepared
1440
+ * file limits as a durable source job.
1441
+ */
1442
+ async function sourceRevisionEnqueue(argv) {
1443
+ const trust = await commandTrust(argv);
1444
+ const store = new ControlPlaneStore({ path: trust.ledger.path });
1445
+ try {
1446
+ const planId = option(argv, '--plan-id');
1447
+ const pluginName = option(argv, '--plugin-name');
1448
+ const filesDir = option(argv, '--files-dir');
1449
+ const expectedBaseCommit = option(argv, '--expected-base-commit');
1450
+ const idempotencyKey = option(argv, '--idempotency-key');
1451
+ const ttlMs = integerOption(argv, '--ttl-ms');
1452
+ const files = await readScopedPluginFiles(filesDir);
1453
+ validateSourceCreationFiles(files);
1454
+ const request = store.recordOwnerInstructionRequest({ planId, pluginName, expectedBaseCommit, files,
1455
+ ttlMs, idempotencyKey, createdAt: Date.now() });
1456
+ process.stdout.write(`${JSON.stringify({ requestId: request.requestId, status: request.status,
1457
+ planId: request.planId, pluginName: request.pluginName, expiresAt: request.expiresAt })}\n`);
1458
+ }
1459
+ finally {
1460
+ store.close();
1461
+ }
1462
+ }
1463
+ async function sourceRevisionShow(argv) {
1464
+ const trust = await commandTrust(argv);
1465
+ const requestId = option(argv, '--request-id');
1466
+ const store = new ControlPlaneStore({ path: trust.ledger.path, readOnly: 'live' });
1467
+ try {
1468
+ const request = store.inspectOwnerInstructionRequest(requestId);
1469
+ if (request === undefined)
1470
+ throw new ControlPlaneStoreError('not-found', 'owner instruction request not found');
1471
+ process.stdout.write(`${JSON.stringify(request)}\n`);
1472
+ }
1473
+ finally {
1474
+ store.close();
1475
+ }
1476
+ }
1426
1477
  function assertReleaseCommand(argv) {
1427
1478
  if (argv.includes('--phase'))
1428
1479
  throw new ControlPlaneCliError('INVALID_ARGUMENT', 'release phase is derived from durable plan status');
@@ -1679,7 +1730,11 @@ export async function runPluginControl(argv = process.argv.slice(2)) {
1679
1730
  return sourceVerifyPrepared(argv);
1680
1731
  if (argv[1] === 'gc')
1681
1732
  return sourceGc(argv);
1682
- throw new ControlPlaneCliError('INVALID_ARGUMENT', 'usage: dsh-plugin-control source <verify-prepared|gc>');
1733
+ if (argv[1] === 'revision-enqueue')
1734
+ return sourceRevisionEnqueue(argv);
1735
+ if (argv[1] === 'revision-show')
1736
+ return sourceRevisionShow(argv);
1737
+ throw new ControlPlaneCliError('INVALID_ARGUMENT', 'usage: dsh-plugin-control source <verify-prepared|gc|revision-enqueue|revision-show --request-id <id>>');
1683
1738
  }
1684
1739
  if (command === 'release-start')
1685
1740
  return releaseStart(argv);
@@ -1693,6 +1748,6 @@ export async function runPluginControl(argv = process.argv.slice(2)) {
1693
1748
  return releaseReconcile(argv);
1694
1749
  if (command === 'activation-plan')
1695
1750
  return activationPlan(argv);
1696
- throw new ControlPlaneCliError('INVALID_ARGUMENT', 'usage: dsh-plugin-control <discover|show|approve|activate|host-request|probe|attest|watch-observe|watch-retract|watch-show|source-plan|scaffold|source verify-prepared|source gc|release-start|release-request|release-step|release-attest|release-reconcile|activation-plan>');
1751
+ throw new ControlPlaneCliError('INVALID_ARGUMENT', 'usage: dsh-plugin-control <discover|show|approve|activate|host-request|probe|attest|watch-observe|watch-retract|watch-show|source-plan|scaffold|source verify-prepared|source gc|source revision-enqueue|source revision-show|release-start|release-request|release-step|release-attest|release-reconcile|activation-plan>');
1697
1752
  }
1698
1753
  //# sourceMappingURL=cli.js.map