oh-my-opencode-cohub 1.13.1 → 1.13.2

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/dist/cli/index.js CHANGED
@@ -49,7 +49,7 @@ function computeStats(records, window = DEFAULT_STATS_WINDOW) {
49
49
  return result;
50
50
  }
51
51
  // package.json
52
- var version = "1.13.1";
52
+ var version = "1.13.2";
53
53
 
54
54
  // src/cli/config-io.ts
55
55
  import * as fs from "node:fs";
@@ -85,11 +85,11 @@ function writeJSON(filePath, data) {
85
85
  `;
86
86
  fs.writeFileSync(filePath, content, "utf-8");
87
87
  }
88
- function addPluginToOpenCodeConfig(version2) {
88
+ function addPluginToOpenCodeConfig(version) {
89
89
  const configPath = getOpencodeConfigPath();
90
90
  let config = readJSON(configPath) ?? {};
91
91
  const plugins = Array.isArray(config.plugin) ? [...config.plugin] : [];
92
- const pkgSpec = `${PACKAGE_NAME}@${version2}`;
92
+ const pkgSpec = `${PACKAGE_NAME}@${version}`;
93
93
  if (plugins.some((p) => p.includes(PACKAGE_NAME))) {
94
94
  for (let i = 0;i < plugins.length; i++) {
95
95
  if (plugins[i].includes(PACKAGE_NAME)) {
@@ -473,7 +473,7 @@ function uninstallCoHub() {
473
473
  messages.push("✅ CoHub 卸载完成。完全关闭 OpenCode 后重新打开即可。");
474
474
  return { success: true, messages };
475
475
  }
476
- function installToCacheDir(version2) {
476
+ function installToCacheDir(version) {
477
477
  try {
478
478
  const targetDir = PACKAGES_CACHE;
479
479
  if (fs.existsSync(targetDir)) {
@@ -482,7 +482,7 @@ function installToCacheDir(version2) {
482
482
  fs.mkdirSync(targetDir, { recursive: true });
483
483
  const pkgJson = { private: true };
484
484
  fs.writeFileSync(path2.join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2), "utf-8");
485
- const pkgSpec = `${PACKAGE_NAME}@${version2}`;
485
+ const pkgSpec = `${PACKAGE_NAME}@${version}`;
486
486
  const cmd = `npm install "${pkgSpec}" --save --legacy-peer-deps`;
487
487
  execSync(cmd, { cwd: targetDir, stdio: "pipe", timeout: 120000 });
488
488
  const entryPath = path2.join(targetDir, "node_modules", PACKAGE_NAME, "dist", "index.js");
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/context/engine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAe,aAAa,EAAkC,MAAM,SAAS,CAAC;AAK3G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEzD,qBAAa,aAAa;IACxB,8BAA8B;IAC9B,OAAO,CAAC,QAAQ,CAAkC;IAClD,sBAAsB;IACtB,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IAU9D;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAYtD;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAA;KAAE,GAClC,OAAO,CAAC,IAAI,CAAC;IAsChB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAa7B;;;;OAIG;IACH,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiBxD;;;OAGG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAoD/C;;;;;OAKG;IACG,aAAa,CACjB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAqDpG;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIvC;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe;IAI/C;;OAEG;IACH,wBAAwB,CAAC,QAAQ,GAAE,MAAuB,GAAG,IAAI;CAQlE"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/context/engine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAe,aAAa,EAAkC,MAAM,SAAS,CAAC;AAK3G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEzD,qBAAa,aAAa;IACxB,8BAA8B;IAC9B,OAAO,CAAC,QAAQ,CAAkC;IAClD,sBAAsB;IACtB,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IAU9D;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAYtD;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAA;KAAE,GAClC,OAAO,CAAC,IAAI,CAAC;IAsChB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAa7B;;;;OAIG;IACH,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiBxD;;;OAGG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAkE/C;;;;;OAKG;IACG,aAAa,CACjB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAqDpG;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIvC;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe;IAI/C;;OAEG;IACH,wBAAwB,CAAC,QAAQ,GAAE,MAAuB,GAAG,IAAI;CAQlE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=engine.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.test.d.ts","sourceRoot":"","sources":["../../src/context/engine.test.ts"],"names":[],"mappings":""}
@@ -32,6 +32,17 @@ export declare function extractDecisions(messages: SdkMessage[], maxDecisions: n
32
32
  * 从 bash 输出中提取编译/测试错误。
33
33
  */
34
34
  export declare function extractErrors(messages: SdkMessage[], maxErrors: number, windowSize: number): string[];
35
+ /**
36
+ * 估算文本的 token 数。
37
+ * 中文字符(CJK 及东亚表意区)按 1 token/字符,其余字符按 0.25 token/字符。
38
+ */
39
+ export declare function estimateTokens(text: string): number;
40
+ /**
41
+ * 强制 prompt 预算约束。
42
+ * 若 full 的估算 token 数 ≤ maxTokens → 返回 full(完整保留);
43
+ * 否则 → 返回 base(丢弃全部 CoHub 注入后缀,只保留用户原始 prompt)。
44
+ */
45
+ export declare function enforcePromptBudget(base: string, full: string, maxTokens: number): string;
35
46
  /**
36
47
  * 按 token 预算截断文本。
37
48
  * P2-5: 估算规则区分中西文——中文字符(CJK 及东亚表意区)按 1 token/字符,
@@ -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,CAoFhB;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;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAqBxE"}
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;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAanD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGzF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAqBxE"}
@@ -41,6 +41,10 @@ export interface ContextConfig {
41
41
  summarizeMaxTokens: number;
42
42
  /** 从父 session 中扫描最近多少条消息 */
43
43
  relevantMessageWindow: number;
44
+ /** 错误列表总字符上限 */
45
+ maxErrorTotalChars: number;
46
+ /** 依赖结果 keyOutput 截断字符数 */
47
+ dependencyKeyOutputChars: number;
44
48
  }
45
49
  /** 默认配置 */
46
50
  export declare const DEFAULT_CONTEXT_CONFIG: ContextConfig;
@@ -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,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,4BAA4B;IAC5B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,WAAW;AACX,eAAO,MAAM,sBAAsB,EAAE,aAqBpC,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,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,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,aAuBpC,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;AA4HlD,QAAA,MAAM,WAAW,EAAE,MAoqBlB,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;AAiIlD,QAAA,MAAM,WAAW,EAAE,MAgrBlB,CAAC;AAEF,eAAO,MAAM,MAAM,QAAc,CAAC;AAClC,eAAe,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -905,18 +905,30 @@ class TaskTracker {
905
905
  lines.push("");
906
906
  if (activeJobs.length > 0) {
907
907
  lines.push("#### Active / Unreconciled");
908
- for (const j of activeJobs) {
908
+ const maxShow = 15;
909
+ const shown = activeJobs.slice(0, maxShow);
910
+ for (const j of shown) {
909
911
  lines.push(` - ${j.agent} / ${j.status} / ${j.sessionId || "pending"} / alias=${j.alias}`);
910
912
  }
913
+ const extra = activeJobs.length - maxShow;
914
+ if (extra > 0) {
915
+ lines.push(` …及 ${extra} 个任务未显示`);
916
+ }
911
917
  lines.push("");
912
918
  }
913
919
  if (reusableJobs.length > 0) {
914
920
  lines.push("#### Reusable Sessions");
915
921
  lines.push(" Session ID | Agent | Alias");
916
922
  lines.push(" ---------------------------------------|--------------|-------");
917
- for (const j of reusableJobs) {
923
+ const maxShow = 15;
924
+ const shown = reusableJobs.slice(0, maxShow);
925
+ for (const j of shown) {
918
926
  lines.push(` \`${j.sessionId}\` | ${j.agent} | _${j.alias}_`);
919
927
  }
928
+ const extra = reusableJobs.length - maxShow;
929
+ if (extra > 0) {
930
+ lines.push(` …及 ${extra} 个任务未显示`);
931
+ }
920
932
  lines.push("");
921
933
  }
922
934
  return lines.join(`
@@ -942,6 +954,19 @@ class TaskTracker {
942
954
  this.persistStats();
943
955
  return staleSessions;
944
956
  }
957
+ pruneTerminalJobs(olderThanMs) {
958
+ const now = Date.now();
959
+ const toDelete = [];
960
+ for (const [alias, job] of this.jobs) {
961
+ if (job.terminalReconciled && job.status !== "running" && now - job.createdAt > olderThanMs) {
962
+ toDelete.push(alias);
963
+ }
964
+ }
965
+ for (const alias of toDelete) {
966
+ this.jobs.delete(alias);
967
+ }
968
+ return toDelete.length;
969
+ }
945
970
  isReusable(alias) {
946
971
  const job = this.jobs.get(alias);
947
972
  return job?.status === "completed" && job.terminalReconciled;
@@ -1221,7 +1246,9 @@ var DEFAULT_CONTEXT_CONFIG = {
1221
1246
  maxDependencies: 8,
1222
1247
  dependencyPropagation: true,
1223
1248
  summarizeMaxTokens: 2000,
1224
- relevantMessageWindow: 20
1249
+ relevantMessageWindow: 20,
1250
+ maxErrorTotalChars: 600,
1251
+ dependencyKeyOutputChars: 200
1225
1252
  };
1226
1253
 
1227
1254
  // src/context/engine.ts
@@ -1278,12 +1305,12 @@ function extractRelevantFiles(messages, maxFiles, windowSize) {
1278
1305
  if (part.type === "tool" && part.state) {
1279
1306
  const state = part.state;
1280
1307
  const input = state.input ?? {};
1281
- const path2 = extractPath(input);
1282
- if (path2) {
1283
- if (!fileMap.has(path2)) {
1284
- fileMap.set(path2, { path: path2, summary: "" });
1308
+ const path = extractPath(input);
1309
+ if (path) {
1310
+ if (!fileMap.has(path)) {
1311
+ fileMap.set(path, { path, summary: "" });
1285
1312
  }
1286
- const existing = fileMap.get(path2);
1313
+ const existing = fileMap.get(path);
1287
1314
  if (typeof input.offset === "number") {
1288
1315
  const limit = typeof input.limit === "number" ? input.limit : 50;
1289
1316
  existing.lines = `${input.offset}-${input.offset + limit}`;
@@ -1342,7 +1369,18 @@ function extractRelevantFiles(messages, maxFiles, windowSize) {
1342
1369
  }
1343
1370
  }
1344
1371
  }
1345
- return Array.from(fileMap.values()).slice(0, maxFiles);
1372
+ const DENY_PATTERNS = [
1373
+ /node_modules/,
1374
+ /\.git\//,
1375
+ /\.config[\\\/]opencode/,
1376
+ /\.local[\\\/]share/,
1377
+ /storage[\\\/]oh-my-opencode-cohub/,
1378
+ /oh-my-opencode-cohub\.schema\.json$/,
1379
+ /stats\.json$/,
1380
+ /\.log$/
1381
+ ];
1382
+ const isDenied = (path) => DENY_PATTERNS.some((p) => p.test(path));
1383
+ return Array.from(fileMap.values()).filter((f) => !isDenied(f.path)).slice(0, maxFiles);
1346
1384
  }
1347
1385
  function extractPath(obj) {
1348
1386
  if (typeof obj.filePath === "string")
@@ -1393,8 +1431,8 @@ function extractErrors(messages, maxErrors, windowSize) {
1393
1431
  const lines = output.split(`
1394
1432
  `);
1395
1433
  for (const line of lines) {
1396
- if (errorPatterns.test(line) && line.length < 300) {
1397
- errors.push(line.trim());
1434
+ if (errorPatterns.test(line)) {
1435
+ errors.push(line.trim().slice(0, 200));
1398
1436
  if (errors.length >= maxErrors)
1399
1437
  return errors;
1400
1438
  }
@@ -1403,9 +1441,25 @@ function extractErrors(messages, maxErrors, windowSize) {
1403
1441
  }
1404
1442
  return errors;
1405
1443
  }
1444
+ function estimateTokens(text) {
1445
+ let used = 0;
1446
+ for (let i = 0;i < text.length; i++) {
1447
+ const code = text.charCodeAt(i);
1448
+ const isCjk = code >= 19968 && code <= 40959 || code >= 13312 && code <= 19903 || code >= 63744 && code <= 64255 || code >= 12352 && code <= 12543 || code >= 44032 && code <= 55215;
1449
+ used += isCjk ? 1 : 0.25;
1450
+ }
1451
+ return used;
1452
+ }
1453
+ function enforcePromptBudget(base, full, maxTokens) {
1454
+ if (estimateTokens(full) <= maxTokens)
1455
+ return full;
1456
+ return truncateByTokens(base, maxTokens);
1457
+ }
1406
1458
  function truncateByTokens(text, maxTokens) {
1407
1459
  if (!text || maxTokens <= 0)
1408
1460
  return "";
1461
+ if (estimateTokens(text) <= maxTokens)
1462
+ return text;
1409
1463
  let used = 0;
1410
1464
  let cut = text.length;
1411
1465
  for (let i = 0;i < text.length; i++) {
@@ -1417,8 +1471,6 @@ function truncateByTokens(text, maxTokens) {
1417
1471
  break;
1418
1472
  }
1419
1473
  }
1420
- if (cut >= text.length)
1421
- return text;
1422
1474
  return text.slice(0, cut) + `
1423
1475
  … [正文已按 token 预算截断]`;
1424
1476
  }
@@ -1590,14 +1642,26 @@ class ContextEngine {
1590
1642
  }
1591
1643
  if (context.errors.length > 0) {
1592
1644
  parts.push("### ⚠️ 近期错误");
1645
+ let totalChars = 0;
1646
+ let omitted = 0;
1593
1647
  for (const e of context.errors) {
1594
- parts.push(`- ${e}`);
1648
+ const line = `- ${e}`;
1649
+ if (totalChars + line.length > this.config.maxErrorTotalChars) {
1650
+ omitted++;
1651
+ } else {
1652
+ parts.push(line);
1653
+ totalChars += line.length;
1654
+ }
1655
+ }
1656
+ if (omitted > 0) {
1657
+ parts.push(`…(错误已省略 ${omitted} 条)`);
1595
1658
  }
1596
1659
  }
1597
1660
  if (context.dependencies.length > 0) {
1598
1661
  parts.push("### \uD83D\uDCE6 依赖结果");
1599
1662
  for (const dep of context.dependencies) {
1600
- parts.push(`- **${dep.agent}**: ${dep.keyOutput}`);
1663
+ const output = dep.keyOutput.length > this.config.dependencyKeyOutputChars ? dep.keyOutput.slice(0, this.config.dependencyKeyOutputChars) + "…" : dep.keyOutput;
1664
+ parts.push(`- **${dep.agent}**: ${output}`);
1601
1665
  }
1602
1666
  }
1603
1667
  return parts.length > 0 ? `
@@ -1622,7 +1686,7 @@ class ContextEngine {
1622
1686
  }
1623
1687
  for (const part of messages[i].parts ?? []) {
1624
1688
  if (part.type === "text" && part.text) {
1625
- keyOutput = part.text.slice(0, 500).replace(/\n/g, " ");
1689
+ keyOutput = part.text.slice(0, this.config.dependencyKeyOutputChars).replace(/\n/g, " ");
1626
1690
  break;
1627
1691
  }
1628
1692
  }
@@ -1942,12 +2006,12 @@ async function abortSession(client, sessionId) {
1942
2006
  appendLog("abortSession", "中止会话失败", err);
1943
2007
  }
1944
2008
  }
1945
- async function promptWithTimeout(client, path4, body, timeoutMs, directory) {
1946
- const sessionId = path4.id;
2009
+ async function promptWithTimeout(client, path, body, timeoutMs, directory) {
2010
+ const sessionId = path.id;
1947
2011
  let timer;
1948
2012
  try {
1949
2013
  const promptPromise = client.session.prompt({
1950
- path: path4,
2014
+ path,
1951
2015
  body,
1952
2016
  query: directory ? { directory } : undefined
1953
2017
  });
@@ -2075,8 +2139,8 @@ ${cr.result}`).join(`
2075
2139
  function formatModelComposition(councillorResults) {
2076
2140
  return councillorResults.map((cr) => `${cr.name}: ${shortModelLabel(cr.model)}`).join(", ");
2077
2141
  }
2078
- function adaptiveKey(config3) {
2079
- return `${config3.variant ?? "default"}\x00${config3.model}`;
2142
+ function adaptiveKey(config) {
2143
+ return `${config.variant ?? "default"}\x00${config.model}`;
2080
2144
  }
2081
2145
  function classifyFailure(message) {
2082
2146
  if (message.includes("Empty response from provider"))
@@ -2298,10 +2362,10 @@ class CouncilManager {
2298
2362
  client;
2299
2363
  directory;
2300
2364
  config;
2301
- constructor(client, directory, config3) {
2365
+ constructor(client, directory, config) {
2302
2366
  this.client = client;
2303
2367
  this.directory = directory;
2304
- this.config = config3;
2368
+ this.config = config;
2305
2369
  }
2306
2370
  async runCouncil(prompt, presetName, parentSessionId) {
2307
2371
  const resolvedPreset = presetName ?? this.config.default_preset ?? "default";
@@ -2351,8 +2415,8 @@ class CouncilManager {
2351
2415
  const results = [];
2352
2416
  const explicitTimeout = this.config.timeout;
2353
2417
  const explicitRetries = this.config.councillor_retries;
2354
- const resolveParams = (config3) => {
2355
- const p = getAdaptiveParams(adaptiveKey(config3), {
2418
+ const resolveParams = (config) => {
2419
+ const p = getAdaptiveParams(adaptiveKey(config), {
2356
2420
  ...DEFAULT_ADAPTIVE_CONFIG,
2357
2421
  defaultTimeoutMs: explicitTimeout ?? timeout,
2358
2422
  defaultRetries: explicitRetries ?? 3
@@ -2367,10 +2431,10 @@ class CouncilManager {
2367
2431
  for (let round = 1;round <= options.maxRounds; round++) {
2368
2432
  const roundResults = [];
2369
2433
  const firstAttemptIsExtra = round > 1;
2370
- for (const [name, config3] of entries) {
2371
- const params = resolveParams(config3);
2434
+ for (const [name, config] of entries) {
2435
+ const params = resolveParams(config);
2372
2436
  const roundPrompt = round > 1 ? buildDeliberationPrompt(prompt, lastRound, name) : prompt;
2373
- const r = await this.runCouncillorWithRetry(name, config3, roundPrompt, parentSessionId, params.timeout, budget, { firstAttemptIsExtra, retries: params.retries });
2437
+ const r = await this.runCouncillorWithRetry(name, config, roundPrompt, parentSessionId, params.timeout, budget, { firstAttemptIsExtra, retries: params.retries });
2374
2438
  roundResults.push(r);
2375
2439
  }
2376
2440
  lastRound = roundResults;
@@ -2381,9 +2445,9 @@ class CouncilManager {
2381
2445
  }
2382
2446
  results.push(...lastRound);
2383
2447
  } else {
2384
- const promises = entries.map(([name, config3]) => {
2385
- const params = resolveParams(config3);
2386
- return this.runCouncillorWithRetry(name, config3, prompt, parentSessionId, params.timeout, budget, { retries: params.retries });
2448
+ const promises = entries.map(([name, config]) => {
2449
+ const params = resolveParams(config);
2450
+ return this.runCouncillorWithRetry(name, config, prompt, parentSessionId, params.timeout, budget, { retries: params.retries });
2387
2451
  });
2388
2452
  const settled = await Promise.allSettled(promises);
2389
2453
  for (let i = 0;i < settled.length; i++) {
@@ -2403,37 +2467,37 @@ class CouncilManager {
2403
2467
  }
2404
2468
  return results;
2405
2469
  }
2406
- async runCouncillorWithRetry(name, config3, prompt, parentSessionId, timeout, budget, options) {
2407
- let attemptPrompt = formatCouncillorPrompt(prompt, config3.prompt);
2408
- let attemptVariant = config3.variant;
2470
+ async runCouncillorWithRetry(name, config, prompt, parentSessionId, timeout, budget, options) {
2471
+ let attemptPrompt = formatCouncillorPrompt(prompt, config.prompt);
2472
+ let attemptVariant = config.variant;
2409
2473
  let lastCategory = null;
2410
2474
  let streak = 0;
2411
2475
  const categoryRetries = {};
2412
2476
  let attempt = 1;
2413
2477
  for (;; ) {
2414
2478
  if (budget.expired()) {
2415
- return { name, model: config3.model, status: "error", error: "budget-exhausted" };
2479
+ return { name, model: config.model, status: "error", error: "budget-exhausted" };
2416
2480
  }
2417
2481
  const isExtraAttempt = attempt > 1 || options?.firstAttemptIsExtra === true;
2418
2482
  if (isExtraAttempt && !budget.consumeRetry()) {
2419
- return { name, model: config3.model, status: "error", error: "budget-exhausted" };
2483
+ return { name, model: config.model, status: "error", error: "budget-exhausted" };
2420
2484
  }
2421
2485
  let category;
2422
2486
  let errorInfo;
2423
2487
  try {
2424
2488
  const session = await this.runAgentSession({
2425
2489
  parentSessionId,
2426
- title: `Council ${name} (${shortModelLabel(config3.model)})`,
2427
- model: config3.model,
2490
+ title: `Council ${name} (${shortModelLabel(config.model)})`,
2491
+ model: config.model,
2428
2492
  promptText: attemptPrompt,
2429
2493
  variant: attemptVariant,
2430
2494
  timeout
2431
2495
  });
2432
2496
  const quality = assessQuality({ output: session.text, decisions: session.summary.entities.length });
2433
2497
  if (quality.passed) {
2434
- recordModelResult(config3.model, "success");
2435
- recordAdaptiveSample(adaptiveKey(config3), true);
2436
- return { name, model: config3.model, status: "completed", result: session.text, summary: session.summary };
2498
+ recordModelResult(config.model, "success");
2499
+ recordAdaptiveSample(adaptiveKey(config), true);
2500
+ return { name, model: config.model, status: "completed", result: session.text, summary: session.summary };
2437
2501
  }
2438
2502
  category = quality.failureCategory ?? "quality-low";
2439
2503
  errorInfo = `Councillor "${name}": quality score ${quality.score} (${category})`;
@@ -2442,8 +2506,8 @@ class CouncilManager {
2442
2506
  category = classifyFailure(msg);
2443
2507
  errorInfo = `Councillor "${name}": ${msg}`;
2444
2508
  }
2445
- recordModelResult(config3.model, category);
2446
- recordAdaptiveSample(adaptiveKey(config3), false);
2509
+ recordModelResult(config.model, category);
2510
+ recordAdaptiveSample(adaptiveKey(config), false);
2447
2511
  streak = lastCategory === category ? streak + 1 : 1;
2448
2512
  lastCategory = category;
2449
2513
  const used = categoryRetries[category] ?? 0;
@@ -2452,18 +2516,18 @@ class CouncilManager {
2452
2516
  if (action === "needs-human") {
2453
2517
  return {
2454
2518
  name,
2455
- model: config3.model,
2519
+ model: config.model,
2456
2520
  status: "needs_human",
2457
2521
  error: `${errorInfo} — ${streak} consecutive ${category} failures, needs human review`
2458
2522
  };
2459
2523
  }
2460
2524
  if (action === "budget-exhausted") {
2461
- return { name, model: config3.model, status: "error", error: "budget-exhausted" };
2525
+ return { name, model: config.model, status: "error", error: "budget-exhausted" };
2462
2526
  }
2463
2527
  if (action === "give-up") {
2464
2528
  return {
2465
2529
  name,
2466
- model: config3.model,
2530
+ model: config.model,
2467
2531
  status: category === "timeout" ? "timed_out" : "failed",
2468
2532
  error: errorInfo
2469
2533
  };
@@ -2819,6 +2883,10 @@ class OrchestrationEngine {
2819
2883
 
2820
2884
  // src/orchestration/contract.ts
2821
2885
  var CONTRACT_PATTERN = /<!-- CONTRACT_BEGIN -->\n([\s\S]*?)<!-- CONTRACT_END -->/;
2886
+ var CONTRACT_STRIP_PATTERN = /<!-- CONTRACT_BEGIN -->[\s\S]*?<!-- CONTRACT_END -->/g;
2887
+ function stripExistingContracts(text) {
2888
+ return text.replace(CONTRACT_STRIP_PATTERN, "");
2889
+ }
2822
2890
 
2823
2891
  class ContractManager {
2824
2892
  extract(output) {
@@ -2971,15 +3039,16 @@ class Scheduler {
2971
3039
  }
2972
3040
 
2973
3041
  // src/orchestration/index.ts
2974
- function createOrchestrationLayer(tracker, config3) {
3042
+ function createOrchestrationLayer(tracker, config) {
2975
3043
  const contractMgr = new ContractManager;
2976
- const retryMgr = new RetryManager(config3?.retryPolicies);
3044
+ const retryMgr = new RetryManager(config?.retryPolicies);
2977
3045
  const engine = new OrchestrationEngine(contractMgr, retryMgr);
2978
- const scheduler = new Scheduler(engine, config3?.concurrency?.maxConcurrency);
3046
+ const scheduler = new Scheduler(engine, config?.concurrency?.maxConcurrency);
2979
3047
  return { engine, contractMgr, retryMgr, scheduler };
2980
3048
  }
2981
3049
 
2982
3050
  // src/index.ts
3051
+ var DEFAULT_PROMPT_MAX_TOKENS = 12000;
2983
3052
  function loadFileOverrides(projectDir) {
2984
3053
  const overrides = {};
2985
3054
  const agentNames = [
@@ -3301,40 +3370,45 @@ var CoHubPlugin = async (input, options) => {
3301
3370
  } catch (err) {
3302
3371
  appendLog("config", "双重注册写入文件失败", err);
3303
3372
  }
3304
- appendLog("config.hook", `config hook 注入完成: ${JSON.stringify(Object.entries(agentConfigs).map(([name, cfg2]) => {
3305
- const c2 = cfg2;
3306
- const prompt = typeof c2.prompt === "string" ? c2.prompt : "";
3373
+ appendLog("config.hook", `config hook 注入完成: ${JSON.stringify(Object.entries(agentConfigs).map(([name, cfg]) => {
3374
+ const c = cfg;
3375
+ const prompt = typeof c.prompt === "string" ? c.prompt : "";
3307
3376
  return {
3308
3377
  name,
3309
3378
  promptLen: prompt.length,
3310
3379
  promptPreview: prompt.slice(0, 120),
3311
- hasModel: !!c2.model,
3312
- hasVariant: !!c2.variant
3380
+ hasModel: !!c.model,
3381
+ hasVariant: !!c.variant
3313
3382
  };
3314
3383
  }))}`);
3315
3384
  },
3316
- "tool.execute.before": async (input2, output) => {
3385
+ "tool.execute.before": async (input, output) => {
3317
3386
  try {
3318
- if (input2.tool === "task") {
3387
+ if (input.tool === "task") {
3388
+ const rawPrompt = typeof output.args?.prompt === "string" ? output.args.prompt : "";
3389
+ const rawDesc = typeof output.args?.description === "string" ? output.args.description : "";
3390
+ const basePrompt = rawPrompt || rawDesc || "";
3319
3391
  const args = output.args ?? {};
3320
3392
  const subagentType = typeof args.subagent_type === "string" ? args.subagent_type : "";
3321
3393
  const description = typeof args.description === "string" ? args.description : "";
3322
3394
  if (typeof args.task_id === "string" && args.task_id !== "" && !args.task_id.startsWith("ses_")) {
3323
3395
  delete output.args.task_id;
3324
3396
  }
3325
- const alias = tracker.registerBeforeTask(input2.sessionID, {
3397
+ const alias = tracker.registerBeforeTask(input.sessionID, {
3326
3398
  description,
3327
3399
  subagent_type: subagentType,
3328
3400
  task_id: typeof output.args.task_id === "string" && output.args.task_id.startsWith("ses_") ? output.args.task_id : undefined,
3329
3401
  background: typeof args.background === "boolean" ? args.background : undefined
3330
3402
  });
3331
3403
  _lastAlias = alias;
3332
- syncTrackerState(input2.sessionID ?? "");
3404
+ syncTrackerState(input.sessionID ?? "");
3405
+ tracker.pruneTerminalJobs(30 * 60 * 1000);
3333
3406
  if (orchestrationLayer && subagentType && description) {
3334
3407
  try {
3335
3408
  orchestrationLayer.engine.registerTask(alias, subagentType, description, []);
3336
3409
  const targetField = output.args?.prompt ? "prompt" : "description";
3337
3410
  if (output.args?.[targetField] && typeof output.args[targetField] === "string") {
3411
+ output.args[targetField] = stripExistingContracts(output.args[targetField]);
3338
3412
  output.args[targetField] += `
3339
3413
  ` + orchestrationLayer.contractMgr.buildPrompt({
3340
3414
  goal: description,
@@ -3358,7 +3432,7 @@ var CoHubPlugin = async (input, options) => {
3358
3432
  `;
3359
3433
  const targetField = typeof output.args.prompt === "string" ? "prompt" : "description";
3360
3434
  output.args[targetField] = ((targetField === "prompt" ? output.args.prompt : description) ?? "") + contextBlock;
3361
- await contextEngine.fillContextAsync(contextId, input2.sessionID, {
3435
+ await contextEngine.fillContextAsync(contextId, input.sessionID, {
3362
3436
  strategy
3363
3437
  });
3364
3438
  const details = contextEngine.formatContextDetails(contextId);
@@ -3379,7 +3453,7 @@ var CoHubPlugin = async (input, options) => {
3379
3453
  const finalPrompt = typeof output.args?.prompt === "string" ? output.args.prompt : "";
3380
3454
  fs5.appendFileSync(logPath, JSON.stringify({
3381
3455
  time: new Date().toISOString(),
3382
- session: input2.sessionID?.slice(0, 20) ?? "?",
3456
+ session: input.sessionID?.slice(0, 20) ?? "?",
3383
3457
  subagent: subagentType,
3384
3458
  strategy,
3385
3459
  targetField,
@@ -3391,25 +3465,29 @@ var CoHubPlugin = async (input, options) => {
3391
3465
  `);
3392
3466
  }
3393
3467
  }
3468
+ const targetField = typeof output.args?.prompt === "string" ? "prompt" : "description";
3469
+ if (output.args?.[targetField] && typeof output.args[targetField] === "string") {
3470
+ output.args[targetField] = enforcePromptBudget(basePrompt, output.args[targetField], DEFAULT_PROMPT_MAX_TOKENS);
3471
+ }
3394
3472
  }
3395
3473
  } catch (err) {
3396
3474
  appendLog("tool.execute.before", "hook 失败", err);
3397
3475
  }
3398
3476
  },
3399
- "tool.execute.after": async (input2, output) => {
3477
+ "tool.execute.after": async (input, output) => {
3400
3478
  try {
3401
- if (input2.tool === "cancel_task") {
3402
- const args = input2.args;
3479
+ if (input.tool === "cancel_task") {
3480
+ const args = input.args;
3403
3481
  const taskId = args?.task_id;
3404
3482
  if (typeof taskId === "string")
3405
3483
  tracker.markCancelled(taskId);
3406
- syncTrackerState(input2.sessionID ?? "");
3484
+ syncTrackerState(input.sessionID ?? "");
3407
3485
  }
3408
- if (input2.tool === "task") {
3486
+ if (input.tool === "task") {
3409
3487
  const childSessionId = extractChildSessionId(output);
3410
- tracker.updateAfterTask(input2.sessionID, "completed", childSessionId);
3411
- syncTrackerState(input2.sessionID ?? "");
3412
- if (orchestrationLayer && input2.tool === "task") {
3488
+ tracker.updateAfterTask(input.sessionID, "completed", childSessionId);
3489
+ syncTrackerState(input.sessionID ?? "");
3490
+ if (orchestrationLayer && input.tool === "task") {
3413
3491
  try {
3414
3492
  if (typeof output === "string") {
3415
3493
  const contract = orchestrationLayer.contractMgr.extract(output);
@@ -3427,9 +3505,9 @@ var CoHubPlugin = async (input, options) => {
3427
3505
  appendLog("tool.execute.after", "hook 失败", err);
3428
3506
  }
3429
3507
  },
3430
- event: async (input2) => {
3508
+ event: async (input) => {
3431
3509
  try {
3432
- const e = input2.event;
3510
+ const e = input.event;
3433
3511
  const sessionId = extractSessionIdFromEvent(e.properties);
3434
3512
  if (!sessionId)
3435
3513
  return;
@@ -3438,13 +3516,13 @@ var CoHubPlugin = async (input, options) => {
3438
3516
  syncTrackerState(tracker.currentParentSessionId);
3439
3517
  if (orchestrationLayer) {
3440
3518
  try {
3441
- const job2 = tracker.getJobBySessionId(sessionId);
3442
- if (job2) {
3443
- orchestrationLayer.engine.transition(job2.alias, "completed");
3444
- orchestrationLayer.scheduler.markCompleted(job2.alias);
3519
+ const job = tracker.getJobBySessionId(sessionId);
3520
+ if (job) {
3521
+ orchestrationLayer.engine.transition(job.alias, "completed");
3522
+ orchestrationLayer.scheduler.markCompleted(job.alias);
3445
3523
  }
3446
- } catch (e2) {
3447
- appendLog("orchestration", "session.idle transition error", e2);
3524
+ } catch (e) {
3525
+ appendLog("orchestration", "session.idle transition error", e);
3448
3526
  }
3449
3527
  }
3450
3528
  const job = tracker.getJobBySessionId(sessionId);
@@ -3486,8 +3564,8 @@ var CoHubPlugin = async (input, options) => {
3486
3564
  }
3487
3565
  }
3488
3566
  }
3489
- } catch (e2) {
3490
- appendLog("orchestration", "session.deleted transition error", e2);
3567
+ } catch (e) {
3568
+ appendLog("orchestration", "session.deleted transition error", e);
3491
3569
  }
3492
3570
  }
3493
3571
  } else if (e.type === "session.error") {
@@ -3507,8 +3585,8 @@ var CoHubPlugin = async (input, options) => {
3507
3585
  }
3508
3586
  }
3509
3587
  }
3510
- } catch (e2) {
3511
- appendLog("orchestration", "session.error transition error", e2);
3588
+ } catch (e) {
3589
+ appendLog("orchestration", "session.error transition error", e);
3512
3590
  }
3513
3591
  }
3514
3592
  }
@@ -3589,7 +3667,7 @@ var CoHubPlugin = async (input, options) => {
3589
3667
  appendLog("messages.transform", "hook 失败", err);
3590
3668
  }
3591
3669
  },
3592
- "experimental.chat.system.transform": async (input2, output) => {
3670
+ "experimental.chat.system.transform": async (input, output) => {
3593
3671
  try {
3594
3672
  if (output?.system && Array.isArray(output.system)) {
3595
3673
  output.system.push(CHINESE_LANGUAGE_INSTRUCTION);
@@ -1,4 +1,10 @@
1
1
  import type { AgentContract } from './types';
2
+ /**
3
+ * 删除文本中所有 `<!-- CONTRACT_BEGIN -->...<!-- CONTRACT_END -->` 块。
4
+ * 用于防止同一次 task() 的 prompt 中契约块重复膨胀。
5
+ * 无匹配时原样返回。
6
+ */
7
+ export declare function stripExistingContracts(text: string): string;
2
8
  export declare class ContractManager {
3
9
  /**
4
10
  * 从代理输出中提取结构化契约块。
@@ -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;AAI7C,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;;;;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"}
@@ -76,6 +76,14 @@ export declare class TaskTracker {
76
76
  * @returns 被标为 errored 的超时任务的 sessionId 列表(无 sessionId 的任务不包含)
77
77
  */
78
78
  cleanupStaleJobs(timeoutMs: number): string[];
79
+ /**
80
+ * 清理超龄终态任务:删除 terminalReconciled 且超过 olderThanMs 未更新的终态 job。
81
+ * 绝不会删除 running/pending 的活跃任务。
82
+ *
83
+ * @param olderThanMs 超龄阈值(毫秒),超过此时间的终态任务会被删除
84
+ * @returns 实际删除的任务数量
85
+ */
86
+ pruneTerminalJobs(olderThanMs: number): number;
79
87
  /**
80
88
  * 检查任务是否可复用
81
89
  */
@@ -1 +1 @@
1
- {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/task-manager/tracker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/task-manager/tracker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode-cohub",
3
- "version": "1.13.1",
3
+ "version": "1.13.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Mr-cjf/oh-my-opencode-cohub.git"