oh-my-opencode-cohub 1.18.6 → 1.18.7

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.
package/README.md CHANGED
@@ -23,22 +23,21 @@ OpenCode 中文智能体编排插件 CoHub——纯调度模式、全中文提
23
23
  ### 前置条件
24
24
 
25
25
  - [OpenCode](https://opencode.ai) 已安装
26
- - [Bun](https://bun.sh) 已安装(CLI 安装和源码构建均需要)
26
+ - [Bun](https://bun.sh) 已安装(隔离 HOME 检验与源码构建需要;官方插件安装不需要)
27
27
 
28
- ### 方式一:CLI 一键安装(推荐)
28
+ ### 方式一:官方插件安装(推荐,真实 HOME)
29
29
 
30
30
  ```bash
31
- bunx oh-my-opencode-cohub install
31
+ opencode plugin oh-my-opencode-cohub@<精确版本号> --global --force
32
32
  ```
33
33
 
34
- CLI 会自动完成以下操作:
34
+ > 安装前先对 `~/.config/opencode/opencode.json` 与 `~/.config/opencode/tui.json` 做**时间戳备份**;命令只写入插件相关条目,**保留其他配置**。
35
35
 
36
- 1. 将 `oh-my-opencode-cohub` 注册到 `~/.config/opencode/opencode.json` 的 `plugin` 数组
37
- 2. 将插件注册到 TUI 配置 `~/.config/opencode/tui.json`
38
- 3. 在 `opencode.json` 的 `agent` 字段中注册全部 12 个 `co-*` 代理
39
- 4. 写入默认配置文件 `~/.config/opencode/oh-my-opencode-cohub.json`
36
+ 1. 将 `oh-my-opencode-cohub@<精确版本号>` 注册到全局 `plugin` 配置(`--global --force` 覆盖为指定精确版本)
37
+ 2. 插件运行时写入默认配置文件 `~/.config/opencode/oh-my-opencode-cohub.json`
38
+ 3. **重启 OpenCode 后生效**——已运行的会话不会热切加载新版本,需用户重启
40
39
 
41
- 重启 OpenCode 即可生效。
40
+ > **bunx 包 CLI 仅用于隔离 HOME 检验**:`bunx oh-my-opencode-cohub install/uninstall` 等包自带 CLI 只在隔离 HOME 环境中验证安装/卸载流程,**不要在真实 HOME 上整文件重写配置**。
42
41
 
43
42
  ### 方式二:npm 安装 + 手动配置
44
43
 
@@ -328,14 +327,19 @@ CoHub 内置轻量编排引擎(`src/orchestration/`,6 模块),**诚实
328
327
 
329
328
  ### 上下文共享
330
329
 
331
- ContextEngine(`src/context/`)按四种策略自动向子代理注入上下文:
330
+ ContextEngine(`src/context/`)默认**不注入**历史上下文——所有代理默认策略为 `none`,每次委派不会自动读取父会话历史(避免无谓的上下文膨胀与 token 消耗)。需要历史上下文时由调度者显式 opt-in:
331
+
332
+ - **任务级 opt-in**:`task` 调用传 `context_override` 参数(如 `context_override="relevant"`);
333
+ - **配置级 opt-in**:在 `oh-my-opencode-cohub.json` 的 `context.strategy` 中为指定代理显式配置非 `none` 策略。
332
334
 
333
- | 策略 | 适用代理 | 注入内容 |
334
- |------|---------|---------|
335
- | `none` | explorer / librarian / observer / rule-* | 不注入上下文,减少干扰 |
336
- | `relevant` | fixer / designer / planner | 相关文件路径 + 前置决策 + 近期错误 |
337
- | `summary` | oracle / council | 同上,且文件正文按 token 预算(2000)截断后注入 |
338
- | `full` | (预留) | 完整正文,目前未分配代理 |
335
+ | 策略 | 注入内容 |
336
+ |------|---------|
337
+ | `none` | 不注入上下文(默认) |
338
+ | `relevant` | 相关文件路径 + 前置决策 + 近期错误 |
339
+ | `summary` | 同上,且文件正文按 token 预算(2000)截断后注入 |
340
+ | `full` | 完整正文(预留) |
341
+
342
+ **依赖传播(`context.dependencyPropagation`,默认 `false`)**:子代理结果默认不自动传播到后续子代理;需要依赖历史摘要时显式配置 `"context": { "dependencyPropagation": true }`。关闭时 `captureResult` 仍照常返回结果与质量/复用预算采样(任务追踪、质量回送不受影响),仅不做依赖缓存写入与注入。
339
343
 
340
344
  **summary 文件读取模式(`context.summaryReadMode`,默认 `bounded`)**:控制 `summary` 策略下**文件正文的读取方式**,与上面的上下文策略 `full` 是**两个不同维度**,不要混淆:
341
345
 
@@ -346,7 +350,11 @@ ContextEngine(`src/context/`)按四种策略自动向子代理注入上下
346
350
 
347
351
  ```json
348
352
  {
349
- "context": { "summaryReadMode": "bounded" }
353
+ "context": {
354
+ "strategy": { "co-fixer": "relevant", "co-oracle": "summary" },
355
+ "dependencyPropagation": true,
356
+ "summaryReadMode": "bounded"
357
+ }
350
358
  }
351
359
  ```
352
360
 
@@ -370,7 +378,7 @@ ContextEngine(`src/context/`)按四种策略自动向子代理注入上下
370
378
  ### 注入膨胀防护
371
379
 
372
380
  - **prompt 预算兜底**:拼接超 12,000 token 时降级为裸用户 prompt,防止 JSON 超长截断
373
- - **CONTRACT 去重**:`stripExistingContracts` 清理历史残留契约块
381
+ - **CONTRACT 去重**:`stripExistingContracts` 仅清理插件自生成的 goal-only 空壳契约块,含约束/前置/其他内容的契约块(含用户手写)保守保留;新契约非空才追加且不重复追加
374
382
  - **错误截断**:单条 ≤200 字符 + 渲染总量 ≤600
375
383
  - **Job Board 折叠**:展示上限 15 条 + `pruneTerminalJobs` 每 30 分钟清理终态任务
376
384
 
package/dist/cli/index.js CHANGED
@@ -77,7 +77,7 @@ function computeStats(records, window = DEFAULT_STATS_WINDOW) {
77
77
  return result;
78
78
  }
79
79
  // package.json
80
- var version = "1.18.6";
80
+ var version = "1.18.7";
81
81
 
82
82
  // src/cli/config-io.ts
83
83
  import * as fs2 from "node:fs";
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/context/extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAuB5C,yBAAyB;AACzB,UAAU,OAAO;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,sBAAsB;AACtB,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,UAAU,EAAE,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,YAAY,EAAE,CAmGhB;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAqBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EAAE,EACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAwBV;AAID,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/context/extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAyB5C,yBAAyB;AACzB,UAAU,OAAO;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,sBAAsB;AACtB,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,UAAU,EAAE,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,YAAY,EAAE,CAmGhB;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAqBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EAAE,EACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAwBV;AAID,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/context/types.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;AAEvE,eAAe;AACf,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,iBAAiB;AACjB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,oBAAoB;AACpB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,cAAc;AACd,MAAM,WAAW,aAAa;IAC5B,kBAAkB;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC1C,kBAAkB;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,uBAAuB;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACrC,4BAA4B;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2BAA2B;IAC3B,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,WAAW;AACX,eAAO,MAAM,sBAAsB,EAAE,aAwBpC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAe;AACf,eAAO,MAAM,2BAA2B,EAAE,iBAGzC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/context/types.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;AAEvE,eAAe;AACf,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,iBAAiB;AACjB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,oBAAoB;AACpB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,cAAc;AACd,MAAM,WAAW,aAAa;IAC5B,kBAAkB;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC1C,kBAAkB;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,uBAAuB;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACrC,4BAA4B;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2BAA2B;IAC3B,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,WAAW;AACX,eAAO,MAAM,sBAAsB,EAAE,aA4BpC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAe;AACf,eAAO,MAAM,2BAA2B,EAAE,iBAGzC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAsIlD,QAAA,MAAM,WAAW,EAAE,MAglClB,CAAC;AAEF,eAAO,MAAM,MAAM,QAAc,CAAC;AAClC,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAsIlD,QAAA,MAAM,WAAW,EAAE,MAuqClB,CAAC;AAEF,eAAO,MAAM,MAAM,QAAc,CAAC;AAClC,eAAe,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -832,11 +832,11 @@ var DEFAULT_CONTEXT_CONFIG = {
832
832
  "co-explorer": "none",
833
833
  "co-librarian": "none",
834
834
  "co-observer": "none",
835
- "co-fixer": "relevant",
836
- "co-designer": "relevant",
837
- "co-planner": "relevant",
838
- "co-oracle": "summary",
839
- "co-council": "summary",
835
+ "co-fixer": "none",
836
+ "co-designer": "none",
837
+ "co-planner": "none",
838
+ "co-oracle": "none",
839
+ "co-council": "none",
840
840
  "co-rule-user": "none",
841
841
  "co-rule-project": "none",
842
842
  "co-rule-app": "none"
@@ -845,7 +845,7 @@ var DEFAULT_CONTEXT_CONFIG = {
845
845
  maxDecisions: 10,
846
846
  maxErrors: 5,
847
847
  maxDependencies: 8,
848
- dependencyPropagation: true,
848
+ dependencyPropagation: false,
849
849
  summarizeMaxTokens: 2000,
850
850
  summaryReadMode: "bounded",
851
851
  relevantMessageWindow: 20,
@@ -1639,7 +1639,7 @@ var KNOWN_EXTENSIONS = new Set([
1639
1639
  "gitignore",
1640
1640
  "editorconfig"
1641
1641
  ]);
1642
- var FILE_PATH_RE = /`?((?:[A-Za-z]:[\\\/]|\.{1,2}[\\\/]|~\/|\/|[\w-]+[\\\/])[\w\-\.\\\/]+\.\w{1,10})`?/g;
1642
+ var FILE_PATH_RE = /`?((?:[A-Za-z]:[\\\/]|\.{1,2}[\\\/]|~\/|\/|[\w\-~]+[\\\/])[\w\-\.\\\/~]+\.\w{1,10})`?/g;
1643
1643
  function extractRelevantFiles(messages, maxFiles, windowSize) {
1644
1644
  const recent = messages.slice(-windowSize);
1645
1645
  const fileMap = new Map;
@@ -3774,7 +3774,15 @@ class OrchestrationEngine {
3774
3774
  var CONTRACT_PATTERN = /<!-- CONTRACT_BEGIN -->\n([\s\S]*?)<!-- CONTRACT_END -->/;
3775
3775
  var CONTRACT_STRIP_PATTERN = /<!-- CONTRACT_BEGIN -->[\s\S]*?<!-- CONTRACT_END -->/g;
3776
3776
  function stripExistingContracts(text) {
3777
- return text.replace(CONTRACT_STRIP_PATTERN, "");
3777
+ return text.replace(CONTRACT_STRIP_PATTERN, (match) => {
3778
+ const inner = match.replace(/<!-- CONTRACT_BEGIN -->/, "").replace(/<!-- CONTRACT_END -->/, "").trim();
3779
+ const lines = inner.split(`
3780
+ `).map((l) => l.trim()).filter(Boolean);
3781
+ if (lines.length === 2 && lines[0] === "## 本任务前置上下文" && lines[1].startsWith("- 目标:")) {
3782
+ return "";
3783
+ }
3784
+ return match;
3785
+ });
3778
3786
  }
3779
3787
 
3780
3788
  class ContractManager {
@@ -3806,8 +3814,7 @@ class ContractManager {
3806
3814
  };
3807
3815
  }
3808
3816
  buildPrompt(context) {
3809
- const lines = ["<!-- CONTRACT_BEGIN -->", "## 本任务前置上下文"];
3810
- lines.push(`- 目标: ${context.goal}`);
3817
+ const lines = [];
3811
3818
  if (context.constraints.length) {
3812
3819
  lines.push("- 约束:");
3813
3820
  context.constraints.forEach((c) => lines.push(` - ${c}`));
@@ -3824,8 +3831,9 @@ class ContractManager {
3824
3831
  }
3825
3832
  });
3826
3833
  }
3827
- lines.push("<!-- CONTRACT_END -->");
3828
- return lines.join(`
3834
+ if (lines.length === 0)
3835
+ return "";
3836
+ return ["<!-- CONTRACT_BEGIN -->", "## 本任务前置上下文", ...lines, "<!-- CONTRACT_END -->"].join(`
3829
3837
  `);
3830
3838
  }
3831
3839
  summarize(contracts) {
@@ -4263,12 +4271,12 @@ var CoHubPlugin = async (input, options) => {
4263
4271
  const aliasAtCall = job.alias;
4264
4272
  const childId = job.sessionId;
4265
4273
  if (!childId)
4266
- return;
4274
+ return false;
4267
4275
  const verdict = await verifyChildTerminal(pluginClient, childId, job.createdAt);
4268
4276
  const nowJob = callAtCall ? tracker.getJobByCall(parentAtCall, callAtCall) : tracker.getJobBySessionId(childId);
4269
4277
  if (!nowJob || nowJob.alias !== aliasAtCall || nowJob.sessionId !== childId || nowJob.status !== "running") {
4270
4278
  appendSafeLog("event", `收尾竞态:候选已变更,不更新新调用 (session=${childId.slice(0, 20)})`);
4271
- return;
4279
+ return false;
4272
4280
  }
4273
4281
  if (verdict === "completed" || verdict === "errored" || verdict === "cancelled") {
4274
4282
  const term = verdict === "completed" ? "completed" : verdict === "cancelled" ? "cancelled" : "errored";
@@ -4287,6 +4295,7 @@ var CoHubPlugin = async (input, options) => {
4287
4295
  durationMs: Date.now() - job.createdAt,
4288
4296
  lastActivityAt: Date.now()
4289
4297
  });
4298
+ return verdict === "completed" && up.ok;
4290
4299
  } else {
4291
4300
  appendSafeLog("event", `无终态证据,保持 running (session=${childId.slice(0, 20)})`);
4292
4301
  taskDiag({
@@ -4297,8 +4306,46 @@ var CoHubPlugin = async (input, options) => {
4297
4306
  status: "running",
4298
4307
  lastActivityAt: Date.now()
4299
4308
  });
4309
+ return false;
4300
4310
  }
4301
4311
  }
4312
+ function captureAndWriteBack(opts) {
4313
+ contextEngine.captureResult(opts.childSessionId, opts.alias, opts.agent, { notBefore: opts.createdAt }).then((captured) => {
4314
+ if (samplingSeq.get(opts.alias) !== opts.seq)
4315
+ return;
4316
+ if (!captured)
4317
+ return;
4318
+ const wantQuality = isQualityEnabled(userConfig.quality);
4319
+ const wantReuse = reuseBudget.enabled;
4320
+ if (!wantQuality && !wantReuse)
4321
+ return;
4322
+ const now = opts.callID ? tracker.getJobByCall(opts.parentSessionId, opts.callID) : tracker.getJobBySessionId(opts.childSessionId);
4323
+ if (!now || now.alias !== opts.alias || now.sessionId !== opts.childSessionId) {
4324
+ appendSafeLog("event", "质量回送竞态:候选已变更,不写回复用/新调用");
4325
+ return;
4326
+ }
4327
+ const freshSample = wantReuse && captured.reuseInput && captured.reuseInput.sampledAt >= opts.createdAt ? captured.reuseInput : undefined;
4328
+ if (wantQuality) {
4329
+ const quality = assessQuality({
4330
+ output: captured.output,
4331
+ decisions: captured.decisions,
4332
+ latencyMs: Date.now() - opts.createdAt,
4333
+ tokens: captured.tokens
4334
+ });
4335
+ tracker.updateQualityByAlias(opts.alias, {
4336
+ score: quality.score,
4337
+ ...quality.failureCategory ? { failureCategory: quality.failureCategory } : {},
4338
+ ...quality.latencyMs !== undefined ? { latencyMs: quality.latencyMs } : {},
4339
+ ...quality.tokens ? { tokens: quality.tokens } : {},
4340
+ ...freshSample ? { reuseInput: freshSample } : {}
4341
+ });
4342
+ } else if (wantReuse) {
4343
+ const prev = tracker.getJobByAlias(opts.alias)?.quality;
4344
+ tracker.updateQualityByAlias(opts.alias, { ...prev ?? {}, reuseInput: freshSample });
4345
+ }
4346
+ syncTrackerState(tracker.currentParentSessionId);
4347
+ }).catch((err) => appendSafeLog("event", "质量回送失败", err));
4348
+ }
4302
4349
  const cleanupTimer = setInterval(() => {
4303
4350
  try {
4304
4351
  const staleSessions = tracker.cleanupStaleJobs(STALE_TASK_MS);
@@ -4427,12 +4474,15 @@ var CoHubPlugin = async (input, options) => {
4427
4474
  const targetField = output.args?.prompt ? "prompt" : "description";
4428
4475
  if (output.args?.[targetField] && typeof output.args[targetField] === "string") {
4429
4476
  output.args[targetField] = stripExistingContracts(output.args[targetField]);
4430
- output.args[targetField] += `
4431
- ` + orchestrationLayer.contractMgr.buildPrompt({
4477
+ const contractText = orchestrationLayer.contractMgr.buildPrompt({
4432
4478
  goal: description,
4433
4479
  prerequisites: [],
4434
4480
  constraints: []
4435
4481
  });
4482
+ if (contractText && !output.args[targetField].includes("<!-- CONTRACT_BEGIN -->")) {
4483
+ output.args[targetField] += `
4484
+ ` + contractText;
4485
+ }
4436
4486
  }
4437
4487
  const lr = orchestrationLayer.engine.applyLifecycleEvent(alias, "running");
4438
4488
  if (!lr.ok) {
@@ -4466,19 +4516,13 @@ var CoHubPlugin = async (input, options) => {
4466
4516
  if (strategy !== "none") {
4467
4517
  const contextId = contextEngine.registerContext({ description });
4468
4518
  output.args ??= {};
4469
- const contextBlock = `
4470
-
4471
- ### \uD83D\uDCCB 任务上下文 (CoHub 自动注入)
4472
- ` + "**当前任务**: " + description + `
4473
- `;
4474
4519
  const targetField = typeof output.args.prompt === "string" ? "prompt" : "description";
4475
- output.args[targetField] = ((targetField === "prompt" ? output.args.prompt : description) ?? "") + contextBlock;
4476
4520
  await contextEngine.fillContextAsync(contextId, input.sessionID, {
4477
4521
  strategy
4478
4522
  });
4479
4523
  const details = contextEngine.formatContextDetails(contextId);
4480
4524
  if (details) {
4481
- output.args[targetField] += details;
4525
+ output.args[targetField] = ((targetField === "prompt" ? output.args.prompt : description) ?? "") + details;
4482
4526
  }
4483
4527
  contextEngine.releaseContext(contextId);
4484
4528
  if (ctxDiag.enabled) {
@@ -4590,6 +4634,13 @@ var CoHubPlugin = async (input, options) => {
4590
4634
  lastActivityAt: Date.now()
4591
4635
  });
4592
4636
  } else {
4637
+ const snapAlias = job0.alias;
4638
+ const snapParent = job0.parentSessionId;
4639
+ const snapCall = job0.callID;
4640
+ const snapChild = childSessionId ?? job0.sessionId;
4641
+ const snapCreatedAt = job0.createdAt;
4642
+ const snapAgent = job0.agent;
4643
+ const wasRunningAtAfter = job0.status === "running";
4593
4644
  taskDiag({
4594
4645
  t: "task",
4595
4646
  callID: input.callID,
@@ -4599,7 +4650,26 @@ var CoHubPlugin = async (input, options) => {
4599
4650
  status: "running",
4600
4651
  lastActivityAt: Date.now()
4601
4652
  });
4602
- await reconcileJobTerminal(job0);
4653
+ const seqAtAfter = samplingSeq.get(snapAlias) ?? 0;
4654
+ const finalizedByThis = await reconcileJobTerminal(job0);
4655
+ if (finalizedByThis && wasRunningAtAfter && snapChild) {
4656
+ if ((samplingSeq.get(snapAlias) ?? 0) === seqAtAfter) {
4657
+ const nowJob = snapCall ? tracker.getJobByCall(snapParent, snapCall) : tracker.getJobBySessionId(snapChild);
4658
+ if (nowJob && nowJob.status === "completed" && nowJob.alias === snapAlias && nowJob.sessionId === snapChild) {
4659
+ const seq = seqAtAfter + 1;
4660
+ samplingSeq.set(snapAlias, seq);
4661
+ captureAndWriteBack({
4662
+ alias: snapAlias,
4663
+ parentSessionId: snapParent,
4664
+ callID: snapCall,
4665
+ childSessionId: snapChild,
4666
+ agent: snapAgent,
4667
+ createdAt: snapCreatedAt,
4668
+ seq
4669
+ });
4670
+ }
4671
+ }
4672
+ }
4603
4673
  }
4604
4674
  } catch (e) {
4605
4675
  appendSafeLog("orchestration", "afterTask engine 同步失败", e);
@@ -4632,48 +4702,22 @@ var CoHubPlugin = async (input, options) => {
4632
4702
  const qAlias = job.alias;
4633
4703
  const qParent = job.parentSessionId;
4634
4704
  const qCall = job.callID;
4635
- const qChild = sessionId;
4636
4705
  const qCreatedAt = job.createdAt;
4706
+ const qAgent = job.agent;
4637
4707
  const seq = (samplingSeq.get(qAlias) ?? 0) + 1;
4638
4708
  samplingSeq.set(qAlias, seq);
4639
4709
  await reconcileJobTerminal(job);
4640
4710
  if (samplingSeq.get(qAlias) !== seq)
4641
4711
  return;
4642
- contextEngine.captureResult(sessionId, qAlias, job.agent, { notBefore: qCreatedAt }).then((captured) => {
4643
- if (samplingSeq.get(qAlias) !== seq)
4644
- return;
4645
- if (!captured)
4646
- return;
4647
- const wantQuality = isQualityEnabled(userConfig.quality);
4648
- const wantReuse = reuseBudget.enabled;
4649
- if (!wantQuality && !wantReuse)
4650
- return;
4651
- const now = qCall ? tracker.getJobByCall(qParent, qCall) : tracker.getJobBySessionId(qChild);
4652
- if (!now || now.alias !== qAlias || now.sessionId !== qChild) {
4653
- appendSafeLog("event", "质量回送竞态:候选已变更,不写回复用/新调用");
4654
- return;
4655
- }
4656
- const freshSample = wantReuse && captured.reuseInput && captured.reuseInput.sampledAt >= qCreatedAt ? captured.reuseInput : undefined;
4657
- if (wantQuality) {
4658
- const quality = assessQuality({
4659
- output: captured.output,
4660
- decisions: captured.decisions,
4661
- latencyMs: Date.now() - qCreatedAt,
4662
- tokens: captured.tokens
4663
- });
4664
- tracker.updateQualityByAlias(qAlias, {
4665
- score: quality.score,
4666
- ...quality.failureCategory ? { failureCategory: quality.failureCategory } : {},
4667
- ...quality.latencyMs !== undefined ? { latencyMs: quality.latencyMs } : {},
4668
- ...quality.tokens ? { tokens: quality.tokens } : {},
4669
- ...freshSample ? { reuseInput: freshSample } : {}
4670
- });
4671
- } else if (wantReuse) {
4672
- const prev = tracker.getJobByAlias(qAlias)?.quality;
4673
- tracker.updateQualityByAlias(qAlias, { ...prev ?? {}, reuseInput: freshSample });
4674
- }
4675
- syncTrackerState(tracker.currentParentSessionId);
4676
- }).catch((err) => appendSafeLog("event", "质量回送失败", err));
4712
+ captureAndWriteBack({
4713
+ alias: qAlias,
4714
+ parentSessionId: qParent,
4715
+ callID: qCall,
4716
+ childSessionId: sessionId,
4717
+ agent: qAgent,
4718
+ createdAt: qCreatedAt,
4719
+ seq
4720
+ });
4677
4721
  } else if (e.type === "session.deleted") {
4678
4722
  if (!sessionId)
4679
4723
  return;
@@ -1,7 +1,12 @@
1
1
  import type { AgentContract } from './types';
2
2
  /**
3
- * 删除文本中所有 `<!-- CONTRACT_BEGIN -->...<!-- CONTRACT_END -->` 块。
4
- * 用于防止同一次 task() 的 prompt 中契约块重复膨胀。
3
+ * 清理历史残留的插件自生成「goal-only 空壳」契约块。
4
+ *
5
+ * 背景:旧版 buildPrompt 在无约束/无前置时仍输出 `## 本任务前置上下文 + - 目标: xxx`,
6
+ * 目标已随本任务 prompt 传递、无独立信息价值,同一次 task() 重复 before 会累积这些空壳块。
7
+ * 仅严格匹配该空壳形态(标题 + 仅一行 `- 目标:`、无任何其他内容)才清除;
8
+ * 含约束/前置结果/其他内容的契约块(含用户手写契约、调度显式约束)**保守保留**——
9
+ * 绝不按中文标题宽泛删除用户内容。
5
10
  * 无匹配时原样返回。
6
11
  */
7
12
  export declare function stripExistingContracts(text: string): string;
@@ -17,7 +22,10 @@ export declare class ContractManager {
17
22
  */
18
23
  private parseLines;
19
24
  /**
20
- * 构建传递给子代理的提示词,包含目标、约束和前置任务结果。
25
+ * 构建传递给子代理的提示词,包含约束和显式前置任务结果交接。
26
+ * 目标不再重复输出(任务目标已在本任务 prompt 中,避免重复膨胀);
27
+ * 仅当存在真正约束或显式前置依赖交接时才生成契约块,否则返回空字符串
28
+ * (入口仅在非空时追加,保证重复 before 不堆积空壳契约)。
21
29
  * 输出包含 CONTRACT_BEGIN/END 标记,子代理可追加自己的契约。
22
30
  */
23
31
  buildPrompt(context: {
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/orchestration/contract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,qBAAa,eAAe;IAC1B;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAM7C;;;OAGG;IACH,OAAO,CAAC,UAAU;IAqBlB;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,aAAa,CAAA;SAAE,EAAE,CAAC;QAC3D,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,GAAG,MAAM;IAuBV;;OAEG;IACH,SAAS,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM;CAY9C"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/orchestration/contract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAY3D;AAED,qBAAa,eAAe;IAC1B;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAM7C;;;OAGG;IACH,OAAO,CAAC,UAAU;IAqBlB;;;;;;OAMG;IACH,WAAW,CAAC,OAAO,EAAE;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,aAAa,CAAA;SAAE,EAAE,CAAC;QAC3D,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,GAAG,MAAM;IAsBV;;OAEG;IACH,SAAS,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM;CAY9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode-cohub",
3
- "version": "1.18.6",
3
+ "version": "1.18.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Mr-cjf/cohub.git",