wildcrane-runner 0.6.17 → 0.6.19

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/runner.mjs CHANGED
@@ -1,4 +1,4 @@
1
- // wildcrane-runner 0.6.17 — built by runner/scripts/build.mjs. Do not edit.
1
+ // wildcrane-runner 0.6.19 — built by runner/scripts/build.mjs. Do not edit.
2
2
  import { createRequire as __ai4kanbanRequire } from 'node:module'
3
3
  const require = __ai4kanbanRequire(import.meta.url)
4
4
  var __create = Object.create;
@@ -7517,7 +7517,7 @@ var require_react_dom_server_node_production = __commonJS({
7517
7517
  "node_modules/react-dom/cjs/react-dom-server.node.production.js"(exports) {
7518
7518
  "use strict";
7519
7519
  var util = __require("util");
7520
- var crypto7 = __require("crypto");
7520
+ var crypto8 = __require("crypto");
7521
7521
  var async_hooks = __require("async_hooks");
7522
7522
  var React4 = require_react();
7523
7523
  var ReactDOM = require_react_dom();
@@ -10382,7 +10382,7 @@ var require_react_dom_server_node_production = __commonJS({
10382
10382
  function createPostbackActionStateKey(permalink, componentKeyPath, hookIndex) {
10383
10383
  if (void 0 !== permalink) return "p" + permalink;
10384
10384
  permalink = JSON.stringify([componentKeyPath, null, hookIndex]);
10385
- componentKeyPath = crypto7.createHash("md5");
10385
+ componentKeyPath = crypto8.createHash("md5");
10386
10386
  componentKeyPath.update(permalink);
10387
10387
  return "k" + componentKeyPath.digest("hex");
10388
10388
  }
@@ -11493,8 +11493,8 @@ var require_react_dom_server_node_production = __commonJS({
11493
11493
  0
11494
11494
  );
11495
11495
  }
11496
- var autoName = JSCompiler_inline_result$jscomp$2, resumableState$jscomp$1 = request2.resumableState, update2 = getViewTransitionClassName(props.default, props.update), enter = getViewTransitionClassName(props.default, props.enter), exit = getViewTransitionClassName(props.default, props.exit), share = getViewTransitionClassName(props.default, props.share), name = props.name;
11497
- null == update2 && (update2 = "auto");
11496
+ var autoName = JSCompiler_inline_result$jscomp$2, resumableState$jscomp$1 = request2.resumableState, update3 = getViewTransitionClassName(props.default, props.update), enter = getViewTransitionClassName(props.default, props.enter), exit = getViewTransitionClassName(props.default, props.exit), share = getViewTransitionClassName(props.default, props.share), name = props.name;
11497
+ null == update3 && (update3 = "auto");
11498
11498
  null == enter && (enter = "auto");
11499
11499
  null == exit && (exit = "auto");
11500
11500
  if (null == name) {
@@ -11505,7 +11505,7 @@ var require_react_dom_server_node_production = __commonJS({
11505
11505
  prevContext$jscomp$0.tagScope & 8 ? resumableState$jscomp$1.instructions |= 128 : exit = "none";
11506
11506
  prevContext$jscomp$0.tagScope & 16 ? resumableState$jscomp$1.instructions |= 128 : enter = "none";
11507
11507
  var viewTransition = {
11508
- update: update2,
11508
+ update: update3,
11509
11509
  enter,
11510
11510
  exit,
11511
11511
  share,
@@ -11515,7 +11515,7 @@ var require_react_dom_server_node_production = __commonJS({
11515
11515
  autoName,
11516
11516
  nameIdx: 0
11517
11517
  }, subtreeScope = prevContext$jscomp$0.tagScope & -25;
11518
- subtreeScope = "none" !== update2 ? subtreeScope | 32 : subtreeScope & -33;
11518
+ subtreeScope = "none" !== update3 ? subtreeScope | 32 : subtreeScope & -33;
11519
11519
  "none" !== enter && (subtreeScope |= 64);
11520
11520
  var JSCompiler_inline_result$jscomp$3 = createFormatContext(
11521
11521
  prevContext$jscomp$0.insertionMode,
@@ -13136,15 +13136,15 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
13136
13136
  request2 = request2.renderLifetimeController;
13137
13137
  null !== request2 && request2.abort("The render ended.");
13138
13138
  }
13139
- function attachAbortSignal(request2, signal) {
13140
- if (signal.aborted) abort(request2, signal.reason);
13139
+ function attachAbortSignal(request2, signal2) {
13140
+ if (signal2.aborted) abort(request2, signal2.reason);
13141
13141
  else {
13142
13142
  var renderLifetimeController = new AbortController();
13143
13143
  request2.renderLifetimeController = renderLifetimeController;
13144
- signal.addEventListener(
13144
+ signal2.addEventListener(
13145
13145
  "abort",
13146
13146
  function() {
13147
- abort(request2, signal.reason);
13147
+ abort(request2, signal2.reason);
13148
13148
  },
13149
13149
  { signal: renderLifetimeController.signal }
13150
13150
  );
@@ -24376,7 +24376,7 @@ var require_react_dom_server_node_development = __commonJS({
24376
24376
  function createPostbackActionStateKey(permalink, componentKeyPath, hookIndex) {
24377
24377
  if (void 0 !== permalink) return "p" + permalink;
24378
24378
  permalink = JSON.stringify([componentKeyPath, null, hookIndex]);
24379
- componentKeyPath = crypto7.createHash("md5");
24379
+ componentKeyPath = crypto8.createHash("md5");
24380
24380
  componentKeyPath.update(permalink);
24381
24381
  return "k" + componentKeyPath.digest("hex");
24382
24382
  }
@@ -25837,8 +25837,8 @@ var require_react_dom_server_node_development = __commonJS({
25837
25837
  var treeId = getTreeId(task.treeContext);
25838
25838
  autoName = makeId(resumableState$jscomp$0, treeId, 0);
25839
25839
  }
25840
- var resumableState$jscomp$1 = request2.resumableState, update2 = getViewTransitionClassName(props.default, props.update), enter = getViewTransitionClassName(props.default, props.enter), exit = getViewTransitionClassName(props.default, props.exit), share = getViewTransitionClassName(props.default, props.share), name$jscomp$0 = props.name, autoName$jscomp$0 = autoName;
25841
- null == update2 && (update2 = "auto");
25840
+ var resumableState$jscomp$1 = request2.resumableState, update3 = getViewTransitionClassName(props.default, props.update), enter = getViewTransitionClassName(props.default, props.enter), exit = getViewTransitionClassName(props.default, props.exit), share = getViewTransitionClassName(props.default, props.share), name$jscomp$0 = props.name, autoName$jscomp$0 = autoName;
25841
+ null == update3 && (update3 = "auto");
25842
25842
  null == enter && (enter = "auto");
25843
25843
  null == exit && (exit = "auto");
25844
25844
  if (null == name$jscomp$0) {
@@ -25849,7 +25849,7 @@ var require_react_dom_server_node_development = __commonJS({
25849
25849
  prevContext$jscomp$0.tagScope & 8 ? resumableState$jscomp$1.instructions |= NeedUpgradeToViewTransitions : exit = "none";
25850
25850
  prevContext$jscomp$0.tagScope & 16 ? resumableState$jscomp$1.instructions |= NeedUpgradeToViewTransitions : enter = "none";
25851
25851
  var viewTransition = {
25852
- update: update2,
25852
+ update: update3,
25853
25853
  enter,
25854
25854
  exit,
25855
25855
  share,
@@ -25859,7 +25859,7 @@ var require_react_dom_server_node_development = __commonJS({
25859
25859
  autoName: autoName$jscomp$0,
25860
25860
  nameIdx: 0
25861
25861
  }, subtreeScope = prevContext$jscomp$0.tagScope & -25;
25862
- subtreeScope = "none" !== update2 ? subtreeScope | 32 : subtreeScope & -33;
25862
+ subtreeScope = "none" !== update3 ? subtreeScope | 32 : subtreeScope & -33;
25863
25863
  "none" !== enter && (subtreeScope |= 64);
25864
25864
  var JSCompiler_inline_result$jscomp$0 = createFormatContext(
25865
25865
  prevContext$jscomp$0.insertionMode,
@@ -27848,15 +27848,15 @@ var require_react_dom_server_node_development = __commonJS({
27848
27848
  request2 = request2.renderLifetimeController;
27849
27849
  null !== request2 && request2.abort(RENDER_ENDED);
27850
27850
  }
27851
- function attachAbortSignal(request2, signal) {
27852
- if (signal.aborted) abort(request2, signal.reason);
27851
+ function attachAbortSignal(request2, signal2) {
27852
+ if (signal2.aborted) abort(request2, signal2.reason);
27853
27853
  else {
27854
27854
  var renderLifetimeController = new AbortController();
27855
27855
  request2.renderLifetimeController = renderLifetimeController;
27856
- signal.addEventListener(
27856
+ signal2.addEventListener(
27857
27857
  "abort",
27858
27858
  function() {
27859
- abort(request2, signal.reason);
27859
+ abort(request2, signal2.reason);
27860
27860
  },
27861
27861
  { signal: renderLifetimeController.signal }
27862
27862
  );
@@ -28038,7 +28038,7 @@ var require_react_dom_server_node_development = __commonJS({
28038
28038
  }
28039
28039
  };
28040
28040
  }
28041
- var util = __require("util"), crypto7 = __require("crypto"), async_hooks = __require("async_hooks"), React4 = require_react(), ReactDOM = require_react_dom(), stream = __require("stream"), REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = /* @__PURE__ */ Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = /* @__PURE__ */ Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo"), REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy"), REACT_SCOPE_TYPE = /* @__PURE__ */ Symbol.for("react.scope"), REACT_ACTIVITY_TYPE = /* @__PURE__ */ Symbol.for("react.activity"), REACT_LEGACY_HIDDEN_TYPE = /* @__PURE__ */ Symbol.for("react.legacy_hidden"), REACT_MEMO_CACHE_SENTINEL = /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel"), REACT_VIEW_TRANSITION_TYPE = /* @__PURE__ */ Symbol.for("react.view_transition"), REACT_RECOVERABLE_TYPE = /* @__PURE__ */ Symbol.for("react.recoverable"), MAYBE_ITERATOR_SYMBOL = Symbol.iterator, ASYNC_ITERATOR = Symbol.asyncIterator, REACT_OPTIMISTIC_KEY = /* @__PURE__ */ Symbol.for("react.optimistic_key"), isArrayImpl = Array.isArray, jsxPropsParents = /* @__PURE__ */ new WeakMap(), jsxChildrenParents = /* @__PURE__ */ new WeakMap(), CLIENT_REFERENCE_TAG = /* @__PURE__ */ Symbol.for("react.client.reference"), hasOwnProperty = Object.prototype.hasOwnProperty, supportsArrayBufferDetached = hasOwnProperty.call(
28041
+ var util = __require("util"), crypto8 = __require("crypto"), async_hooks = __require("async_hooks"), React4 = require_react(), ReactDOM = require_react_dom(), stream = __require("stream"), REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = /* @__PURE__ */ Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = /* @__PURE__ */ Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo"), REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy"), REACT_SCOPE_TYPE = /* @__PURE__ */ Symbol.for("react.scope"), REACT_ACTIVITY_TYPE = /* @__PURE__ */ Symbol.for("react.activity"), REACT_LEGACY_HIDDEN_TYPE = /* @__PURE__ */ Symbol.for("react.legacy_hidden"), REACT_MEMO_CACHE_SENTINEL = /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel"), REACT_VIEW_TRANSITION_TYPE = /* @__PURE__ */ Symbol.for("react.view_transition"), REACT_RECOVERABLE_TYPE = /* @__PURE__ */ Symbol.for("react.recoverable"), MAYBE_ITERATOR_SYMBOL = Symbol.iterator, ASYNC_ITERATOR = Symbol.asyncIterator, REACT_OPTIMISTIC_KEY = /* @__PURE__ */ Symbol.for("react.optimistic_key"), isArrayImpl = Array.isArray, jsxPropsParents = /* @__PURE__ */ new WeakMap(), jsxChildrenParents = /* @__PURE__ */ new WeakMap(), CLIENT_REFERENCE_TAG = /* @__PURE__ */ Symbol.for("react.client.reference"), hasOwnProperty = Object.prototype.hasOwnProperty, supportsArrayBufferDetached = hasOwnProperty.call(
28042
28042
  ArrayBuffer.prototype,
28043
28043
  "detached"
28044
28044
  ), didWarnAboutMissingArrayBufferDetached = false, scheduleMicrotask = queueMicrotask, currentView = null, writtenBytes = 0, destinationHasCapacity$1 = true, textEncoder = new util.TextEncoder(), assign = Object.assign, VALID_ATTRIBUTE_NAME_REGEX = RegExp(
@@ -41256,8 +41256,8 @@ var require_JSXTransformer = __commonJS({
41256
41256
  }
41257
41257
  if (this.isAutomaticRuntime) {
41258
41258
  if (this.importProcessor) {
41259
- for (const [path37, resolvedName] of Object.entries(this.cjsAutomaticModuleNameResolutions)) {
41260
- prefix += `var ${resolvedName} = require("${path37}");`;
41259
+ for (const [path40, resolvedName] of Object.entries(this.cjsAutomaticModuleNameResolutions)) {
41260
+ prefix += `var ${resolvedName} = require("${path40}");`;
41261
41261
  }
41262
41262
  } else {
41263
41263
  const { createElement: createElementResolution, ...otherResolutions } = this.esmAutomaticImportNameResolutions;
@@ -41450,11 +41450,11 @@ var require_JSXTransformer = __commonJS({
41450
41450
  }
41451
41451
  claimAutoImportedName(funcName, importPathSuffix) {
41452
41452
  if (this.importProcessor) {
41453
- const path37 = this.jsxImportSource + importPathSuffix;
41454
- if (!this.cjsAutomaticModuleNameResolutions[path37]) {
41455
- this.cjsAutomaticModuleNameResolutions[path37] = this.importProcessor.getFreeIdentifierForPath(path37);
41453
+ const path40 = this.jsxImportSource + importPathSuffix;
41454
+ if (!this.cjsAutomaticModuleNameResolutions[path40]) {
41455
+ this.cjsAutomaticModuleNameResolutions[path40] = this.importProcessor.getFreeIdentifierForPath(path40);
41456
41456
  }
41457
- return `${this.cjsAutomaticModuleNameResolutions[path37]}.${funcName}`;
41457
+ return `${this.cjsAutomaticModuleNameResolutions[path40]}.${funcName}`;
41458
41458
  } else {
41459
41459
  if (!this.esmAutomaticImportNameResolutions[funcName]) {
41460
41460
  this.esmAutomaticImportNameResolutions[funcName] = this.nameManager.claimFreeName(
@@ -41890,7 +41890,7 @@ var require_CJSImportProcessor = __commonJS({
41890
41890
  */
41891
41891
  pruneTypeOnlyImports() {
41892
41892
  this.nonTypeIdentifiers = _getNonTypeIdentifiers.getNonTypeIdentifiers.call(void 0, this.tokens, this.options);
41893
- for (const [path37, importInfo] of this.importInfoByPath.entries()) {
41893
+ for (const [path40, importInfo] of this.importInfoByPath.entries()) {
41894
41894
  if (importInfo.hasBareImport || importInfo.hasStarExport || importInfo.exportStarNames.length > 0 || importInfo.namedExports.length > 0) {
41895
41895
  continue;
41896
41896
  }
@@ -41900,7 +41900,7 @@ var require_CJSImportProcessor = __commonJS({
41900
41900
  ...importInfo.namedImports.map(({ localName }) => localName)
41901
41901
  ];
41902
41902
  if (names2.every((name) => this.shouldAutomaticallyElideImportedName(name))) {
41903
- this.importsToReplace.set(path37, "");
41903
+ this.importsToReplace.set(path40, "");
41904
41904
  }
41905
41905
  }
41906
41906
  }
@@ -41908,7 +41908,7 @@ var require_CJSImportProcessor = __commonJS({
41908
41908
  return this.isTypeScriptTransformEnabled && !this.keepUnusedImports && !this.nonTypeIdentifiers.has(name);
41909
41909
  }
41910
41910
  generateImportReplacements() {
41911
- for (const [path37, importInfo] of this.importInfoByPath.entries()) {
41911
+ for (const [path40, importInfo] of this.importInfoByPath.entries()) {
41912
41912
  const {
41913
41913
  defaultNames,
41914
41914
  wildcardNames,
@@ -41918,17 +41918,17 @@ var require_CJSImportProcessor = __commonJS({
41918
41918
  hasStarExport
41919
41919
  } = importInfo;
41920
41920
  if (defaultNames.length === 0 && wildcardNames.length === 0 && namedImports.length === 0 && namedExports.length === 0 && exportStarNames.length === 0 && !hasStarExport) {
41921
- this.importsToReplace.set(path37, `require('${path37}');`);
41921
+ this.importsToReplace.set(path40, `require('${path40}');`);
41922
41922
  continue;
41923
41923
  }
41924
- const primaryImportName = this.getFreeIdentifierForPath(path37);
41924
+ const primaryImportName = this.getFreeIdentifierForPath(path40);
41925
41925
  let secondaryImportName;
41926
41926
  if (this.enableLegacyTypeScriptModuleInterop) {
41927
41927
  secondaryImportName = primaryImportName;
41928
41928
  } else {
41929
- secondaryImportName = wildcardNames.length > 0 ? wildcardNames[0] : this.getFreeIdentifierForPath(path37);
41929
+ secondaryImportName = wildcardNames.length > 0 ? wildcardNames[0] : this.getFreeIdentifierForPath(path40);
41930
41930
  }
41931
- let requireCode = `var ${primaryImportName} = require('${path37}');`;
41931
+ let requireCode = `var ${primaryImportName} = require('${path40}');`;
41932
41932
  if (wildcardNames.length > 0) {
41933
41933
  for (const wildcardName of wildcardNames) {
41934
41934
  const moduleExpr = this.enableLegacyTypeScriptModuleInterop ? primaryImportName : `${this.helperManager.getHelperName("interopRequireWildcard")}(${primaryImportName})`;
@@ -41956,7 +41956,7 @@ var require_CJSImportProcessor = __commonJS({
41956
41956
  "createStarExport"
41957
41957
  )}(${primaryImportName});`;
41958
41958
  }
41959
- this.importsToReplace.set(path37, requireCode);
41959
+ this.importsToReplace.set(path40, requireCode);
41960
41960
  for (const defaultName of defaultNames) {
41961
41961
  this.identifierReplacements.set(defaultName, `${secondaryImportName}.default`);
41962
41962
  }
@@ -41965,8 +41965,8 @@ var require_CJSImportProcessor = __commonJS({
41965
41965
  }
41966
41966
  }
41967
41967
  }
41968
- getFreeIdentifierForPath(path37) {
41969
- const components = path37.split("/");
41968
+ getFreeIdentifierForPath(path40) {
41969
+ const components = path40.split("/");
41970
41970
  const lastComponent = components[components.length - 1];
41971
41971
  const baseName = lastComponent.replace(/\W/g, "");
41972
41972
  return this.nameManager.claimFreeName(`_${baseName}`);
@@ -42011,8 +42011,8 @@ var require_CJSImportProcessor = __commonJS({
42011
42011
  if (!this.tokens.matches1AtIndex(index, _types.TokenType.string)) {
42012
42012
  throw new Error("Expected string token at the end of import statement.");
42013
42013
  }
42014
- const path37 = this.tokens.stringValueAtIndex(index);
42015
- const importInfo = this.getImportInfo(path37);
42014
+ const path40 = this.tokens.stringValueAtIndex(index);
42015
+ const importInfo = this.getImportInfo(path40);
42016
42016
  importInfo.defaultNames.push(...defaultNames);
42017
42017
  importInfo.wildcardNames.push(...wildcardNames);
42018
42018
  importInfo.namedImports.push(...namedImports);
@@ -42079,8 +42079,8 @@ var require_CJSImportProcessor = __commonJS({
42079
42079
  if (!this.tokens.matches1AtIndex(index, _types.TokenType.string)) {
42080
42080
  throw new Error("Expected string token at the end of import statement.");
42081
42081
  }
42082
- const path37 = this.tokens.stringValueAtIndex(index);
42083
- const importInfo = this.getImportInfo(path37);
42082
+ const path40 = this.tokens.stringValueAtIndex(index);
42083
+ const importInfo = this.getImportInfo(path40);
42084
42084
  importInfo.namedExports.push(...namedImports);
42085
42085
  }
42086
42086
  preprocessExportStarAtIndex(index) {
@@ -42095,8 +42095,8 @@ var require_CJSImportProcessor = __commonJS({
42095
42095
  if (!this.tokens.matches1AtIndex(index, _types.TokenType.string)) {
42096
42096
  throw new Error("Expected string token at the end of star export statement.");
42097
42097
  }
42098
- const path37 = this.tokens.stringValueAtIndex(index);
42099
- const importInfo = this.getImportInfo(path37);
42098
+ const path40 = this.tokens.stringValueAtIndex(index);
42099
+ const importInfo = this.getImportInfo(path40);
42100
42100
  if (exportedName !== null) {
42101
42101
  importInfo.exportStarNames.push(exportedName);
42102
42102
  } else {
@@ -42136,8 +42136,8 @@ var require_CJSImportProcessor = __commonJS({
42136
42136
  * Get a mutable import info object for this path, creating one if it doesn't
42137
42137
  * exist yet.
42138
42138
  */
42139
- getImportInfo(path37) {
42140
- const existingInfo = this.importInfoByPath.get(path37);
42139
+ getImportInfo(path40) {
42140
+ const existingInfo = this.importInfoByPath.get(path40);
42141
42141
  if (existingInfo) {
42142
42142
  return existingInfo;
42143
42143
  }
@@ -42150,7 +42150,7 @@ var require_CJSImportProcessor = __commonJS({
42150
42150
  exportStarNames: [],
42151
42151
  hasStarExport: false
42152
42152
  };
42153
- this.importInfoByPath.set(path37, newInfo);
42153
+ this.importInfoByPath.set(path40, newInfo);
42154
42154
  return newInfo;
42155
42155
  }
42156
42156
  addExportBinding(localName, exportedName) {
@@ -42730,16 +42730,16 @@ var require_resolve_uri_umd = __commonJS({
42730
42730
  }
42731
42731
  function parseFileUrl(input) {
42732
42732
  const match = fileRegex.exec(input);
42733
- const path37 = match[2];
42734
- return makeUrl("file:", "", match[1] || "", "", isAbsolutePath(path37) ? path37 : "/" + path37, match[3] || "", match[4] || "");
42733
+ const path40 = match[2];
42734
+ return makeUrl("file:", "", match[1] || "", "", isAbsolutePath(path40) ? path40 : "/" + path40, match[3] || "", match[4] || "");
42735
42735
  }
42736
- function makeUrl(scheme, user, host, port, path37, query, hash) {
42736
+ function makeUrl(scheme, user, host, port, path40, query, hash) {
42737
42737
  return {
42738
42738
  scheme,
42739
42739
  user,
42740
42740
  host,
42741
42741
  port,
42742
- path: path37,
42742
+ path: path40,
42743
42743
  query,
42744
42744
  hash,
42745
42745
  type: 7
@@ -42769,11 +42769,11 @@ var require_resolve_uri_umd = __commonJS({
42769
42769
  url.type = input ? input.startsWith("?") ? 3 : input.startsWith("#") ? 2 : 4 : 1;
42770
42770
  return url;
42771
42771
  }
42772
- function stripPathFilename(path37) {
42773
- if (path37.endsWith("/.."))
42774
- return path37;
42775
- const index = path37.lastIndexOf("/");
42776
- return path37.slice(0, index + 1);
42772
+ function stripPathFilename(path40) {
42773
+ if (path40.endsWith("/.."))
42774
+ return path40;
42775
+ const index = path40.lastIndexOf("/");
42776
+ return path40.slice(0, index + 1);
42777
42777
  }
42778
42778
  function mergePaths(url, base) {
42779
42779
  normalizePath(base, base.type);
@@ -42811,14 +42811,14 @@ var require_resolve_uri_umd = __commonJS({
42811
42811
  pieces[pointer++] = piece;
42812
42812
  positive++;
42813
42813
  }
42814
- let path37 = "";
42814
+ let path40 = "";
42815
42815
  for (let i = 1; i < pointer; i++) {
42816
- path37 += "/" + pieces[i];
42816
+ path40 += "/" + pieces[i];
42817
42817
  }
42818
- if (!path37 || addTrailingSlash && !path37.endsWith("/..")) {
42819
- path37 += "/";
42818
+ if (!path40 || addTrailingSlash && !path40.endsWith("/..")) {
42819
+ path40 += "/";
42820
42820
  }
42821
- url.path = path37;
42821
+ url.path = path40;
42822
42822
  }
42823
42823
  function resolve2(input, base) {
42824
42824
  if (!input && !base)
@@ -42859,13 +42859,13 @@ var require_resolve_uri_umd = __commonJS({
42859
42859
  case 3:
42860
42860
  return queryHash;
42861
42861
  case 4: {
42862
- const path37 = url.path.slice(1);
42863
- if (!path37)
42862
+ const path40 = url.path.slice(1);
42863
+ if (!path40)
42864
42864
  return queryHash || ".";
42865
- if (isRelative(base || input) && !isRelative(path37)) {
42866
- return "./" + path37 + queryHash;
42865
+ if (isRelative(base || input) && !isRelative(path40)) {
42866
+ return "./" + path40 + queryHash;
42867
42867
  }
42868
- return path37 + queryHash;
42868
+ return path40 + queryHash;
42869
42869
  }
42870
42870
  case 5:
42871
42871
  return url.path + queryHash;
@@ -42964,10 +42964,10 @@ var require_trace_mapping_umd = __commonJS({
42964
42964
  module2.exports = __toCommonJS(trace_mapping_exports);
42965
42965
  var import_sourcemap_codec = __toESM2(require_sourcemap_codec());
42966
42966
  var import_resolve_uri = __toESM2(require_resolve_uri());
42967
- function stripFilename(path37) {
42968
- if (!path37) return "";
42969
- const index = path37.lastIndexOf("/");
42970
- return path37.slice(0, index + 1);
42967
+ function stripFilename(path40) {
42968
+ if (!path40) return "";
42969
+ const index = path40.lastIndexOf("/");
42970
+ return path40.slice(0, index + 1);
42971
42971
  }
42972
42972
  function resolver(mapUrl, sourceRoot) {
42973
42973
  const from = stripFilename(mapUrl);
@@ -44175,9 +44175,9 @@ var require_util2 = __commonJS({
44175
44175
  /** @class */
44176
44176
  (function(_super) {
44177
44177
  __extends(VError2, _super);
44178
- function VError2(path37, message) {
44178
+ function VError2(path40, message) {
44179
44179
  var _this = _super.call(this, message) || this;
44180
- _this.path = path37;
44180
+ _this.path = path40;
44181
44181
  Object.setPrototypeOf(_this, VError2.prototype);
44182
44182
  return _this;
44183
44183
  }
@@ -44237,26 +44237,26 @@ var require_util2 = __commonJS({
44237
44237
  (_b = this._messages).push.apply(_b, best._messages);
44238
44238
  }
44239
44239
  };
44240
- DetailContext2.prototype.getError = function(path37) {
44240
+ DetailContext2.prototype.getError = function(path40) {
44241
44241
  var msgParts = [];
44242
44242
  for (var i = this._propNames.length - 1; i >= 0; i--) {
44243
44243
  var p2 = this._propNames[i];
44244
- path37 += typeof p2 === "number" ? "[" + p2 + "]" : p2 ? "." + p2 : "";
44244
+ path40 += typeof p2 === "number" ? "[" + p2 + "]" : p2 ? "." + p2 : "";
44245
44245
  var m = this._messages[i];
44246
44246
  if (m) {
44247
- msgParts.push(path37 + " " + m);
44247
+ msgParts.push(path40 + " " + m);
44248
44248
  }
44249
44249
  }
44250
- return new VError(path37, msgParts.join("; "));
44250
+ return new VError(path40, msgParts.join("; "));
44251
44251
  };
44252
- DetailContext2.prototype.getErrorDetail = function(path37) {
44252
+ DetailContext2.prototype.getErrorDetail = function(path40) {
44253
44253
  var details = [];
44254
44254
  for (var i = this._propNames.length - 1; i >= 0; i--) {
44255
44255
  var p2 = this._propNames[i];
44256
- path37 += typeof p2 === "number" ? "[" + p2 + "]" : p2 ? "." + p2 : "";
44256
+ path40 += typeof p2 === "number" ? "[" + p2 + "]" : p2 ? "." + p2 : "";
44257
44257
  var message = this._messages[i];
44258
44258
  if (message) {
44259
- details.push({ path: path37, message });
44259
+ details.push({ path: path40, message });
44260
44260
  }
44261
44261
  }
44262
44262
  var detail = null;
@@ -45069,8 +45069,8 @@ var require_dist = __commonJS({
45069
45069
  this.checkerPlain = this.ttype.getChecker(suite, false);
45070
45070
  this.checkerStrict = this.ttype.getChecker(suite, true);
45071
45071
  }
45072
- Checker2.prototype.setReportedPath = function(path37) {
45073
- this._path = path37;
45072
+ Checker2.prototype.setReportedPath = function(path40) {
45073
+ this._path = path40;
45074
45074
  };
45075
45075
  Checker2.prototype.check = function(value) {
45076
45076
  return this._doCheck(this.checkerPlain, value);
@@ -50503,9 +50503,9 @@ var require_CJSImportTransformer = __commonJS({
50503
50503
  if (shouldElideImport) {
50504
50504
  this.tokens.removeToken();
50505
50505
  } else {
50506
- const path37 = this.tokens.stringValue();
50507
- this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path37));
50508
- this.tokens.appendCode(this.importProcessor.claimImportCode(path37));
50506
+ const path40 = this.tokens.stringValue();
50507
+ this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path40));
50508
+ this.tokens.appendCode(this.importProcessor.claimImportCode(path40));
50509
50509
  }
50510
50510
  _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
50511
50511
  if (this.tokens.matches1(_types.TokenType.semi)) {
@@ -51085,8 +51085,8 @@ var require_CJSImportTransformer = __commonJS({
51085
51085
  }
51086
51086
  if (this.tokens.matchesContextual(_keywords.ContextualKeyword._from)) {
51087
51087
  this.tokens.removeToken();
51088
- const path37 = this.tokens.stringValue();
51089
- this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path37));
51088
+ const path40 = this.tokens.stringValue();
51089
+ this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path40));
51090
51090
  _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
51091
51091
  } else {
51092
51092
  this.tokens.appendCode(exportStatements.join(" "));
@@ -51100,8 +51100,8 @@ var require_CJSImportTransformer = __commonJS({
51100
51100
  while (!this.tokens.matches1(_types.TokenType.string)) {
51101
51101
  this.tokens.removeToken();
51102
51102
  }
51103
- const path37 = this.tokens.stringValue();
51104
- this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path37));
51103
+ const path40 = this.tokens.stringValue();
51104
+ this.tokens.replaceTokenTrimmingLeftWhitespace(this.importProcessor.claimImportCode(path40));
51105
51105
  _removeMaybeImportAttributes.removeMaybeImportAttributes.call(void 0, this.tokens);
51106
51106
  if (this.tokens.matches1(_types.TokenType.semi)) {
51107
51107
  this.tokens.removeToken();
@@ -80745,9 +80745,9 @@ var init_bundle_mjs = __esm({
80745
80745
  processClassesRecursively(value, getPart(classPartObject, key), classGroupId, theme);
80746
80746
  }
80747
80747
  };
80748
- getPart = (classPartObject, path37) => {
80748
+ getPart = (classPartObject, path40) => {
80749
80749
  let current = classPartObject;
80750
- const parts = path37.split(CLASS_PART_SEPARATOR);
80750
+ const parts = path40.split(CLASS_PART_SEPARATOR);
80751
80751
  const len = parts.length;
80752
80752
  for (let i = 0; i < len; i++) {
80753
80753
  const part = parts[i];
@@ -80772,7 +80772,7 @@ var init_bundle_mjs = __esm({
80772
80772
  let cacheSize = 0;
80773
80773
  let cache = /* @__PURE__ */ Object.create(null);
80774
80774
  let previousCache = /* @__PURE__ */ Object.create(null);
80775
- const update2 = (key, value) => {
80775
+ const update3 = (key, value) => {
80776
80776
  cache[key] = value;
80777
80777
  cacheSize++;
80778
80778
  if (cacheSize > maxCacheSize) {
@@ -80788,7 +80788,7 @@ var init_bundle_mjs = __esm({
80788
80788
  return value;
80789
80789
  }
80790
80790
  if ((value = previousCache[key]) !== void 0) {
80791
- update2(key, value);
80791
+ update3(key, value);
80792
80792
  return value;
80793
80793
  }
80794
80794
  },
@@ -80796,7 +80796,7 @@ var init_bundle_mjs = __esm({
80796
80796
  if (key in cache) {
80797
80797
  cache[key] = value;
80798
80798
  } else {
80799
- update2(key, value);
80799
+ update3(key, value);
80800
80800
  }
80801
80801
  }
80802
80802
  };
@@ -84001,8 +84001,8 @@ var mockup_exports = {};
84001
84001
  __export(mockup_exports, {
84002
84002
  drawMockup: () => drawMockup
84003
84003
  });
84004
- import fs20 from "node:fs";
84005
- import path23 from "node:path";
84004
+ import fs21 from "node:fs";
84005
+ import path25 from "node:path";
84006
84006
  import vm from "node:vm";
84007
84007
  async function loadPackage(id) {
84008
84008
  if (id === "react") return import_react3.default;
@@ -84015,20 +84015,20 @@ async function loadPackage(id) {
84015
84015
  return { __esModule: true, ...await load() };
84016
84016
  }
84017
84017
  function resolveFile(id, from, folder) {
84018
- const at2 = path23.resolve(path23.dirname(from), id);
84019
- if (at2 !== folder && !at2.startsWith(folder + path23.sep)) throw new Error(`${id} is outside the mockup's folder`);
84020
- const tries = path23.extname(at2) ? [at2] : [...CODE_EXTS.map((e2) => at2 + e2), ...CODE_EXTS.map((e2) => path23.join(at2, `index${e2}`))];
84021
- const file2 = tries.find((f2) => fs20.statSync(f2, { throwIfNoEntry: false })?.isFile());
84018
+ const at2 = path25.resolve(path25.dirname(from), id);
84019
+ if (at2 !== folder && !at2.startsWith(folder + path25.sep)) throw new Error(`${id} is outside the mockup's folder`);
84020
+ const tries = path25.extname(at2) ? [at2] : [...CODE_EXTS.map((e2) => at2 + e2), ...CODE_EXTS.map((e2) => path25.join(at2, `index${e2}`))];
84021
+ const file2 = tries.find((f2) => fs21.statSync(f2, { throwIfNoEntry: false })?.isFile());
84022
84022
  if (!file2) throw new Error(`no such file: ${id}`);
84023
84023
  return file2;
84024
84024
  }
84025
84025
  async function readGraph(entry) {
84026
- const folder = path23.dirname(entry);
84026
+ const folder = path25.dirname(entry);
84027
84027
  const graph = { code: /* @__PURE__ */ new Map(), links: /* @__PURE__ */ new Map(), sheets: [], packages: /* @__PURE__ */ new Map() };
84028
84028
  const walk = async (file2) => {
84029
84029
  if (graph.code.has(file2)) return;
84030
84030
  if (graph.code.size >= MAX_FILES) throw new Error(`a mockup imports more than ${MAX_FILES} files`);
84031
- const js = (0, import_sucrase.transform)(fs20.readFileSync(file2, "utf8"), { transforms: ["typescript", "jsx", "imports"], jsxRuntime: "automatic", production: true, filePath: file2 }).code;
84031
+ const js = (0, import_sucrase.transform)(fs21.readFileSync(file2, "utf8"), { transforms: ["typescript", "jsx", "imports"], jsxRuntime: "automatic", production: true, filePath: file2 }).code;
84032
84032
  graph.code.set(file2, js);
84033
84033
  const link = /* @__PURE__ */ new Map();
84034
84034
  graph.links.set(file2, link);
@@ -84040,7 +84040,7 @@ async function readGraph(entry) {
84040
84040
  const target = resolveFile(id, file2, folder);
84041
84041
  link.set(id, target);
84042
84042
  if (!target.endsWith(".css")) await walk(target);
84043
- else if (!graph.sheets.some((s) => s.path === target)) graph.sheets.push({ path: target, content: fs20.readFileSync(target, "utf8") });
84043
+ else if (!graph.sheets.some((s) => s.path === target)) graph.sheets.push({ path: target, content: fs21.readFileSync(target, "utf8") });
84044
84044
  }
84045
84045
  };
84046
84046
  await walk(entry);
@@ -84054,25 +84054,25 @@ function run(script, context, name, deadline) {
84054
84054
  async function tailwindFor(markup, sheets) {
84055
84055
  const source = sheets.length ? sheets.map((s) => s.content).join("\n") : '@import "tailwindcss";';
84056
84056
  const compiler = await zf(source, {
84057
- base: sheets.length ? path23.dirname(sheets[0].path) : "/",
84057
+ base: sheets.length ? path25.dirname(sheets[0].path) : "/",
84058
84058
  loadStylesheet: async (id, from) => {
84059
84059
  if (id === "tailwindcss" || id.startsWith("tailwindcss/")) return { path: "tailwindcss/index.css", base: "/", content: tailwindcss_default };
84060
84060
  if (!id.startsWith(".")) throw new Error(`a mockup cannot import ${id}`);
84061
- const at2 = path23.resolve(from, id);
84062
- return { path: at2, base: path23.dirname(at2), content: fs20.readFileSync(at2, "utf8") };
84061
+ const at2 = path25.resolve(from, id);
84062
+ return { path: at2, base: path25.dirname(at2), content: fs21.readFileSync(at2, "utf8") };
84063
84063
  }
84064
84064
  });
84065
84065
  const classes = [...markup.matchAll(/class="([^"]*)"/g)].flatMap((m) => m[1].split(/\s+/));
84066
84066
  return compiler.build(classes.filter(Boolean));
84067
84067
  }
84068
84068
  function dressPage(file2) {
84069
- const folder = path23.dirname(file2);
84070
- const html = fs20.readFileSync(file2, "utf8").replace(/<link\b[^>]*>/gi, (tag) => {
84069
+ const folder = path25.dirname(file2);
84070
+ const html = fs21.readFileSync(file2, "utf8").replace(/<link\b[^>]*>/gi, (tag) => {
84071
84071
  const href = /\bhref\s*=\s*["']?([^"'\s>]+)/i.exec(tag)?.[1];
84072
84072
  if (!/\brel\s*=\s*["']?stylesheet/i.test(tag) || !href || /^[a-z][\w+.-]*:|^\/\//i.test(href)) return tag;
84073
- const at3 = path23.resolve(folder, href);
84074
- if (!at3.startsWith(folder + path23.sep) || !fs20.existsSync(at3)) return tag;
84075
- return `<style>${fs20.readFileSync(at3, "utf8").replace(/<\/style/gi, "<\\/style")}</style>`;
84073
+ const at3 = path25.resolve(folder, href);
84074
+ if (!at3.startsWith(folder + path25.sep) || !fs21.existsSync(at3)) return tag;
84075
+ return `<style>${fs21.readFileSync(at3, "utf8").replace(/<\/style/gi, "<\\/style")}</style>`;
84076
84076
  });
84077
84077
  const at2 = /^\uFEFF?\s*(?:<!--[\s\S]*?-->\s*)*(?:<!doctype\b[^>]*>)?/i.exec(html)[0].length;
84078
84078
  return /<!doctype\b|<html[\s>]|<head[\s>]/i.test(html) ? html.slice(0, at2) + head + html.slice(at2) : `<!doctype html><html><head>${head}</head><body>${html}</body></html>`;
@@ -84272,6 +84272,9 @@ async function refresh() {
84272
84272
  }
84273
84273
  const fresh = sessionFrom(await response.json(), now);
84274
84274
  if (!fresh) return { ok: false, reason: "expired" };
84275
+ const current = readSession();
84276
+ if (!current || current.subject !== now.subject) return { ok: false, reason: "expired" };
84277
+ if (current.refreshToken !== now.refreshToken) return { ok: true, token: current.accessToken, session: current };
84275
84278
  writeSession(fresh);
84276
84279
  return { ok: true, token: fresh.accessToken, session: fresh };
84277
84280
  } finally {
@@ -84435,8 +84438,8 @@ function status(line) {
84435
84438
  }
84436
84439
 
84437
84440
  // src/protocol.ts
84438
- var VERSION = true ? "0.6.17" : "0.0.0";
84439
- var AKB_VERSION = true ? "0.11.14" : "0.0.0";
84441
+ var VERSION = true ? "0.6.19" : "0.0.0";
84442
+ var AKB_VERSION = true ? "0.11.17" : "0.0.0";
84440
84443
  var AKB_CONTRACT = true ? 5 : 0;
84441
84444
  var EXIT_REFUSED = 10;
84442
84445
  var EXIT_OUTDATED = 11;
@@ -84447,7 +84450,40 @@ import { spawn as spawn2 } from "node:child_process";
84447
84450
  import { randomUUID } from "node:crypto";
84448
84451
  import fs11 from "node:fs";
84449
84452
  import os4 from "node:os";
84450
- import path14 from "node:path";
84453
+ import path15 from "node:path";
84454
+
84455
+ // ../cli/src/lib/agent/language.ts
84456
+ var LANGUAGE_ENV = "AI4KANBAN_LANGUAGE";
84457
+ var LANGUAGE_LINE_ENV = "AI4KANBAN_LANGUAGE_LINE";
84458
+ var SYSTEM_LANGUAGE_ZH = "\u7528\u4E2D\u6587\u56DE\u590D\u3002\u6210\u679C\u8BED\u8A00\u4F9D\u6B21\u9075\u5FAA\u7528\u6237\u660E\u786E\u8981\u6C42\u3001\u6210\u679C\u4E13\u7528\u6307\u4EE4\u3001\u65E2\u6709\u6210\u679C\u6216\u9879\u76EE\u60EF\u4F8B\uFF0C\u6700\u540E\u91C7\u7528\u672C\u6B21\u8BF7\u6C42\u7684\u8BED\u8A00\u3002\u4EE3\u7801\u3001\u547D\u4EE4\u3001\u6587\u4EF6\u540D\u548C\u5B57\u6BB5\u540D\u4FDD\u6301\u539F\u6587\u3002";
84459
+ var SYSTEM_LANGUAGE_EN = "Reply in English. Artifact language follows explicit user requests, artifact instructions, existing or project conventions, then the current request's language. Keep code, commands, filenames, and field names unchanged.";
84460
+ var systemLanguages = /* @__PURE__ */ new Set([
84461
+ SYSTEM_LANGUAGE_ZH,
84462
+ SYSTEM_LANGUAGE_EN,
84463
+ "\u65E0\u8BBA\u5BF9\u8BDD\u91CC\u7684\u5361\u7247\u3001\u4EE3\u7801\u548C\u8D34\u8FDB\u6765\u7684\u5185\u5BB9\u662F\u4EC0\u4E48\u8BED\u8A00\uFF0C\u56DE\u590D\u548C\u5199\u4E0B\u7684\u5185\u5BB9\u90FD\u7528\u4E2D\u6587\u3002\u4EE3\u7801\u3001\u547D\u4EE4\u3001\u6587\u4EF6\u540D\u548C\u5B57\u6BB5\u540D\u4FDD\u6301\u539F\u6587\u3002",
84464
+ "\u5BF9\u8BDD\u548C\u89E3\u91CA\u4F7F\u7528\u4E2D\u6587\uFF1B\u4EA7\u7269\u4F7F\u7528\u7528\u6237\u660E\u786E\u6307\u5B9A\u7684\u8BED\u8A00\uFF0C\u5426\u5219\u4F7F\u7528\u6240\u5C5E agent \u660E\u786E\u6307\u5B9A\u7684\u8BED\u8A00\uFF0C\u5747\u672A\u6307\u5B9A\u65F6\u4F7F\u7528\u4E2D\u6587\u3002\u4EE3\u7801\u3001\u547D\u4EE4\u3001\u6587\u4EF6\u540D\u548C\u5B57\u6BB5\u540D\u4FDD\u6301\u539F\u6587\u3002",
84465
+ "Use English for conversation and explanations. For artifacts, use the language explicitly specified by the user, otherwise the responsible agent, otherwise English. Keep code, commands, filenames, and field names unchanged."
84466
+ ]);
84467
+ var isSystemLanguage = (text3) => systemLanguages.has(text3);
84468
+ var legacy = /* @__PURE__ */ new Map([
84469
+ ["\u8BF7\u7528\u4E2D\u6587\u56DE\u590D\u548C\u5199\u4F5C\u3002", "zh"],
84470
+ ["Reply and write in English.", "en"]
84471
+ ]);
84472
+ var languageCode = (value) => value === "zh" || value === "en" ? value : null;
84473
+ var legacyLanguage = (line) => typeof line === "string" ? legacy.get(line) ?? null : null;
84474
+ var systemLanguage = (language) => language === "zh" ? SYSTEM_LANGUAGE_ZH : language === "en" ? SYSTEM_LANGUAGE_EN : "";
84475
+ function cloudLanguage(input) {
84476
+ if (input.test) return null;
84477
+ return "language" in input ? languageCode(input.language) : legacyLanguage(input.languageLine);
84478
+ }
84479
+ function cloudPrompt(input) {
84480
+ if ("language" in input || input.test || input.flow || input.review || input.agent || !legacyLanguage(input.languageLine)) return input.prompt;
84481
+ if (input.prompt === input.languageLine) return "";
84482
+ const suffix = `
84483
+
84484
+ ${input.languageLine}`;
84485
+ return input.prompt.endsWith(suffix) ? input.prompt.slice(0, -suffix.length) : input.prompt;
84486
+ }
84451
84487
 
84452
84488
  // ../cli/src/lib/agent/argv.ts
84453
84489
  function splitCommand(command) {
@@ -84483,6 +84519,7 @@ function quoteArg(arg) {
84483
84519
  }
84484
84520
 
84485
84521
  // ../cli/src/lib/agent/harnesses/types.ts
84522
+ import { createHash } from "node:crypto";
84486
84523
  import fs3 from "node:fs";
84487
84524
  import path4 from "node:path";
84488
84525
  var SKILL_SENTENCE = "Use the kanban skill";
@@ -84506,7 +84543,8 @@ var MCP_CONFIG = {
84506
84543
  help: "A JSON file of `mcpServers`, as Claude Code's --mcp-config takes. Empty opens no servers."
84507
84544
  };
84508
84545
  function machineFile(name, text3) {
84509
- const file2 = path4.join(machineHome(), "prompts", name);
84546
+ const suffix = createHash("sha256").update(text3).digest("hex").slice(0, 16);
84547
+ const file2 = path4.join(machineHome(), "prompts", `${path4.parse(name).name}-${suffix}${path4.extname(name)}`);
84510
84548
  try {
84511
84549
  if (fs3.readFileSync(file2, "utf8") === text3) return file2;
84512
84550
  } catch {
@@ -85292,10 +85330,10 @@ function findRollout(threadId) {
85292
85330
  }
85293
85331
  for (const name of names2) {
85294
85332
  if (!name.startsWith("rollout-") || !name.endsWith(tail)) continue;
85295
- const path37 = join(dir, name);
85333
+ const path40 = join(dir, name);
85296
85334
  try {
85297
- const at2 = statSync(path37).mtimeMs;
85298
- if (!newest2 || at2 > newest2.at) newest2 = { path: path37, at: at2 };
85335
+ const at2 = statSync(path40).mtimeMs;
85336
+ if (!newest2 || at2 > newest2.at) newest2 = { path: path40, at: at2 };
85299
85337
  } catch {
85300
85338
  }
85301
85339
  }
@@ -85303,11 +85341,11 @@ function findRollout(threadId) {
85303
85341
  return newest2?.path;
85304
85342
  }
85305
85343
  function readCodexRunFacts(threadId) {
85306
- const path37 = findRollout(threadId);
85307
- if (!path37) return void 0;
85344
+ const path40 = findRollout(threadId);
85345
+ if (!path40) return void 0;
85308
85346
  let raw;
85309
85347
  try {
85310
- raw = readFileSync(path37, "utf8");
85348
+ raw = readFileSync(path40, "utf8");
85311
85349
  } catch {
85312
85350
  return void 0;
85313
85351
  }
@@ -85415,14 +85453,19 @@ function renderCompleted(item) {
85415
85453
  const changes = Array.isArray(item.changes) ? item.changes : [];
85416
85454
  return changes.map((raw) => {
85417
85455
  const change = obj(raw);
85418
- const path37 = str(change.path);
85419
- return path37 ? `\u23FA ${str(change.kind) || "change"}(${path37})
85456
+ const path40 = str(change.path);
85457
+ return path40 ? `\u23FA ${str(change.kind) || "change"}(${path40})
85420
85458
  ` : "";
85421
85459
  }).join("");
85422
85460
  }
85423
85461
  case "error":
85424
85462
  return `[error] ${str(item.message)}
85425
85463
  `;
85464
+ case "mcp_tool_call": {
85465
+ const error = str(obj(item.error).message);
85466
+ return error ? `[MCP ${str(item.server)}.${str(item.tool)}] ${error}
85467
+ ` : "";
85468
+ }
85426
85469
  default:
85427
85470
  return "";
85428
85471
  }
@@ -85538,16 +85581,13 @@ function createCodexStreamRenderer(carried = false) {
85538
85581
  return renderCompleted(item);
85539
85582
  }
85540
85583
  case "turn.completed":
85584
+ failure = void 0;
85541
85585
  total = codexUsage(ev.usage) ?? total;
85542
85586
  return "";
85543
- // The two events that say the RUN did not get there — the turn giving up, and the
85544
- // stream itself failing. Kept as the run's failure (#525), because `codex exec`
85545
- // reports its reason on this stream and nowhere else, and a retry has to read it.
85546
- // An item's own `error` is not one of them: a tool that failed is the agent's
85547
- // problem to work around, and the turn around it may still finish.
85587
+ // 最终失败保留;连接提示只在没有后续成功完成时才作为失败。
85548
85588
  case "turn.failed": {
85549
- const said = str(obj(ev.error).message);
85550
- failure = said || failure;
85589
+ const said = str(obj(ev.error).message) || failure || "Codex \u6267\u884C\u5931\u8D25";
85590
+ failure = said;
85551
85591
  events.error(said);
85552
85592
  return `[error] ${said}
85553
85593
  `;
@@ -85555,8 +85595,9 @@ function createCodexStreamRenderer(carried = false) {
85555
85595
  case "error": {
85556
85596
  const said = str(ev.message);
85557
85597
  failure = said || failure;
85558
- events.error(said);
85559
- return `[error] ${said}
85598
+ events.log(`[Codex] ${said}
85599
+ `);
85600
+ return `[Codex] ${said}
85560
85601
  `;
85561
85602
  }
85562
85603
  default:
@@ -85664,10 +85705,10 @@ function readKimiRunFacts(sessionId) {
85664
85705
  const total = { input: 0, cacheCreation: 0, cacheRead: 0, output: 0 };
85665
85706
  let model;
85666
85707
  let holding = 0;
85667
- for (const { path: path37, main: main2 } of wireFiles(entry.sessionDir)) {
85708
+ for (const { path: path40, main: main2 } of wireFiles(entry.sessionDir)) {
85668
85709
  let raw;
85669
85710
  try {
85670
- raw = readFileSync2(path37, "utf8");
85711
+ raw = readFileSync2(path40, "utf8");
85671
85712
  } catch {
85672
85713
  continue;
85673
85714
  }
@@ -85923,7 +85964,7 @@ function createTail(log) {
85923
85964
  // ../cli/package.json
85924
85965
  var package_default = {
85925
85966
  name: "wildcrane",
85926
- version: "0.11.14",
85967
+ version: "0.11.17",
85927
85968
  akbContract: 5,
85928
85969
  description: "WildCrane's `akb` command \u2014 scaffold the Markdown board under docs/kanban/, put an agent on a card, and carry the flows the agent works by.",
85929
85970
  keywords: [
@@ -85983,13 +86024,13 @@ var CLIENT_CAPABILITIES = {
85983
86024
  fs: { readTextFile: false, writeTextFile: false },
85984
86025
  terminal: false
85985
86026
  };
85986
- function whereHint(update2, title) {
85987
- const locations = Array.isArray(update2.locations) ? update2.locations : [];
85988
- const path37 = str(obj(locations[0]).path);
85989
- return path37 && !title.includes(path37) ? hint(path37) : "";
86027
+ function whereHint(update3, title) {
86028
+ const locations = Array.isArray(update3.locations) ? update3.locations : [];
86029
+ const path40 = str(obj(locations[0]).path);
86030
+ return path40 && !title.includes(path40) ? hint(path40) : "";
85990
86031
  }
85991
- function saidBy(update2) {
85992
- const content = Array.isArray(update2.content) ? update2.content : [];
86032
+ function saidBy(update3) {
86033
+ const content = Array.isArray(update3.content) ? update3.content : [];
85993
86034
  for (const raw of content) {
85994
86035
  const part = obj(raw);
85995
86036
  const text3 = str(obj(part.content).text) || str(part.text);
@@ -86631,9 +86672,12 @@ var CLAUDE_CODE = {
86631
86672
  forkArgs(argv, resumeId, sessionId) {
86632
86673
  return [...claudeStreamArgs(argv), "--resume", resumeId, "--fork-session", "--session-id", sessionId];
86633
86674
  },
86634
- // Set again on every turn, a resumed one too: the system prompt is not part of the session.
86675
+ // 默认快照会忽略续跑传入的新规则;仅显式关闭快照的会话支持更新。
86635
86676
  systemPrompt(text3, argv) {
86636
- return namesFlag(argv, ["--append-system-prompt", "--append-system-prompt-file"]) ? {} : { args: ["--append-system-prompt", text3] };
86677
+ if (namesFlag(argv, ["--append-system-prompt", "--append-system-prompt-file"])) return {};
86678
+ const snapshot = argv.flatMap((arg, i) => arg === "--system-prompt-snapshot" ? [argv[i + 1]] : arg.startsWith("--system-prompt-snapshot=") ? [arg.slice("--system-prompt-snapshot=".length)] : []).at(-1);
86679
+ if (namesFlag(argv, ["--resume", "-r", "--continue", "-c"]) && snapshot !== "off") return {};
86680
+ return { args: ["--append-system-prompt", text3] };
86637
86681
  },
86638
86682
  // What Claude Code takes: who pays for the run, where that run goes, a model id, and how
86639
86683
  // hard that model thinks. The provider comes first because it decides the rest — which
@@ -87015,6 +87059,40 @@ function setBoardRoot(root, named2 = false) {
87015
87059
  }
87016
87060
  setBoardRoot(process.cwd());
87017
87061
 
87062
+ // ../cli/src/lib/agent/proxy.ts
87063
+ import { execFileSync } from "node:child_process";
87064
+ var PROXY_KEYS = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy", "ALL_PROXY", "all_proxy"];
87065
+ function codexProxyEnv(env2 = process.env, platform = process.platform, read2 = () => execFileSync("/usr/sbin/scutil", ["--proxy"], { encoding: "utf8", timeout: 1e3, stdio: ["ignore", "pipe", "ignore"] })) {
87066
+ const out = { ...env2 };
87067
+ if (platform !== "darwin" || PROXY_KEYS.some((key) => env2[key] !== void 0)) return out;
87068
+ let raw;
87069
+ try {
87070
+ raw = read2();
87071
+ } catch {
87072
+ return out;
87073
+ }
87074
+ const value = (key) => new RegExp(`^\\s*${key}\\s*:\\s*(.*?)\\s*$`, "m").exec(raw)?.[1];
87075
+ for (const [name, key, scheme] of [["HTTP", "HTTP_PROXY", "http"], ["HTTPS", "HTTPS_PROXY", "http"], ["SOCKS", "ALL_PROXY", "socks5"]]) {
87076
+ if (value(`${name}Enable`) !== "1") continue;
87077
+ const host = value(`${name}Proxy`);
87078
+ const port = Number(value(`${name}Port`));
87079
+ if (!host || /[\s/@?#]/.test(host) || !Number.isInteger(port) || port < 1 || port > 65535) continue;
87080
+ try {
87081
+ out[key] = new URL(`${scheme}://${host.includes(":") && !host.startsWith("[") ? `[${host}]` : host}:${port}`).href.replace(/\/$/, "");
87082
+ } catch {
87083
+ }
87084
+ }
87085
+ if (PROXY_KEYS.some((key) => out[key]) && env2.NO_PROXY === void 0 && env2.no_proxy === void 0) {
87086
+ const exceptions = /ExceptionsList\s*:\s*<array>\s*\{([^}]+)\}/.exec(raw)?.[1];
87087
+ const bypass = exceptions?.split("\n").flatMap((line) => {
87088
+ const host = /^\s*\d+\s*:\s*(\S+)\s*$/.exec(line)?.[1];
87089
+ return host ? [host.startsWith("*.") ? host.slice(1) : host] : [];
87090
+ });
87091
+ if (bypass?.length) out.NO_PROXY = bypass.join(",");
87092
+ }
87093
+ return out;
87094
+ }
87095
+
87018
87096
  // ../cli/src/lib/agent/worktree.ts
87019
87097
  import { spawnSync as spawnSync2 } from "node:child_process";
87020
87098
  var MAX_OUTPUT = 64 * 1024 * 1024;
@@ -87174,10 +87252,15 @@ var CODEX = {
87174
87252
  forkArgs(argv, resumeId, _sessionId, cwd) {
87175
87253
  return [...codexExtraArgs(argv, cwd), "fork", resumeId];
87176
87254
  },
87177
- // Codex's developer message. `-c` is global, so it may sit before `exec`. It stands in for a
87178
- // `developer_instructions` in the user's config.toml, which is then left out of board runs.
87255
+ // 自设系统指令保持原样;无法追加时由调用方明确提示。
87179
87256
  systemPrompt(text3, argv) {
87180
- return namesFlag(argv, ["developer_instructions"]) ? {} : { args: ["-c", `developer_instructions=${JSON.stringify(text3)}`] };
87257
+ if (namesFlag(argv, ["developer_instructions", "model_instructions_file"])) return {};
87258
+ try {
87259
+ const config = fs7.readFileSync(path10.join(process.env.CODEX_HOME || home2(".codex"), "config.toml"), "utf8");
87260
+ if (/^\s*(?:developer_instructions|model_instructions_file)\s*=/m.test(config)) return {};
87261
+ } catch {
87262
+ }
87263
+ return { args: ["-c", `developer_instructions=${JSON.stringify(text3)}`] };
87181
87264
  },
87182
87265
  // What Codex takes, in the order Claude Code's dialog draws the same four: who pays for
87183
87266
  // the run and where it goes, that endpoint's address, the key, a model, and how hard that
@@ -87305,7 +87388,7 @@ var CODEX = {
87305
87388
  // No `providerEnv` either: the pick already drops and sets OPENAI_API_KEY, and there is
87306
87389
  // no second variable to drop. Codex reads no base URL from the environment — an exported
87307
87390
  // OPENAI_BASE_URL moves nothing — and where a run goes is settled on its command line.
87308
- env: () => ({ ...process.env }),
87391
+ env: () => codexProxyEnv(),
87309
87392
  // A completed turn carries its token counts and nothing else. The model comes from the
87310
87393
  // session's rollout on disk instead, and the price is worked out from the two
87311
87394
  // (agent/wire/codex-stream.ts). A model whose rates the board doesn't know shows no price.
@@ -87498,9 +87581,9 @@ var DSH = {
87498
87581
  // dsh-acp's persona IS its system prompt, so the ask is added to the one in effect: the
87499
87582
  // user's own `DSH_SYSTEM_PROMPT`, else the bridge's default. A `--persona` on the command
87500
87583
  // line outranks both, and is left alone.
87501
- systemPrompt(text3, argv) {
87584
+ systemPrompt(text3, argv, env2) {
87502
87585
  if (namesFlag(argv, ["--persona"])) return {};
87503
- return { env: { DSH_SYSTEM_PROMPT: `${process.env.DSH_SYSTEM_PROMPT || DSH_PERSONA}
87586
+ return { env: { DSH_SYSTEM_PROMPT: `${env2.DSH_SYSTEM_PROMPT || DSH_PERSONA}
87504
87587
 
87505
87588
  ${text3}` } };
87506
87589
  },
@@ -87891,12 +87974,12 @@ var OPENCODE = {
87891
87974
  env: () => ({ ...process.env }),
87892
87975
  // `instructions` files join the system prompt, and OpenCode adds this config's list to the
87893
87976
  // user's own rather than replacing it.
87894
- systemPrompt(text3) {
87977
+ systemPrompt(text3, _argv, env2) {
87895
87978
  const file2 = machineFile("opencode-language.md", `${text3}
87896
87979
  `);
87897
87980
  let own = {};
87898
87981
  try {
87899
- own = obj2(JSON.parse(process.env.OPENCODE_CONFIG_CONTENT ?? "{}"));
87982
+ own = obj2(JSON.parse(env2.OPENCODE_CONFIG_CONTENT ?? "{}"));
87900
87983
  } catch {
87901
87984
  }
87902
87985
  const instructions = [...Array.isArray(own.instructions) ? own.instructions : [], file2];
@@ -88039,26 +88122,18 @@ var HARNESSES = [CLAUDE_CODE, CODEX, CURSOR, OPENCODE, KIMI, DSH, ZCODE, GROK].m
88039
88122
  var DEFAULT_HARNESS = HARNESSES[0];
88040
88123
  checkHarnesses(HARNESSES);
88041
88124
 
88125
+ // ../cli/src/lib/agent/run-settings.ts
88126
+ import path12 from "node:path";
88127
+
88042
88128
  // ../cli/src/lib/agent/env.ts
88043
88129
  var RUN_ENV = "KANBAN_RUN";
88044
88130
  var SCHEDULED_AGENTS_ENV = "AI4KANBAN_SCHEDULED_AGENTS";
88131
+ var STOP_ENV = "KANBAN_STOP_ID";
88045
88132
  var SESSION_VARS = {
88046
88133
  "claude-code": "CLAUDE_CODE_SESSION_ID",
88047
88134
  codex: "CODEX_THREAD_ID"
88048
88135
  };
88049
88136
 
88050
- // ../cli/src/lib/agent/language.ts
88051
- var LANGUAGE_LINE_ENV = "AI4KANBAN_LANGUAGE_LINE";
88052
- var LINES = {
88053
- en: "",
88054
- zh: "\u8BF7\u7528\u4E2D\u6587\u56DE\u590D\u548C\u5199\u4F5C\u3002"
88055
- };
88056
- var SYSTEM = {
88057
- [LINES.zh]: "\u65E0\u8BBA\u5BF9\u8BDD\u91CC\u7684\u5361\u7247\u3001\u4EE3\u7801\u548C\u8D34\u8FDB\u6765\u7684\u5185\u5BB9\u662F\u4EC0\u4E48\u8BED\u8A00\uFF0C\u56DE\u590D\u548C\u5199\u4E0B\u7684\u5185\u5BB9\u90FD\u7528\u4E2D\u6587\u3002\u4EE3\u7801\u3001\u547D\u4EE4\u3001\u6587\u4EF6\u540D\u548C\u5B57\u6BB5\u540D\u4FDD\u6301\u539F\u6587\u3002"
88058
- };
88059
- var asksForTranslation = (line) => line !== "" && !/\bEnglish\b/.test(line);
88060
- var systemLanguage = (line) => asksForTranslation(line) ? SYSTEM[line] ?? line : "";
88061
-
88062
88137
  // ../cli/src/lib/agent/providers.ts
88063
88138
  function providerSetting(settings) {
88064
88139
  return settings.find((s) => s.kind === "provider");
@@ -88150,11 +88225,34 @@ function effectiveValues(harness, held4) {
88150
88225
  }
88151
88226
  return out;
88152
88227
  }
88153
- function withSystemLanguage(harness, argv, env2, line) {
88154
- const text3 = systemLanguage(line);
88155
- const added = text3 && argv.length ? harness.systemPrompt?.(text3, argv) : void 0;
88156
- if (!added) return { argv, env: env2 };
88157
- return { argv: [argv[0], ...added.args ?? [], ...argv.slice(1)], env: { ...env2, ...added.env } };
88228
+ function languageEnv(given) {
88229
+ const env2 = { ...given };
88230
+ if (env2.DSH_SYSTEM_PROMPT) {
88231
+ const parts = env2.DSH_SYSTEM_PROMPT.split("\n\n");
88232
+ while (isSystemLanguage(parts.at(-1) ?? "")) parts.pop();
88233
+ env2.DSH_SYSTEM_PROMPT = parts.join("\n\n");
88234
+ }
88235
+ try {
88236
+ const config = JSON.parse(env2.OPENCODE_CONFIG_CONTENT ?? "{}");
88237
+ if (Array.isArray(config.instructions)) {
88238
+ config.instructions = config.instructions.filter((file2) => typeof file2 !== "string" || path12.dirname(file2) !== path12.join(machineHome(), "prompts") || !/^opencode-language(?:-[a-f0-9]{16})?\.md$/.test(path12.basename(file2)));
88239
+ env2.OPENCODE_CONFIG_CONTENT = JSON.stringify(config);
88240
+ }
88241
+ } catch {
88242
+ }
88243
+ return env2;
88244
+ }
88245
+ function withSystemLanguage(harness, argv, env2, language) {
88246
+ env2 = languageEnv(env2);
88247
+ const text3 = systemLanguage(language);
88248
+ if (!text3) return { argv, env: env2 };
88249
+ const added = argv.length ? harness.systemPrompt?.(text3, argv, env2) : void 0;
88250
+ if (!added?.args?.length && !Object.keys(added?.env ?? {}).length) {
88251
+ const note = language === "zh" ? "\u8BED\u8A00\u89C4\u5219\u672A\u80FD\u66F4\u65B0\uFF1A\u8FDE\u63A5\u5668\u3001\u5DF2\u6709\u4F1A\u8BDD\u6216\u81EA\u8BBE\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E0D\u652F\u6301\u6CE8\u5165\u3002\u8BF7\u81EA\u884C\u53E6\u5F00\u652F\u6301\u7684\u4F1A\u8BDD\u4EE5\u91C7\u7528\u65B0\u89C4\u5219\u3002" : "Language rules could not be updated: the connector, existing session, or custom system prompt prevents injection. Open a supported new session to apply them.";
88252
+ return { argv, env: env2, note };
88253
+ }
88254
+ const at2 = /^node(?:\.exe)?$/.test(path12.basename(argv[0])) && argv[1] && !argv[1].startsWith("-") ? 2 : 1;
88255
+ return { argv: [...argv.slice(0, at2), ...added?.args ?? [], ...argv.slice(at2)], env: { ...env2, ...added?.env } };
88158
88256
  }
88159
88257
 
88160
88258
  // src/api.ts
@@ -88167,6 +88265,7 @@ __export(api_exports, {
88167
88265
  claimJob: () => claimJob,
88168
88266
  commitBoard: () => commitBoard,
88169
88267
  createArtifact: () => createArtifact,
88268
+ createProject: () => createProject,
88170
88269
  createUpload: () => createUpload,
88171
88270
  createVersion: () => createVersion,
88172
88271
  dispatchReview: () => dispatchReview,
@@ -88216,14 +88315,14 @@ __export(api_exports, {
88216
88315
 
88217
88316
  // src/goals.ts
88218
88317
  import fs9 from "node:fs";
88219
- import path12 from "node:path";
88318
+ import path13 from "node:path";
88220
88319
  function goalsOf(held4) {
88221
88320
  if (held4.goals !== void 0 && held4.ideas !== void 0 && JSON.stringify(held4.goals) !== JSON.stringify(held4.ideas)) throw new Error("goals and ideas do not match.");
88222
88321
  const goals = held4.goals ?? held4.ideas;
88223
88322
  if (!Array.isArray(goals)) throw new Error("The goals array is missing or invalid.");
88224
88323
  return goals;
88225
88324
  }
88226
- var goalsFileOf = (job) => path12.join(runnerHome(), "ideas", `${job.id}.json`);
88325
+ var goalsFileOf = (job) => path13.join(runnerHome(), "ideas", `${job.id}.json`);
88227
88326
  async function writeGoals(job, list) {
88228
88327
  let goals;
88229
88328
  try {
@@ -88240,7 +88339,7 @@ async function writeGoals(job, list) {
88240
88339
  after = page.value.next;
88241
88340
  } while (after);
88242
88341
  const file2 = goalsFileOf(job);
88243
- fs9.mkdirSync(path12.dirname(file2), { recursive: true });
88342
+ fs9.mkdirSync(path13.dirname(file2), { recursive: true });
88244
88343
  fs9.writeFileSync(file2, `${JSON.stringify({ workflows, ideas: goals }, null, 2)}
88245
88344
  `);
88246
88345
  return { file: file2 };
@@ -88257,8 +88356,8 @@ function readGoals(file2) {
88257
88356
 
88258
88357
  // src/secrets.ts
88259
88358
  import fs10 from "node:fs";
88260
- import path13 from "node:path";
88261
- var file = () => path13.join(runnerHome(), "secrets.json");
88359
+ import path14 from "node:path";
88360
+ var file = () => path14.join(runnerHome(), "secrets.json");
88262
88361
  function readAll() {
88263
88362
  try {
88264
88363
  const held4 = JSON.parse(fs10.readFileSync(file(), "utf8"));
@@ -88287,10 +88386,10 @@ var secretsSet = () => Object.fromEntries(Object.entries(readAll()).map(([id, ke
88287
88386
 
88288
88387
  // src/api.ts
88289
88388
  var RUNTIMES = ["claude-code", "codex", "cursor", "opencode", "kimi", "dsh", "zcode", "grok"];
88290
- var PROTOCOL = 13;
88389
+ var PROTOCOL = 15;
88291
88390
  var TIMEOUT_MS = 2e4;
88292
- async function send(method, route, body, signedIn, ms, type) {
88293
- const headers = { accept: "application/json" };
88391
+ async function send(method, route, body, signedIn, ms, type, extra = {}) {
88392
+ const headers = { accept: "application/json", ...extra };
88294
88393
  if (signedIn) {
88295
88394
  const token = await accessToken();
88296
88395
  if (!token.ok) {
@@ -88374,7 +88473,11 @@ var listFolders = (machineId2) => request("GET", `/v1/runner/folders?machine=${q
88374
88473
  var listElsewhere = (machineId2) => request("GET", `/v1/runner/folders?machine=${q(machineId2)}&elsewhere=1`);
88375
88474
  var listMachineTasks = (machineId2) => request("GET", `/v1/runner/tasks?machine=${q(machineId2)}`);
88376
88475
  var stopTask = (task, how) => request("POST", how === "chat" ? `/v1/tasks/${q(task)}/chat/stop` : `/v1/tasks/${q(task)}/stop`, {});
88377
- var addFolder = (machineId2, name, path37, project) => request("POST", "/v1/runner/folders", { machineId: machineId2, name, path: path37, ...project ? { project } : {} });
88476
+ var addFolder = (machineId2, name, path40, project) => request("POST", "/v1/runner/folders", { machineId: machineId2, name, path: path40, ...project ? { project } : {} });
88477
+ async function createProject(name, workspace, key) {
88478
+ const sent = await send("POST", "/v1/projects", { name, workspace }, true, TIMEOUT_MS, void 0, { "idempotency-key": key });
88479
+ return sent.ok ? answerOf(sent.value) : sent;
88480
+ }
88378
88481
  var removeFolder = (machineId2, project) => request("POST", `/v1/runner/folders/${q(project)}/remove`, { machineId: machineId2 });
88379
88482
  var readBoard = (project, since) => request("GET", `/v1/projects/${q(project)}/board${since === null ? "" : `?since=${since}`}`);
88380
88483
  async function commitBoard(project, job, machine, files) {
@@ -88448,7 +88551,7 @@ function commandOf(harness) {
88448
88551
  const [binary, ...flags] = splitCommand(harness.command);
88449
88552
  return [spawnName(chooseCopy(harness, SPAWN_MS), binary), ...flags];
88450
88553
  }
88451
- function plan(runtime, prompt, cwd, resume, model, given = {}, fork = false, line = "") {
88554
+ function plan(runtime, prompt, cwd, resume, model, given = {}, fork = false, language = null) {
88452
88555
  const harness = HARNESSES2[runtime];
88453
88556
  const values = held(runtime, model, given);
88454
88557
  const [binary, ...flags] = commandOf(harness);
@@ -88457,21 +88560,22 @@ function plan(runtime, prompt, cwd, resume, model, given = {}, fork = false, lin
88457
88560
  const copied = fork && resume && harness.forkArgs ? harness.forkArgs(base, resume, id, cwd) : null;
88458
88561
  if (fork) resume = null;
88459
88562
  const extra = copied ?? (resume ? harness.resumeArgs(base, resume, cwd) : harness.extraArgs(base, id, cwd));
88460
- const quiet = (line2) => harness.quietStderr?.(line2) ?? false;
88461
- const { argv, env: env2 } = withSystemLanguage(harness, [...base, ...extra], copyEnv(binary, { ...process.env, ...settingEnv(harness, values, given.secrets ?? {}, cwd), WILDCRANE_JOB: "1" }), line);
88563
+ const quiet = (line) => harness.quietStderr?.(line) ?? false;
88564
+ const { argv, env: env2, note } = withSystemLanguage(harness, [...base, ...extra], copyEnv(binary, { ...process.env, ...settingEnv(harness, values, given.secrets ?? {}, cwd), WILDCRANE_JOB: "1", [LANGUAGE_ENV]: language ?? "", [LANGUAGE_LINE_ENV]: void 0 }), language);
88462
88565
  if (harness.client) {
88463
88566
  const client = harness.client(effectiveValues(harness, values));
88464
88567
  let session;
88465
88568
  let named2;
88466
88569
  return {
88570
+ startNote: note,
88467
88571
  command: argv[0],
88468
88572
  args: argv.slice(1),
88469
88573
  env: env2,
88470
- converse: (child, event2) => client.turn({
88574
+ converse: (child, event2, restart = true) => client.turn({
88471
88575
  stdout: child.stdout,
88472
88576
  stdin: child.stdin,
88473
88577
  prompt,
88474
- restartPrompt: prompt,
88578
+ restartPrompt: restart ? prompt : void 0,
88475
88579
  cwd,
88476
88580
  resumeId: resume ?? void 0,
88477
88581
  log: () => {
@@ -88490,6 +88594,7 @@ function plan(runtime, prompt, cwd, resume, model, given = {}, fork = false, lin
88490
88594
  const renderer = harness.renderer(cwd, argv[0], argv);
88491
88595
  const toStdin = harness.stdinPrompt?.(argv);
88492
88596
  return {
88597
+ startNote: note,
88493
88598
  command: argv[0],
88494
88599
  args: [...argv.slice(1), ...toStdin ? [] : [prompt]],
88495
88600
  env: env2,
@@ -88541,14 +88646,14 @@ var listed = (harness) => async () => (harness.models?.() ?? []).map((id) => ({
88541
88646
  var readModels = (name) => (MODELS[name] ?? listed(HARNESSES2[name]))().catch(() => []);
88542
88647
  var MODELS = {
88543
88648
  "claude-code": async () => {
88544
- const cached2 = claudeModels(path14.join(process.env.CLAUDE_CONFIG_DIR || path14.join(os4.homedir(), ".claude"), "cache", "model-catalog"));
88649
+ const cached2 = claudeModels(path15.join(process.env.CLAUDE_CONFIG_DIR || path15.join(os4.homedir(), ".claude"), "cache", "model-catalog"));
88545
88650
  return cached2.length ? cached2 : [
88546
88651
  { id: "opus", name: "Opus" },
88547
88652
  { id: "sonnet", name: "Sonnet" },
88548
88653
  { id: "haiku", name: "Haiku" }
88549
88654
  ];
88550
88655
  },
88551
- codex: async () => codexModels(path14.join(process.env.CODEX_HOME || path14.join(os4.homedir(), ".codex"), "models_cache.json")),
88656
+ codex: async () => codexModels(path15.join(process.env.CODEX_HOME || path15.join(os4.homedir(), ".codex"), "models_cache.json")),
88552
88657
  opencode: async () => opencodeModels(await output(commandOf(HARNESSES2.opencode)[0], ["models"])),
88553
88658
  cursor: () => cursorModels()
88554
88659
  };
@@ -88620,7 +88725,7 @@ function claudeModels(dir) {
88620
88725
  }
88621
88726
  const catalogs = files.flatMap((f2) => {
88622
88727
  try {
88623
- const data = obj(JSON.parse(fs11.readFileSync(path14.join(dir, f2), "utf8")));
88728
+ const data = obj(JSON.parse(fs11.readFileSync(path15.join(dir, f2), "utf8")));
88624
88729
  const catalog = obj(data.catalog);
88625
88730
  const surface = str(catalog.surface);
88626
88731
  return surface === "cc" || surface === "ccd" ? [{ surface, at: Number(data.fetchedAt) || 0, config: obj(catalog.config) }] : [];
@@ -88812,6 +88917,114 @@ function connectCloudLive(handlers) {
88812
88917
  };
88813
88918
  }
88814
88919
 
88920
+ // src/folders.ts
88921
+ import fs12 from "node:fs";
88922
+ import path16 from "node:path";
88923
+ var projectsFile = () => path16.join(runnerHome(), "projects.json");
88924
+ function realpath(p2) {
88925
+ try {
88926
+ return fs12.realpathSync(p2);
88927
+ } catch {
88928
+ return null;
88929
+ }
88930
+ }
88931
+ function readGrants() {
88932
+ try {
88933
+ const held4 = JSON.parse(fs12.readFileSync(projectsFile(), "utf8"));
88934
+ if (!Array.isArray(held4.projects)) throw new Error("invalid_projects");
88935
+ return held4;
88936
+ } catch (error) {
88937
+ if (error.code === "ENOENT") return { projects: [] };
88938
+ throw error;
88939
+ }
88940
+ }
88941
+ function listOf(held4) {
88942
+ const subject = readSession()?.subject;
88943
+ if (!held4.accounts) return held4.projects;
88944
+ return subject ? held4.accounts[subject] ?? [] : [];
88945
+ }
88946
+ function update2(change) {
88947
+ fs12.mkdirSync(runnerHome(), { recursive: true, mode: 448 });
88948
+ const lock = `${projectsFile()}.lock`;
88949
+ let fd;
88950
+ const until = Date.now() + 2e3;
88951
+ for (; ; ) {
88952
+ try {
88953
+ fd = fs12.openSync(lock, "wx", 384);
88954
+ fs12.writeFileSync(fd, String(process.pid));
88955
+ break;
88956
+ } catch {
88957
+ try {
88958
+ const pid = Number(fs12.readFileSync(lock, "utf8"));
88959
+ if (pid > 0) {
88960
+ try {
88961
+ process.kill(pid, 0);
88962
+ } catch {
88963
+ fs12.rmSync(lock, { force: true });
88964
+ }
88965
+ }
88966
+ } catch {
88967
+ }
88968
+ if (Date.now() > until) throw new Error("authorization_busy");
88969
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
88970
+ }
88971
+ }
88972
+ try {
88973
+ const held4 = readGrants();
88974
+ const subject = readSession()?.subject;
88975
+ const before = listOf(held4);
88976
+ const after = [...new Set(change(before))];
88977
+ if (subject) {
88978
+ if (!held4.accounts) {
88979
+ held4.accounts = { [subject]: held4.projects };
88980
+ held4.legacyOwner = subject;
88981
+ }
88982
+ held4.accounts[subject] = after;
88983
+ held4.projects = after;
88984
+ } else held4.projects = after;
88985
+ const tmp = `${projectsFile()}.${process.pid}.tmp`;
88986
+ fs12.writeFileSync(tmp, `${JSON.stringify(held4, null, 2)}
88987
+ `, { mode: 384 });
88988
+ fs12.renameSync(tmp, projectsFile());
88989
+ return before.filter((p2) => !after.includes(p2));
88990
+ } finally {
88991
+ fs12.closeSync(fd);
88992
+ fs12.rmSync(lock, { force: true });
88993
+ }
88994
+ }
88995
+ function adoptLegacy() {
88996
+ if (readSession() && !readGrants().accounts) update2((list) => list);
88997
+ }
88998
+ function allowedProjects() {
88999
+ return listOf(readGrants()).flatMap((p2) => {
89000
+ const real = realpath(p2);
89001
+ return real && fs12.statSync(real).isDirectory() ? [real] : [];
89002
+ });
89003
+ }
89004
+
89005
+ // src/connect.ts
89006
+ import fs36 from "node:fs";
89007
+ import path39 from "node:path";
89008
+
89009
+ // src/settings-control.ts
89010
+ import fs13 from "node:fs";
89011
+ import path17 from "node:path";
89012
+ var directory = () => path17.join(runnerHome(), "executors");
89013
+ var epochFile = () => path17.join(runnerHome(), "account-generation");
89014
+ function accountGeneration() {
89015
+ try {
89016
+ return fs13.readFileSync(epochFile(), "utf8");
89017
+ } catch {
89018
+ return "";
89019
+ }
89020
+ }
89021
+ function executorRecord() {
89022
+ fs13.mkdirSync(directory(), { recursive: true, mode: 448 });
89023
+ const file2 = path17.join(directory(), `${process.pid}.json`);
89024
+ fs13.writeFileSync(file2, "{}", { mode: 384 });
89025
+ return () => fs13.rmSync(file2, { force: true });
89026
+ }
89027
+
88815
89028
  // ../cli/src/lib/cadence.ts
88816
89029
  var AUTO_CADENCE = "auto";
88817
89030
  var isAuto = (cadence) => cadence.trim().toLowerCase() === AUTO_CADENCE;
@@ -88927,48 +89140,48 @@ async function askDueAgents(machineId2, now = Date.now()) {
88927
89140
 
88928
89141
  // src/akb-version.ts
88929
89142
  import { execFile } from "node:child_process";
88930
- import fs16 from "node:fs";
88931
- import path19 from "node:path";
89143
+ import fs17 from "node:fs";
89144
+ import path21 from "node:path";
88932
89145
 
88933
89146
  // src/akb.ts
88934
89147
  import { spawn as spawn3 } from "node:child_process";
88935
- import fs12 from "node:fs";
88936
- import path15 from "node:path";
89148
+ import fs14 from "node:fs";
89149
+ import path18 from "node:path";
88937
89150
  import { fileURLToPath } from "node:url";
88938
- var akbHome = () => path15.join(runnerHome(), "bin", "akb");
89151
+ var akbHome = () => path18.join(runnerHome(), "bin", "akb");
88939
89152
  var BUNDLED_SHIM = `bundled-${AKB_VERSION}`;
88940
89153
  var bundledAkb = () => ({
88941
89154
  command: process.execPath,
88942
- args: [path15.join(path15.dirname(fileURLToPath(import.meta.url)), "akb", "bin", "ai4kanban.mjs")],
89155
+ args: [path18.join(path18.dirname(fileURLToPath(import.meta.url)), "akb", "bin", "ai4kanban.mjs")],
88943
89156
  version: AKB_VERSION,
88944
- shim: path15.join(akbHome(), BUNDLED_SHIM)
89157
+ shim: path18.join(akbHome(), BUNDLED_SHIM)
88945
89158
  });
88946
89159
  var quote = (s) => /^[\w./:@=-]+$/.test(s) ? s : `"${s}"`;
88947
89160
  function shimDir(akb) {
88948
89161
  const dir = akb.shim;
88949
89162
  const line = [akb.command, ...akb.args].map(quote).join(" ");
88950
- const [file2, text3] = process.platform === "win32" ? [path15.join(dir, "akb.cmd"), `@${line} %*\r
88951
- `] : [path15.join(dir, "akb"), `#!/bin/sh
89163
+ const [file2, text3] = process.platform === "win32" ? [path18.join(dir, "akb.cmd"), `@${line} %*\r
89164
+ `] : [path18.join(dir, "akb"), `#!/bin/sh
88952
89165
  exec ${line} "$@"
88953
89166
  `];
88954
89167
  try {
88955
- if (fs12.readFileSync(file2, "utf8") === text3) return dir;
89168
+ if (fs14.readFileSync(file2, "utf8") === text3) return dir;
88956
89169
  } catch {
88957
89170
  }
88958
- fs12.mkdirSync(dir, { recursive: true });
88959
- fs12.writeFileSync(file2, text3, { mode: 493 });
89171
+ fs14.mkdirSync(dir, { recursive: true });
89172
+ fs14.writeFileSync(file2, text3, { mode: 493 });
88960
89173
  return dir;
88961
89174
  }
88962
- function akbEnv(akb, board, task, line) {
89175
+ function akbEnv(akb, board, task, language, base = process.env) {
88963
89176
  const env2 = {};
88964
- const dropped = ["AI4KANBAN_BOARD", "AI4KANBAN_TASK", "AI4KANBAN_EXECUTION", LANGUAGE_LINE_ENV, SCHEDULED_AGENTS_ENV];
88965
- for (const [k, v2] of Object.entries(process.env)) if (!k.startsWith("KANBAN_") && !dropped.includes(k)) env2[k] = v2;
89177
+ const dropped = ["AI4KANBAN_BOARD", "AI4KANBAN_TASK", "AI4KANBAN_EXECUTION", LANGUAGE_ENV, LANGUAGE_LINE_ENV, SCHEDULED_AGENTS_ENV];
89178
+ for (const [k, v2] of Object.entries(base)) if (!k.startsWith("KANBAN_") && !dropped.includes(k)) env2[k] = v2;
88966
89179
  const key = Object.keys(env2).find((k) => k.toUpperCase() === "PATH") ?? "PATH";
88967
- env2[key] = [shimDir(akb), env2[key]].filter(Boolean).join(path15.delimiter);
89180
+ env2[key] = [shimDir(akb), env2[key]].filter(Boolean).join(path18.delimiter);
88968
89181
  env2.AI4KANBAN_COMMAND = "akb";
88969
89182
  if (board) env2.AI4KANBAN_BOARD = board;
88970
89183
  if (task) env2.AI4KANBAN_TASK = task;
88971
- if (line !== void 0) env2[LANGUAGE_LINE_ENV] = line;
89184
+ if (language !== void 0) env2[LANGUAGE_ENV] = language ?? "";
88972
89185
  return env2;
88973
89186
  }
88974
89187
  var KEEP = 256 * 1024;
@@ -88982,7 +89195,7 @@ function runAkb(akb, args, opts) {
88982
89195
  };
88983
89196
  let child;
88984
89197
  try {
88985
- child = spawn3(akb.command, [...akb.args, ...args], { cwd: opts.cwd, env: { ...akbEnv(akb, opts.board, opts.task, opts.line), ...opts.env }, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
89198
+ child = spawn3(akb.command, [...akb.args, ...args], { cwd: opts.cwd, env: { ...akbEnv(akb, opts.board, opts.task, opts.language), ...opts.env }, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
88986
89199
  } catch (e2) {
88987
89200
  return resolve2({ code: null, out: "", last: e2 instanceof Error ? e2.message : String(e2) });
88988
89201
  }
@@ -89013,65 +89226,45 @@ function answerOf2(ran) {
89013
89226
 
89014
89227
  // src/cache.ts
89015
89228
  import { spawnSync as spawnSync3 } from "node:child_process";
89016
- import fs14 from "node:fs";
89017
- import path17 from "node:path";
89018
-
89019
- // src/folders.ts
89020
- import fs13 from "node:fs";
89021
- import path16 from "node:path";
89022
- var projectsFile = () => path16.join(runnerHome(), "projects.json");
89023
- function realpath(p2) {
89024
- try {
89025
- return fs13.realpathSync(p2);
89026
- } catch {
89027
- return null;
89028
- }
89029
- }
89030
- function readList() {
89031
- try {
89032
- const held4 = JSON.parse(fs13.readFileSync(projectsFile(), "utf8"));
89033
- return Array.isArray(held4.projects) ? held4.projects.filter((p2) => typeof p2 === "string") : [];
89034
- } catch {
89035
- return [];
89036
- }
89037
- }
89038
- function allowedProjects() {
89039
- return readList().flatMap((p2) => [realpath(p2)].filter((r2) => !!r2));
89040
- }
89041
-
89042
- // src/cache.ts
89229
+ import crypto2 from "node:crypto";
89230
+ import fs15 from "node:fs";
89231
+ import path19 from "node:path";
89043
89232
  var IDLE_MS = 7 * 24 * 60 * 6e4;
89044
89233
  var SWEEP_MS = 24 * 60 * 6e4;
89045
89234
  var PROJECT_ID = /^[\w-]{1,100}$/;
89046
- var cacheRoot = () => path17.join(runnerHome(), "cache");
89235
+ var cacheRoot = () => path19.join(runnerHome(), "cache");
89047
89236
  function cacheDir(project) {
89048
89237
  if (!PROJECT_ID.test(project)) throw new Error("This job names no project.");
89049
- return path17.join(cacheRoot(), project);
89238
+ return path19.join(cacheRoot(), project);
89050
89239
  }
89051
- var akbDir = (dir) => path17.join(dir, ".akb");
89052
- var usedFile = (dir) => path17.join(akbDir(dir), "used.json");
89053
- var unsentFile = (dir) => path17.join(akbDir(dir), "unsent");
89240
+ var akbDir = (dir) => path19.join(dir, ".akb");
89241
+ var usedFile = (dir) => path19.join(akbDir(dir), "used.json");
89242
+ var unsentFile = (dir) => path19.join(akbDir(dir), "unsent");
89054
89243
  function markUsed(dir, at2 = Date.now()) {
89055
- fs14.mkdirSync(akbDir(dir), { recursive: true });
89056
- fs14.writeFileSync(usedFile(dir), `${JSON.stringify({ at: at2 })}
89244
+ fs15.mkdirSync(akbDir(dir), { recursive: true });
89245
+ fs15.writeFileSync(usedFile(dir), `${JSON.stringify({ at: at2 })}
89057
89246
  `);
89058
89247
  }
89059
89248
  function usedAt(dir) {
89060
89249
  try {
89061
- const at2 = JSON.parse(fs14.readFileSync(usedFile(dir), "utf8")).at;
89250
+ const at2 = JSON.parse(fs15.readFileSync(usedFile(dir), "utf8")).at;
89062
89251
  return typeof at2 === "number" ? at2 : null;
89063
89252
  } catch {
89064
89253
  return null;
89065
89254
  }
89066
89255
  }
89067
- var unsent = (dir) => fs14.existsSync(unsentFile(dir));
89256
+ var unsent = (dir) => fs15.existsSync(unsentFile(dir));
89068
89257
  function markUnsent(dir, unsent2) {
89069
- if (!unsent2) return void fs14.rmSync(unsentFile(dir), { force: true });
89070
- fs14.mkdirSync(akbDir(dir), { recursive: true });
89071
- fs14.writeFileSync(unsentFile(dir), "");
89258
+ if (!unsent2) return void fs15.rmSync(unsentFile(dir), { force: true });
89259
+ fs15.mkdirSync(akbDir(dir), { recursive: true });
89260
+ fs15.writeFileSync(unsentFile(dir), "");
89072
89261
  }
89073
89262
  var holds = /* @__PURE__ */ new Map();
89263
+ var holdsDir = () => path19.join(runnerHome(), "holds");
89264
+ var holdFile = (dir) => path19.join(holdsDir(), `${crypto2.createHash("sha256").update(dir).digest("hex")}.${process.pid}`);
89074
89265
  function hold(dir) {
89266
+ fs15.mkdirSync(holdsDir(), { recursive: true, mode: 448 });
89267
+ fs15.writeFileSync(holdFile(dir), "", { mode: 384 });
89075
89268
  holds.set(dir, (holds.get(dir) ?? 0) + 1);
89076
89269
  let held4 = true;
89077
89270
  return () => {
@@ -89079,30 +89272,49 @@ function hold(dir) {
89079
89272
  held4 = false;
89080
89273
  const left = holds.get(dir) - 1;
89081
89274
  if (left) holds.set(dir, left);
89082
- else holds.delete(dir);
89275
+ else {
89276
+ holds.delete(dir);
89277
+ fs15.rmSync(holdFile(dir), { force: true });
89278
+ }
89083
89279
  };
89084
89280
  }
89085
89281
  function paused(dir) {
89086
89282
  try {
89087
- const file2 = path17.join(projectStateDir(path17.join(akbDir(dir), "kanban")), SESSIONS_FILE);
89088
- const held4 = JSON.parse(fs14.readFileSync(file2, "utf8"));
89283
+ const file2 = path19.join(projectStateDir(path19.join(akbDir(dir), "kanban")), SESSIONS_FILE);
89284
+ const held4 = JSON.parse(fs15.readFileSync(file2, "utf8"));
89089
89285
  return (held4.deliveries ?? []).some((d2) => d2.status === "active");
89090
89286
  } catch {
89091
89287
  return false;
89092
89288
  }
89093
89289
  }
89094
- var busy = (dir) => holds.has(dir);
89290
+ function busy(dir) {
89291
+ if (holds.has(dir)) return true;
89292
+ const prefix = path19.basename(holdFile(dir)).split(".")[0] + ".";
89293
+ try {
89294
+ return fs15.readdirSync(holdsDir()).filter((file2) => file2.startsWith(prefix)).some((file2) => {
89295
+ try {
89296
+ process.kill(Number(file2.split(".")[1]), 0);
89297
+ return true;
89298
+ } catch {
89299
+ fs15.rmSync(path19.join(holdsDir(), file2), { force: true });
89300
+ return false;
89301
+ }
89302
+ });
89303
+ } catch {
89304
+ return false;
89305
+ }
89306
+ }
89095
89307
  var kept2 = (dir) => busy(dir) || unsent(dir) || paused(dir);
89096
89308
  function clearAkb(dir) {
89097
89309
  dir = realpath2(dir);
89098
89310
  if (kept2(dir)) return false;
89099
89311
  let entries;
89100
89312
  try {
89101
- entries = fs14.readdirSync(akbDir(dir));
89313
+ entries = fs15.readdirSync(akbDir(dir));
89102
89314
  } catch {
89103
89315
  return true;
89104
89316
  }
89105
- for (const name of entries) fs14.rmSync(path17.join(akbDir(dir), name), { recursive: true, force: true });
89317
+ for (const name of entries) fs15.rmSync(path19.join(akbDir(dir), name), { recursive: true, force: true });
89106
89318
  spawnSync3("git", ["-C", dir, "worktree", "prune"], { stdio: "ignore" });
89107
89319
  return true;
89108
89320
  }
@@ -89117,7 +89329,7 @@ async function readAccess(machineId2) {
89117
89329
  }
89118
89330
  function realpath2(p2) {
89119
89331
  try {
89120
- return fs14.realpathSync(p2);
89332
+ return fs15.realpathSync(p2);
89121
89333
  } catch {
89122
89334
  return p2;
89123
89335
  }
@@ -89132,22 +89344,22 @@ async function sweepCache(machineId2, now = Date.now()) {
89132
89344
  const swept = [];
89133
89345
  let cached2 = [];
89134
89346
  try {
89135
- cached2 = fs14.readdirSync(cacheRoot(), { withFileTypes: true });
89347
+ cached2 = fs15.readdirSync(cacheRoot(), { withFileTypes: true });
89136
89348
  } catch {
89137
89349
  }
89138
89350
  for (const e2 of cached2) {
89139
- const dir = path17.join(cacheRoot(), e2.name);
89351
+ const dir = path19.join(cacheRoot(), e2.name);
89140
89352
  if (!e2.isDirectory() || kept2(dir)) continue;
89141
89353
  const gone = access && !access.projects.has(e2.name);
89142
89354
  if (idle(dir, now) || gone) {
89143
- fs14.rmSync(dir, { recursive: true, force: true });
89355
+ fs15.rmSync(dir, { recursive: true, force: true });
89144
89356
  swept.push(dir);
89145
89357
  }
89146
89358
  }
89147
89359
  for (const dir of allowedProjects()) {
89148
89360
  let held4 = [];
89149
89361
  try {
89150
- held4 = fs14.readdirSync(akbDir(dir));
89362
+ held4 = fs15.readdirSync(akbDir(dir));
89151
89363
  } catch {
89152
89364
  }
89153
89365
  if (!held4.length) continue;
@@ -89158,31 +89370,31 @@ async function sweepCache(machineId2, now = Date.now()) {
89158
89370
  }
89159
89371
  function removeLegacy() {
89160
89372
  const home4 = runnerHome();
89161
- for (const name of ["boards", "akb-bin"]) fs14.rmSync(path17.join(home4, name), { recursive: true, force: true });
89373
+ for (const name of ["boards", "akb-bin"]) fs15.rmSync(path19.join(home4, name), { recursive: true, force: true });
89162
89374
  let entries = [];
89163
89375
  try {
89164
- entries = fs14.readdirSync(home4, { withFileTypes: true });
89376
+ entries = fs15.readdirSync(home4, { withFileTypes: true });
89165
89377
  } catch {
89166
89378
  return;
89167
89379
  }
89168
89380
  for (const e2 of entries) {
89169
- const account = path17.join(home4, e2.name);
89170
- if (!e2.isDirectory() || !fs14.existsSync(path17.join(account, "unfiled"))) continue;
89171
- fs14.rmSync(path17.join(account, "unfiled"), { recursive: true, force: true });
89172
- if (!fs14.readdirSync(account).length) fs14.rmdirSync(account);
89381
+ const account = path19.join(home4, e2.name);
89382
+ if (!e2.isDirectory() || !fs15.existsSync(path19.join(account, "unfiled"))) continue;
89383
+ fs15.rmSync(path19.join(account, "unfiled"), { recursive: true, force: true });
89384
+ if (!fs15.readdirSync(account).length) fs15.rmdirSync(account);
89173
89385
  }
89174
89386
  }
89175
89387
 
89176
89388
  // src/update.ts
89177
- import crypto2 from "node:crypto";
89178
- import fs15 from "node:fs";
89179
- import path18 from "node:path";
89389
+ import crypto3 from "node:crypto";
89390
+ import fs16 from "node:fs";
89391
+ import path20 from "node:path";
89180
89392
  import zlib from "node:zlib";
89181
89393
  var TIMEOUT_MS2 = 6e4;
89182
89394
  var registry = () => (process.env.npm_config_registry || "https://registry.npmjs.org").replace(/\/+$/, "");
89183
89395
  async function installPackage(pkg, version, nodeVersion = process.versions.node) {
89184
89396
  const dir = pkg.dirOf(version);
89185
- if (fs15.existsSync(pkg.entryOf(dir))) return { ok: true, dir };
89397
+ if (fs16.existsSync(pkg.entryOf(dir))) return { ok: true, dir };
89186
89398
  let meta;
89187
89399
  let tarball;
89188
89400
  try {
@@ -89199,17 +89411,17 @@ async function installPackage(pkg, version, nodeVersion = process.versions.node)
89199
89411
  return { ok: false, why: "unreachable", detail: e2 instanceof Error ? e2.message : String(e2) };
89200
89412
  }
89201
89413
  const expected = /^sha512-(.+)$/.exec(meta.dist.integrity ?? "")?.[1];
89202
- const actual = crypto2.createHash("sha512").update(tarball).digest("base64");
89414
+ const actual = crypto3.createHash("sha512").update(tarball).digest("base64");
89203
89415
  if (!expected || expected !== actual) return { ok: false, why: "integrity", detail: "sha512 mismatch" };
89204
89416
  const staging = `${dir}.${process.pid}.tmp`;
89205
89417
  try {
89206
- fs15.rmSync(staging, { recursive: true, force: true });
89418
+ fs16.rmSync(staging, { recursive: true, force: true });
89207
89419
  extract(zlib.gunzipSync(tarball), staging);
89208
- if (!fs15.existsSync(pkg.entryOf(staging))) throw new Error(`the package carries no ${path18.relative(staging, pkg.entryOf(staging))}`);
89209
- fs15.rmSync(dir, { recursive: true, force: true });
89210
- fs15.renameSync(staging, dir);
89420
+ if (!fs16.existsSync(pkg.entryOf(staging))) throw new Error(`the package carries no ${path20.relative(staging, pkg.entryOf(staging))}`);
89421
+ fs16.rmSync(dir, { recursive: true, force: true });
89422
+ fs16.renameSync(staging, dir);
89211
89423
  } catch (e2) {
89212
- fs15.rmSync(staging, { recursive: true, force: true });
89424
+ fs16.rmSync(staging, { recursive: true, force: true });
89213
89425
  return { ok: false, why: "integrity", detail: e2 instanceof Error ? e2.message : String(e2) };
89214
89426
  }
89215
89427
  return { ok: true, dir };
@@ -89239,12 +89451,12 @@ function extract(tar, into) {
89239
89451
  if (type !== "0" && type !== "5") continue;
89240
89452
  const relative = name.replace(/^[^/]+\//, "");
89241
89453
  if (!relative) continue;
89242
- const target = path18.resolve(into, relative);
89243
- if (!target.startsWith(path18.resolve(into) + path18.sep)) throw new Error(`refusing ${name}`);
89244
- if (type === "5") fs15.mkdirSync(target, { recursive: true });
89454
+ const target = path20.resolve(into, relative);
89455
+ if (!target.startsWith(path20.resolve(into) + path20.sep)) throw new Error(`refusing ${name}`);
89456
+ if (type === "5") fs16.mkdirSync(target, { recursive: true });
89245
89457
  else {
89246
- fs15.mkdirSync(path18.dirname(target), { recursive: true });
89247
- fs15.writeFileSync(target, body);
89458
+ fs16.mkdirSync(path20.dirname(target), { recursive: true });
89459
+ fs16.writeFileSync(target, body);
89248
89460
  }
89249
89461
  }
89250
89462
  }
@@ -89292,33 +89504,33 @@ function meets(version, comparator) {
89292
89504
  // src/akb-version.ts
89293
89505
  var CHECK_MS2 = 1e4;
89294
89506
  var VERSION2 = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
89295
- var stateFile = () => path19.join(akbHome(), "state.json");
89296
- var akbVersionDir = (version) => path19.join(akbHome(), version);
89297
- var entryOf = (dir) => path19.join(dir, "bin", "ai4kanban.mjs");
89507
+ var stateFile = () => path21.join(akbHome(), "state.json");
89508
+ var akbVersionDir = (version) => path21.join(akbHome(), version);
89509
+ var entryOf = (dir) => path21.join(dir, "bin", "ai4kanban.mjs");
89298
89510
  var AKB = { name: "wildcrane", dirOf: akbVersionDir, entryOf };
89299
89511
  function readAkbState() {
89300
89512
  try {
89301
- const held4 = JSON.parse(fs16.readFileSync(stateFile(), "utf8"));
89513
+ const held4 = JSON.parse(fs17.readFileSync(stateFile(), "utf8"));
89302
89514
  return { current: held4.current ?? null, bad: held4.bad ?? [] };
89303
89515
  } catch {
89304
89516
  return { current: null, bad: [] };
89305
89517
  }
89306
89518
  }
89307
89519
  function writeAkbState(state) {
89308
- fs16.mkdirSync(akbHome(), { recursive: true });
89520
+ fs17.mkdirSync(akbHome(), { recursive: true });
89309
89521
  const staging = `${stateFile()}.${process.pid}.tmp`;
89310
- fs16.writeFileSync(staging, `${JSON.stringify(state, null, 2)}
89522
+ fs17.writeFileSync(staging, `${JSON.stringify(state, null, 2)}
89311
89523
  `);
89312
- fs16.renameSync(staging, stateFile());
89524
+ fs17.renameSync(staging, stateFile());
89313
89525
  }
89314
89526
  function contractOf(dir) {
89315
89527
  try {
89316
- return JSON.parse(fs16.readFileSync(path19.join(dir, "package.json"), "utf8")).akbContract ?? null;
89528
+ return JSON.parse(fs17.readFileSync(path21.join(dir, "package.json"), "utf8")).akbContract ?? null;
89317
89529
  } catch {
89318
89530
  return null;
89319
89531
  }
89320
89532
  }
89321
- var usable = (dir) => fs16.existsSync(entryOf(dir)) && contractOf(dir) === AKB_CONTRACT;
89533
+ var usable = (dir) => fs17.existsSync(entryOf(dir)) && contractOf(dir) === AKB_CONTRACT;
89322
89534
  function currentAkb() {
89323
89535
  const { current } = readAkbState();
89324
89536
  if (!current) return bundledAkb();
@@ -89326,10 +89538,10 @@ function currentAkb() {
89326
89538
  if (!usable(dir)) return bundledAkb();
89327
89539
  try {
89328
89540
  const now = /* @__PURE__ */ new Date();
89329
- fs16.utimesSync(dir, now, now);
89541
+ fs17.utimesSync(dir, now, now);
89330
89542
  } catch {
89331
89543
  }
89332
- return { command: process.execPath, args: [entryOf(dir)], version: current, shim: path19.join(dir, "shim") };
89544
+ return { command: process.execPath, args: [entryOf(dir)], version: current, shim: path21.join(dir, "shim") };
89333
89545
  }
89334
89546
  var answers2 = (dir, version) => new Promise((resolve2) => {
89335
89547
  execFile(process.execPath, [entryOf(dir), "--version"], { timeout: CHECK_MS2, killSignal: "SIGKILL" }, (error, stdout) => resolve2(!error && stdout.trim().split(/\s+/).at(-1) === version));
@@ -89369,20 +89581,20 @@ function sweepAkb(now = Date.now()) {
89369
89581
  const { current } = readAkbState();
89370
89582
  let entries = [];
89371
89583
  try {
89372
- entries = fs16.readdirSync(akbHome(), { withFileTypes: true });
89584
+ entries = fs17.readdirSync(akbHome(), { withFileTypes: true });
89373
89585
  } catch {
89374
89586
  return;
89375
89587
  }
89376
89588
  for (const e2 of entries) {
89377
89589
  if (!e2.isDirectory() || e2.name === current || e2.name === BUNDLED_SHIM) continue;
89378
- const dir = path19.join(akbHome(), e2.name);
89379
- if (now - fs16.statSync(dir).mtimeMs > IDLE_MS) fs16.rmSync(dir, { recursive: true, force: true });
89590
+ const dir = path21.join(akbHome(), e2.name);
89591
+ if (now - fs17.statSync(dir).mtimeMs > IDLE_MS) fs17.rmSync(dir, { recursive: true, force: true });
89380
89592
  }
89381
89593
  }
89382
89594
 
89383
89595
  // src/chats.ts
89384
- import fs18 from "node:fs";
89385
- import path21 from "node:path";
89596
+ import fs19 from "node:fs";
89597
+ import path23 from "node:path";
89386
89598
 
89387
89599
  // ../cloud/src/config.ts
89388
89600
  var CLAIM_LEASE_SECONDS = 15 * 60;
@@ -89398,18 +89610,18 @@ var CONTACT_ATTEMPT_WINDOW_SECONDS = 60 * 60;
89398
89610
  var CHAT_KEEP_DAYS = 30;
89399
89611
 
89400
89612
  // ../cli/src/lib/agent/task-chats.ts
89401
- import fs17 from "node:fs";
89402
- import path20 from "node:path";
89613
+ import fs18 from "node:fs";
89614
+ import path22 from "node:path";
89403
89615
  var TASK = /^[\w-]{1,100}$/;
89404
- var taskChatsDir = (home4 = machineHome()) => path20.join(home4, "chats");
89616
+ var taskChatsDir = (home4 = machineHome()) => path22.join(home4, "chats");
89405
89617
  function taskChatFile(task, home4) {
89406
89618
  if (!TASK.test(task)) throw new Error(`${task} is not a task id.`);
89407
- return path20.join(taskChatsDir(home4), `${task}.jsonl`);
89619
+ return path22.join(taskChatsDir(home4), `${task}.jsonl`);
89408
89620
  }
89409
89621
  function readTaskChat(task, home4) {
89410
89622
  let text3;
89411
89623
  try {
89412
- text3 = fs17.readFileSync(taskChatFile(task, home4), "utf8");
89624
+ text3 = fs18.readFileSync(taskChatFile(task, home4), "utf8");
89413
89625
  } catch {
89414
89626
  return [];
89415
89627
  }
@@ -89430,18 +89642,18 @@ var DAY_MS = 24 * 60 * 6e4;
89430
89642
  var REVIEW_EVERY_MS = DAY_MS;
89431
89643
  var KEEP_MS2 = CHAT_KEEP_DAYS * DAY_MS;
89432
89644
  var home3 = () => runnerHome();
89433
- var reviewedFile = () => path21.join(taskChatsDir(home3()), "reviewed.json");
89645
+ var reviewedFile = () => path23.join(taskChatsDir(home3()), "reviewed.json");
89434
89646
  function readReviewed() {
89435
89647
  try {
89436
- const held4 = JSON.parse(fs18.readFileSync(reviewedFile(), "utf8"));
89648
+ const held4 = JSON.parse(fs19.readFileSync(reviewedFile(), "utf8"));
89437
89649
  return { checkedAt: Number(held4.checkedAt) || 0, tasks: held4.tasks ?? {} };
89438
89650
  } catch {
89439
89651
  return { checkedAt: 0, tasks: {} };
89440
89652
  }
89441
89653
  }
89442
89654
  function writeReviewed(reviewed) {
89443
- fs18.mkdirSync(path21.dirname(reviewedFile()), { recursive: true });
89444
- fs18.writeFileSync(reviewedFile(), `${JSON.stringify(reviewed, null, 2)}
89655
+ fs19.mkdirSync(path23.dirname(reviewedFile()), { recursive: true });
89656
+ fs19.writeFileSync(reviewedFile(), `${JSON.stringify(reviewed, null, 2)}
89445
89657
  `);
89446
89658
  }
89447
89659
  function keepChat(job, reply, at2 = Date.now()) {
@@ -89451,16 +89663,16 @@ function keepChat(job, reply, at2 = Date.now()) {
89451
89663
  { id: job.id, role: "agent", text: reply, at: at2, runtime: job.runtime, model: job.model ?? null }
89452
89664
  ];
89453
89665
  const file2 = taskChatFile(job.task, home3());
89454
- fs18.mkdirSync(path21.dirname(file2), { recursive: true });
89455
- fs18.appendFileSync(file2, lines.map((l2) => `${JSON.stringify(l2)}
89666
+ fs19.mkdirSync(path23.dirname(file2), { recursive: true });
89667
+ fs19.appendFileSync(file2, lines.map((l2) => `${JSON.stringify(l2)}
89456
89668
  `).join(""));
89457
89669
  }
89458
89670
  function keepCloudChat(task, messages) {
89459
89671
  const file2 = taskChatFile(task, home3());
89460
- if (!messages.length) return fs18.rmSync(file2, { force: true });
89672
+ if (!messages.length) return fs19.rmSync(file2, { force: true });
89461
89673
  const lines = messages.map((m) => ({ id: m.id, role: m.role, text: m.body, at: Date.parse(m.created_at) || Date.now() }));
89462
- fs18.mkdirSync(path21.dirname(file2), { recursive: true });
89463
- fs18.writeFileSync(file2, lines.map((l2) => `${JSON.stringify(l2)}
89674
+ fs19.mkdirSync(path23.dirname(file2), { recursive: true });
89675
+ fs19.writeFileSync(file2, lines.map((l2) => `${JSON.stringify(l2)}
89464
89676
  `).join(""));
89465
89677
  }
89466
89678
  async function keepDecisions(task, read2) {
@@ -89478,13 +89690,13 @@ async function keepDecisions(task, read2) {
89478
89690
  const ids = new Set(comments.map((c2) => c2.id));
89479
89691
  const lines = [...readTaskChat(task, home3()).filter((l2) => !ids.has(l2.id)), ...comments].sort((a3, b2) => a3.at - b2.at);
89480
89692
  const file2 = taskChatFile(task, home3());
89481
- fs18.mkdirSync(path21.dirname(file2), { recursive: true });
89482
- fs18.writeFileSync(file2, lines.map((l2) => `${JSON.stringify(l2)}
89693
+ fs19.mkdirSync(path23.dirname(file2), { recursive: true });
89694
+ fs19.writeFileSync(file2, lines.map((l2) => `${JSON.stringify(l2)}
89483
89695
  `).join(""));
89484
89696
  }
89485
89697
  var tasksKept = () => {
89486
89698
  try {
89487
- return fs18.readdirSync(taskChatsDir(home3())).map((n) => /^([\w-]{1,100})\.jsonl$/.exec(n)?.[1]).filter((t2) => !!t2);
89699
+ return fs19.readdirSync(taskChatsDir(home3())).map((n) => /^([\w-]{1,100})\.jsonl$/.exec(n)?.[1]).filter((t2) => !!t2);
89488
89700
  } catch {
89489
89701
  return [];
89490
89702
  }
@@ -89495,9 +89707,9 @@ function pruneChats(now = Date.now()) {
89495
89707
  const kept4 = lines.filter((l2) => now - l2.at < KEEP_MS2);
89496
89708
  if (kept4.length === lines.length) continue;
89497
89709
  const file2 = taskChatFile(task, home3());
89498
- if (kept4.length) fs18.writeFileSync(file2, kept4.map((l2) => `${JSON.stringify(l2)}
89710
+ if (kept4.length) fs19.writeFileSync(file2, kept4.map((l2) => `${JSON.stringify(l2)}
89499
89711
  `).join(""));
89500
- else fs18.rmSync(file2, { force: true });
89712
+ else fs19.rmSync(file2, { force: true });
89501
89713
  }
89502
89714
  }
89503
89715
  var lastOf = (task) => readTaskChat(task, home3()).at(-1);
@@ -89535,28 +89747,28 @@ async function reviewChats(machineId2, runtimes, now = Date.now()) {
89535
89747
  }
89536
89748
 
89537
89749
  // src/board-watch.ts
89538
- import fs22 from "node:fs";
89539
- import path25 from "node:path";
89750
+ import fs23 from "node:fs";
89751
+ import path27 from "node:path";
89540
89752
 
89541
89753
  // ../cloud/src/board-files.ts
89542
89754
  var ROOTS = ["todo/", ".archive/", "memory/", "workflows/", "triage/", "assets/", "rules/"];
89543
89755
  var SETTINGS = ["modules.md", "config.md"];
89544
- var isSettingsPath = (path37) => SETTINGS.includes(path37) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path37);
89756
+ var isSettingsPath = (path40) => SETTINGS.includes(path40) || /^rules\/(?:workflows\/)?[^/]+\.md$/.test(path40);
89545
89757
  var BOARD_FILE_LIMIT = 256 * 1024;
89546
- function isBoardPath(path37) {
89547
- if (!path37 || path37.length > 500 || path37.startsWith("/") || path37.includes("\\") || path37.includes("\0")) return false;
89548
- if (path37.split("/").some((part) => !part || part === "." || part === "..")) return false;
89549
- if (path37 === "todo/README.md" || path37.split("/").includes(".sync")) return false;
89550
- if (path37.startsWith("workflows/")) return true;
89551
- if (SETTINGS.includes(path37)) return true;
89552
- if (path37.startsWith("rules/")) return isSettingsPath(path37);
89553
- if (path37.startsWith("assets/")) return path37.endsWith(".json");
89554
- return ROOTS.some((root) => path37.startsWith(root)) && path37.endsWith(".md");
89758
+ function isBoardPath(path40) {
89759
+ if (!path40 || path40.length > 500 || path40.startsWith("/") || path40.includes("\\") || path40.includes("\0")) return false;
89760
+ if (path40.split("/").some((part) => !part || part === "." || part === "..")) return false;
89761
+ if (path40 === "todo/README.md" || path40.split("/").includes(".sync")) return false;
89762
+ if (path40.startsWith("workflows/")) return true;
89763
+ if (SETTINGS.includes(path40)) return true;
89764
+ if (path40.startsWith("rules/")) return isSettingsPath(path40);
89765
+ if (path40.startsWith("assets/")) return path40.endsWith(".json");
89766
+ return ROOTS.some((root) => path40.startsWith(root)) && path40.endsWith(".md");
89555
89767
  }
89556
89768
  var CARD = /^(?:todo|\.archive)\/(?:[^/]+\/)*(\d+)-[^/]*\.md$/;
89557
89769
  var ROOT = /^(?:todo|\.archive)\/(?:[^/]+\/)*(\d+)-[^/]+\/root\.md$/;
89558
- function numberOfCard(path37) {
89559
- const n = ROOT.exec(path37)?.[1] ?? CARD.exec(path37)?.[1];
89770
+ function numberOfCard(path40) {
89771
+ const n = ROOT.exec(path40)?.[1] ?? CARD.exec(path40)?.[1];
89560
89772
  return n ? Number(n) : null;
89561
89773
  }
89562
89774
  var cardSlug = (task) => task.toLowerCase().replace(/_/g, "-");
@@ -89764,9 +89976,9 @@ function collect(body, card, host) {
89764
89976
 
89765
89977
  // src/task-folder.ts
89766
89978
  import { spawnSync as spawnSync4 } from "node:child_process";
89767
- import crypto3 from "node:crypto";
89768
- import fs21 from "node:fs";
89769
- import path24 from "node:path";
89979
+ import crypto4 from "node:crypto";
89980
+ import fs22 from "node:fs";
89981
+ import path26 from "node:path";
89770
89982
 
89771
89983
  // node_modules/node-diff3/dist/diff3.mjs
89772
89984
  function LCS(buffer1, buffer2) {
@@ -90005,8 +90217,8 @@ function unquote2(v2) {
90005
90217
 
90006
90218
  // ../cloud/src/triage-files.ts
90007
90219
  var ITEM = /^triage\/(?:(dismissed|archived)\/)?([^/]+)\.md$/;
90008
- function itemAt(path37) {
90009
- const m = ITEM.exec(path37);
90220
+ function itemAt(path40) {
90221
+ const m = ITEM.exec(path40);
90010
90222
  if (!m) return null;
90011
90223
  return { state: m[1] === "dismissed" ? "dismissed" : m[1] === "archived" ? "carded" : "open", name: m[2] };
90012
90224
  }
@@ -90023,11 +90235,11 @@ function stampItem(content, fields) {
90023
90235
  }
90024
90236
 
90025
90237
  // ../cli/src/lib/agents/shared-memory.ts
90026
- import fs19 from "node:fs";
90027
- import path22 from "node:path";
90238
+ import fs20 from "node:fs";
90239
+ import path24 from "node:path";
90028
90240
 
90029
90241
  // ../cli/src/agents/builder/AGENT.md
90030
- var AGENT_default = "---\nname: builder\ndescription: \u5B9E\u73B0\u5E76\u843D\u5730\u5DF2\u6279\u51C6\u7684\u8F6F\u4EF6\u53D8\u66F4\u3002\n---\n\n- **\u5DF2\u6279\u51C6\u7684\u754C\u9762\u9884\u89C8**\uFF1A\u5B9E\u73B0\u5361\u7247\u9884\u89C8\u4E2D\u7684\u6BCF\u4E2A\u5C4F\u5E55\uFF0C\u5728\u684C\u9762\u548C\u624B\u673A\u5BBD\u5EA6\u4E0B\u9010\u4E00\u5BF9\u7167\u5E76\u4FEE\u6B63\u5DEE\u5F02\u3002\n- **\u5DF2\u6279\u51C6\u7684 API \u9884\u89C8**\uFF1A\u6309\u5361\u7247\u9884\u89C8\u5B9E\u73B0\u6BCF\u4E2A\u63A5\u53E3\uFF0C\u5E76\u636E\u6B64\u7F16\u5199\u6587\u6863\u3002\n- **\u5DF2\u6279\u51C6\u7684\u6570\u636E\u5E93\u9884\u89C8**\uFF1A\u7F16\u5199\u8FC1\u79FB\uFF0C\u4F7F\u6700\u7EC8\u7ED3\u6784\u4E0E\u5361\u7247\u6279\u51C6\u7684\u9884\u89C8\u4E00\u81F4\u3002\n- **\u5B8C\u6210\u8FB9\u754C**\uFF1A\u9664\u975E\u5361\u7247\u7684\u76EE\u6807\u672C\u8EAB\u5C31\u662F\u90E8\u7F72\u6216\u53D1\u5E03\uFF0C\u5426\u5219\u53D8\u66F4\u843D\u5730\u4E14\u68C0\u67E5\u901A\u8FC7\u5373\u5B8C\u6210\uFF1B\u4E0D\u8981\u4E3A\u5B8C\u6210\u4EFB\u52A1\u800C\u63A8\u9001\u3001\u90E8\u7F72\u6216\u53D1\u5E03\u3002\n- **\u9700\u8981\u4E0A\u7EBF\u7684\u4E8B\u9879**\uFF1A\u628A\u5FC5\u987B\u4E0A\u7EBF\u624D\u80FD\u8FDB\u884C\u7684\u9A8C\u8BC1\u6216\u6B65\u9AA4\u4EA4\u7ED9\u7528\u6237\uFF0C\u53EA\u5728\u6700\u7EC8\u56DE\u590D\u4E2D\u8BF4\u660E\uFF0C\u4E0D\u521B\u5EFA\u8DDF\u8FDB\u4EFB\u52A1\uFF0C\u4E5F\u4E0D\u63D0\u95EE\u3002\n- **\u68C0\u67E5**\uFF1A\u6B64\u540E\u6CA1\u6709\u4EBA\u518D\u5BA1\u67E5\u5B9E\u73B0\uFF1B\u8FD0\u884C\u53D7\u5F71\u54CD\u8DEF\u5F84\u7684\u9488\u5BF9\u6027\u68C0\u67E5\uFF0C\u4EE5\u53CA\u4ED3\u5E93\u8981\u6C42\u7684\u5168\u90E8\u68C0\u67E5\u3002\n";
90242
+ var AGENT_default = "---\nname: builder\ndescription: Implement and land approved software changes.\n---\n\n- **Autonomy**: Decide technical details within the approved scope and existing permissions. Diagnose build problems, fix them, verify the result, and continue; do not repeat the same failed attempt instead of investigating. Ask the user only for decisions or blockers only they can resolve.\n- **Approved UI previews**: Implement every screen in the approved previews, compare desktop and mobile layouts, and correct differences.\n- **Approved API previews**: Implement every approved endpoint and document it.\n- **Approved database previews**: Write migrations that match the approved structure.\n- **Completion**: Finish when changes have landed and checks pass, unless deployment or publication is the task's goal. Do not push, deploy, or publish merely to finish a task.\n- **Production steps**: Hand the user any steps or validation that require production access. Mention them only in the final response; create no follow-up tasks and ask no questions.\n- **Checks**: Nobody reviews the implementation after you. Run targeted checks for the affected paths and all checks the repository requires for the changes.\n";
90031
90243
 
90032
90244
  // ../cli/src/agents/api-designer/AGENT.md
90033
90245
  var AGENT_default2 = "---\nname: api-designer\ndescription: Writes the caller's documentation for the APIs a card changes.\nakb:\n when: A card adds or changes a surface a user or third party calls directly, such as HTTP routes, WebSocket messages, MCP tools, or CLI commands and flags. Not a protocol between the project's own components.\n i18n:\n en:\n title: API docs\n zh:\n title: \u63A5\u53E3\u6587\u6863\n description: \u4E3A\u5361\u7247\u6539\u52A8\u7684\u63A5\u53E3\u5199\u51FA\u8C03\u7528\u65B9\u6587\u6863\u3002\n when: \u5361\u7247\u65B0\u589E\u6216\u4FEE\u6539\u7528\u6237\u6216\u7B2C\u4E09\u65B9\u76F4\u63A5\u8C03\u7528\u7684\u63A5\u53E3\u65F6\uFF0C\u5982 HTTP \u8DEF\u7531\u3001WebSocket \u6D88\u606F\u3001MCP \u5DE5\u5177\u3001CLI \u547D\u4EE4\u548C\u53C2\u6570\u3002\u4E0D\u542B\u9879\u76EE\u81EA\u8EAB\u7EC4\u4EF6\u4E4B\u95F4\u7684\u534F\u8BAE\u3002\n---\n\nWrite each surface this card changes as the documentation entry that will be committed, in\nthe project's existing docs style: inputs, outputs, errors and one example call. Use a\n`diff` when an entry already exists, and a `mermaid` `sequenceDiagram` for a message\nexchange. One design, no alternatives, nothing else in the section.\n\n- **Glanceable**: a junior engineer understands each entry in one read. Plain names, short\n lines, no field or error the card does not change.\n";
@@ -90602,10 +90814,10 @@ function readScalar(value) {
90602
90814
  function readBlock(lines, start, parentIndent) {
90603
90815
  if (start >= lines.length || lines[start].indent <= parentIndent) return ["", start];
90604
90816
  const indent = lines[start].indent;
90605
- if (lines[start].text.startsWith("-")) return readList2(lines, start, indent);
90817
+ if (lines[start].text.startsWith("-")) return readList(lines, start, indent);
90606
90818
  return readMap(lines, start, indent);
90607
90819
  }
90608
- function readList2(lines, start, indent) {
90820
+ function readList(lines, start, indent) {
90609
90821
  const out = [];
90610
90822
  let i = start;
90611
90823
  while (i < lines.length && lines[i].indent === indent && lines[i].text.startsWith("-")) {
@@ -90757,16 +90969,16 @@ function sharedMemory(board) {
90757
90969
  const at2 = /^([^/]+)\/AGENT\.md$/.exec(key);
90758
90970
  if (at2) take(at2[1], text3, true);
90759
90971
  }
90760
- const root = path22.join(board, "agents");
90972
+ const root = path24.join(board, "agents");
90761
90973
  let folders2 = [];
90762
90974
  try {
90763
- folders2 = fs19.readdirSync(root, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !e2.name.startsWith(".")).map((e2) => e2.name);
90975
+ folders2 = fs20.readdirSync(root, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !e2.name.startsWith(".")).map((e2) => e2.name);
90764
90976
  } catch {
90765
90977
  }
90766
90978
  for (const folder of folders2.sort()) {
90767
90979
  let text3 = null;
90768
90980
  try {
90769
- text3 = fs19.readFileSync(path22.join(root, folder, "AGENT.md"), "utf8");
90981
+ text3 = fs20.readFileSync(path24.join(root, folder, "AGENT.md"), "utf8");
90770
90982
  } catch {
90771
90983
  }
90772
90984
  take(folder, text3, false);
@@ -90790,16 +91002,16 @@ function openTaskFolder(job, projectDir2) {
90790
91002
  projectDir2 ??= openCached(job.project);
90791
91003
  keepOutOfGit(projectDir2);
90792
91004
  markUsed(projectDir2);
90793
- const board = path24.join(projectDir2, ".akb", "kanban");
90794
- fs21.mkdirSync(board, { recursive: true });
91005
+ const board = path26.join(projectDir2, ".akb", "kanban");
91006
+ fs22.mkdirSync(board, { recursive: true });
90795
91007
  return { task, cloudTask: task, board, cwd: projectDir2, project: job.project, number: job.number ?? null, workflow: job.workflow ?? job.flow?.workflow ?? null };
90796
91008
  }
90797
91009
  function openAgentFolder(job, projectDir2) {
90798
91010
  if (!job.project || !job.agent) throw new Error("This job names no project.");
90799
91011
  keepOutOfGit(projectDir2);
90800
91012
  markUsed(projectDir2);
90801
- const board = path24.join(projectDir2, ".akb", "kanban");
90802
- fs21.mkdirSync(board, { recursive: true });
91013
+ const board = path26.join(projectDir2, ".akb", "kanban");
91014
+ fs22.mkdirSync(board, { recursive: true });
90803
91015
  return {
90804
91016
  task: `agent-${safe(job.agent.workflow ? `${job.agent.workflow}-${job.agent.name}` : job.agent.name)}`,
90805
91017
  cloudTask: job.agent.tasks?.[0]?.id ?? null,
@@ -90814,13 +91026,13 @@ function openDiscussionFolder(job, projectDir2) {
90814
91026
  if (!job.project) throw new Error("This job names no project.");
90815
91027
  keepOutOfGit(projectDir2);
90816
91028
  markUsed(projectDir2);
90817
- const board = path24.join(projectDir2, ".akb", "kanban");
90818
- fs21.mkdirSync(board, { recursive: true });
91029
+ const board = path26.join(projectDir2, ".akb", "kanban");
91030
+ fs22.mkdirSync(board, { recursive: true });
90819
91031
  return { task: "discussion", cloudTask: null, board, cwd: projectDir2, project: job.project, number: null, workflow: null };
90820
91032
  }
90821
91033
  var boardFolder = (ws) => ({ task: "board", cloudTask: null, board: ws.board, cwd: ws.cwd, project: ws.project, number: null, workflow: null });
90822
91034
  function keptBoard(cwd) {
90823
- const board = path24.join(cwd, ".akb", "kanban");
91035
+ const board = path26.join(cwd, ".akb", "kanban");
90824
91036
  const base = readBase({ cwd });
90825
91037
  const project = base.project ?? /^projects\/([^/]+)\//.exec(Object.keys(base.files).find((p2) => p2.startsWith("projects/")) ?? "")?.[1];
90826
91038
  return base.head_seq === null || !project ? null : boardFolder({ board, cwd, project });
@@ -90837,8 +91049,8 @@ function inFolder(dir, work) {
90837
91049
  var safe = (name) => name.replace(/[^\w.-]/g, "_") || "default";
90838
91050
  function openCached(project) {
90839
91051
  const dir = cacheDir(project);
90840
- if (fs21.existsSync(path24.join(dir, ".git"))) return dir;
90841
- fs21.mkdirSync(dir, { recursive: true });
91052
+ if (fs22.existsSync(path26.join(dir, ".git"))) return dir;
91053
+ fs22.mkdirSync(dir, { recursive: true });
90842
91054
  const git4 = (...args) => spawnSync4("git", ["-C", dir, ...args], { encoding: "utf8" }).status === 0;
90843
91055
  if (git4("init", "-q")) git4("-c", "user.name=WildCrane", "-c", "user.email=runner@wildcrane.ai", "commit", "-q", "--allow-empty", "-m", "general");
90844
91056
  return dir;
@@ -90851,56 +91063,56 @@ function keepOutOfGit(dir, folders2 = SKILL_FOLDERS) {
90851
91063
  if (inside.status !== 0) return;
90852
91064
  const missing = [".akb/", ...folders2.map((f2) => `${f2}/`)].filter((p2) => git4("check-ignore", "-q", p2).status !== 0);
90853
91065
  if (!missing.length) return;
90854
- const exclude = path24.resolve(dir, inside.stdout.trim());
90855
- fs21.mkdirSync(path24.dirname(exclude), { recursive: true });
90856
- const held4 = fs21.existsSync(exclude) ? fs21.readFileSync(exclude, "utf8") : "";
90857
- fs21.writeFileSync(exclude, `${held4}${held4 && !held4.endsWith("\n") ? "\n" : ""}${missing.join("\n")}
91066
+ const exclude = path26.resolve(dir, inside.stdout.trim());
91067
+ fs22.mkdirSync(path26.dirname(exclude), { recursive: true });
91068
+ const held4 = fs22.existsSync(exclude) ? fs22.readFileSync(exclude, "utf8") : "";
91069
+ fs22.writeFileSync(exclude, `${held4}${held4 && !held4.endsWith("\n") ? "\n" : ""}${missing.join("\n")}
90858
91070
  `);
90859
91071
  }
90860
- var cardFile = (ws) => (ws.number ? findCard(ws.board, ws.number) : null) ?? path24.join(ws.board, "todo", `${ws.number ?? 0}-task.md`);
91072
+ var cardFile = (ws) => (ws.number ? findCard(ws.board, ws.number) : null) ?? path26.join(ws.board, "todo", `${ws.number ?? 0}-task.md`);
90861
91073
  function findCard(board, number) {
90862
91074
  const named2 = new RegExp(`^${number}-`);
90863
91075
  const walk = (dir) => {
90864
91076
  let entries;
90865
91077
  try {
90866
- entries = fs21.readdirSync(dir, { withFileTypes: true });
91078
+ entries = fs22.readdirSync(dir, { withFileTypes: true });
90867
91079
  } catch {
90868
91080
  return null;
90869
91081
  }
90870
91082
  for (const e2 of entries) {
90871
91083
  if (!named2.test(e2.name)) continue;
90872
- if (e2.isFile() && e2.name.endsWith(".md")) return path24.join(dir, e2.name);
90873
- const root = path24.join(dir, e2.name, "root.md");
90874
- if (e2.isDirectory() && fs21.existsSync(root)) return root;
91084
+ if (e2.isFile() && e2.name.endsWith(".md")) return path26.join(dir, e2.name);
91085
+ const root = path26.join(dir, e2.name, "root.md");
91086
+ if (e2.isDirectory() && fs22.existsSync(root)) return root;
90875
91087
  }
90876
91088
  for (const e2 of entries) if (e2.isDirectory() && !e2.name.startsWith(".")) {
90877
- const hit = walk(path24.join(dir, e2.name));
91089
+ const hit = walk(path26.join(dir, e2.name));
90878
91090
  if (hit) return hit;
90879
91091
  }
90880
91092
  return null;
90881
91093
  };
90882
- return walk(path24.join(board, "todo")) ?? walk(path24.join(board, ".archive"));
91094
+ return walk(path26.join(board, "todo")) ?? walk(path26.join(board, ".archive"));
90883
91095
  }
90884
91096
  function akbCardOf(ws) {
90885
91097
  return ws.number && findCard(ws.board, ws.number) ? ws.number : void 0;
90886
91098
  }
90887
- var recordFile = (ws) => path24.join(ws.board, ".sync", `${ws.task}.json`);
91099
+ var recordFile = (ws) => path26.join(ws.board, ".sync", `${ws.task}.json`);
90888
91100
  function readRecord(ws) {
90889
91101
  try {
90890
- return { ...EMPTY, ...JSON.parse(fs21.readFileSync(recordFile(ws), "utf8")) };
91102
+ return { ...EMPTY, ...JSON.parse(fs22.readFileSync(recordFile(ws), "utf8")) };
90891
91103
  } catch {
90892
91104
  return structuredClone(EMPTY);
90893
91105
  }
90894
91106
  }
90895
91107
  function writeRecord(ws, record) {
90896
- fs21.mkdirSync(path24.dirname(recordFile(ws)), { recursive: true });
90897
- fs21.writeFileSync(recordFile(ws), `${JSON.stringify(record, null, 2)}
91108
+ fs22.mkdirSync(path26.dirname(recordFile(ws)), { recursive: true });
91109
+ fs22.writeFileSync(recordFile(ws), `${JSON.stringify(record, null, 2)}
90898
91110
  `);
90899
91111
  }
90900
- var baseFile = (ws) => path24.join(ws.cwd, ".akb", "base.json");
91112
+ var baseFile = (ws) => path26.join(ws.cwd, ".akb", "base.json");
90901
91113
  function readBase(ws) {
90902
91114
  try {
90903
- const base = JSON.parse(fs21.readFileSync(baseFile(ws), "utf8"));
91115
+ const base = JSON.parse(fs22.readFileSync(baseFile(ws), "utf8"));
90904
91116
  if (base.layout === "workspace") return base;
90905
91117
  } catch {
90906
91118
  }
@@ -90908,25 +91120,25 @@ function readBase(ws) {
90908
91120
  }
90909
91121
  function writeBase(ws, base) {
90910
91122
  const file2 = baseFile(ws);
90911
- fs21.mkdirSync(path24.dirname(file2), { recursive: true });
90912
- fs21.writeFileSync(`${file2}.tmp`, JSON.stringify(base));
90913
- fs21.renameSync(`${file2}.tmp`, file2);
91123
+ fs22.mkdirSync(path26.dirname(file2), { recursive: true });
91124
+ fs22.writeFileSync(`${file2}.tmp`, JSON.stringify(base));
91125
+ fs22.renameSync(`${file2}.tmp`, file2);
90914
91126
  }
90915
- var hashOf = (bytes2) => crypto3.createHash("sha256").update(bytes2).digest("hex");
91127
+ var hashOf = (bytes2) => crypto4.createHash("sha256").update(bytes2).digest("hex");
90916
91128
  function hashFile(file2) {
90917
91129
  try {
90918
- return hashOf(fs21.readFileSync(file2));
91130
+ return hashOf(fs22.readFileSync(file2));
90919
91131
  } catch {
90920
91132
  return null;
90921
91133
  }
90922
91134
  }
90923
91135
  function write(file2, bytes2) {
90924
- fs21.mkdirSync(path24.dirname(file2), { recursive: true });
90925
- fs21.writeFileSync(file2, bytes2);
91136
+ fs22.mkdirSync(path26.dirname(file2), { recursive: true });
91137
+ fs22.writeFileSync(file2, bytes2);
90926
91138
  }
90927
91139
  function readText(file2) {
90928
91140
  try {
90929
- return fs21.readFileSync(file2, "utf8");
91141
+ return fs22.readFileSync(file2, "utf8");
90930
91142
  } catch {
90931
91143
  return null;
90932
91144
  }
@@ -90934,14 +91146,14 @@ function readText(file2) {
90934
91146
  function filesUnder(dir, base = dir) {
90935
91147
  let entries;
90936
91148
  try {
90937
- entries = fs21.readdirSync(dir, { withFileTypes: true });
91149
+ entries = fs22.readdirSync(dir, { withFileTypes: true });
90938
91150
  } catch {
90939
91151
  return [];
90940
91152
  }
90941
91153
  return entries.flatMap((e2) => {
90942
- const full = path24.join(dir, e2.name);
91154
+ const full = path26.join(dir, e2.name);
90943
91155
  if (e2.isDirectory()) return filesUnder(full, base);
90944
- return e2.isFile() ? [path24.relative(base, full).split(path24.sep).join("/")] : [];
91156
+ return e2.isFile() ? [path26.relative(base, full).split(path26.sep).join("/")] : [];
90945
91157
  });
90946
91158
  }
90947
91159
  var kept3 = isBoardPath;
@@ -90957,18 +91169,18 @@ function viewOf(ws, base, shared) {
90957
91169
  }
90958
91170
  function assetFiles(ws, list) {
90959
91171
  const card = cardFile(ws);
90960
- return new Set(Object.keys(assetsOf(list)).map((name) => assetPath(ws, name, card)).filter((f2) => !!f2).map((f2) => path24.relative(ws.board, f2).split(path24.sep).join("/")));
91172
+ return new Set(Object.keys(assetsOf(list)).map((name) => assetPath(ws, name, card)).filter((f2) => !!f2).map((f2) => path26.relative(ws.board, f2).split(path26.sep).join("/")));
90961
91173
  }
90962
91174
  var assetsListOf = (ws, base) => base.files[rootOf(ws) + assetsFile(ws.task)]?.content;
90963
91175
  var localFiles = (ws, base) => {
90964
- const assets = /* @__PURE__ */ new Set([...assetFiles(ws, assetsListOf(ws, base)), ...assetFiles(ws, readText(path24.join(ws.board, assetsFile(ws.task))))]);
91176
+ const assets = /* @__PURE__ */ new Set([...assetFiles(ws, assetsListOf(ws, base)), ...assetFiles(ws, readText(path26.join(ws.board, assetsFile(ws.task))))]);
90965
91177
  return filesUnder(ws.board).filter((p2) => kept3(p2) && !assets.has(p2));
90966
91178
  };
90967
91179
  var SKILL_PATH = /^skills\/([a-z0-9]+(?:-[a-z0-9]+)*)\/((?:[\w.-]+\/)*[\w.-]+)$/;
90968
91180
  function pullTools(ws, record, files) {
90969
91181
  const held4 = record.tools?.skills ?? {};
90970
91182
  const ours = new Set(Object.keys(held4).map((name) => name.split("/")[1]));
90971
- const theirs = (skill) => !ours.has(skill) && SKILL_ROOTS.some((root) => fs21.existsSync(path24.join(ws.cwd, root, skill)));
91183
+ const theirs = (skill) => !ours.has(skill) && SKILL_ROOTS.some((root) => fs22.existsSync(path26.join(ws.cwd, root, skill)));
90972
91184
  const kept4 = Object.keys(files).map((name) => SKILL_PATH.exec(name)?.[1]).filter((skill) => skill && !theirs(skill));
90973
91185
  const skills = {};
90974
91186
  for (const [name, text3] of Object.entries(files)) {
@@ -90976,7 +91188,7 @@ function pullTools(ws, record, files) {
90976
91188
  if (!at2 || at2[1] === "kanban" || !kept4.includes(at2[1]) || at2[2].split("/").includes("..")) continue;
90977
91189
  skills[name] = hashOf(text3);
90978
91190
  for (const root of SKILL_ROOTS) {
90979
- const file2 = path24.join(ws.cwd, root, at2[1], at2[2]);
91191
+ const file2 = path26.join(ws.cwd, root, at2[1], at2[2]);
90980
91192
  if (held4[name] !== skills[name] || hashFile(file2) !== skills[name]) write(file2, text3);
90981
91193
  }
90982
91194
  }
@@ -90985,8 +91197,8 @@ function pullTools(ws, record, files) {
90985
91197
  const [, skill, rest] = SKILL_PATH.exec(name) ?? [];
90986
91198
  if (!skill || !rest) continue;
90987
91199
  for (const root of SKILL_ROOTS) {
90988
- fs21.rmSync(path24.join(ws.cwd, root, skill, rest), { force: true });
90989
- if (!Object.keys(skills).some((n) => n.startsWith(`skills/${skill}/`))) fs21.rmSync(path24.join(ws.cwd, root, skill), { recursive: true, force: true });
91200
+ fs22.rmSync(path26.join(ws.cwd, root, skill, rest), { force: true });
91201
+ if (!Object.keys(skills).some((n) => n.startsWith(`skills/${skill}/`))) fs22.rmSync(path26.join(ws.cwd, root, skill), { recursive: true, force: true });
90990
91202
  }
90991
91203
  }
90992
91204
  const names2 = [...new Set(Object.keys(skills).map((name) => name.split("/")[1]))];
@@ -91014,7 +91226,7 @@ async function pullWorkflow(ws, record, base) {
91014
91226
  files = read2.value.files;
91015
91227
  found = true;
91016
91228
  if (!record.builtin) {
91017
- for (const [name, text3] of Object.entries(files)) if (isBoardPath(root + name)) write(path24.join(ws.board, root, name), text3);
91229
+ for (const [name, text3] of Object.entries(files)) if (isBoardPath(root + name)) write(path26.join(ws.board, root, name), text3);
91018
91230
  }
91019
91231
  }
91020
91232
  }
@@ -91022,14 +91234,14 @@ async function pullWorkflow(ws, record, base) {
91022
91234
  }
91023
91235
  pullAgents(ws, base, ws.workflow && !record.builtin ? files : {});
91024
91236
  const placed = readPlaced(ws);
91025
- for (const name of Object.keys(record.agents ?? {})) if (!placed[name]) fs21.rmSync(path24.join(ws.board, name), { force: true });
91237
+ for (const name of Object.keys(record.agents ?? {})) if (!placed[name]) fs22.rmSync(path26.join(ws.board, name), { force: true });
91026
91238
  record.agents = {};
91027
91239
  return { ok: true, value: void 0 };
91028
91240
  }
91029
- var agentsRecord = (ws) => path24.join(ws.board, ".sync", "agents.json");
91241
+ var agentsRecord = (ws) => path26.join(ws.board, ".sync", "agents.json");
91030
91242
  function readPlaced(ws) {
91031
91243
  try {
91032
- return JSON.parse(fs21.readFileSync(agentsRecord(ws), "utf8"));
91244
+ return JSON.parse(fs22.readFileSync(agentsRecord(ws), "utf8"));
91033
91245
  } catch {
91034
91246
  return {};
91035
91247
  }
@@ -91050,8 +91262,8 @@ function pullAgents(ws, base, own) {
91050
91262
  const names2 = /* @__PURE__ */ new Set();
91051
91263
  for (const [name, text3] of Object.entries(files)) {
91052
91264
  const agent = /^agents\/([^/]+)\//.exec(name)?.[1];
91053
- const file2 = path24.resolve(ws.board, name);
91054
- if (!agent || taken.has(agent) || !file2.startsWith(path24.join(ws.board, "agents") + path24.sep)) continue;
91265
+ const file2 = path26.resolve(ws.board, name);
91266
+ if (!agent || taken.has(agent) || !file2.startsWith(path26.join(ws.board, "agents") + path26.sep)) continue;
91055
91267
  names2.add(agent);
91056
91268
  agents[name] = text3;
91057
91269
  }
@@ -91061,51 +91273,51 @@ function pullAgents(ws, base, own) {
91061
91273
  const hashes = {};
91062
91274
  for (const [name, text3] of Object.entries(agents)) {
91063
91275
  hashes[name] = hashOf(text3);
91064
- if (hashFile(path24.join(ws.board, name)) !== hashes[name]) write(path24.join(ws.board, name), text3);
91276
+ if (hashFile(path26.join(ws.board, name)) !== hashes[name]) write(path26.join(ws.board, name), text3);
91065
91277
  }
91066
- for (const name of Object.keys(before)) if (!hashes[name]) fs21.rmSync(path24.join(ws.board, name), { force: true });
91278
+ for (const name of Object.keys(before)) if (!hashes[name]) fs22.rmSync(path26.join(ws.board, name), { force: true });
91067
91279
  write(agentsRecord(ws), `${JSON.stringify(hashes, null, 2)}
91068
91280
  `);
91069
91281
  }
91070
91282
  function pullDraftAgents(ws, record) {
91071
91283
  if (ws.workflow !== BUILDER) return;
91072
- const root = path24.join(ws.board, "workflows", draftFolder(ws.task), "agents");
91284
+ const root = path26.join(ws.board, "workflows", draftFolder(ws.task), "agents");
91073
91285
  record.agents ??= {};
91074
91286
  for (const name of filesUnder(root)) {
91075
- const text3 = fs21.readFileSync(path24.join(root, name));
91287
+ const text3 = fs22.readFileSync(path26.join(root, name));
91076
91288
  const at2 = `agents/${name}`;
91077
91289
  record.agents[at2] = hashOf(text3);
91078
- if (hashFile(path24.join(ws.board, at2)) !== record.agents[at2]) write(path24.join(ws.board, at2), text3);
91290
+ if (hashFile(path26.join(ws.board, at2)) !== record.agents[at2]) write(path26.join(ws.board, at2), text3);
91079
91291
  }
91080
91292
  }
91081
91293
  function workflowFile(ws) {
91082
91294
  if (!ws.workflow || readRecord(ws).builtin) return null;
91083
- const file2 = path24.join(ws.board, "workflows", workflowFolder(ws.workflow), "WORKFLOW.md");
91084
- return fs21.existsSync(file2) ? file2 : null;
91295
+ const file2 = path26.join(ws.board, "workflows", workflowFolder(ws.workflow), "WORKFLOW.md");
91296
+ return fs22.existsSync(file2) ? file2 : null;
91085
91297
  }
91086
91298
  function assetPath(ws, name, owner) {
91087
91299
  if (/^[a-z][\w+.-]*:/i.test(name) || name.startsWith("#") || name.startsWith("//")) return null;
91088
- if (path24.isAbsolute(name)) throw new Error("\u8D44\u6E90\u8DEF\u5F84\u4E0D\u80FD\u662F\u7EDD\u5BF9\u8DEF\u5F84");
91300
+ if (path26.isAbsolute(name)) throw new Error("\u8D44\u6E90\u8DEF\u5F84\u4E0D\u80FD\u662F\u7EDD\u5BF9\u8DEF\u5F84");
91089
91301
  const held4 = /^\.(assets|mockups)\//.exec(name);
91090
91302
  const state = stateDir(ws);
91091
- const root = held4 && state ? path24.join(state, held4[1]) : ws.board;
91092
- const file2 = held4 && state ? path24.resolve(root, name.slice(held4[0].length)) : path24.resolve(path24.dirname(owner), name);
91093
- const roots = [ws.board, ...state ? ["assets", "mockups"].map((folder) => path24.join(state, folder, ...ws.number ? [String(ws.number)] : [])) : []];
91094
- const inside = (file3, root2) => file3.startsWith(root2 + path24.sep);
91303
+ const root = held4 && state ? path26.join(state, held4[1]) : ws.board;
91304
+ const file2 = held4 && state ? path26.resolve(root, name.slice(held4[0].length)) : path26.resolve(path26.dirname(owner), name);
91305
+ const roots = [ws.board, ...state ? ["assets", "mockups"].map((folder) => path26.join(state, folder, ...ws.number ? [String(ws.number)] : [])) : []];
91306
+ const inside = (file3, root2) => file3.startsWith(root2 + path26.sep);
91095
91307
  if (!roots.some((root2) => inside(file2, root2))) throw new Error("\u8D44\u6E90\u8DEF\u5F84\u8D8A\u754C");
91096
91308
  let existing = file2;
91097
- while (!fs21.existsSync(existing) && path24.dirname(existing) !== existing) existing = path24.dirname(existing);
91098
- const real = fs21.realpathSync(existing);
91309
+ while (!fs22.existsSync(existing) && path26.dirname(existing) !== existing) existing = path26.dirname(existing);
91310
+ const real = fs22.realpathSync(existing);
91099
91311
  let stateParent = state;
91100
- while (stateParent && !fs21.existsSync(stateParent) && path24.dirname(stateParent) !== stateParent) stateParent = path24.dirname(stateParent);
91101
- const realState = state && stateParent ? path24.resolve(fs21.realpathSync(stateParent), path24.relative(stateParent, state)) : null;
91102
- const realRoots = [fs21.realpathSync(ws.board), ...realState ? ["assets", "mockups"].map((folder) => path24.join(realState, folder, ...ws.number ? [String(ws.number)] : [])) : []];
91312
+ while (stateParent && !fs22.existsSync(stateParent) && path26.dirname(stateParent) !== stateParent) stateParent = path26.dirname(stateParent);
91313
+ const realState = state && stateParent ? path26.resolve(fs22.realpathSync(stateParent), path26.relative(stateParent, state)) : null;
91314
+ const realRoots = [fs22.realpathSync(ws.board), ...realState ? ["assets", "mockups"].map((folder) => path26.join(realState, folder, ...ws.number ? [String(ws.number)] : [])) : []];
91103
91315
  if (!realRoots.some((root2) => real === root2 || inside(real, root2))) throw new Error("\u7B26\u53F7\u94FE\u63A5\u8D8A\u754C");
91104
91316
  return file2;
91105
91317
  }
91106
91318
  function cardAssetsDir(ws, id) {
91107
91319
  const state = stateDir(ws);
91108
- return state && path24.join(state, "assets", String(id));
91320
+ return state && path26.join(state, "assets", String(id));
91109
91321
  }
91110
91322
  function stateDir(ws) {
91111
91323
  try {
@@ -91144,13 +91356,13 @@ var pullChanges = (ws) => inFolder(ws.cwd, async () => {
91144
91356
  const p2 = f2.path.startsWith(root) ? f2.path.slice(root.length) : f2.path;
91145
91357
  const isShared = p2.startsWith(MEMORY2) && shared(p2.slice(MEMORY2.length));
91146
91358
  if (kept3(p2) && isShared === !f2.path.startsWith(root)) {
91147
- const file2 = path24.join(ws.board, p2);
91359
+ const file2 = path26.join(ws.board, p2);
91148
91360
  const here = readText(file2);
91149
91361
  if (here !== f2.content && here !== (base.files[f2.path]?.content ?? null)) {
91150
91362
  skipped = true;
91151
91363
  continue;
91152
91364
  }
91153
- if (f2.content === null) fs21.rmSync(file2, { force: true });
91365
+ if (f2.content === null) fs22.rmSync(file2, { force: true });
91154
91366
  else if (here !== f2.content) write(file2, f2.content);
91155
91367
  }
91156
91368
  if (f2.content === null) delete base.files[f2.path];
@@ -91181,13 +91393,13 @@ async function readCloud(ws) {
91181
91393
  writeBase(ws, base);
91182
91394
  const reset = (view2) => {
91183
91395
  for (const [p2, content] of view2) {
91184
- const file2 = path24.join(ws.board, p2);
91396
+ const file2 = path26.join(ws.board, p2);
91185
91397
  if (kept3(p2) && readText(file2) !== content) write(file2, content);
91186
91398
  }
91187
91399
  };
91188
91400
  const view = viewOf(ws, base, sharedMemory(ws.board));
91189
91401
  const first = (p2) => isSettingsPath(p2) && !known2.has(rootOf(ws) + p2);
91190
- for (const p2 of localFiles(ws, base)) if (!view.has(p2) && !first(p2)) fs21.rmSync(path24.join(ws.board, p2), { force: true });
91402
+ for (const p2 of localFiles(ws, base)) if (!view.has(p2) && !first(p2)) fs22.rmSync(path26.join(ws.board, p2), { force: true });
91191
91403
  reset(view);
91192
91404
  const record = readRecord(ws);
91193
91405
  const workflow = await pullWorkflow(ws, record, base);
@@ -91201,18 +91413,18 @@ async function readCloud(ws) {
91201
91413
  return { ok: true, value: void 0 };
91202
91414
  }
91203
91415
  function writeIndex(board) {
91204
- const todo = path24.join(board, "todo");
91416
+ const todo = path26.join(board, "todo");
91205
91417
  const cards = [];
91206
91418
  let entries = [];
91207
91419
  try {
91208
- entries = fs21.readdirSync(todo, { withFileTypes: true });
91420
+ entries = fs22.readdirSync(todo, { withFileTypes: true });
91209
91421
  } catch {
91210
91422
  }
91211
91423
  for (const e2 of entries) {
91212
91424
  const id = /^(\d+)-/.exec(e2.name)?.[1];
91213
91425
  if (!id) continue;
91214
91426
  const link = e2.isDirectory() ? `${e2.name}/root.md` : e2.name.endsWith(".md") ? e2.name : null;
91215
- const text4 = link && readText(path24.join(todo, link));
91427
+ const text4 = link && readText(path26.join(todo, link));
91216
91428
  if (text4 === null || !link) continue;
91217
91429
  cards.push({ id: Number(id), title: cardFront(text4).title ?? e2.name.replace(/\.md$/, ""), link });
91218
91430
  }
@@ -91227,7 +91439,7 @@ the number at the front of a filename is the task id.
91227
91439
 
91228
91440
  ${list}
91229
91441
  `;
91230
- if (readText(path24.join(todo, "README.md")) !== text3) write(path24.join(todo, "README.md"), text3);
91442
+ if (readText(path26.join(todo, "README.md")) !== text3) write(path26.join(todo, "README.md"), text3);
91231
91443
  }
91232
91444
  var workspaceOf = (ws) => readBase(ws).workspace ?? null;
91233
91445
  async function pullAssets(ws, record, assets) {
@@ -91235,51 +91447,74 @@ async function pullAssets(ws, record, assets) {
91235
91447
  record.task = ws.cloudTask;
91236
91448
  record.number = ws.number;
91237
91449
  }
91238
- const isCompare = (value) => !!value && typeof value === "object" && "a" in value && "b" in value;
91239
91450
  const file2 = cardFile(ws);
91240
91451
  const out = {};
91241
91452
  for (const [name, ref] of Object.entries(assets)) {
91242
91453
  const target = assetPath(ws, name, file2);
91243
91454
  if (!target) continue;
91455
+ const held4 = record.assets[name];
91456
+ let kind = held4?.artifact === ref.artifact ? held4.kind : void 0;
91244
91457
  let version = ref.version;
91245
- if (version === void 0) {
91458
+ const localHash = hashFile(target);
91459
+ const current = held4?.artifact === ref.artifact && held4.version === version && localHash === (held4.localHash ?? held4.hash);
91460
+ if (version === void 0 || !kind || !current && kind !== "html") {
91246
91461
  const latest = await readArtifact(ref.artifact);
91247
91462
  if (!latest.ok) return latest;
91248
- version = latest.value.latest_version ?? 0;
91463
+ version ??= latest.value.latest_version ?? 0;
91464
+ kind = latest.value.type;
91465
+ if (!kind) return { ok: false, code: "bad_request", error: `${name}: \u8D44\u6E90\u7C7B\u578B\u7F3A\u5931` };
91249
91466
  }
91250
- const held4 = record.assets[name];
91251
- if (held4?.kind === "html" && held4.artifact === ref.artifact) {
91252
- out[name] = { ...held4, version };
91467
+ if (kind === "html" && held4?.artifact === ref.artifact) {
91468
+ out[name] = { ...held4, kind, version };
91253
91469
  continue;
91254
91470
  }
91255
- if (held4?.artifact === ref.artifact && held4.version === version && hashFile(target) === (held4.localHash ?? held4.hash)) {
91256
- out[name] = held4;
91471
+ if (held4?.artifact === ref.artifact && held4.version === version && current) {
91472
+ out[name] = { ...held4, kind };
91257
91473
  continue;
91258
91474
  }
91259
91475
  const bytes2 = await readAsset(ref.artifact, version);
91260
91476
  if (!bytes2.ok) return bytes2;
91261
91477
  let local = bytes2.value;
91262
- let kind = held4?.kind;
91263
91478
  const dependencies = [];
91264
- if (/\.json$/i.test(target)) {
91479
+ if (kind === "storyboard" || kind === "compare") {
91480
+ let data;
91265
91481
  try {
91266
- const data = JSON.parse(Buffer.from(local).toString());
91482
+ data = JSON.parse(Buffer.from(local).toString());
91483
+ } catch {
91484
+ }
91485
+ if (data !== void 0) {
91486
+ let changed = false;
91267
91487
  const restore = (src) => {
91268
91488
  const name2 = Object.entries(assets).find(([, r2]) => r2.artifact === src)?.[0];
91269
- if (name2) dependencies.push(name2);
91270
91489
  if (!name2) return src;
91271
- if (/^\.(assets|mockups)\//.test(name2)) return name2;
91272
91490
  const child = assetPath(ws, name2, file2);
91273
- return child ? path24.relative(path24.dirname(target), child).split(path24.sep).join("/") : src;
91491
+ if (!child) return src;
91492
+ dependencies.push(name2);
91493
+ const restored2 = /^\.(assets|mockups)\//.test(name2) ? name2 : path26.relative(path26.dirname(target), child).split(path26.sep).join("/");
91494
+ if (restored2 !== src) changed = true;
91495
+ return restored2;
91274
91496
  };
91275
- const restored = mapReferences(mapReferences(data, "storyboard", restore), "compare", restore);
91276
- if (dependencies.length) kind = isCompare(data) ? "compare" : "storyboard";
91277
- local = Buffer.from(JSON.stringify(restored));
91278
- } catch {
91497
+ const restored = mapReferences(data, kind, restore);
91498
+ if (changed) local = Buffer.from(JSON.stringify(restored));
91499
+ }
91500
+ }
91501
+ if (held4?.artifact === ref.artifact && held4.version === version && !held4.localHash && (kind === "storyboard" || kind === "compare")) {
91502
+ const source = readText(target);
91503
+ if (source !== null) {
91504
+ let same = false;
91505
+ try {
91506
+ same = JSON.stringify(JSON.parse(source)) === JSON.stringify(JSON.parse(Buffer.from(local).toString()));
91507
+ } catch {
91508
+ }
91509
+ if (!same) {
91510
+ out[name] = { ...held4, kind };
91511
+ continue;
91512
+ }
91513
+ local = Buffer.from(source);
91279
91514
  }
91280
91515
  }
91281
91516
  write(target, local);
91282
- out[name] = { ...held4, kind, dependencies: dependencies.length ? [...new Set(dependencies)] : held4?.dependencies, artifact: ref.artifact, version, hash: hashOf(bytes2.value), localHash: hashOf(local) };
91517
+ out[name] = { ...held4, kind, dependencies: [...new Set(dependencies)], artifact: ref.artifact, version, hash: hashOf(bytes2.value), localHash: hashOf(local) };
91283
91518
  }
91284
91519
  record.assets = out;
91285
91520
  return { ok: true, value: void 0 };
@@ -91292,14 +91527,14 @@ async function readAsset(artifact, version) {
91292
91527
  return { ok: true, value: Buffer.from(typeof content === "string" ? content : JSON.stringify(content)) };
91293
91528
  }
91294
91529
  async function sentOf(file2) {
91295
- const ext = path24.extname(file2).toLowerCase();
91530
+ const ext = path26.extname(file2).toLowerCase();
91296
91531
  const kind = kindOf(ext);
91297
- if (kind !== "html") return { bytes: fs21.readFileSync(file2), type: TYPES[ext] ?? "application/octet-stream", kind };
91532
+ if (kind !== "html") return { bytes: fs22.readFileSync(file2), type: TYPES[ext] ?? "application/octet-stream", kind };
91298
91533
  let page;
91299
91534
  try {
91300
91535
  page = await (await Promise.resolve().then(() => (init_mockup(), mockup_exports))).drawMockup(file2);
91301
91536
  } catch (e2) {
91302
- console.error(`runner: ${path24.basename(file2)} could not be drawn: ${e2 instanceof Error ? e2.message : String(e2)}`);
91537
+ console.error(`runner: ${path26.basename(file2)} could not be drawn: ${e2 instanceof Error ? e2.message : String(e2)}`);
91303
91538
  page = UNDRAWN;
91304
91539
  }
91305
91540
  return { bytes: Buffer.from(page), type: "text/html", kind };
@@ -91324,14 +91559,14 @@ async function pushAssets(ws, record, before) {
91324
91559
  if (body === null) return { ok: true, value: void 0 };
91325
91560
  const refs = {};
91326
91561
  const canonical = (target) => {
91327
- const real = (file3) => fs21.existsSync(file3) ? fs21.realpathSync(file3) : file3;
91562
+ const real = (file3) => fs22.existsSync(file3) ? fs22.realpathSync(file3) : file3;
91328
91563
  target = real(target);
91329
91564
  const state = stateDir(ws);
91330
91565
  for (const folder of ["assets", "mockups"]) {
91331
- const root = state && real(path24.join(state, folder));
91332
- if (root && target.startsWith(root + path24.sep)) return `.${folder}/${path24.relative(root, target).split(path24.sep).join("/")}`;
91566
+ const root = state && real(path26.join(state, folder));
91567
+ if (root && target.startsWith(root + path26.sep)) return `.${folder}/${path26.relative(root, target).split(path26.sep).join("/")}`;
91333
91568
  }
91334
- return path24.relative(real(path24.dirname(file2)), target).split(path24.sep).join("/");
91569
+ return path26.relative(real(path26.dirname(file2)), target).split(path26.sep).join("/");
91335
91570
  };
91336
91571
  try {
91337
91572
  const graph = collect(body, file2, {
@@ -91341,13 +91576,13 @@ async function pushAssets(ws, record, before) {
91341
91576
  const target = assetPath(ws, src, owner);
91342
91577
  if (!target) return null;
91343
91578
  const key = owner === file2 && before[src] ? src : canonical(target);
91344
- if (!fs21.statSync(target, { throwIfNoEntry: false })?.isFile()) {
91579
+ if (!fs22.statSync(target, { throwIfNoEntry: false })?.isFile()) {
91345
91580
  if (before[key]) return { key, file: null };
91346
91581
  throw new Error("\u660E\u786E\u5F15\u7528\u7684\u8D44\u6E90\u6587\u4EF6\u4E0D\u5B58\u5728");
91347
91582
  }
91348
- return { key: canonical(fs21.realpathSync(target)), file: target };
91583
+ return { key: canonical(fs22.realpathSync(target)), file: target };
91349
91584
  },
91350
- readJSON: (target) => JSON.parse(fs21.readFileSync(target, "utf8")),
91585
+ readJSON: (target) => JSON.parse(fs22.readFileSync(target, "utf8")),
91351
91586
  retained: (key) => (record.assets[key]?.dependencies ?? []).map((src) => {
91352
91587
  const kind = record.assets[src]?.kind;
91353
91588
  return { src, purpose: kind === "storyboard" || kind === "compare" ? kind : "asset" };
@@ -91368,6 +91603,13 @@ async function pushAssets(ws, record, before) {
91368
91603
  }
91369
91604
  continue;
91370
91605
  }
91606
+ const kind = node.kind === "asset" ? kindOf(path26.extname(node.file).toLowerCase()) : node.kind;
91607
+ if (previous && previous.kind !== kind) {
91608
+ const artifact = await readArtifact(previous.artifact);
91609
+ if (!artifact.ok) return artifact;
91610
+ if (!artifact.value.type) return { ok: false, code: "bad_request", error: `${node.key}: \u8D44\u6E90\u7C7B\u578B\u7F3A\u5931` };
91611
+ previous.kind = artifact.value.type;
91612
+ }
91371
91613
  let out = await sentOf(node.file);
91372
91614
  if (node.kind !== "asset") {
91373
91615
  const data = mapReferences(node.data, node.kind, (src) => {
@@ -91378,9 +91620,12 @@ async function pushAssets(ws, record, before) {
91378
91620
  }
91379
91621
  const hash = hashOf(out.bytes);
91380
91622
  let entry = previous;
91381
- if (!entry || entry.hash !== hash || entry.kind && entry.kind !== out.kind) {
91623
+ const localHash = hashFile(node.file);
91624
+ const restored = node.kind !== "asset" || out.kind === "html" && previous?.hash === localHash;
91625
+ const unchanged = restored && previous && (previous.kind ?? out.kind) === out.kind && localHash === (previous.localHash ?? previous.hash) && [...node.dependencies.values()].every((key) => refs[key]?.artifact === before[key]?.artifact);
91626
+ if (!entry || !unchanged && entry.hash !== hash || entry.kind && entry.kind !== out.kind) {
91382
91627
  if (!ws.cloudTask) continue;
91383
- let artifact = previous && (previous.kind ?? kindOf(path24.extname(node.file).toLowerCase())) === out.kind ? previous.artifact : void 0;
91628
+ let artifact = previous && (previous.kind ?? kindOf(path26.extname(node.file).toLowerCase())) === out.kind ? previous.artifact : void 0;
91384
91629
  if (!artifact) {
91385
91630
  const made = await createArtifact(ws.cloudTask, node.key.slice(0, 200), out.kind);
91386
91631
  if (!made.ok) return { ...made, error: `${node.key}: ${made.error}` };
@@ -91390,7 +91635,7 @@ async function pushAssets(ws, record, before) {
91390
91635
  if (!sent.ok) return { ...sent, error: `${node.key}: ${sent.error}` };
91391
91636
  entry = { artifact, version: sent.value, hash, kind: out.kind };
91392
91637
  }
91393
- entry = { ...entry, localHash: hashFile(node.file) ?? void 0, dependencies: [...node.dependencies.values()] };
91638
+ entry = { ...entry, localHash: localHash ?? void 0, dependencies: [...node.dependencies.values()] };
91394
91639
  for (const alias of node.aliases) {
91395
91640
  record.assets[alias] = entry;
91396
91641
  refs[alias] = { artifact: entry.artifact, version: entry.version };
@@ -91400,24 +91645,24 @@ async function pushAssets(ws, record, before) {
91400
91645
  } catch (error) {
91401
91646
  return { ok: false, code: "bad_request", error: error instanceof Error ? error.message : String(error) };
91402
91647
  }
91403
- const list = path24.join(ws.board, assetsFile(ws.task));
91404
- if (Object.keys(refs).length || fs21.existsSync(list)) {
91648
+ const list = path26.join(ws.board, assetsFile(ws.task));
91649
+ if (Object.keys(refs).length || fs22.existsSync(list)) {
91405
91650
  if (JSON.stringify(assetsOf(readText(list))) !== JSON.stringify(refs)) write(list, assetsText(refs));
91406
91651
  }
91407
91652
  return { ok: true, value: void 0 };
91408
91653
  }
91409
91654
  function resetSorted(ws) {
91410
- for (const sub of ["dismissed", "archived"]) fs21.rmSync(path24.join(ws.board, "triage", sub), { recursive: true, force: true });
91655
+ for (const sub of ["dismissed", "archived"]) fs22.rmSync(path26.join(ws.board, "triage", sub), { recursive: true, force: true });
91411
91656
  for (const [p2, content] of viewOf(ws, readBase(ws), sharedMemory(ws.board))) {
91412
- if (/^triage\/(?:dismissed|archived)\//.test(p2)) write(path24.join(ws.board, p2), content);
91657
+ if (/^triage\/(?:dismissed|archived)\//.test(p2)) write(path26.join(ws.board, p2), content);
91413
91658
  }
91414
91659
  }
91415
91660
  function proposeItems(ws, base) {
91416
91661
  const root = rootOf(ws);
91417
91662
  for (const p2 of localFiles(ws, base)) {
91418
91663
  if (itemAt(p2)?.state !== "open" || base.files[root + p2]) continue;
91419
- const file2 = path24.join(ws.board, p2);
91420
- const content = fs21.readFileSync(file2, "utf8");
91664
+ const file2 = path26.join(ws.board, p2);
91665
+ const content = fs22.readFileSync(file2, "utf8");
91421
91666
  const said = (key) => new RegExp(`^${key}:`, "m").test(content.split(/\n---/)[0] ?? "");
91422
91667
  const fields = {
91423
91668
  ...ws.cloudTask && !said("source_task") ? { source_task: ws.cloudTask } : {},
@@ -91457,9 +91702,9 @@ async function send2(ws, job) {
91457
91702
  const record = readRecord(ws);
91458
91703
  const base = readBase(ws);
91459
91704
  const fail = (error, conflict = false, code) => ({ ok: false, conflict, error, ...code ? {} : { offline: true } });
91460
- const tasks = ws.cloudTask ? [{ folder: ws, record }] : filesUnder(path24.join(ws.board, ".sync")).filter((name) => name.endsWith(".json")).flatMap((name) => {
91705
+ const tasks = ws.cloudTask ? [{ folder: ws, record }] : filesUnder(path26.join(ws.board, ".sync")).filter((name) => name.endsWith(".json")).flatMap((name) => {
91461
91706
  try {
91462
- const held4 = JSON.parse(fs21.readFileSync(path24.join(ws.board, ".sync", name), "utf8"));
91707
+ const held4 = JSON.parse(fs22.readFileSync(path26.join(ws.board, ".sync", name), "utf8"));
91463
91708
  if (!held4.task || !TASK_ID.test(held4.task) || !held4.number) return [];
91464
91709
  return [{ folder: { ...ws, task: held4.task, cloudTask: held4.task, number: held4.number }, record: held4 }];
91465
91710
  } catch {
@@ -91479,7 +91724,7 @@ async function send2(ws, job) {
91479
91724
  const owed = (p2) => !job.id && busy(ws.cwd) && itemAt(p2)?.state === "open" && !base.files[rootOf(ws) + p2];
91480
91725
  for (const p2 of local) {
91481
91726
  if (owed(p2)) continue;
91482
- const content = fs21.readFileSync(path24.join(ws.board, p2), "utf8");
91727
+ const content = fs22.readFileSync(path26.join(ws.board, p2), "utf8");
91483
91728
  const remote = remoteOf(ws, shared, p2);
91484
91729
  localOf.set(remote, p2);
91485
91730
  const held4 = base.files[remote];
@@ -91511,15 +91756,15 @@ async function send2(ws, job) {
91511
91756
  changes.splice(at2, 1);
91512
91757
  delete base.files[theirs.path];
91513
91758
  writeBase(ws, base);
91514
- fs21.rmSync(path24.join(ws.board, p2), { force: true });
91759
+ fs22.rmSync(path26.join(ws.board, p2), { force: true });
91515
91760
  writeIndex(ws.board);
91516
91761
  continue;
91517
91762
  }
91518
91763
  if (at2 < 0) continue;
91519
91764
  const merged = itemAt(p2) ? { content: theirs.content } : mergeFile(base.files[theirs.path]?.content ?? null, changes[at2].content, theirs.content);
91520
91765
  changes[at2] = { path: theirs.path, content: merged.content, base_seq: theirs.seq };
91521
- if (merged.content === null) fs21.rmSync(path24.join(ws.board, p2), { force: true });
91522
- else write(path24.join(ws.board, p2), merged.content);
91766
+ if (merged.content === null) fs22.rmSync(path26.join(ws.board, p2), { force: true });
91767
+ else write(path26.join(ws.board, p2), merged.content);
91523
91768
  }
91524
91769
  if (!changes.length) return { ok: true, changed: false };
91525
91770
  sent = await commitBoard(ws.project, job.id, job.machineId, changes);
@@ -91541,7 +91786,7 @@ var BUSY_MS = 3e4;
91541
91786
  function keptFolders() {
91542
91787
  let cached2 = [];
91543
91788
  try {
91544
- cached2 = fs22.readdirSync(cacheRoot()).map((name) => path25.join(cacheRoot(), name));
91789
+ cached2 = fs23.readdirSync(cacheRoot()).map((name) => path27.join(cacheRoot(), name));
91545
91790
  } catch {
91546
91791
  }
91547
91792
  return [.../* @__PURE__ */ new Set([...allowedProjects(), ...cached2])].flatMap((dir) => [keptBoard(dir)].filter((ws) => !!ws));
@@ -91577,8 +91822,8 @@ function watchBoards(machineId2, ms, lookMs = LOOK_MS, pullMs = PULL_MS, busyMs
91577
91822
  const watch = (ws) => {
91578
91823
  let watcher;
91579
91824
  try {
91580
- watcher = fs22.watch(ws.board, { recursive: true }, (_event, name) => {
91581
- const p2 = name && String(name).split(path25.sep).join("/");
91825
+ watcher = fs23.watch(ws.board, { recursive: true }, (_event, name) => {
91826
+ const p2 = name && String(name).split(path27.sep).join("/");
91582
91827
  const held4 = watched.get(ws.cwd);
91583
91828
  if (closed || !p2 || !isBoardPath(p2) || !held4) return;
91584
91829
  const job = busy(ws.cwd);
@@ -91641,8 +91886,8 @@ function watchBoards(machineId2, ms, lookMs = LOOK_MS, pullMs = PULL_MS, busyMs
91641
91886
 
91642
91887
  // src/diff.ts
91643
91888
  import { execFile as execFile2 } from "node:child_process";
91644
- import fs23 from "node:fs";
91645
- import path26 from "node:path";
91889
+ import fs24 from "node:fs";
91890
+ import path28 from "node:path";
91646
91891
  var FILE_BYTES = 256 * 1024;
91647
91892
  var TOTAL_BYTES = 1024 * 1024;
91648
91893
  var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
@@ -91659,7 +91904,7 @@ var git2 = (dir, args, maxBuffer = 4 * TOTAL_BYTES) => new Promise(
91659
91904
  function projectDir(placed, project) {
91660
91905
  if (placed) {
91661
91906
  const real = realpath3(placed);
91662
- return real && allowedProjects().some((root) => real === root || real.startsWith(root + path26.sep)) ? real : null;
91907
+ return real && allowedProjects().some((root) => real === root || real.startsWith(root + path28.sep)) ? real : null;
91663
91908
  }
91664
91909
  let cached2;
91665
91910
  try {
@@ -91667,11 +91912,11 @@ function projectDir(placed, project) {
91667
91912
  } catch {
91668
91913
  return null;
91669
91914
  }
91670
- return fs23.existsSync(path26.join(cached2, ".git")) ? cached2 : null;
91915
+ return fs24.existsSync(path28.join(cached2, ".git")) ? cached2 : null;
91671
91916
  }
91672
91917
  function realpath3(p2) {
91673
91918
  try {
91674
- return fs23.realpathSync(p2);
91919
+ return fs24.realpathSync(p2);
91675
91920
  } catch {
91676
91921
  return null;
91677
91922
  }
@@ -91717,18 +91962,21 @@ async function answerHint(machineId2, hint2) {
91717
91962
 
91718
91963
  // ../cli/src/lib/agent/stop.ts
91719
91964
  import { spawnSync as spawnSync5 } from "node:child_process";
91720
- import fs24 from "node:fs";
91965
+ import { randomUUID as randomUUID2 } from "node:crypto";
91966
+ import fs25 from "node:fs";
91721
91967
  var runMark = (sessionId) => ({ name: RUN_ENV, value: sessionId });
91968
+ var stopMark = () => ({ name: STOP_ENV, value: randomUUID2() });
91969
+ var markEnv = (env2, mark) => ({ ...env2, [mark.name]: mark.value });
91722
91970
  var WINDOWS3 = process.platform === "win32";
91723
91971
  function markedInProc(entry, strict = false) {
91724
91972
  const pids = [];
91725
- for (const name of fs24.readdirSync("/proc")) {
91973
+ for (const name of fs25.readdirSync("/proc")) {
91726
91974
  if (!/^\d+$/.test(name)) continue;
91727
91975
  try {
91728
- if (fs24.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
91976
+ if (fs25.readFileSync(`/proc/${name}/environ`, "utf8").split("\0").includes(entry)) pids.push(Number(name));
91729
91977
  } catch (error) {
91730
91978
  if (strict && !["ENOENT", "ESRCH"].includes(error.code ?? "")) {
91731
- const stat = fs24.statSync(`/proc/${name}`, { throwIfNoEntry: false });
91979
+ const stat = fs25.statSync(`/proc/${name}`, { throwIfNoEntry: false });
91732
91980
  if (stat?.uid === process.getuid?.()) throw error;
91733
91981
  }
91734
91982
  }
@@ -91761,6 +92009,24 @@ function markedInPs(entry, roots) {
91761
92009
  }
91762
92010
  return [...found];
91763
92011
  }
92012
+ function marked(mark, roots = []) {
92013
+ if (WINDOWS3 || !mark.value) return [];
92014
+ const entry = `${mark.name}=${mark.value}`;
92015
+ try {
92016
+ const pids = process.platform === "linux" ? markedInProc(entry) : markedInPs(entry, roots);
92017
+ return pids.filter((pid) => pid !== process.pid);
92018
+ } catch {
92019
+ return [];
92020
+ }
92021
+ }
92022
+ function signal(pids, sig) {
92023
+ for (const pid of pids) {
92024
+ try {
92025
+ process.kill(pid, sig);
92026
+ } catch {
92027
+ }
92028
+ }
92029
+ }
91764
92030
  function startedUnder(list, root, since) {
91765
92031
  const self2 = list.procs.find((p2) => p2.pid === root);
91766
92032
  const from = since ?? self2?.startedAt;
@@ -91804,6 +92070,21 @@ function procsOnWindows() {
91804
92070
  });
91805
92071
  return ps.status === 0 && ps.stdout ? readProcList(ps.stdout) : void 0;
91806
92072
  }
92073
+ function killUnderOnWindows(pid, since) {
92074
+ if (!WINDOWS3 || !pid) return;
92075
+ const list = procsOnWindows();
92076
+ if (!list) return;
92077
+ const pids = startedUnder(list, pid, since).filter((p2) => p2 !== process.pid);
92078
+ if (!pids.length) return;
92079
+ spawnSync5("taskkill", ["/F", ...pids.flatMap((p2) => ["/PID", String(p2)])], { stdio: "ignore", windowsHide: true, timeout: 1e4 });
92080
+ }
92081
+ function killMarked(mark, agent) {
92082
+ if (WINDOWS3) {
92083
+ if (agent) killUnderOnWindows(agent.pid, agent.since);
92084
+ return;
92085
+ }
92086
+ signal(marked(mark), "SIGKILL");
92087
+ }
91807
92088
  function processesStopped(mark, root) {
91808
92089
  try {
91809
92090
  if (WINDOWS3) {
@@ -91821,8 +92102,8 @@ function processesStopped(mark, root) {
91821
92102
  }
91822
92103
 
91823
92104
  // src/flow.ts
91824
- import fs29 from "node:fs";
91825
- import path31 from "node:path";
92105
+ import fs30 from "node:fs";
92106
+ import path33 from "node:path";
91826
92107
 
91827
92108
  // ../cli/src/lib/question-block.ts
91828
92109
  var MODES = ["single", "multi"];
@@ -91980,13 +92261,13 @@ function reviewOf(meta) {
91980
92261
  }
91981
92262
 
91982
92263
  // src/triage.ts
91983
- import fs25 from "node:fs";
91984
- import path27 from "node:path";
92264
+ import fs26 from "node:fs";
92265
+ import path29 from "node:path";
91985
92266
 
91986
92267
  // ../cli/src/lib/signals/identity.ts
91987
- import { createHash } from "node:crypto";
92268
+ import { createHash as createHash2 } from "node:crypto";
91988
92269
  var DERIVED = "derived-";
91989
- var derivedSourceId = (seed) => `${DERIVED}${createHash("sha256").update(seed).digest("hex").slice(0, 16)}`;
92270
+ var derivedSourceId = (seed) => `${DERIVED}${createHash2("sha256").update(seed).digest("hex").slice(0, 16)}`;
91990
92271
 
91991
92272
  // ../cli/src/lib/slug.ts
91992
92273
  function slugify(s) {
@@ -91995,8 +92276,8 @@ function slugify(s) {
91995
92276
  }
91996
92277
 
91997
92278
  // src/triage.ts
91998
- var triageDir = (board) => path27.join(board, "triage");
91999
- var rejectedDir = (board) => path27.join(board, ".archive", REJECTIONS_TO_REVIEW);
92279
+ var triageDir = (board) => path29.join(board, "triage");
92280
+ var rejectedDir = (board) => path29.join(board, ".archive", REJECTIONS_TO_REVIEW);
92000
92281
  var SCRATCH = ["dismissed", "archived"];
92001
92282
  var stamp = (at2) => formatStamp(at2 ? new Date(at2) : /* @__PURE__ */ new Date());
92002
92283
  function slugOf(title) {
@@ -92005,7 +92286,7 @@ function slugOf(title) {
92005
92286
  function freeName(dir, title) {
92006
92287
  const stem = slugOf(title);
92007
92288
  let name = `${stem}.md`;
92008
- for (let n = 2; fs25.existsSync(path27.join(dir, name)); n++) name = `${stem}-${n}.md`;
92289
+ for (let n = 2; fs26.existsSync(path29.join(dir, name)); n++) name = `${stem}-${n}.md`;
92009
92290
  return name;
92010
92291
  }
92011
92292
  function itemText(item, fields = {}) {
@@ -92025,17 +92306,17 @@ ${item.text.trim()}
92025
92306
  `;
92026
92307
  }
92027
92308
  function writeItem(dir, item, fields = {}) {
92028
- fs25.mkdirSync(dir, { recursive: true });
92309
+ fs26.mkdirSync(dir, { recursive: true });
92029
92310
  const name = freeName(dir, item.title);
92030
- fs25.writeFileSync(path27.join(dir, name), itemText(item, item.workflow ? { workflow: workflowName(item.workflow), ...fields } : fields));
92311
+ fs26.writeFileSync(path29.join(dir, name), itemText(item, item.workflow ? { workflow: workflowName(item.workflow), ...fields } : fields));
92031
92312
  return name;
92032
92313
  }
92033
92314
  function layOut(board, rejections) {
92034
92315
  clearLaid(board);
92035
92316
  const dir = rejectedDir(board);
92036
- fs25.mkdirSync(dir, { recursive: true });
92317
+ fs26.mkdirSync(dir, { recursive: true });
92037
92318
  for (const t2 of rejections.tasks) {
92038
- fs25.writeFileSync(path27.join(dir, `${t2.number}-${slugify(t2.title)}.md`), [
92319
+ fs26.writeFileSync(path29.join(dir, `${t2.number}-${slugify(t2.title)}.md`), [
92039
92320
  "---",
92040
92321
  `title: ${yamlScalar(t2.title)}`,
92041
92322
  "status: todo",
@@ -92050,20 +92331,20 @@ function layOut(board, rejections) {
92050
92331
  ].join("\n"));
92051
92332
  }
92052
92333
  for (const item of rejections.triage) {
92053
- const sub = path27.join(triageDir(board), item.state === "dismissed" ? "dismissed" : "archived");
92334
+ const sub = path29.join(triageDir(board), item.state === "dismissed" ? "dismissed" : "archived");
92054
92335
  const fields = item.state === "dismissed" ? { dismissed_at: stamp(item.dismissed_at), dismissed_by: "user", ...item.dismissed_reason ? { dismissed_reason: item.dismissed_reason } : {} } : {};
92055
92336
  writeItem(sub, item, fields);
92056
92337
  }
92057
92338
  return { tasks: rejections.tasks.map((t2) => t2.id), triage: rejections.triage.map((i) => i.id) };
92058
92339
  }
92059
92340
  function clearLaid(board) {
92060
- fs25.rmSync(rejectedDir(board), { recursive: true, force: true });
92061
- for (const sub of SCRATCH) fs25.rmSync(path27.join(triageDir(board), sub), { recursive: true, force: true });
92341
+ fs26.rmSync(rejectedDir(board), { recursive: true, force: true });
92342
+ for (const sub of SCRATCH) fs26.rmSync(path29.join(triageDir(board), sub), { recursive: true, force: true });
92062
92343
  }
92063
92344
 
92064
92345
  // src/kanban-skill.ts
92065
- import fs26 from "node:fs";
92066
- import path28 from "node:path";
92346
+ import fs27 from "node:fs";
92347
+ import path30 from "node:path";
92067
92348
 
92068
92349
  // ../skill/SKILL.md
92069
92350
  var SKILL_default = '---\nname: kanban\ndescription: Manage the file-based task board with akb. Use for creating and planning tasks, revising or implementing cards, and managing background runs; also for "what\'s running".\n---\n\n# Kanban board\n\nThe board is worked only through `akb`. Find the command for the job in `akb help` and run\nit; never do its work by hand or from memory.\n\n- **`--print` does it here**: `akb card revise 12 --print "drop the export"` prints the whole\n flow for that card on this board; follow it to its closing steps. Without `--print` the\n same command starts a separate agent.\n- **Keys**: never read or write API keys; the user sets them with `akb agent set apiKey`.\n';
@@ -92076,24 +92357,24 @@ var CONTENT = `${SKILL_default.trimEnd()}
92076
92357
  var MANAGED = /^<!-- ai4kanban \S+ -->$/m;
92077
92358
  function prepareKanbanSkill(root) {
92078
92359
  for (const folder of SKILL_FOLDERS) {
92079
- const dir = path28.join(root, folder);
92080
- const file2 = path28.join(dir, "SKILL.md");
92360
+ const dir = path30.join(root, folder);
92361
+ const file2 = path30.join(dir, "SKILL.md");
92081
92362
  try {
92082
92363
  let linked = false;
92083
92364
  let at2 = root;
92084
92365
  for (const name of `${folder}/SKILL.md`.split("/")) {
92085
- at2 = path28.join(at2, name);
92086
- if (fs26.lstatSync(at2, { throwIfNoEntry: false })?.isSymbolicLink()) linked = true;
92366
+ at2 = path30.join(at2, name);
92367
+ if (fs27.lstatSync(at2, { throwIfNoEntry: false })?.isSymbolicLink()) linked = true;
92087
92368
  }
92088
- const held4 = fs26.existsSync(file2) ? fs26.readFileSync(file2, "utf8") : null;
92089
- const exists = fs26.lstatSync(dir, { throwIfNoEntry: false }) !== void 0;
92369
+ const held4 = fs27.existsSync(file2) ? fs27.readFileSync(file2, "utf8") : null;
92370
+ const exists = fs27.lstatSync(dir, { throwIfNoEntry: false }) !== void 0;
92090
92371
  if (linked || exists && !MANAGED.test(held4 ?? "")) {
92091
92372
  if (!held4?.trim()) throw new Error("\u7528\u6237\u81EA\u6709\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\u4E2D\u7F3A\u5C11\u53EF\u8BFB\u7684 SKILL.md");
92092
92373
  continue;
92093
92374
  }
92094
92375
  if (held4 !== CONTENT) {
92095
- fs26.mkdirSync(dir, { recursive: true });
92096
- fs26.writeFileSync(file2, CONTENT);
92376
+ fs27.mkdirSync(dir, { recursive: true });
92377
+ fs27.writeFileSync(file2, CONTENT);
92097
92378
  }
92098
92379
  } catch (e2) {
92099
92380
  throw new Error(`\u65E0\u6CD5\u51C6\u5907 ${folder}\uFF1A${e2 instanceof Error ? e2.message : String(e2)}`);
@@ -92103,8 +92384,8 @@ function prepareKanbanSkill(root) {
92103
92384
 
92104
92385
  // ../cli/src/lib/skill/hook.ts
92105
92386
  import { spawnSync as spawnSync6 } from "node:child_process";
92106
- import fs27 from "node:fs";
92107
- import path29 from "node:path";
92387
+ import fs28 from "node:fs";
92388
+ import path31 from "node:path";
92108
92389
 
92109
92390
  // ../cli/src/lib/agent/landing-lock.ts
92110
92391
  var landingLimits = { staleMs: 5 * 6e4 };
@@ -92131,7 +92412,7 @@ var CHECK = [
92131
92412
  "}catch(e){process.exit(0)}"
92132
92413
  ].join("");
92133
92414
  var quoted = (p2) => `"${p2.replace(/(["$`\\])/g, "\\$1")}"`;
92134
- var guardLine = (sessions) => `if command -v node >/dev/null 2>&1; then node -e '${CHECK}' ${quoted(sessions)} ${quoted(path29.join(path29.dirname(sessions), SESSIONS_FOLDER, ".landing.lock"))} || exit 1; fi`;
92415
+ var guardLine = (sessions) => `if command -v node >/dev/null 2>&1; then node -e '${CHECK}' ${quoted(sessions)} ${quoted(path31.join(path31.dirname(sessions), SESSIONS_FOLDER, ".landing.lock"))} || exit 1; fi`;
92135
92416
  function hookScript(sessions) {
92136
92417
  return [
92137
92418
  "#!/bin/sh",
@@ -92147,7 +92428,7 @@ function hookScript(sessions) {
92147
92428
  }
92148
92429
  var read = (file2) => {
92149
92430
  try {
92150
- return fs27.readFileSync(file2, "utf8");
92431
+ return fs28.readFileSync(file2, "utf8");
92151
92432
  } catch {
92152
92433
  return null;
92153
92434
  }
@@ -92159,7 +92440,7 @@ function hooksDir(root) {
92159
92440
  windowsHide: true
92160
92441
  });
92161
92442
  if (out.error || out.status !== 0 || !out.stdout.trim()) return null;
92162
- return path29.resolve(root, out.stdout.trim());
92443
+ return path31.resolve(root, out.stdout.trim());
92163
92444
  }
92164
92445
  function hasOwnHooksPath(root) {
92165
92446
  const out = spawnSync6("git", ["config", "--get", "core.hooksPath"], {
@@ -92169,21 +92450,21 @@ function hasOwnHooksPath(root) {
92169
92450
  });
92170
92451
  return !out.error && out.status === 0 && !!out.stdout.trim();
92171
92452
  }
92172
- var sessionsPath = (root, board) => path29.join(projectStateDir(board, root), SESSIONS_FILE);
92173
- function readCommitHook(root, board = path29.join(root, "docs", "kanban")) {
92453
+ var sessionsPath = (root, board) => path31.join(projectStateDir(board, root), SESSIONS_FILE);
92454
+ function readCommitHook(root, board = path31.join(root, "docs", "kanban")) {
92174
92455
  const dir = hooksDir(root);
92175
92456
  if (!dir) return { state: "no-git" };
92176
- const where = shown(root, path29.join(dir, "pre-commit"));
92457
+ const where = shown(root, path31.join(dir, "pre-commit"));
92177
92458
  const line = guardLine(sessionsPath(root, board));
92178
92459
  if (hasOwnHooksPath(root)) return { state: "hooks-path", path: where, line };
92179
- const file2 = path29.join(dir, "pre-commit");
92180
- if (fs27.lstatSync(file2, { throwIfNoEntry: false })?.isSymbolicLink()) return { state: "foreign", path: where, line };
92460
+ const file2 = path31.join(dir, "pre-commit");
92461
+ if (fs28.lstatSync(file2, { throwIfNoEntry: false })?.isSymbolicLink()) return { state: "foreign", path: where, line };
92181
92462
  const text3 = read(file2);
92182
92463
  if (text3 === null) return { state: "absent", path: where };
92183
92464
  return MARKER_LINE.test(text3) ? { state: "ours", path: where } : { state: "foreign", path: where, line };
92184
92465
  }
92185
- var shown = (root, p2) => (path29.relative(root, p2) || p2).split(path29.sep).join("/");
92186
- function installCommitHook(root, board = path29.join(root, "docs", "kanban")) {
92466
+ var shown = (root, p2) => (path31.relative(root, p2) || p2).split(path31.sep).join("/");
92467
+ function installCommitHook(root, board = path31.join(root, "docs", "kanban")) {
92187
92468
  try {
92188
92469
  return placeCommitHook(root, board);
92189
92470
  } catch (e2) {
@@ -92200,12 +92481,12 @@ function placeCommitHook(root, board) {
92200
92481
  ${hook.line}`
92201
92482
  };
92202
92483
  }
92203
- const file2 = path29.join(root, hook.path);
92484
+ const file2 = path31.join(root, hook.path);
92204
92485
  try {
92205
- fs27.mkdirSync(path29.dirname(file2), { recursive: true });
92486
+ fs28.mkdirSync(path31.dirname(file2), { recursive: true });
92206
92487
  const text3 = hookScript(sessionsPath(root, board));
92207
- if (read(file2) !== text3) fs27.writeFileSync(file2, text3, { mode: 493 });
92208
- fs27.chmodSync(file2, 493);
92488
+ if (read(file2) !== text3) fs28.writeFileSync(file2, text3, { mode: 493 });
92489
+ fs28.chmodSync(file2, 493);
92209
92490
  } catch (e2) {
92210
92491
  return { note: `${hook.path} could not be written \u2014 ${e2 instanceof Error ? e2.message : String(e2)}` };
92211
92492
  }
@@ -92213,8 +92494,8 @@ ${hook.line}`
92213
92494
  }
92214
92495
 
92215
92496
  // src/built.ts
92216
- import fs28 from "node:fs";
92217
- import path30 from "node:path";
92497
+ import fs29 from "node:fs";
92498
+ import path32 from "node:path";
92218
92499
 
92219
92500
  // ../cli/src/lib/spec-agent-names.ts
92220
92501
  var LEGACY_SPEC_AGENT_NAMES = {
@@ -92390,12 +92671,12 @@ function checkWorkflowFiles(value, official = false) {
92390
92671
 
92391
92672
  // src/built.ts
92392
92673
  var isBuilder = (ws) => ws.workflow === BUILDER;
92393
- var draftDir = (ws) => path30.join(ws.board, "workflows", draftFolder(ws.task));
92394
- var draftLine = (ws) => `<!-- workflow folder: ${path30.relative(ws.cwd, draftDir(ws)).split(path30.sep).join("/")}/ -->`;
92674
+ var draftDir = (ws) => path32.join(ws.board, "workflows", draftFolder(ws.task));
92675
+ var draftLine = (ws) => `<!-- workflow folder: ${path32.relative(ws.cwd, draftDir(ws)).split(path32.sep).join("/")}/ -->`;
92395
92676
  var skeleton = (name) => ["---", `name: ${/\s#/.test(name) ? JSON.stringify(name) : yamlScalar(name)}`, "needsArtifact: false", "---", ""].join("\n");
92396
92677
  async function placeDraft(ws, read2) {
92397
92678
  const dir = draftDir(ws);
92398
- if (fs28.existsSync(path30.join(dir, WORKFLOW_FILE))) return { ok: true, value: void 0 };
92679
+ if (fs29.existsSync(path32.join(dir, WORKFLOW_FILE))) return { ok: true, value: void 0 };
92399
92680
  const task = await read2.readTask(ws.task);
92400
92681
  if (!task.ok) return task;
92401
92682
  let files = { [WORKFLOW_FILE]: skeleton(task.value.title.trim() || "Untitled") };
@@ -92406,10 +92687,10 @@ async function placeDraft(ws, read2) {
92406
92687
  files = held4.value.files;
92407
92688
  }
92408
92689
  for (const [name, text3] of Object.entries(files)) {
92409
- const file2 = path30.resolve(dir, name);
92410
- if (!file2.startsWith(dir + path30.sep)) continue;
92411
- fs28.mkdirSync(path30.dirname(file2), { recursive: true });
92412
- fs28.writeFileSync(file2, text3);
92690
+ const file2 = path32.resolve(dir, name);
92691
+ if (!file2.startsWith(dir + path32.sep)) continue;
92692
+ fs29.mkdirSync(path32.dirname(file2), { recursive: true });
92693
+ fs29.writeFileSync(file2, text3);
92413
92694
  }
92414
92695
  return { ok: true, value: void 0 };
92415
92696
  }
@@ -92421,19 +92702,19 @@ async function builtWorkflow(ws, akb) {
92421
92702
  const dir = draftDir(ws);
92422
92703
  let source;
92423
92704
  try {
92424
- source = fs28.readFileSync(path30.join(dir, WORKFLOW_FILE), "utf8");
92705
+ source = fs29.readFileSync(path32.join(dir, WORKFLOW_FILE), "utf8");
92425
92706
  } catch {
92426
92707
  return fail(`workflows/${id}/${WORKFLOW_FILE} is missing.`);
92427
92708
  }
92428
92709
  const read2 = parseWorkflowFile(id, source, `workflows/${id}/${WORKFLOW_FILE}`);
92429
92710
  if ("problem" in read2) return fail(read2.problem);
92430
92711
  const names2 = named(read2.def);
92431
- fs28.rmSync(path30.join(dir, "agents"), { recursive: true, force: true });
92712
+ fs29.rmSync(path32.join(dir, "agents"), { recursive: true, force: true });
92432
92713
  for (const name of names2) {
92433
- const from = path30.join(ws.board, "agents", name);
92434
- if (fs28.existsSync(from)) fs28.cpSync(from, path30.join(dir, "agents", name), { recursive: true });
92714
+ const from = path32.join(ws.board, "agents", name);
92715
+ if (fs29.existsSync(from)) fs29.cpSync(from, path32.join(dir, "agents", name), { recursive: true });
92435
92716
  }
92436
- const files = Object.fromEntries(filesUnder(dir).map((f2) => [f2, fs28.readFileSync(path30.join(dir, f2), "utf8")]));
92717
+ const files = Object.fromEntries(filesUnder(dir).map((f2) => [f2, fs29.readFileSync(path32.join(dir, f2), "utf8")]));
92437
92718
  const checked = checkWorkflowFiles(files);
92438
92719
  if (!checked.ok) return fail(`${checked.file} ${checked.reason}.`);
92439
92720
  const run2 = async (args) => answerOf2(await runAkb(akb, [...args, "--json", "--board", ws.board], { cwd: ws.cwd, board: ws.board }));
@@ -92468,7 +92749,7 @@ function spentBy(runs) {
92468
92749
  }
92469
92750
  function reviewOf2(file2) {
92470
92751
  try {
92471
- const meta = parseFrontmatter(fs29.readFileSync(file2, "utf8")).meta;
92752
+ const meta = parseFrontmatter(fs30.readFileSync(file2, "utf8")).meta;
92472
92753
  return meta ? reviewOf(meta) : null;
92473
92754
  } catch {
92474
92755
  return null;
@@ -92476,8 +92757,8 @@ function reviewOf2(file2) {
92476
92757
  }
92477
92758
  function mcpConfig(servers, file2) {
92478
92759
  if (!Object.keys(servers).length) return null;
92479
- fs29.mkdirSync(path31.dirname(file2), { recursive: true });
92480
- fs29.writeFileSync(file2, `${JSON.stringify({ mcpServers: servers }, null, 2)}
92760
+ fs30.mkdirSync(path33.dirname(file2), { recursive: true });
92761
+ fs30.writeFileSync(file2, `${JSON.stringify({ mcpServers: servers }, null, 2)}
92481
92762
  `);
92482
92763
  return file2;
92483
92764
  }
@@ -92516,7 +92797,7 @@ function deliveryEnding(ws, id) {
92516
92797
  if (!id) return null;
92517
92798
  let held4;
92518
92799
  try {
92519
- held4 = JSON.parse(fs29.readFileSync(path31.join(ws.board, "deliveries", `${path31.basename(id)}.json`), "utf8"));
92800
+ held4 = JSON.parse(fs30.readFileSync(path33.join(ws.board, "deliveries", `${path33.basename(id)}.json`), "utf8"));
92520
92801
  } catch {
92521
92802
  return null;
92522
92803
  }
@@ -92528,16 +92809,16 @@ function deliveryEnding(ws, id) {
92528
92809
  return null;
92529
92810
  }
92530
92811
  function checkoutsOf(ws, id) {
92531
- const dir = path31.join(ws.board, "deliveries");
92812
+ const dir = path33.join(ws.board, "deliveries");
92532
92813
  let names2;
92533
92814
  try {
92534
- names2 = fs29.readdirSync(dir).filter((n) => n.endsWith(".json"));
92815
+ names2 = fs30.readdirSync(dir).filter((n) => n.endsWith(".json"));
92535
92816
  } catch {
92536
92817
  return [];
92537
92818
  }
92538
92819
  return names2.flatMap((n) => {
92539
92820
  try {
92540
- const held4 = JSON.parse(fs29.readFileSync(path31.join(dir, n), "utf8"));
92821
+ const held4 = JSON.parse(fs30.readFileSync(path33.join(dir, n), "utf8"));
92541
92822
  return held4.cardId === id && held4.worktree && held4.deliveryId ? [held4.deliveryId] : [];
92542
92823
  } catch {
92543
92824
  return [];
@@ -92591,7 +92872,7 @@ function boardAkb(job, ws, deps, io, task) {
92591
92872
  const at2 = args.includes("--") ? args.indexOf("--") : args.length;
92592
92873
  const full = [...args.slice(0, at2), "--board", ws.board, ...args.slice(at2)];
92593
92874
  const log = show ? logLines(io.show, io.event) : void 0;
92594
- const ran = await runAkb(deps.akb, full, { cwd: ws.cwd, board: ws.board, task, line: job.languageLine ?? "", env: { ...env2, AI4KANBAN_EXECUTION: JSON.stringify({ job: job.id, machine: deps.machineId }) }, show: log?.push, track: io.track });
92875
+ const ran = await runAkb(deps.akb, full, { cwd: ws.cwd, board: ws.board, task, language: cloudLanguage(job), env: { ...env2, AI4KANBAN_EXECUTION: JSON.stringify({ job: job.id, machine: deps.machineId }) }, show: log?.push, track: io.track });
92595
92876
  log?.end();
92596
92877
  return ran;
92597
92878
  };
@@ -92601,7 +92882,7 @@ async function setUp(job, ws, deps, io) {
92601
92882
  const runtime = job.runtime;
92602
92883
  const missing = unavailable(runtime, await deps.runtimes());
92603
92884
  if (missing) return missing;
92604
- if (!fs29.existsSync(path31.join(ws.board, "config.md"))) {
92885
+ if (!fs30.existsSync(path33.join(ws.board, "config.md"))) {
92605
92886
  const init = await akb(["raw", "init"]);
92606
92887
  if (init.code !== 0) return refusal(init);
92607
92888
  }
@@ -92618,7 +92899,7 @@ async function setUp(job, ws, deps, io) {
92618
92899
  const used = await akb(["agent", "use", runtime]);
92619
92900
  if (used.code !== 0) return refusal(used);
92620
92901
  const servers = mcpServersOf(ws);
92621
- const mcpFile = path31.join(ws.board, ".sync", `${ws.task}.mcp.json`);
92902
+ const mcpFile = path33.join(ws.board, ".sync", `${ws.task}.mcp.json`);
92622
92903
  const takes = new Set(settingKeys(runtime));
92623
92904
  const given = job.settings ?? {};
92624
92905
  const values = { ...given, model: job.model ?? "" };
@@ -92676,8 +92957,8 @@ async function reviewRejections(job, ws, deps, io) {
92676
92957
  if (ran.code !== 0) return { runs, failure: refusal(ran) };
92677
92958
  const sent = await push2({ id: job.id, machineId: deps.machineId });
92678
92959
  if (!sent.ok) return { runs, failure: sent.error };
92679
- const marked = await deps.api.markReviewed(laid.tasks, laid.triage);
92680
- return marked.ok ? { runs } : { runs, failure: marked.error };
92960
+ const marked2 = await deps.api.markReviewed(laid.tasks, laid.triage);
92961
+ return marked2.ok ? { runs } : { runs, failure: marked2.error };
92681
92962
  });
92682
92963
  }
92683
92964
  async function learnRejections(job, ws, deps, io) {
@@ -92718,19 +92999,19 @@ async function runAgent(job, ws, deps, io) {
92718
92999
  }
92719
93000
  function writeTranscript(job, dir) {
92720
93001
  if (!job.transcript) return "";
92721
- const file2 = path31.join(dir, "discussion.md");
92722
- fs29.mkdirSync(dir, { recursive: true });
92723
- fs29.writeFileSync(file2, `${job.transcript}
93002
+ const file2 = path33.join(dir, "discussion.md");
93003
+ fs30.mkdirSync(dir, { recursive: true });
93004
+ fs30.writeFileSync(file2, `${job.transcript}
92724
93005
  `);
92725
93006
  return file2;
92726
93007
  }
92727
93008
  function pictures(job, ws, id) {
92728
93009
  const dir = cardAssetsDir(ws, id);
92729
93010
  if (!job.attachments.length || !dir) return "";
92730
- fs29.mkdirSync(dir, { recursive: true });
93011
+ fs30.mkdirSync(dir, { recursive: true });
92731
93012
  return job.attachments.map((a3, i) => {
92732
- const file2 = `${i + 1}-${path31.basename(a3.name).replace(/[^\w.-]+/g, "-")}`;
92733
- fs29.writeFileSync(path31.join(dir, file2), Buffer.from(a3.content, "base64"));
93013
+ const file2 = `${i + 1}-${path33.basename(a3.name).replace(/[^\w.-]+/g, "-")}`;
93014
+ fs30.writeFileSync(path33.join(dir, file2), Buffer.from(a3.content, "base64"));
92734
93015
  return `
92735
93016
  <Asset src=".assets/${id}/${file2}" label="${a3.name.replace(/["<>]/g, "") || file2}" />
92736
93017
  `;
@@ -92738,7 +93019,7 @@ function pictures(job, ws, id) {
92738
93019
  }
92739
93020
  function cardFlags(ws, fields) {
92740
93021
  if (!fields) return [];
92741
- const open = (keys) => (keys ?? []).map((k) => Number(/(\d+)$/.exec(k)?.[1])).filter((n) => n > 0 && n !== ws.number && findCard(ws.board, n)?.startsWith(path31.join(ws.board, "todo") + path31.sep));
93022
+ const open = (keys) => (keys ?? []).map((k) => Number(/(\d+)$/.exec(k)?.[1])).filter((n) => n > 0 && n !== ws.number && findCard(ws.board, n)?.startsWith(path33.join(ws.board, "todo") + path33.sep));
92742
93023
  const lists = [["--modules", fields.modules ?? []], ["--related", open(fields.related)], ["--blocked-by", open(fields.blocked_by)]];
92743
93024
  return [
92744
93025
  ...fields.priority ? ["--priority", fields.priority] : [],
@@ -92772,10 +93053,10 @@ async function runFlow(job, ws, deps, io) {
92772
93053
  const placed = await placeDraft(ws, deps.api);
92773
93054
  if (!placed.ok) return fail(placed.error);
92774
93055
  }
92775
- const body = path31.join(ws.board, ".sync", `${ws.task}.request.md`);
92776
- fs29.mkdirSync(path31.dirname(body), { recursive: true });
93056
+ const body = path33.join(ws.board, ".sync", `${ws.task}.request.md`);
93057
+ fs30.mkdirSync(path33.dirname(body), { recursive: true });
92777
93058
  if (!ws.number) return fail("This task has no number yet.");
92778
- fs29.writeFileSync(body, `${said.request || title}
93059
+ fs30.writeFileSync(body, `${said.request || title}
92779
93060
  ${isBuilder(ws) ? `
92780
93061
  ${draftLine(ws)}
92781
93062
  ` : ""}${pictures(job, ws, ws.number)}`);
@@ -92793,7 +93074,7 @@ ${draftLine(ws)}
92793
93074
  ...cardFlags(ws, flow.metadata),
92794
93075
  "--json"
92795
93076
  ]));
92796
- fs29.rmSync(body, { force: true });
93077
+ fs30.rmSync(body, { force: true });
92797
93078
  if (!created.ok) return fail(created.error);
92798
93079
  id = created.value;
92799
93080
  }
@@ -92809,7 +93090,7 @@ ${draftLine(ws)}
92809
93090
  if (io.lost()) return fail("");
92810
93091
  return await workOn(card);
92811
93092
  } finally {
92812
- fs29.rmSync(path31.join(ws.board, ".sync", `${ws.task}.discussion`), { recursive: true, force: true });
93093
+ fs30.rmSync(path33.join(ws.board, ".sync", `${ws.task}.discussion`), { recursive: true, force: true });
92813
93094
  const left = working.get(key) - 1;
92814
93095
  if (left) working.set(key, left);
92815
93096
  else working.delete(key);
@@ -92882,7 +93163,7 @@ ${draftLine(ws)}
92882
93163
  }
92883
93164
  function carryArgs(verb) {
92884
93165
  if (!["refine", "resolve", "revise", "implement"].includes(verb)) return [];
92885
- const transcript = writeTranscript(job, path31.join(ws.board, ".sync", `${ws.task}.discussion`));
93166
+ const transcript = writeTranscript(job, path33.join(ws.board, ".sync", `${ws.task}.discussion`));
92886
93167
  return [
92887
93168
  ...job.resume ? ["--session", job.resume, ...job.fork ? ["--fork"] : []] : [],
92888
93169
  ...transcript ? ["--transcript", transcript] : []
@@ -92901,15 +93182,15 @@ async function confirmFlowStopped(ws, job, deps) {
92901
93182
  }
92902
93183
 
92903
93184
  // src/recovery.ts
92904
- import fs30 from "node:fs";
92905
- import path32 from "node:path";
93185
+ import fs31 from "node:fs";
93186
+ import path34 from "node:path";
92906
93187
  var PREPARE_REASON = "Runner update prepare.";
92907
93188
  var UPDATE_REASON = "Runner update checkpoint.";
92908
93189
  var UNCERTAIN = "\u66F4\u65B0\u4E2D\u65AD\u65F6\u6709\u64CD\u4F5C\u7ED3\u679C\u5C1A\u672A\u786E\u8BA4\uFF0C\u8BF7\u68C0\u67E5\u5DF2\u6709\u7ED3\u679C\u540E\u518D\u7EED\u8DD1\u3002";
92909
- var fileOf = (id) => path32.join(runnerHome(), "checkpoints", `${encodeURIComponent(id)}.json`);
93190
+ var fileOf = (id) => path34.join(runnerHome(), "checkpoints", `${encodeURIComponent(id)}.json`);
92910
93191
  function readCheckpoint(id) {
92911
93192
  try {
92912
- const held4 = JSON.parse(fs30.readFileSync(fileOf(id), "utf8"));
93193
+ const held4 = JSON.parse(fs31.readFileSync(fileOf(id), "utf8"));
92913
93194
  if (!held4 || !Array.isArray(held4.pending) || !held4.pending.every((id2) => typeof id2 === "string")) throw new Error("\u8FDB\u5EA6\u683C\u5F0F\u9519\u8BEF");
92914
93195
  return held4;
92915
93196
  } catch (e2) {
@@ -92919,12 +93200,12 @@ function readCheckpoint(id) {
92919
93200
  }
92920
93201
  function saveCheckpoint(id, checkpoint) {
92921
93202
  const file2 = fileOf(id);
92922
- fs30.mkdirSync(path32.dirname(file2), { recursive: true, mode: 448 });
92923
- fs30.writeFileSync(`${file2}.tmp`, JSON.stringify(checkpoint), { mode: 384 });
92924
- fs30.renameSync(`${file2}.tmp`, file2);
93203
+ fs31.mkdirSync(path34.dirname(file2), { recursive: true, mode: 448 });
93204
+ fs31.writeFileSync(`${file2}.tmp`, JSON.stringify(checkpoint), { mode: 384 });
93205
+ fs31.renameSync(`${file2}.tmp`, file2);
92925
93206
  }
92926
93207
  function clearCheckpoint(id) {
92927
- fs30.rmSync(fileOf(id), { force: true });
93208
+ fs31.rmSync(fileOf(id), { force: true });
92928
93209
  }
92929
93210
  function checkpointEvent(checkpoint, event2) {
92930
93211
  if (event2.kind === "tool_call") {
@@ -92936,19 +93217,30 @@ function checkpointEvent(checkpoint, event2) {
92936
93217
  checkpoint.pending = checkpoint.pending.filter((id) => id !== event2.data.toolCallId);
92937
93218
  }
92938
93219
  }
93220
+ async function stopReply(id, timeout) {
93221
+ const checkpoint = readCheckpoint(id);
93222
+ if (!checkpoint?.mark) return checkpoint?.started === false;
93223
+ killMarked(checkpoint.mark, checkpoint.process);
93224
+ const until = Date.now() + timeout;
93225
+ while (!processesStopped(checkpoint.mark, checkpoint.process)) {
93226
+ if (Date.now() >= until) return false;
93227
+ await new Promise((resolve2) => setTimeout(resolve2, 50));
93228
+ }
93229
+ return true;
93230
+ }
92939
93231
 
92940
93232
  // src/lock.ts
92941
- import crypto4 from "node:crypto";
92942
- import fs31 from "node:fs";
93233
+ import crypto5 from "node:crypto";
93234
+ import fs32 from "node:fs";
92943
93235
  import net from "node:net";
92944
- import path33 from "node:path";
93236
+ import path35 from "node:path";
92945
93237
  var RETRY_MS = 5e3;
92946
93238
  var KIND_MS = 1e3;
92947
93239
  var HOLDERS = ["claude-code", "codex", "terminal"];
92948
93240
  function lockPath() {
92949
93241
  const home4 = runnerHome();
92950
- if (process.platform === "win32") return `\\\\.\\pipe\\wildcrane-${crypto4.createHash("sha256").update(home4).digest("hex").slice(0, 16)}`;
92951
- return path33.join(home4, "runner.sock");
93242
+ if (process.platform === "win32") return `\\\\.\\pipe\\wildcrane-${crypto5.createHash("sha256").update(home4).digest("hex").slice(0, 16)}`;
93243
+ return path35.join(home4, "runner.sock");
92952
93244
  }
92953
93245
  function holdMachine(kind, onHeld, onWaiting) {
92954
93246
  const where = lockPath();
@@ -92963,12 +93255,12 @@ function holdMachine(kind, onHeld, onWaiting) {
92963
93255
  const attempt = () => {
92964
93256
  timer = null;
92965
93257
  if (closed) return;
92966
- if (process.platform !== "win32") fs31.mkdirSync(path33.dirname(where), { recursive: true, mode: 448 });
93258
+ if (process.platform !== "win32") fs32.mkdirSync(path35.dirname(where), { recursive: true, mode: 448 });
92967
93259
  const s = net.createServer((peer) => {
92968
93260
  peers.add(peer);
92969
93261
  peer.on("error", () => {
92970
93262
  }).on("close", () => peers.delete(peer));
92971
- peer.write(`${JSON.stringify({ kind })}
93263
+ peer.write(`${JSON.stringify({ kind, settings: 1 })}
92972
93264
  `);
92973
93265
  });
92974
93266
  s.once("error", (e2) => {
@@ -93011,7 +93303,7 @@ function holdMachine(kind, onHeld, onWaiting) {
93011
93303
  later(0);
93012
93304
  });
93013
93305
  removeIfStill.once("error", () => {
93014
- fs31.rmSync(where, { force: true });
93306
+ fs32.rmSync(where, { force: true });
93015
93307
  later(50 + Math.random() * 200);
93016
93308
  });
93017
93309
  return;
@@ -93040,17 +93332,17 @@ function holdMachine(kind, onHeld, onWaiting) {
93040
93332
  }
93041
93333
 
93042
93334
  // src/handoff.ts
93043
- import crypto5 from "node:crypto";
93044
- import fs32 from "node:fs";
93045
- import path34 from "node:path";
93046
- import { execFileSync } from "node:child_process";
93047
- var digest = (value) => crypto5.createHash("sha256").update(value).digest("hex");
93048
- var git3 = (cwd, ...args) => execFileSync("git", ["-C", cwd, ...args], { encoding: "utf8", timeout: 15e3, maxBuffer: 16 * 1024 * 1024, stdio: ["ignore", "pipe", "ignore"] });
93335
+ import crypto6 from "node:crypto";
93336
+ import fs33 from "node:fs";
93337
+ import path36 from "node:path";
93338
+ import { execFileSync as execFileSync2 } from "node:child_process";
93339
+ var digest = (value) => crypto6.createHash("sha256").update(value).digest("hex");
93340
+ var git3 = (cwd, ...args) => execFileSync2("git", ["-C", cwd, ...args], { encoding: "utf8", timeout: 15e3, maxBuffer: 16 * 1024 * 1024, stdio: ["ignore", "pipe", "ignore"] });
93049
93341
  function authorizedFolder(folder) {
93050
93342
  if (!folder) return null;
93051
93343
  try {
93052
- const real = fs32.realpathSync(folder);
93053
- return allowedProjects().some((root) => real === root || real.startsWith(root + path34.sep)) ? real : null;
93344
+ const real = fs33.realpathSync(folder);
93345
+ return allowedProjects().some((root) => real === root || real.startsWith(root + path36.sep)) ? real : null;
93054
93346
  } catch {
93055
93347
  return null;
93056
93348
  }
@@ -93060,9 +93352,9 @@ function progressFolder(job, folder) {
93060
93352
  if (!token) return folder;
93061
93353
  if (!/^[a-zA-Z0-9-]{1,80}$/.test(token)) return null;
93062
93354
  try {
93063
- const dir = fs32.realpathSync(path34.join(folder, ".akb", "handoffs", token));
93064
- if (!dir.startsWith(fs32.realpathSync(folder) + path34.sep)) return null;
93065
- const marker = JSON.parse(fs32.readFileSync(path34.join(dir, ".akb", "handoff.json"), "utf8"));
93355
+ const dir = fs33.realpathSync(path36.join(folder, ".akb", "handoffs", token));
93356
+ if (!dir.startsWith(fs33.realpathSync(folder) + path36.sep)) return null;
93357
+ const marker = JSON.parse(fs33.readFileSync(path36.join(dir, ".akb", "handoff.json"), "utf8"));
93066
93358
  return marker.task === job.task && marker.isolation === token ? dir : null;
93067
93359
  } catch {
93068
93360
  return null;
@@ -93070,24 +93362,24 @@ function progressFolder(job, folder) {
93070
93362
  }
93071
93363
  function progressEvidence(job, folder) {
93072
93364
  try {
93073
- const board = path34.join(folder, ".akb", "kanban");
93074
- const deliveries = path34.join(board, "deliveries");
93075
- const rows = fs32.readdirSync(deliveries).filter((name) => name.endsWith(".json")).map((name) => JSON.parse(fs32.readFileSync(path34.join(deliveries, name), "utf8"))).filter((row2) => row2.cardId === job.number).sort((a3, b2) => (b2.startedAt ?? 0) - (a3.startedAt ?? 0));
93365
+ const board = path36.join(folder, ".akb", "kanban");
93366
+ const deliveries = path36.join(board, "deliveries");
93367
+ const rows = fs33.readdirSync(deliveries).filter((name) => name.endsWith(".json")).map((name) => JSON.parse(fs33.readFileSync(path36.join(deliveries, name), "utf8"))).filter((row2) => row2.cardId === job.number).sort((a3, b2) => (b2.startedAt ?? 0) - (a3.startedAt ?? 0));
93076
93368
  const row = rows[0];
93077
93369
  if (!row?.deliveryId || !row.base || !row.branch || !row.worktree || row.commitMode !== "auto") return null;
93078
- const tree = fs32.realpathSync(path34.resolve(folder, row.worktree));
93079
- if (!tree.startsWith(fs32.realpathSync(folder) + path34.sep)) return null;
93370
+ const tree = fs33.realpathSync(path36.resolve(folder, row.worktree));
93371
+ if (!tree.startsWith(fs33.realpathSync(folder) + path36.sep)) return null;
93080
93372
  if (git3(tree, "rev-parse", "--show-toplevel").trim() !== tree) return null;
93081
93373
  if (git3(tree, "branch", "--show-current").trim() !== row.branch) return null;
93082
93374
  git3(tree, "cat-file", "-e", `${row.base}^{commit}`);
93083
93375
  const changed = git3(tree, "ls-files", "-z", "--cached", "--others", "--exclude-standard").split("\0").filter(Boolean).sort();
93084
93376
  const files = changed.map((name) => {
93085
- const file2 = path34.join(tree, name);
93377
+ const file2 = path36.join(tree, name);
93086
93378
  try {
93087
- const stat = fs32.lstatSync(file2);
93088
- if (stat.isSymbolicLink()) return [name, "link", digest(fs32.readlinkSync(file2))];
93379
+ const stat = fs33.lstatSync(file2);
93380
+ if (stat.isSymbolicLink()) return [name, "link", digest(fs33.readlinkSync(file2))];
93089
93381
  if (!stat.isFile()) throw new Error("\u65E0\u6CD5\u9A8C\u8BC1\u6587\u4EF6\u7C7B\u578B");
93090
- return [name, stat.mode & 511, digest(fs32.readFileSync(file2))];
93382
+ return [name, stat.mode & 511, digest(fs33.readFileSync(file2))];
93091
93383
  } catch (e2) {
93092
93384
  if (e2.code === "ENOENT") return [name, "deleted"];
93093
93385
  throw e2;
@@ -93112,56 +93404,56 @@ function progressEvidence(job, folder) {
93112
93404
  function restartFolder(job, folder) {
93113
93405
  const token = job.handoff?.isolation ?? job.id;
93114
93406
  if (!/^[a-zA-Z0-9-]{1,80}$/.test(token)) throw new Error("\u9694\u79BB\u76EE\u5F55\u7F16\u53F7\u65E0\u6548\u3002");
93115
- const dir = path34.join(folder, ".akb", "handoffs", token);
93116
- const marker = path34.join(dir, ".akb", "handoff.json");
93117
- for (const parent of [path34.join(folder, ".akb"), path34.dirname(dir), dir]) {
93118
- if (fs32.lstatSync(parent, { throwIfNoEntry: false })?.isSymbolicLink()) throw new Error("\u9694\u79BB\u76EE\u5F55\u4E0D\u80FD\u901A\u8FC7\u7B26\u53F7\u94FE\u63A5\u521B\u5EFA\u3002");
93407
+ const dir = path36.join(folder, ".akb", "handoffs", token);
93408
+ const marker = path36.join(dir, ".akb", "handoff.json");
93409
+ for (const parent of [path36.join(folder, ".akb"), path36.dirname(dir), dir]) {
93410
+ if (fs33.lstatSync(parent, { throwIfNoEntry: false })?.isSymbolicLink()) throw new Error("\u9694\u79BB\u76EE\u5F55\u4E0D\u80FD\u901A\u8FC7\u7B26\u53F7\u94FE\u63A5\u521B\u5EFA\u3002");
93119
93411
  }
93120
- if (fs32.existsSync(dir)) {
93412
+ if (fs33.existsSync(dir)) {
93121
93413
  if (job.handoff?.recovering) {
93122
93414
  try {
93123
- const saved = JSON.parse(fs32.readFileSync(marker, "utf8"));
93124
- if (saved.task === job.task && saved.isolation === token && git3(dir, "rev-parse", "--show-toplevel").trim() === fs32.realpathSync(dir)) return dir;
93415
+ const saved = JSON.parse(fs33.readFileSync(marker, "utf8"));
93416
+ if (saved.task === job.task && saved.isolation === token && git3(dir, "rev-parse", "--show-toplevel").trim() === fs33.realpathSync(dir)) return dir;
93125
93417
  } catch {
93126
93418
  }
93127
93419
  }
93128
93420
  throw new Error("\u91CD\u542F\u76EE\u5F55\u5DF2\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u3002");
93129
93421
  }
93130
- fs32.mkdirSync(path34.dirname(dir), { recursive: true });
93422
+ fs33.mkdirSync(path36.dirname(dir), { recursive: true });
93131
93423
  git3(folder, "worktree", "add", "--detach", dir, "HEAD");
93132
- if (fs32.lstatSync(path34.dirname(marker), { throwIfNoEntry: false })?.isSymbolicLink()) throw new Error("\u9694\u79BB\u6807\u8BB0\u76EE\u5F55\u65E0\u6548\u3002");
93133
- fs32.mkdirSync(path34.dirname(marker), { recursive: true });
93134
- fs32.writeFileSync(marker, JSON.stringify({ task: job.task, isolation: token }), { flag: "wx" });
93424
+ if (fs33.lstatSync(path36.dirname(marker), { throwIfNoEntry: false })?.isSymbolicLink()) throw new Error("\u9694\u79BB\u6807\u8BB0\u76EE\u5F55\u65E0\u6548\u3002");
93425
+ fs33.mkdirSync(path36.dirname(marker), { recursive: true });
93426
+ fs33.writeFileSync(marker, JSON.stringify({ task: job.task, isolation: token }), { flag: "wx" });
93135
93427
  return dir;
93136
93428
  }
93137
93429
 
93138
93430
  // src/jobs.ts
93139
93431
  import { spawn as spawn4 } from "node:child_process";
93140
- import fs34 from "node:fs";
93141
- import path36 from "node:path";
93432
+ import fs35 from "node:fs";
93433
+ import path38 from "node:path";
93142
93434
 
93143
93435
  // ../cli/src/lib/agent/catalog.ts
93144
- import fs33 from "node:fs";
93145
- import path35 from "node:path";
93436
+ import fs34 from "node:fs";
93437
+ import path37 from "node:path";
93146
93438
  var CATALOGUE = "https://models.dev/api.json";
93147
93439
  var FRESH_FOR_MS = 24 * 60 * 6e4;
93148
93440
  var ANSWER_BY_MS = 2e4;
93149
93441
  function cacheFile() {
93150
- return path35.join(machineHome(), "models-dev.json");
93442
+ return path37.join(machineHome(), "models-dev.json");
93151
93443
  }
93152
93444
  var held2;
93153
93445
  function readCache() {
93154
93446
  const file2 = cacheFile();
93155
93447
  let stamp2;
93156
93448
  try {
93157
- const stat = fs33.statSync(file2);
93449
+ const stat = fs34.statSync(file2);
93158
93450
  stamp2 = `${stat.mtimeMs}:${stat.size}`;
93159
93451
  } catch {
93160
93452
  return void 0;
93161
93453
  }
93162
93454
  if (held2?.stamp === stamp2) return held2.cached;
93163
93455
  try {
93164
- const data = JSON.parse(fs33.readFileSync(file2, "utf8"));
93456
+ const data = JSON.parse(fs34.readFileSync(file2, "utf8"));
93165
93457
  if (!data || typeof data.limits !== "object" || !data.limits) return void 0;
93166
93458
  const cached2 = {
93167
93459
  fetchedAt: typeof data.fetchedAt === "number" ? data.fetchedAt : 0,
@@ -93200,8 +93492,8 @@ function refreshCatalog() {
93200
93492
  const limits = limitsIn(await answer.json());
93201
93493
  if (Object.keys(limits).length === 0) return;
93202
93494
  const file2 = cacheFile();
93203
- fs33.mkdirSync(path35.dirname(file2), { recursive: true });
93204
- fs33.writeFileSync(file2, JSON.stringify({ fetchedAt: Date.now(), limits }));
93495
+ fs34.mkdirSync(path37.dirname(file2), { recursive: true });
93496
+ fs34.writeFileSync(file2, JSON.stringify({ fetchedAt: Date.now(), limits }));
93205
93497
  } catch {
93206
93498
  }
93207
93499
  })();
@@ -93276,32 +93568,32 @@ var defaultDeps = (machineId2) => ({
93276
93568
  akb: currentAkb,
93277
93569
  runtimes: detectRuntimes
93278
93570
  });
93279
- var ownFolder = () => path36.join(runnerHome(), "work");
93571
+ var ownFolder = () => path38.join(runnerHome(), "work");
93280
93572
  function folderFor(job) {
93281
93573
  if (!job.projectDir) {
93282
- const cwd = path36.join(ownFolder(), job.id);
93283
- fs34.mkdirSync(cwd, { recursive: true });
93574
+ const cwd = path38.join(ownFolder(), job.id);
93575
+ fs35.mkdirSync(cwd, { recursive: true });
93284
93576
  return { cwd };
93285
93577
  }
93286
93578
  const real = realpath4(job.projectDir);
93287
- if (real && allowedProjects().some((root) => real === root || real.startsWith(root + path36.sep))) return { cwd: real };
93579
+ if (real && allowedProjects().some((root) => real === root || real.startsWith(root + path38.sep))) return { cwd: real };
93288
93580
  return { refused: `This Runner is not allowed to work in ${job.projectDir}.` };
93289
93581
  }
93290
93582
  function realpath4(p2) {
93291
93583
  try {
93292
- return fs34.realpathSync(p2);
93584
+ return fs35.realpathSync(p2);
93293
93585
  } catch {
93294
93586
  return null;
93295
93587
  }
93296
93588
  }
93297
- var attachmentsOf = (job) => path36.join(runnerHome(), "attachments", job.id);
93589
+ var attachmentsOf = (job) => path38.join(runnerHome(), "attachments", job.id);
93298
93590
  function writeAttachments(job) {
93299
93591
  if (!job.attachments.length) return "";
93300
93592
  const dir = attachmentsOf(job);
93301
- fs34.mkdirSync(dir, { recursive: true });
93593
+ fs35.mkdirSync(dir, { recursive: true });
93302
93594
  const files = job.attachments.map((a3) => {
93303
- const file2 = path36.join(dir, path36.basename(a3.name));
93304
- fs34.writeFileSync(file2, Buffer.from(a3.content, "base64"));
93595
+ const file2 = path38.join(dir, path38.basename(a3.name));
93596
+ fs35.writeFileSync(file2, Buffer.from(a3.content, "base64"));
93305
93597
  return file2;
93306
93598
  });
93307
93599
  return `
@@ -93568,24 +93860,32 @@ async function runJob(id, deps, track = () => {
93568
93860
  const claim = await deps.api.claimJob(deps.machineId, id);
93569
93861
  if (!claim.ok || !claim.value.claimed || stopped2() || !held3(deps, lease)) return;
93570
93862
  const job = claim.value.job;
93863
+ if (deps.authorize && !await deps.authorize(job)) {
93864
+ await deps.api.finishJob(deps.machineId, id, "failed", "\u9879\u76EE\u76EE\u5F55\u6388\u6743\u5DF2\u64A4\u9500\u6216\u6682\u65F6\u65E0\u6CD5\u9A8C\u8BC1\uFF0C\u8BF7\u91CD\u65B0\u786E\u8BA4\u540E\u91CD\u8BD5\u3002");
93865
+ return;
93866
+ }
93571
93867
  const releases = [];
93572
93868
  try {
93573
93869
  await runClaimed(job, { ...deps, akb: deps.akb() }, track, (dir) => releases.push(hold(dir)), lease);
93574
93870
  } finally {
93575
93871
  for (const release2 of releases) release2();
93576
- fs34.rmSync(attachmentsOf(job), { recursive: true, force: true });
93872
+ fs35.rmSync(attachmentsOf(job), { recursive: true, force: true });
93577
93873
  }
93578
93874
  }
93579
93875
  async function runClaimed(job, deps, track, keep2, lease) {
93580
93876
  const id = job.id;
93581
- const checkpoint = readCheckpoint(id) ?? { pending: [] };
93582
- const recovering = checkpoint.updating === true;
93877
+ const checkpoint = readCheckpoint(id) ?? (job.recoveryOf ? readCheckpoint(job.recoveryOf) : null) ?? { pending: [], started: false };
93878
+ const recovering = checkpoint.updating === true || !!job.recoveryOf;
93583
93879
  const persist = () => {
93584
93880
  checkpoint.updating ||= readCheckpoint(id)?.updating;
93585
93881
  saveCheckpoint(id, checkpoint);
93586
93882
  };
93587
- if (recovering && !checkpoint.completed && (checkpoint.pending.length || checkpoint.opaque || !job.flow && checkpoint.started && !checkpoint.session)) {
93588
- await deps.api.finishJob(deps.machineId, id, "failed", UNCERTAIN);
93883
+ const manual = job.manualRecovery === true;
93884
+ if (manual && checkpoint.completed?.outcome === "failed") delete checkpoint.completed;
93885
+ checkpoint.session ??= job.resume ?? void 0;
93886
+ if (recovering && !checkpoint.completed && (!manual && (checkpoint.pending.length || checkpoint.opaque) || !job.flow && checkpoint.started && !checkpoint.session)) {
93887
+ saveCheckpoint(id, checkpoint);
93888
+ await deps.api.finishJob(deps.machineId, id, "failed", job.recoveryOf ? "recovery_failed" : UNCERTAIN);
93589
93889
  return;
93590
93890
  }
93591
93891
  if (recovering && checkpoint.session) job = { ...job, resume: checkpoint.session, fork: false };
@@ -93653,7 +93953,7 @@ async function runClaimed(job, deps, track, keep2, lease) {
93653
93953
  discussed = board.board;
93654
93954
  }
93655
93955
  if (job.discussion) {
93656
- const wrote = recovering && fs34.existsSync(goalsFileOf(job)) ? { file: goalsFileOf(job) } : await writeGoals({ ...job, discussion: job.discussion }, deps.api.listWorkflows);
93956
+ const wrote = recovering && fs35.existsSync(goalsFileOf(job)) ? { file: goalsFileOf(job) } : await writeGoals({ ...job, discussion: job.discussion }, deps.api.listWorkflows);
93657
93957
  if ("error" in wrote) throw new Error(wrote.error);
93658
93958
  goalFile = wrote.file;
93659
93959
  }
@@ -93666,7 +93966,7 @@ async function runClaimed(job, deps, track, keep2, lease) {
93666
93966
  This task came out of a discussion. Read it for what the card leaves out: ${file2}` : "";
93667
93967
  const promptOf = (said2) => {
93668
93968
  const lines = (head2 + said2).trim();
93669
- const prompt = recovering && checkpoint.started ? "Runner \u66F4\u65B0\u540E\u7EE7\u7EED\u4E4B\u524D\u4E2D\u65AD\u7684\u4EFB\u52A1\u3002\u5148\u6838\u5BF9\u5DF2\u6709\u8FDB\u5EA6\u548C\u7ED3\u679C\uFF0C\u4FDD\u7559\u5B8C\u6210\u7684\u5DE5\u4F5C\uFF0C\u4E0D\u8981\u91CD\u590D\u5DF2\u7ECF\u6267\u884C\u7684\u64CD\u4F5C\u3002" : job.prompt;
93969
+ const prompt = recovering && (checkpoint.started || job.recoveryOf) ? "\u8FDE\u63A5\u6062\u590D\u540E\u7EE7\u7EED\u4E4B\u524D\u4E2D\u65AD\u7684\u56DE\u590D\u3002\u5148\u6838\u5BF9\u5DF2\u6709\u8FDB\u5EA6\u548C\u7ED3\u679C\uFF0C\u4FDD\u7559\u5B8C\u6210\u7684\u5DE5\u4F5C\uFF0C\u4E0D\u8981\u91CD\u590D\u5DF2\u7ECF\u6267\u884C\u7684\u64CD\u4F5C\u3002" : cloudPrompt(job);
93670
93970
  return lines ? `${lines}
93671
93971
 
93672
93972
  ---
@@ -93674,9 +93974,9 @@ This task came out of a discussion. Read it for what the card leaves out: ${file
93674
93974
  ${prompt}` : prompt;
93675
93975
  };
93676
93976
  const planned = (resume, fork, said2 = "") => {
93677
- let p2 = deps.plan(job.runtime, promptOf(said2), cwd, resume, job.model, settingsOf(job), fork, job.languageLine ?? "");
93678
- if (ws) p2 = { ...p2, env: { ...p2.env, ...akbEnv(deps.akb, ws.board, ws.task, job.languageLine ?? "") } };
93679
- if (goalFile) p2 = { ...p2, env: { ...p2.env, ...akbEnv(deps.akb, discussed, void 0, job.languageLine ?? ""), KANBAN_GOALS: goalFile, KANBAN_IDEAS: goalFile } };
93977
+ let p2 = deps.plan(job.runtime, promptOf(said2), cwd, resume, job.model, settingsOf(job), fork, cloudLanguage(job));
93978
+ if (ws) p2 = { ...p2, env: akbEnv(deps.akb, ws.board, ws.task, cloudLanguage(job), p2.env) };
93979
+ if (goalFile) p2 = { ...p2, env: { ...akbEnv(deps.akb, discussed, void 0, cloudLanguage(job), p2.env), KANBAN_GOALS: goalFile, KANBAN_IDEAS: goalFile } };
93680
93980
  if (job.chat || job.discussion) {
93681
93981
  const { CLAUDE_CODE_MAX_RETRIES: _2, ...env2 } = p2.env;
93682
93982
  p2 = { ...p2, env: env2 };
@@ -93695,9 +93995,14 @@ ${prompt}` : prompt;
93695
93995
  await deps.api.finishJob(deps.machineId, id, "failed", e2 instanceof Error ? e2.message : String(e2));
93696
93996
  return;
93697
93997
  }
93998
+ if (deps.authorize && !await deps.authorize(job)) return finish(deps, id, lease, "failed", "\u9879\u76EE\u76EE\u5F55\u6388\u6743\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u786E\u8BA4\u540E\u91CD\u8BD5\u3002");
93698
93999
  if (!held3(deps, lease)) return;
93699
94000
  const stream = streamOut(deps, id, lease);
94001
+ if (run2?.startNote) stream.out(`${run2.startNote}
94002
+ `);
94003
+ let replyProgress = false;
93700
94004
  const remember = (event2) => {
94005
+ if (["message", "tool_call", "tool_call_update"].includes(event2.kind)) replyProgress = true;
93701
94006
  if (stream.lost()) return;
93702
94007
  checkpointEvent(checkpoint, event2);
93703
94008
  const session = run2?.session();
@@ -93758,6 +94063,7 @@ ${prompt}` : prompt;
93758
94063
  if (!ended3.ok) return finish(deps, id, lease, "failed", ended3.reason, proof);
93759
94064
  return sendBack(deps, id, lease, ws, { ...ended3.report, ...proof }, () => keepChat(job, ended3.report.reply));
93760
94065
  }
94066
+ checkpoint.mark = stopMark();
93761
94067
  checkpoint.started = true;
93762
94068
  checkpoint.opaque = !run2.converse && !run2.renderer?.events;
93763
94069
  checkpoint.session = run2.session() ?? checkpoint.session;
@@ -93765,7 +94071,11 @@ ${prompt}` : prompt;
93765
94071
  if (run2.converse) {
93766
94072
  const plan3 = run2;
93767
94073
  const converse = run2.converse;
93768
- const child = spawn4(plan3.command, plan3.args, { cwd, env: plan3.env, detached: process.platform !== "win32", stdio: ["pipe", "pipe", "pipe"] });
94074
+ const child = spawn4(plan3.command, plan3.args, { cwd, env: markEnv(plan3.env, checkpoint.mark), detached: process.platform !== "win32", stdio: ["pipe", "pipe", "pipe"] });
94075
+ if (child.pid) {
94076
+ checkpoint.process = { pid: child.pid, since: Date.now() };
94077
+ persist();
94078
+ }
93769
94079
  stream.adopt(child);
93770
94080
  child.stdin.on("error", () => {
93771
94081
  });
@@ -93773,22 +94083,33 @@ ${prompt}` : prompt;
93773
94083
  child.stderr.on("data", (chunk) => stream.out(quietLines(plan3, chunk)));
93774
94084
  const spawned = new Promise((resolve2) => child.on("error", (e2) => resolve2(e2.message)));
93775
94085
  const end = await Promise.race([
93776
- converse(child, remember),
94086
+ converse(child, remember, false),
93777
94087
  spawned.then((error) => ({ ok: false, error })),
93778
94088
  // Killed, the turn ends with what it said so far; one that never does is not waited on.
93779
94089
  stream.whenAsked.then(() => wait2(KILL_GRACE_MS * 2)).then(() => ({ ok: true }))
93780
94090
  ]);
94091
+ if (!end.ok && !recovering && job.resume && (job.chat || job.discussion) && sessionMissing(end.error ?? "") && !replyProgress && !checkpoint.pending.length && !stream.asked() && !stream.lost()) {
94092
+ stop(child);
94093
+ await stream.end();
94094
+ if (!await stream.confirmed()) return finish(deps, id, lease, "failed", "recovery_failed");
94095
+ clearCheckpoint(id);
94096
+ return runClaimed({ ...job, resume: null, fork: false, sessionFallback: true }, deps, track, keep2, lease);
94097
+ }
93781
94098
  if (!stream.asked()) complete(end.ok ? "done" : "failed", told(report(job, plan3, end)), end.ok ? void 0 : end.error);
93782
94099
  stop(child);
93783
94100
  await stream.end();
93784
94101
  if (stream.lost()) return;
93785
94102
  const ok = end.ok || stream.asked();
93786
94103
  const said2 = told(stream.asked() ? cutShort(report(job, plan3, end), stream.said()) : report(job, plan3, end));
93787
- if (!ok || !ws) return finish(deps, id, lease, ok ? "done" : "failed", ok ? "" : end.error ?? "", said2);
94104
+ if (!ok || !ws) return finish(deps, id, lease, ok ? "done" : "failed", ok ? "" : replyReason(end.ok ? void 0 : end.error, job), said2);
93788
94105
  return sendBack(deps, id, lease, ws, said2, () => keepChat(job, said2.reply));
93789
94106
  }
93790
94107
  const printed = async (plan3) => {
93791
- const child = spawn4(plan3.command, plan3.args, { cwd, env: plan3.env, detached: process.platform !== "win32", stdio: ["pipe", "pipe", "pipe"] });
94108
+ const child = spawn4(plan3.command, plan3.args, { cwd, env: markEnv(plan3.env, checkpoint.mark), detached: process.platform !== "win32", stdio: ["pipe", "pipe", "pipe"] });
94109
+ if (child.pid) {
94110
+ checkpoint.process = { pid: child.pid, since: Date.now() };
94111
+ persist();
94112
+ }
93792
94113
  stream.adopt(child);
93793
94114
  child.stdin.on("error", () => {
93794
94115
  });
@@ -93830,17 +94151,18 @@ ${prompt}` : prompt;
93830
94151
  if (fresh && ended2.code !== 0 && plan2.renderer.result() === void 0 && !ended2.acted && (!missingSessionOnly || sessionMissing(`${ended2.stderr}
93831
94152
  ${plan2.renderer.failure?.() ?? ""}`)) && !stream.asked() && !stream.lost()) {
93832
94153
  plan2 = fresh();
94154
+ job = { ...job, sessionFallback: missingSessionOnly };
93833
94155
  run2 = plan2;
93834
94156
  ended2 = await printed(plan2);
93835
94157
  }
93836
94158
  const renderer = plan2.renderer;
93837
94159
  if (!stream.asked()) {
93838
- const reason = ended2.error ?? renderer.failure?.() ?? (ended2.code === 0 ? void 0 : `Exited with code ${ended2.code}.`);
94160
+ const reason = replyReason(ended2.error ?? renderer.failure?.() ?? (ended2.code === 0 ? void 0 : `Exited with code ${ended2.code}.`), job);
93839
94161
  complete(reason ? "failed" : "done", told(report(job, plan2)), reason);
93840
94162
  }
93841
94163
  await stream.end();
93842
94164
  if (stream.lost()) return;
93843
- const failure = stream.asked() ? void 0 : ended2.error ?? renderer.failure?.() ?? (ended2.code === 0 ? void 0 : `Exited with code ${ended2.code}.`);
94165
+ const failure = stream.asked() ? void 0 : replyReason(ended2.error ?? renderer.failure?.() ?? (ended2.code === 0 ? void 0 : `Exited with code ${ended2.code}.`), job);
93844
94166
  const said = told(stream.asked() ? cutShort(report(job, plan2), stream.said()) : report(job, plan2));
93845
94167
  if (failure || !ws) return finish(deps, id, lease, failure ? "failed" : "done", failure, said);
93846
94168
  return sendBack(deps, id, lease, ws, said, () => keepChat(job, said.reply));
@@ -93880,18 +94202,24 @@ async function runAgentJob(job, deps, track, keep2, lease, checkpoint, persist)
93880
94202
  }
93881
94203
  var quietLines = (plan2, chunk) => chunk.split("\n").filter((line) => line && !plan2.quiet(line)).map((line) => `${line}
93882
94204
  `).join("");
93883
- var sessionMissing = (text3) => /\b(?:session|conversation|thread)\b[^\n]*(?:\bnot found\b|\bdoes not exist\b)/i.test(text3) || /\bno (?:conversation|session|thread) found\b/i.test(text3);
93884
- function signalTree(child, signal) {
94205
+ var replyReason = (reason, job) => {
94206
+ if (!reason) return reason;
94207
+ if (job.recoveryOf) return "recovery_failed";
94208
+ if (job.sessionFallback && !/auth|login|log.in|permission|directory|ENOENT|not installed|rate.limit|network|connect|登录|权限|目录/i.test(reason)) return "session_unavailable";
94209
+ return reason;
94210
+ };
94211
+ var sessionMissing = (text3) => !/auth|unauthorized|forbidden|login|log.in|登录|权限/i.test(text3) && (/\b(?:session|conversation|thread)\b[^\n]*(?:\bnot found\b|\bdoes not exist\b)/i.test(text3) || /\bno (?:conversation|session|thread) found\b/i.test(text3));
94212
+ function signalTree(child, signal2) {
93885
94213
  if (!child.pid) return;
93886
94214
  if (process.platform === "win32") {
93887
94215
  const kill = spawn4("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" });
93888
- kill.on("error", () => child.kill(signal));
94216
+ kill.on("error", () => child.kill(signal2));
93889
94217
  return;
93890
94218
  }
93891
94219
  try {
93892
- process.kill(-child.pid, signal);
94220
+ process.kill(-child.pid, signal2);
93893
94221
  } catch {
93894
- child.kill(signal);
94222
+ child.kill(signal2);
93895
94223
  }
93896
94224
  }
93897
94225
  var stop = (child) => {
@@ -93964,8 +94292,8 @@ async function runTest(job, deps, lease) {
93964
94292
  const report2 = (await deps.runtimes()).find((r2) => r2.name === job.runtime);
93965
94293
  if (!report2?.installed) return finish2("failed", "not_installed");
93966
94294
  if (report2.loggedIn === false) return finish2("failed", "signed_out");
93967
- const cwd = path36.join(ownFolder(), "test");
93968
- fs34.mkdirSync(cwd, { recursive: true });
94295
+ const cwd = path38.join(ownFolder(), "test");
94296
+ fs35.mkdirSync(cwd, { recursive: true });
93969
94297
  const run2 = deps.plan(job.runtime, job.prompt, cwd, null, job.model, settingsOf(job));
93970
94298
  const startedAt = Date.now();
93971
94299
  if (run2.converse) {
@@ -94120,9 +94448,15 @@ var POLL_MS = 6e4;
94120
94448
  var BACKOFF_MS3 = [1e3, 2e3, 5e3, 1e4, 3e4];
94121
94449
  var LOOK_LATE_MS = 6e4;
94122
94450
  function startRunner(options) {
94451
+ adoptLegacy();
94452
+ const account = readSession()?.subject;
94453
+ const generation = accountGeneration();
94454
+ const releaseRecord = executorRecord();
94123
94455
  let registration = null;
94124
94456
  let connected = false;
94125
94457
  let closed = false;
94458
+ let ownsExecution = false;
94459
+ let redetect = false;
94126
94460
  let preparing = false;
94127
94461
  let attempts = 0;
94128
94462
  let live = null;
@@ -94140,7 +94474,12 @@ function startRunner(options) {
94140
94474
  const stopped2 = new Promise((resolve2) => stop2 = resolve2);
94141
94475
  const deps = () => {
94142
94476
  const base = options.deps ?? defaultDeps(registration.machineId);
94143
- return { ...base, runtimes: () => base.runtimes().then(redetected) };
94477
+ return { ...base, ...options.deps ? {} : { authorize: async (job) => {
94478
+ if (!validAccount()) return false;
94479
+ if (!job.projectDir) return true;
94480
+ const folders2 = await listFolders(registration.machineId);
94481
+ return validAccount() && folders2.ok && folders2.value.folders.some((f2) => f2.project === job.project && f2.path === job.projectDir);
94482
+ } }, runtimes: () => base.runtimes().then(redetected) };
94144
94483
  };
94145
94484
  const register2 = async () => {
94146
94485
  const answer = await register(registration);
@@ -94155,7 +94494,7 @@ function startRunner(options) {
94155
94494
  };
94156
94495
  const idle2 = () => running.size ? t.running(running.size) : t.connected(registration.machineName);
94157
94496
  const schedule = (ms) => {
94158
- if (closed) return;
94497
+ if (closed || !ownsExecution) return;
94159
94498
  if (timer) clearTimeout(timer);
94160
94499
  timer = setTimeout(tick, ms);
94161
94500
  };
@@ -94203,9 +94542,14 @@ function startRunner(options) {
94203
94542
  await close(false);
94204
94543
  stop2(s);
94205
94544
  };
94545
+ const validAccount = () => readSession()?.subject === account && accountGeneration() === generation;
94206
94546
  const tick = async () => {
94207
- if (!registration) registration = await options.registration();
94208
- else {
94547
+ if (closed || !ownsExecution) return;
94548
+ if (!validAccount()) return finish2({ reason: "refused", message: "\u8D26\u53F7\u5DF2\u9000\u51FA\u6216\u5207\u6362\u3002" });
94549
+ if (!registration || redetect) {
94550
+ redetect = false;
94551
+ registration = await options.registration();
94552
+ } else {
94209
94553
  const was = registration.runtimes;
94210
94554
  const runtimes = await refreshModels(was);
94211
94555
  if (registration.runtimes === was) registration = { ...registration, runtimes };
@@ -94283,15 +94627,17 @@ function startRunner(options) {
94283
94627
  for (const id of attempted) if (!listed2.has(id) && !running.has(id)) attempted.delete(id);
94284
94628
  for (const job of list.value.jobs) {
94285
94629
  if (closed || preparing) break;
94286
- if (job.state === "claimed" && !running.has(job.id) && !attempted.has(job.id)) {
94630
+ if ((job.state === "claimed" || job.state === "interrupted") && !running.has(job.id) && (!attempted.has(job.id) || !job.flow && !!(job.chat || job.discussion))) {
94287
94631
  const failure = await stopLeftOn(job);
94288
94632
  if (closed || preparing) break;
94289
94633
  const checkpoint = readCheckpoint(job.id);
94290
- const uncertain = checkpoint?.updating && !checkpoint.completed && (checkpoint.pending.length > 0 || checkpoint.opaque || !job.flow && checkpoint.started && !checkpoint.session);
94634
+ const reply = !job.flow && !!(job.chat || job.discussion);
94635
+ const processesStopped2 = reply ? await stopReply(job.id, deps().stopMs) : false;
94636
+ const uncertain = !!checkpoint && (checkpoint.updating || reply) && !checkpoint.completed && (checkpoint.pending.length > 0 || checkpoint.opaque || !job.flow && checkpoint.started && !checkpoint.session);
94291
94637
  if (checkpoint?.updating) status(uncertain ? UNCERTAIN : "\u6B63\u5728\u6062\u590D\u66F4\u65B0\u524D\u7684\u4EFB\u52A1\u2026");
94292
- const reason = failure ?? (uncertain ? UNCERTAIN : checkpoint?.updating ? UPDATE_REASON : "The Runner restarted while this job was running.");
94293
- const recovered = await finishJob(registration.machineId, job.id, "failed", reason, checkpoint?.session ? { session: checkpoint.session } : void 0);
94294
- if (recovered.ok && reason === UPDATE_REASON) readAgain = true;
94638
+ const reason = failure ?? (checkpoint?.updating ? uncertain ? UNCERTAIN : UPDATE_REASON : "The Runner restarted while this job was running.");
94639
+ const recovered = await finishJob(registration.machineId, job.id, "failed", reason, { ...checkpoint?.session ? { session: checkpoint.session } : {}, ...processesStopped2 ? { processesStopped: true } : {} });
94640
+ if (recovered.ok && (reason === UPDATE_REASON || processesStopped2 && recovered.value.finished)) readAgain = true;
94295
94641
  continue;
94296
94642
  }
94297
94643
  if (job.state !== "waiting" || running.has(job.id)) continue;
@@ -94381,20 +94727,42 @@ function startRunner(options) {
94381
94727
  else await Promise.race([stops, new Promise((resolve2) => timeout = setTimeout(resolve2, (options.deps?.stopMs ?? STOP_MS) + 1e3))]);
94382
94728
  clearTimeout(timeout);
94383
94729
  if (release2 && !lock.waited() && registration) await release(registration.machineId);
94730
+ clearInterval(accountWatch);
94384
94731
  lock.release();
94732
+ releaseRecord();
94385
94733
  };
94734
+ let refresh2 = "";
94735
+ const accountWatch = setInterval(() => {
94736
+ try {
94737
+ const value = fs36.readFileSync(path39.join(runnerHome(), "settings-refresh"), "utf8");
94738
+ if (value !== refresh2) {
94739
+ refresh2 = value;
94740
+ redetect = true;
94741
+ if (!closed) schedule(0);
94742
+ }
94743
+ } catch {
94744
+ }
94745
+ if (!closed && !validAccount()) void finish2({ reason: "refused", message: "\u8D26\u53F7\u5DF2\u9000\u51FA\u6216\u5207\u6362\u3002" });
94746
+ }, 200);
94747
+ accountWatch.unref();
94386
94748
  removeLegacy();
94387
94749
  pruneChats();
94388
- const lock = holdMachine(options.kind, () => void tick(), (holder) => {
94750
+ const lock = holdMachine(options.kind, () => {
94751
+ ownsExecution = true;
94752
+ void tick();
94753
+ }, (holder) => {
94389
94754
  status(t.waitingHere(holder));
94390
94755
  options.onWaiting?.(holder);
94391
94756
  });
94392
- return { stopped: stopped2, close: () => close(true), update: () => close(false, true), renew: () => schedule(0) };
94757
+ return { stopped: stopped2, close: () => close(true), update: () => close(false, true), renew: () => {
94758
+ redetect = true;
94759
+ schedule(0);
94760
+ } };
94393
94761
  }
94394
94762
 
94395
94763
  // src/signin.ts
94396
94764
  import { spawn as spawn5 } from "node:child_process";
94397
- import crypto6 from "node:crypto";
94765
+ import crypto7 from "node:crypto";
94398
94766
  import http from "node:http";
94399
94767
  var WAIT_MS = 10 * 60 * 1e3;
94400
94768
  var CRANE = "M2 55.75C2 55.69 2.69 55.09 3.55 54.37L5.09 53.1L6.61 53.1L8.13 53.1L12.49 49.87C14.9 48.07 16.83 46.58 16.77 46.53C16.75 46.47 15.56 46.64 14.15 46.89C12.74 47.13 11.55 47.3 11.53 47.24C11.47 47.19 12.02 46.22 12.71 45.06L14.01 42.96L18.21 41.34C20.53 40.45 22.41 39.65 22.41 39.57C22.41 39.49 21.27 38.55 19.92 37.5C15.92 34.4 13.13 31.31 10.86 27.47L10.01 26.01L10.53 26.18C10.84 26.26 12.96 27.2 15.28 28.25C17.6 29.32 19.53 30.12 19.59 30.07C19.64 30.01 17.68 28.69 15.25 27.11C12.8 25.57 10.56 23.97 10.31 23.58C9.15 21.95 7.25 17.5 7.25 16.46C7.25 16.26 10.01 17.95 13.38 20.18C16.77 22.45 19.73 24.38 19.95 24.49C20.42 24.74 20.25 24.57 13.9 19.02L9.18 14.88L8.6 13.36C7.72 10.96 6.97 7.54 6.97 5.77L6.97 4.14L9.4 6.46C12.66 9.61 15.56 12.01 23.57 18.25C27.32 21.18 30.5 23.72 30.64 23.91C30.8 24.08 31.68 26.51 32.6 29.3L34.28 34.35L35.72 35.65C37.54 37.3 37.98 37.47 36.85 36.09L35.96 35.01L37.12 34.71C39.11 34.21 40.16 33.52 42.54 31.12L44.8 28.8L46.02 28.96C46.71 29.07 47.56 29.07 47.95 28.96L48.67 28.8L48.67 28.25L48.67 27.69L49.27 27.69C49.63 27.69 50.1 27.58 50.32 27.42L50.71 27.17L51.01 27.42C51.18 27.56 52.78 27.86 54.58 28.08C56.37 28.3 57.86 28.52 57.94 28.58C58 28.63 56.73 28.85 55.13 29.05C53.53 29.24 50.96 29.77 49.44 30.21C47.92 30.65 46.57 31.01 46.43 31.01C46.29 31.01 44.22 32.97 41.85 35.37C37.57 39.62 34.42 42.14 30.83 44.15L29.14 45.09L25.97 45.73L22.77 46.39L14.59 51.11L6.42 55.86L4.21 55.86C2.99 55.86 2 55.8 2 55.75Z";
@@ -94419,8 +94787,9 @@ var PAGE = (outcome, lang) => {
94419
94787
  return `<!doctype html><html lang="${lang === "zh" ? "zh-CN" : "en"}"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title} \xB7 WildCrane</title><style>*{margin:0;box-sizing:border-box}body{display:flex;flex-direction:column;min-height:100dvh;background:#fff;color:#24231f;font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}header{padding:16px 36px 0}main{flex:1;display:flex;align-items:center;justify-content:center;padding:0 36px 64px;text-align:center}.icon{display:flex;margin:0 auto;width:44px;height:44px;border-radius:50%;align-items:center;justify-content:center;background:${ok ? "#e4f3ea" : "#f7ddce"};color:${ok ? "#2f6b46" : "#b83a12"}}h1{margin-top:16px;font-size:24px;font-weight:850;line-height:1.25;letter-spacing:-0.02em}p{margin:8px auto 0;max-width:38ch;font-size:13px;line-height:19px;color:#565550}</style><header><svg viewBox="2 4 56 52" width="23.7" height="22" aria-hidden="true"><path d="${CRANE}" fill="#24231f"/><path d="${CREST}" fill="#dd4f1e"/></svg></header><main><div><span class="icon"><svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${icon}</svg></span><h1>${title}</h1><p>${body}</p></div></main></html>`;
94420
94788
  };
94421
94789
  async function signIn(options = {}) {
94422
- const verifier = base64url(crypto6.randomBytes(32));
94423
- const challenge = base64url(crypto6.createHash("sha256").update(verifier).digest());
94790
+ const generation = accountGeneration();
94791
+ const verifier = base64url(crypto7.randomBytes(32));
94792
+ const challenge = base64url(crypto7.createHash("sha256").update(verifier).digest());
94424
94793
  let finish2 = () => {
94425
94794
  };
94426
94795
  const answered = new Promise((resolve2) => finish2 = resolve2);
@@ -94435,7 +94804,7 @@ async function signIn(options = {}) {
94435
94804
  const params = asked.searchParams;
94436
94805
  const code = params.get("code");
94437
94806
  const error = params.get("error");
94438
- const failure2 = code ? await exchange(code, verifier) : {
94807
+ const failure2 = code ? await exchange(code, verifier, generation, options.signal) : {
94439
94808
  why: error === "access_denied" ? "denied" : "other",
94440
94809
  detail: params.get("error_description") || error || "no code came back"
94441
94810
  };
@@ -94465,7 +94834,7 @@ async function signIn(options = {}) {
94465
94834
  server.close();
94466
94835
  return failure;
94467
94836
  }
94468
- async function exchange(code, verifier) {
94837
+ async function exchange(code, verifier, generation, signal2) {
94469
94838
  const { supabaseUrl, anonKey } = cloudEndpoints();
94470
94839
  let response;
94471
94840
  try {
@@ -94479,6 +94848,7 @@ async function exchange(code, verifier) {
94479
94848
  }
94480
94849
  const session = response.ok ? sessionFrom(await response.json().catch(() => ({}))) : null;
94481
94850
  if (!session) return { why: "other", detail: `Cloud answered ${response.status}` };
94851
+ if (signal2?.aborted || accountGeneration() !== generation) return { why: "cancelled", detail: "cancelled" };
94482
94852
  writeSession(session);
94483
94853
  return null;
94484
94854
  }
@@ -94523,8 +94893,8 @@ async function main() {
94523
94893
  tell({ type: "update-deferred" });
94524
94894
  });
94525
94895
  });
94526
- for (const signal of ["SIGINT", "SIGTERM"]) {
94527
- process.on(signal, async () => {
94896
+ for (const signal2 of ["SIGINT", "SIGTERM"]) {
94897
+ process.on(signal2, async () => {
94528
94898
  await runner.close();
94529
94899
  status(t.stopped());
94530
94900
  process.exit(0);