larkway 0.3.33 → 0.3.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -43,6 +43,89 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
43
43
  mod
44
44
  ));
45
45
 
46
+ // src/config/paths.ts
47
+ var paths_exports = {};
48
+ __export(paths_exports, {
49
+ LEGACY_BOT_ID: () => LEGACY_BOT_ID,
50
+ larkwayHome: () => larkwayHome,
51
+ resolveAgentSessionPath: () => resolveAgentSessionPath,
52
+ resolveAgentWorkspacePath: () => resolveAgentWorkspacePath,
53
+ resolveAgentWorkspacePathFromHome: () => resolveAgentWorkspacePathFromHome,
54
+ resolveAgentWorkspaceReposDir: () => resolveAgentWorkspaceReposDir,
55
+ resolveAgentWorkspaceSessionsDir: () => resolveAgentWorkspaceSessionsDir,
56
+ resolveCandidateAlertsPath: () => resolveCandidateAlertsPath,
57
+ resolveLarkwayDir: () => resolveLarkwayDir,
58
+ resolveLogsDir: () => resolveLogsDir,
59
+ resolveSessionsPath: () => resolveSessionsPath,
60
+ resolveTaskHandlesPath: () => resolveTaskHandlesPath,
61
+ resolveTaskTeamRegistryPath: () => resolveTaskTeamRegistryPath,
62
+ resolveWorktreePath: () => resolveWorktreePath,
63
+ resolveWorktreesDir: () => resolveWorktreesDir
64
+ });
65
+ import { homedir } from "node:os";
66
+ import { join, resolve } from "node:path";
67
+ function larkwayHome() {
68
+ const env = process.env.LARKWAY_HOME;
69
+ if (env && env.trim() !== "") return resolve(env);
70
+ return join(homedir(), ".larkway");
71
+ }
72
+ function resolveLarkwayDir(botId) {
73
+ if (botId === void 0 || botId === LEGACY_BOT_ID) {
74
+ return larkwayHome();
75
+ }
76
+ return join(larkwayHome(), botId);
77
+ }
78
+ function resolveSessionsPath(botId) {
79
+ return join(resolveLarkwayDir(botId), "sessions.json");
80
+ }
81
+ function resolveWorktreePath(botId, threadId) {
82
+ return join(resolveLarkwayDir(botId), "worktrees", threadId);
83
+ }
84
+ function resolveWorktreesDir(botId) {
85
+ return join(resolveLarkwayDir(botId), "worktrees");
86
+ }
87
+ function resolveLogsDir(botId) {
88
+ return join(resolveLarkwayDir(botId), "logs");
89
+ }
90
+ function resolveTaskHandlesPath(botId) {
91
+ return join(resolveLarkwayDir(botId), "task-handles.json");
92
+ }
93
+ function resolveTaskTeamRegistryPath() {
94
+ return join(larkwayHome(), "task-team.json");
95
+ }
96
+ function resolveCandidateAlertsPath(tasklistGuid) {
97
+ return join(larkwayHome(), `candidate-alerts-${tasklistGuid}.json`);
98
+ }
99
+ function assertSafePathSegment(label, value) {
100
+ if (!/^[A-Za-z0-9_-]+$/.test(value)) {
101
+ throw new Error(`${label} must be a safe path segment`);
102
+ }
103
+ }
104
+ function resolveAgentWorkspacePathFromHome(home, agentId) {
105
+ assertSafePathSegment("agentId", agentId);
106
+ return join(home, "agents", agentId, "workspace");
107
+ }
108
+ function resolveAgentWorkspacePath(agentId) {
109
+ return resolveAgentWorkspacePathFromHome(larkwayHome(), agentId);
110
+ }
111
+ function resolveAgentWorkspaceSessionsDir(agentId) {
112
+ return join(resolveAgentWorkspacePath(agentId), "sessions");
113
+ }
114
+ function resolveAgentSessionPath(agentId, threadId) {
115
+ assertSafePathSegment("threadId", threadId);
116
+ return join(resolveAgentWorkspaceSessionsDir(agentId), threadId);
117
+ }
118
+ function resolveAgentWorkspaceReposDir(agentId) {
119
+ return join(resolveAgentWorkspacePath(agentId), "repos");
120
+ }
121
+ var LEGACY_BOT_ID;
122
+ var init_paths = __esm({
123
+ "src/config/paths.ts"() {
124
+ "use strict";
125
+ LEGACY_BOT_ID = "v1-default";
126
+ }
127
+ });
128
+
46
129
  // node_modules/.pnpm/dotenv@16.6.1/node_modules/dotenv/package.json
47
130
  var require_package = __commonJS({
48
131
  "node_modules/.pnpm/dotenv@16.6.1/node_modules/dotenv/package.json"(exports, module) {
@@ -10740,6 +10823,769 @@ var require_proxy_from_env = __commonJS({
10740
10823
  }
10741
10824
  });
10742
10825
 
10826
+ // node_modules/.pnpm/ms@2.1.3/node_modules/ms/index.js
10827
+ var require_ms = __commonJS({
10828
+ "node_modules/.pnpm/ms@2.1.3/node_modules/ms/index.js"(exports, module) {
10829
+ var s = 1e3;
10830
+ var m = s * 60;
10831
+ var h = m * 60;
10832
+ var d = h * 24;
10833
+ var w = d * 7;
10834
+ var y = d * 365.25;
10835
+ module.exports = function(val, options) {
10836
+ options = options || {};
10837
+ var type2 = typeof val;
10838
+ if (type2 === "string" && val.length > 0) {
10839
+ return parse(val);
10840
+ } else if (type2 === "number" && isFinite(val)) {
10841
+ return options.long ? fmtLong(val) : fmtShort(val);
10842
+ }
10843
+ throw new Error(
10844
+ "val is not a non-empty string or a valid number. val=" + JSON.stringify(val)
10845
+ );
10846
+ };
10847
+ function parse(str2) {
10848
+ str2 = String(str2);
10849
+ if (str2.length > 100) {
10850
+ return;
10851
+ }
10852
+ var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
10853
+ str2
10854
+ );
10855
+ if (!match) {
10856
+ return;
10857
+ }
10858
+ var n = parseFloat(match[1]);
10859
+ var type2 = (match[2] || "ms").toLowerCase();
10860
+ switch (type2) {
10861
+ case "years":
10862
+ case "year":
10863
+ case "yrs":
10864
+ case "yr":
10865
+ case "y":
10866
+ return n * y;
10867
+ case "weeks":
10868
+ case "week":
10869
+ case "w":
10870
+ return n * w;
10871
+ case "days":
10872
+ case "day":
10873
+ case "d":
10874
+ return n * d;
10875
+ case "hours":
10876
+ case "hour":
10877
+ case "hrs":
10878
+ case "hr":
10879
+ case "h":
10880
+ return n * h;
10881
+ case "minutes":
10882
+ case "minute":
10883
+ case "mins":
10884
+ case "min":
10885
+ case "m":
10886
+ return n * m;
10887
+ case "seconds":
10888
+ case "second":
10889
+ case "secs":
10890
+ case "sec":
10891
+ case "s":
10892
+ return n * s;
10893
+ case "milliseconds":
10894
+ case "millisecond":
10895
+ case "msecs":
10896
+ case "msec":
10897
+ case "ms":
10898
+ return n;
10899
+ default:
10900
+ return void 0;
10901
+ }
10902
+ }
10903
+ function fmtShort(ms) {
10904
+ var msAbs = Math.abs(ms);
10905
+ if (msAbs >= d) {
10906
+ return Math.round(ms / d) + "d";
10907
+ }
10908
+ if (msAbs >= h) {
10909
+ return Math.round(ms / h) + "h";
10910
+ }
10911
+ if (msAbs >= m) {
10912
+ return Math.round(ms / m) + "m";
10913
+ }
10914
+ if (msAbs >= s) {
10915
+ return Math.round(ms / s) + "s";
10916
+ }
10917
+ return ms + "ms";
10918
+ }
10919
+ function fmtLong(ms) {
10920
+ var msAbs = Math.abs(ms);
10921
+ if (msAbs >= d) {
10922
+ return plural(ms, msAbs, d, "day");
10923
+ }
10924
+ if (msAbs >= h) {
10925
+ return plural(ms, msAbs, h, "hour");
10926
+ }
10927
+ if (msAbs >= m) {
10928
+ return plural(ms, msAbs, m, "minute");
10929
+ }
10930
+ if (msAbs >= s) {
10931
+ return plural(ms, msAbs, s, "second");
10932
+ }
10933
+ return ms + " ms";
10934
+ }
10935
+ function plural(ms, msAbs, n, name) {
10936
+ var isPlural = msAbs >= n * 1.5;
10937
+ return Math.round(ms / n) + " " + name + (isPlural ? "s" : "");
10938
+ }
10939
+ }
10940
+ });
10941
+
10942
+ // node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/common.js
10943
+ var require_common = __commonJS({
10944
+ "node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/common.js"(exports, module) {
10945
+ function setup(env) {
10946
+ createDebug.debug = createDebug;
10947
+ createDebug.default = createDebug;
10948
+ createDebug.coerce = coerce2;
10949
+ createDebug.disable = disable;
10950
+ createDebug.enable = enable;
10951
+ createDebug.enabled = enabled;
10952
+ createDebug.humanize = require_ms();
10953
+ createDebug.destroy = destroy;
10954
+ Object.keys(env).forEach((key) => {
10955
+ createDebug[key] = env[key];
10956
+ });
10957
+ createDebug.names = [];
10958
+ createDebug.skips = [];
10959
+ createDebug.formatters = {};
10960
+ function selectColor(namespace) {
10961
+ let hash = 0;
10962
+ for (let i = 0; i < namespace.length; i++) {
10963
+ hash = (hash << 5) - hash + namespace.charCodeAt(i);
10964
+ hash |= 0;
10965
+ }
10966
+ return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
10967
+ }
10968
+ createDebug.selectColor = selectColor;
10969
+ function createDebug(namespace) {
10970
+ let prevTime;
10971
+ let enableOverride = null;
10972
+ let namespacesCache;
10973
+ let enabledCache;
10974
+ function debug(...args) {
10975
+ if (!debug.enabled) {
10976
+ return;
10977
+ }
10978
+ const self2 = debug;
10979
+ const curr = Number(/* @__PURE__ */ new Date());
10980
+ const ms = curr - (prevTime || curr);
10981
+ self2.diff = ms;
10982
+ self2.prev = prevTime;
10983
+ self2.curr = curr;
10984
+ prevTime = curr;
10985
+ args[0] = createDebug.coerce(args[0]);
10986
+ if (typeof args[0] !== "string") {
10987
+ args.unshift("%O");
10988
+ }
10989
+ let index = 0;
10990
+ args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
10991
+ if (match === "%%") {
10992
+ return "%";
10993
+ }
10994
+ index++;
10995
+ const formatter = createDebug.formatters[format];
10996
+ if (typeof formatter === "function") {
10997
+ const val = args[index];
10998
+ match = formatter.call(self2, val);
10999
+ args.splice(index, 1);
11000
+ index--;
11001
+ }
11002
+ return match;
11003
+ });
11004
+ createDebug.formatArgs.call(self2, args);
11005
+ const logFn = self2.log || createDebug.log;
11006
+ logFn.apply(self2, args);
11007
+ }
11008
+ debug.namespace = namespace;
11009
+ debug.useColors = createDebug.useColors();
11010
+ debug.color = createDebug.selectColor(namespace);
11011
+ debug.extend = extend3;
11012
+ debug.destroy = createDebug.destroy;
11013
+ Object.defineProperty(debug, "enabled", {
11014
+ enumerable: true,
11015
+ configurable: false,
11016
+ get: () => {
11017
+ if (enableOverride !== null) {
11018
+ return enableOverride;
11019
+ }
11020
+ if (namespacesCache !== createDebug.namespaces) {
11021
+ namespacesCache = createDebug.namespaces;
11022
+ enabledCache = createDebug.enabled(namespace);
11023
+ }
11024
+ return enabledCache;
11025
+ },
11026
+ set: (v) => {
11027
+ enableOverride = v;
11028
+ }
11029
+ });
11030
+ if (typeof createDebug.init === "function") {
11031
+ createDebug.init(debug);
11032
+ }
11033
+ return debug;
11034
+ }
11035
+ function extend3(namespace, delimiter) {
11036
+ const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
11037
+ newDebug.log = this.log;
11038
+ return newDebug;
11039
+ }
11040
+ function enable(namespaces) {
11041
+ createDebug.save(namespaces);
11042
+ createDebug.namespaces = namespaces;
11043
+ createDebug.names = [];
11044
+ createDebug.skips = [];
11045
+ const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean);
11046
+ for (const ns of split) {
11047
+ if (ns[0] === "-") {
11048
+ createDebug.skips.push(ns.slice(1));
11049
+ } else {
11050
+ createDebug.names.push(ns);
11051
+ }
11052
+ }
11053
+ }
11054
+ function matchesTemplate(search, template) {
11055
+ let searchIndex = 0;
11056
+ let templateIndex = 0;
11057
+ let starIndex = -1;
11058
+ let matchIndex = 0;
11059
+ while (searchIndex < search.length) {
11060
+ if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === "*")) {
11061
+ if (template[templateIndex] === "*") {
11062
+ starIndex = templateIndex;
11063
+ matchIndex = searchIndex;
11064
+ templateIndex++;
11065
+ } else {
11066
+ searchIndex++;
11067
+ templateIndex++;
11068
+ }
11069
+ } else if (starIndex !== -1) {
11070
+ templateIndex = starIndex + 1;
11071
+ matchIndex++;
11072
+ searchIndex = matchIndex;
11073
+ } else {
11074
+ return false;
11075
+ }
11076
+ }
11077
+ while (templateIndex < template.length && template[templateIndex] === "*") {
11078
+ templateIndex++;
11079
+ }
11080
+ return templateIndex === template.length;
11081
+ }
11082
+ function disable() {
11083
+ const namespaces = [
11084
+ ...createDebug.names,
11085
+ ...createDebug.skips.map((namespace) => "-" + namespace)
11086
+ ].join(",");
11087
+ createDebug.enable("");
11088
+ return namespaces;
11089
+ }
11090
+ function enabled(name) {
11091
+ for (const skip of createDebug.skips) {
11092
+ if (matchesTemplate(name, skip)) {
11093
+ return false;
11094
+ }
11095
+ }
11096
+ for (const ns of createDebug.names) {
11097
+ if (matchesTemplate(name, ns)) {
11098
+ return true;
11099
+ }
11100
+ }
11101
+ return false;
11102
+ }
11103
+ function coerce2(val) {
11104
+ if (val instanceof Error) {
11105
+ return val.stack || val.message;
11106
+ }
11107
+ return val;
11108
+ }
11109
+ function destroy() {
11110
+ console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
11111
+ }
11112
+ createDebug.enable(createDebug.load());
11113
+ return createDebug;
11114
+ }
11115
+ module.exports = setup;
11116
+ }
11117
+ });
11118
+
11119
+ // node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/browser.js
11120
+ var require_browser = __commonJS({
11121
+ "node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/browser.js"(exports, module) {
11122
+ exports.formatArgs = formatArgs;
11123
+ exports.save = save;
11124
+ exports.load = load2;
11125
+ exports.useColors = useColors;
11126
+ exports.storage = localstorage();
11127
+ exports.destroy = /* @__PURE__ */ (() => {
11128
+ let warned = false;
11129
+ return () => {
11130
+ if (!warned) {
11131
+ warned = true;
11132
+ console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
11133
+ }
11134
+ };
11135
+ })();
11136
+ exports.colors = [
11137
+ "#0000CC",
11138
+ "#0000FF",
11139
+ "#0033CC",
11140
+ "#0033FF",
11141
+ "#0066CC",
11142
+ "#0066FF",
11143
+ "#0099CC",
11144
+ "#0099FF",
11145
+ "#00CC00",
11146
+ "#00CC33",
11147
+ "#00CC66",
11148
+ "#00CC99",
11149
+ "#00CCCC",
11150
+ "#00CCFF",
11151
+ "#3300CC",
11152
+ "#3300FF",
11153
+ "#3333CC",
11154
+ "#3333FF",
11155
+ "#3366CC",
11156
+ "#3366FF",
11157
+ "#3399CC",
11158
+ "#3399FF",
11159
+ "#33CC00",
11160
+ "#33CC33",
11161
+ "#33CC66",
11162
+ "#33CC99",
11163
+ "#33CCCC",
11164
+ "#33CCFF",
11165
+ "#6600CC",
11166
+ "#6600FF",
11167
+ "#6633CC",
11168
+ "#6633FF",
11169
+ "#66CC00",
11170
+ "#66CC33",
11171
+ "#9900CC",
11172
+ "#9900FF",
11173
+ "#9933CC",
11174
+ "#9933FF",
11175
+ "#99CC00",
11176
+ "#99CC33",
11177
+ "#CC0000",
11178
+ "#CC0033",
11179
+ "#CC0066",
11180
+ "#CC0099",
11181
+ "#CC00CC",
11182
+ "#CC00FF",
11183
+ "#CC3300",
11184
+ "#CC3333",
11185
+ "#CC3366",
11186
+ "#CC3399",
11187
+ "#CC33CC",
11188
+ "#CC33FF",
11189
+ "#CC6600",
11190
+ "#CC6633",
11191
+ "#CC9900",
11192
+ "#CC9933",
11193
+ "#CCCC00",
11194
+ "#CCCC33",
11195
+ "#FF0000",
11196
+ "#FF0033",
11197
+ "#FF0066",
11198
+ "#FF0099",
11199
+ "#FF00CC",
11200
+ "#FF00FF",
11201
+ "#FF3300",
11202
+ "#FF3333",
11203
+ "#FF3366",
11204
+ "#FF3399",
11205
+ "#FF33CC",
11206
+ "#FF33FF",
11207
+ "#FF6600",
11208
+ "#FF6633",
11209
+ "#FF9900",
11210
+ "#FF9933",
11211
+ "#FFCC00",
11212
+ "#FFCC33"
11213
+ ];
11214
+ function useColors() {
11215
+ if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) {
11216
+ return true;
11217
+ }
11218
+ if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
11219
+ return false;
11220
+ }
11221
+ let m;
11222
+ return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773
11223
+ typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31?
11224
+ // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
11225
+ typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
11226
+ typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
11227
+ }
11228
+ function formatArgs(args) {
11229
+ args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module.exports.humanize(this.diff);
11230
+ if (!this.useColors) {
11231
+ return;
11232
+ }
11233
+ const c = "color: " + this.color;
11234
+ args.splice(1, 0, c, "color: inherit");
11235
+ let index = 0;
11236
+ let lastC = 0;
11237
+ args[0].replace(/%[a-zA-Z%]/g, (match) => {
11238
+ if (match === "%%") {
11239
+ return;
11240
+ }
11241
+ index++;
11242
+ if (match === "%c") {
11243
+ lastC = index;
11244
+ }
11245
+ });
11246
+ args.splice(lastC, 0, c);
11247
+ }
11248
+ exports.log = console.debug || console.log || (() => {
11249
+ });
11250
+ function save(namespaces) {
11251
+ try {
11252
+ if (namespaces) {
11253
+ exports.storage.setItem("debug", namespaces);
11254
+ } else {
11255
+ exports.storage.removeItem("debug");
11256
+ }
11257
+ } catch (error) {
11258
+ }
11259
+ }
11260
+ function load2() {
11261
+ let r;
11262
+ try {
11263
+ r = exports.storage.getItem("debug") || exports.storage.getItem("DEBUG");
11264
+ } catch (error) {
11265
+ }
11266
+ if (!r && typeof process !== "undefined" && "env" in process) {
11267
+ r = process.env.DEBUG;
11268
+ }
11269
+ return r;
11270
+ }
11271
+ function localstorage() {
11272
+ try {
11273
+ return localStorage;
11274
+ } catch (error) {
11275
+ }
11276
+ }
11277
+ module.exports = require_common()(exports);
11278
+ var { formatters } = module.exports;
11279
+ formatters.j = function(v) {
11280
+ try {
11281
+ return JSON.stringify(v);
11282
+ } catch (error) {
11283
+ return "[UnexpectedJSONParseError]: " + error.message;
11284
+ }
11285
+ };
11286
+ }
11287
+ });
11288
+
11289
+ // node_modules/.pnpm/has-flag@4.0.0/node_modules/has-flag/index.js
11290
+ var require_has_flag = __commonJS({
11291
+ "node_modules/.pnpm/has-flag@4.0.0/node_modules/has-flag/index.js"(exports, module) {
11292
+ "use strict";
11293
+ module.exports = (flag, argv = process.argv) => {
11294
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
11295
+ const position = argv.indexOf(prefix + flag);
11296
+ const terminatorPosition = argv.indexOf("--");
11297
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
11298
+ };
11299
+ }
11300
+ });
11301
+
11302
+ // node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js
11303
+ var require_supports_color = __commonJS({
11304
+ "node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports, module) {
11305
+ "use strict";
11306
+ var os2 = __require("os");
11307
+ var tty = __require("tty");
11308
+ var hasFlag = require_has_flag();
11309
+ var { env } = process;
11310
+ var forceColor;
11311
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
11312
+ forceColor = 0;
11313
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
11314
+ forceColor = 1;
11315
+ }
11316
+ if ("FORCE_COLOR" in env) {
11317
+ if (env.FORCE_COLOR === "true") {
11318
+ forceColor = 1;
11319
+ } else if (env.FORCE_COLOR === "false") {
11320
+ forceColor = 0;
11321
+ } else {
11322
+ forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3);
11323
+ }
11324
+ }
11325
+ function translateLevel(level) {
11326
+ if (level === 0) {
11327
+ return false;
11328
+ }
11329
+ return {
11330
+ level,
11331
+ hasBasic: true,
11332
+ has256: level >= 2,
11333
+ has16m: level >= 3
11334
+ };
11335
+ }
11336
+ function supportsColor(haveStream, streamIsTTY) {
11337
+ if (forceColor === 0) {
11338
+ return 0;
11339
+ }
11340
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
11341
+ return 3;
11342
+ }
11343
+ if (hasFlag("color=256")) {
11344
+ return 2;
11345
+ }
11346
+ if (haveStream && !streamIsTTY && forceColor === void 0) {
11347
+ return 0;
11348
+ }
11349
+ const min = forceColor || 0;
11350
+ if (env.TERM === "dumb") {
11351
+ return min;
11352
+ }
11353
+ if (process.platform === "win32") {
11354
+ const osRelease = os2.release().split(".");
11355
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
11356
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
11357
+ }
11358
+ return 1;
11359
+ }
11360
+ if ("CI" in env) {
11361
+ if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
11362
+ return 1;
11363
+ }
11364
+ return min;
11365
+ }
11366
+ if ("TEAMCITY_VERSION" in env) {
11367
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
11368
+ }
11369
+ if (env.COLORTERM === "truecolor") {
11370
+ return 3;
11371
+ }
11372
+ if ("TERM_PROGRAM" in env) {
11373
+ const version = parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
11374
+ switch (env.TERM_PROGRAM) {
11375
+ case "iTerm.app":
11376
+ return version >= 3 ? 3 : 2;
11377
+ case "Apple_Terminal":
11378
+ return 2;
11379
+ }
11380
+ }
11381
+ if (/-256(color)?$/i.test(env.TERM)) {
11382
+ return 2;
11383
+ }
11384
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
11385
+ return 1;
11386
+ }
11387
+ if ("COLORTERM" in env) {
11388
+ return 1;
11389
+ }
11390
+ return min;
11391
+ }
11392
+ function getSupportLevel(stream) {
11393
+ const level = supportsColor(stream, stream && stream.isTTY);
11394
+ return translateLevel(level);
11395
+ }
11396
+ module.exports = {
11397
+ supportsColor: getSupportLevel,
11398
+ stdout: translateLevel(supportsColor(true, tty.isatty(1))),
11399
+ stderr: translateLevel(supportsColor(true, tty.isatty(2)))
11400
+ };
11401
+ }
11402
+ });
11403
+
11404
+ // node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/node.js
11405
+ var require_node = __commonJS({
11406
+ "node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/node.js"(exports, module) {
11407
+ var tty = __require("tty");
11408
+ var util2 = __require("util");
11409
+ exports.init = init;
11410
+ exports.log = log;
11411
+ exports.formatArgs = formatArgs;
11412
+ exports.save = save;
11413
+ exports.load = load2;
11414
+ exports.useColors = useColors;
11415
+ exports.destroy = util2.deprecate(
11416
+ () => {
11417
+ },
11418
+ "Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."
11419
+ );
11420
+ exports.colors = [6, 2, 3, 4, 5, 1];
11421
+ try {
11422
+ const supportsColor = require_supports_color();
11423
+ if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
11424
+ exports.colors = [
11425
+ 20,
11426
+ 21,
11427
+ 26,
11428
+ 27,
11429
+ 32,
11430
+ 33,
11431
+ 38,
11432
+ 39,
11433
+ 40,
11434
+ 41,
11435
+ 42,
11436
+ 43,
11437
+ 44,
11438
+ 45,
11439
+ 56,
11440
+ 57,
11441
+ 62,
11442
+ 63,
11443
+ 68,
11444
+ 69,
11445
+ 74,
11446
+ 75,
11447
+ 76,
11448
+ 77,
11449
+ 78,
11450
+ 79,
11451
+ 80,
11452
+ 81,
11453
+ 92,
11454
+ 93,
11455
+ 98,
11456
+ 99,
11457
+ 112,
11458
+ 113,
11459
+ 128,
11460
+ 129,
11461
+ 134,
11462
+ 135,
11463
+ 148,
11464
+ 149,
11465
+ 160,
11466
+ 161,
11467
+ 162,
11468
+ 163,
11469
+ 164,
11470
+ 165,
11471
+ 166,
11472
+ 167,
11473
+ 168,
11474
+ 169,
11475
+ 170,
11476
+ 171,
11477
+ 172,
11478
+ 173,
11479
+ 178,
11480
+ 179,
11481
+ 184,
11482
+ 185,
11483
+ 196,
11484
+ 197,
11485
+ 198,
11486
+ 199,
11487
+ 200,
11488
+ 201,
11489
+ 202,
11490
+ 203,
11491
+ 204,
11492
+ 205,
11493
+ 206,
11494
+ 207,
11495
+ 208,
11496
+ 209,
11497
+ 214,
11498
+ 215,
11499
+ 220,
11500
+ 221
11501
+ ];
11502
+ }
11503
+ } catch (error) {
11504
+ }
11505
+ exports.inspectOpts = Object.keys(process.env).filter((key) => {
11506
+ return /^debug_/i.test(key);
11507
+ }).reduce((obj, key) => {
11508
+ const prop = key.substring(6).toLowerCase().replace(/_([a-z])/g, (_, k) => {
11509
+ return k.toUpperCase();
11510
+ });
11511
+ let val = process.env[key];
11512
+ if (/^(yes|on|true|enabled)$/i.test(val)) {
11513
+ val = true;
11514
+ } else if (/^(no|off|false|disabled)$/i.test(val)) {
11515
+ val = false;
11516
+ } else if (val === "null") {
11517
+ val = null;
11518
+ } else {
11519
+ val = Number(val);
11520
+ }
11521
+ obj[prop] = val;
11522
+ return obj;
11523
+ }, {});
11524
+ function useColors() {
11525
+ return "colors" in exports.inspectOpts ? Boolean(exports.inspectOpts.colors) : tty.isatty(process.stderr.fd);
11526
+ }
11527
+ function formatArgs(args) {
11528
+ const { namespace: name, useColors: useColors2 } = this;
11529
+ if (useColors2) {
11530
+ const c = this.color;
11531
+ const colorCode = "\x1B[3" + (c < 8 ? c : "8;5;" + c);
11532
+ const prefix = ` ${colorCode};1m${name} \x1B[0m`;
11533
+ args[0] = prefix + args[0].split("\n").join("\n" + prefix);
11534
+ args.push(colorCode + "m+" + module.exports.humanize(this.diff) + "\x1B[0m");
11535
+ } else {
11536
+ args[0] = getDate() + name + " " + args[0];
11537
+ }
11538
+ }
11539
+ function getDate() {
11540
+ if (exports.inspectOpts.hideDate) {
11541
+ return "";
11542
+ }
11543
+ return (/* @__PURE__ */ new Date()).toISOString() + " ";
11544
+ }
11545
+ function log(...args) {
11546
+ return process.stderr.write(util2.formatWithOptions(exports.inspectOpts, ...args) + "\n");
11547
+ }
11548
+ function save(namespaces) {
11549
+ if (namespaces) {
11550
+ process.env.DEBUG = namespaces;
11551
+ } else {
11552
+ delete process.env.DEBUG;
11553
+ }
11554
+ }
11555
+ function load2() {
11556
+ return process.env.DEBUG;
11557
+ }
11558
+ function init(debug) {
11559
+ debug.inspectOpts = {};
11560
+ const keys = Object.keys(exports.inspectOpts);
11561
+ for (let i = 0; i < keys.length; i++) {
11562
+ debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
11563
+ }
11564
+ }
11565
+ module.exports = require_common()(exports);
11566
+ var { formatters } = module.exports;
11567
+ formatters.o = function(v) {
11568
+ this.inspectOpts.colors = this.useColors;
11569
+ return util2.inspect(v, this.inspectOpts).split("\n").map((str2) => str2.trim()).join(" ");
11570
+ };
11571
+ formatters.O = function(v) {
11572
+ this.inspectOpts.colors = this.useColors;
11573
+ return util2.inspect(v, this.inspectOpts);
11574
+ };
11575
+ }
11576
+ });
11577
+
11578
+ // node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/index.js
11579
+ var require_src = __commonJS({
11580
+ "node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/index.js"(exports, module) {
11581
+ if (typeof process === "undefined" || process.type === "renderer" || process.browser === true || process.__nwjs) {
11582
+ module.exports = require_browser();
11583
+ } else {
11584
+ module.exports = require_node();
11585
+ }
11586
+ }
11587
+ });
11588
+
10743
11589
  // node_modules/.pnpm/follow-redirects@1.16.0/node_modules/follow-redirects/debug.js
10744
11590
  var require_debug = __commonJS({
10745
11591
  "node_modules/.pnpm/follow-redirects@1.16.0/node_modules/follow-redirects/debug.js"(exports, module) {
@@ -10747,7 +11593,7 @@ var require_debug = __commonJS({
10747
11593
  module.exports = function() {
10748
11594
  if (!debug) {
10749
11595
  try {
10750
- debug = __require("debug")("follow-redirects");
11596
+ debug = require_src()("follow-redirects");
10751
11597
  } catch (error) {
10752
11598
  }
10753
11599
  if (typeof debug !== "function") {
@@ -111535,11 +112381,132 @@ var init_channelPostClient = __esm({
111535
112381
  }
111536
112382
  });
111537
112383
 
112384
+ // src/lark/channelCotClient.ts
112385
+ async function withTimeout(p, ms, label) {
112386
+ let timer;
112387
+ const timeout = new Promise((_resolve, reject) => {
112388
+ timer = setTimeout(
112389
+ () => reject(new Error(`[channel.cot] ${label} timed out after ${ms}ms`)),
112390
+ ms
112391
+ );
112392
+ timer.unref?.();
112393
+ });
112394
+ try {
112395
+ return await Promise.race([p, timeout]);
112396
+ } finally {
112397
+ if (timer) clearTimeout(timer);
112398
+ }
112399
+ }
112400
+ function assertOk(res, label) {
112401
+ if (res.code !== void 0 && res.code !== 0) {
112402
+ throw new Error(
112403
+ `[channel.cot] ${label} failed: code=${res.code} msg=${res.msg ?? "<no msg>"}`
112404
+ );
112405
+ }
112406
+ }
112407
+ function stringField(data, key) {
112408
+ const value = data?.[key];
112409
+ return typeof value === "string" ? value : void 0;
112410
+ }
112411
+ var COT_REQUEST_TIMEOUT_MS, ChannelCotClient;
112412
+ var init_channelCotClient = __esm({
112413
+ "src/lark/channelCotClient.ts"() {
112414
+ "use strict";
112415
+ COT_REQUEST_TIMEOUT_MS = 8e3;
112416
+ ChannelCotClient = class {
112417
+ resolveChannel;
112418
+ constructor(opts) {
112419
+ this.resolveChannel = opts.resolveChannel;
112420
+ }
112421
+ channel() {
112422
+ const ch = this.resolveChannel();
112423
+ if (!ch) {
112424
+ throw new Error("[channel.cot] outbound called before the Channel SDK connected");
112425
+ }
112426
+ return ch;
112427
+ }
112428
+ /** Every COT call goes through here, so all get the same hard timeout. */
112429
+ request(opts) {
112430
+ return withTimeout(
112431
+ this.channel().rawClient.request(opts),
112432
+ COT_REQUEST_TIMEOUT_MS,
112433
+ `${opts.method} ${opts.url}`
112434
+ );
112435
+ }
112436
+ async create(target) {
112437
+ const receiveIdType = target.threadId ? "thread_id" : "chat_id";
112438
+ const receiveId = target.threadId ?? target.chatId;
112439
+ const res = await this.request({
112440
+ url: "/open-apis/im/v1/message_cot",
112441
+ method: "POST",
112442
+ params: { receive_id_type: receiveIdType },
112443
+ data: {
112444
+ receive_id: receiveId,
112445
+ // origin_message_id only anchors non-thread bubbles; harmless if set,
112446
+ // but omitted for topic threads where the thread id already anchors.
112447
+ ...!target.threadId && target.originMessageId ? { origin_message_id: target.originMessageId } : {}
112448
+ }
112449
+ });
112450
+ assertOk(res, "create");
112451
+ const cotId = stringField(res.data, "cot_id");
112452
+ const messageId = stringField(res.data, "message_id");
112453
+ if (!cotId || !messageId) {
112454
+ throw new Error(
112455
+ `[channel.cot] create returned no cot_id/message_id (${JSON.stringify(res.data ?? {}).slice(0, 200)})`
112456
+ );
112457
+ }
112458
+ return { cotId, messageId };
112459
+ }
112460
+ async update(ref, events) {
112461
+ if (events.length === 0) return;
112462
+ const res = await this.request({
112463
+ url: "/open-apis/im/v1/message_cot",
112464
+ method: "PUT",
112465
+ data: { cot_id: ref.cotId, message_id: ref.messageId, events: [...events] }
112466
+ });
112467
+ assertOk(res, "update");
112468
+ }
112469
+ async complete(ref, reason) {
112470
+ const res = await this.request({
112471
+ url: `/open-apis/im/v1/message_cot/complete/${encodeURIComponent(ref.cotId)}`,
112472
+ method: "POST",
112473
+ params: { message_id: ref.messageId, reason }
112474
+ });
112475
+ assertOk(res, "complete");
112476
+ }
112477
+ async resolveThreadId(messageId) {
112478
+ try {
112479
+ const res = await this.request({
112480
+ url: `/open-apis/im/v1/messages/${encodeURIComponent(messageId)}`,
112481
+ method: "GET"
112482
+ });
112483
+ if (res.code !== void 0 && res.code !== 0) return void 0;
112484
+ const items = res.data?.["items"];
112485
+ if (!Array.isArray(items)) return void 0;
112486
+ for (const item of items) {
112487
+ if (item && typeof item === "object") {
112488
+ const threadId = item["thread_id"];
112489
+ if (typeof threadId === "string" && threadId.startsWith("omt_")) {
112490
+ return threadId;
112491
+ }
112492
+ }
112493
+ }
112494
+ return void 0;
112495
+ } catch {
112496
+ return void 0;
112497
+ }
112498
+ }
112499
+ };
112500
+ }
112501
+ });
112502
+
111538
112503
  // src/lark/channelClient.ts
111539
112504
  var channelClient_exports = {};
111540
112505
  __export(channelClient_exports, {
111541
112506
  ChannelClient: () => ChannelClient,
112507
+ DEFAULT_OPEN_CHAT_DISCOVERY_MS: () => DEFAULT_OPEN_CHAT_DISCOVERY_MS,
111542
112508
  channelMsgToLarkEvent: () => channelMsgToLarkEvent,
112509
+ resolveOpenChatDiscoveryMs: () => resolveOpenChatDiscoveryMs,
111543
112510
  resolveRecoveredThreadId: () => resolveRecoveredThreadId,
111544
112511
  synthesizeCardActionEvent: () => synthesizeCardActionEvent
111545
112512
  });
@@ -111583,13 +112550,13 @@ function arrayField(obj, key) {
111583
112550
  const value = obj[key];
111584
112551
  return Array.isArray(value) ? value : null;
111585
112552
  }
111586
- function stringField(obj, key) {
112553
+ function stringField2(obj, key) {
111587
112554
  if (!obj || typeof obj !== "object") return void 0;
111588
112555
  const value = obj[key];
111589
112556
  return typeof value === "string" ? value : void 0;
111590
112557
  }
111591
112558
  function nonEmptyStringField(obj, key) {
111592
- const value = stringField(obj, key);
112559
+ const value = stringField2(obj, key);
111593
112560
  return value && value.length > 0 ? value : void 0;
111594
112561
  }
111595
112562
  function parseLarkCliMessages(stdout2) {
@@ -111692,6 +112659,7 @@ var init_channelClient = __esm({
111692
112659
  init_channelCardClient();
111693
112660
  init_channelCardKitClient();
111694
112661
  init_channelPostClient();
112662
+ init_channelCotClient();
111695
112663
  execFile4 = promisify4(execFileCallback);
111696
112664
  LEARNED_CHATS_LIMIT = 100;
111697
112665
  SEEN_MESSAGES_LIMIT = 1e3;
@@ -111827,6 +112795,8 @@ var init_channelClient = __esm({
111827
112795
  cardKitClient = null;
111828
112796
  /** Lazily built and only requested by main.ts when post outbound gates are configured. */
111829
112797
  postClient = null;
112798
+ /** Lazily built COT (思维链) client; only requested when a bot enables cot != off. */
112799
+ cotClient = null;
111830
112800
  constructor(opts) {
111831
112801
  if (!opts.appId || !opts.appSecret) {
111832
112802
  throw new Error(
@@ -112044,6 +113014,20 @@ var init_channelClient = __esm({
112044
113014
  log(`cardAction \u2192 synthesized turn: value=${JSON.stringify(evt.action?.value)} thread=${ev.thread_id ?? "?"}`);
112045
113015
  this.queue.push(ev);
112046
113016
  }
113017
+ /**
113018
+ * Push an already-built synthetic LarkMessageEvent onto the inbound queue,
113019
+ * so handler.ts processes it as an ordinary turn. Same mechanism as
113020
+ * {@link handleCardAction}'s cardAction → queue.push, generalized for other
113021
+ * bridge-external signal sources — currently only the task-handle comment
113022
+ * poller (src/tasklist/commentPoller.ts), which synthesizes a turn from a
113023
+ * new Feishu task comment. Public because the poller lives outside this
113024
+ * class (main.ts wires it); the caller is fully responsible for building a
113025
+ * well-formed event (thread_id/root_id resolved, etc.) — this method does
113026
+ * no validation of its own, mirroring handleCardAction's contract.
113027
+ */
113028
+ enqueueSyntheticEvent(ev) {
113029
+ this.queue.push(ev);
113030
+ }
112047
113031
  /**
112048
113032
  * Return an OutboundCardClient bound to this client's channel handle.
112049
113033
  *
@@ -112078,6 +113062,22 @@ var init_channelClient = __esm({
112078
113062
  }
112079
113063
  return this.cardKitClient;
112080
113064
  }
113065
+ /**
113066
+ * Return an OutboundCotClient bound to this client's channel handle.
113067
+ *
113068
+ * main.ts only calls this when the bot's `cot` config is not "off". The COT
113069
+ * bubble uses the SDK's generic rawClient.request() (tenant token auto-
113070
+ * injected via the same token manager as every other outbound call), so it
113071
+ * adds no auth surface. Resolves the live channel lazily at call time.
113072
+ */
113073
+ outboundCotClient() {
113074
+ if (!this.cotClient) {
113075
+ this.cotClient = new ChannelCotClient({
113076
+ resolveChannel: () => this.channel
113077
+ });
113078
+ }
113079
+ return this.cotClient;
113080
+ }
112081
113081
  /**
112082
113082
  * Return an OutboundPostClient bound to this client's channel handle.
112083
113083
  *
@@ -112381,7 +113381,7 @@ var init_channelClient = __esm({
112381
113381
  const chats = parseLarkCliMessages(stdout2) ?? [];
112382
113382
  fetched += chats.length;
112383
113383
  for (const raw of chats) {
112384
- const chatId = stringField(raw, "chat_id");
113384
+ const chatId = stringField2(raw, "chat_id");
112385
113385
  if (!chatId?.startsWith("oc_")) continue;
112386
113386
  discoveredChatIds.add(chatId);
112387
113387
  const before = this.recentlySeenChatIds.size;
@@ -112395,7 +113395,7 @@ var init_channelClient = __esm({
112395
113395
  const hasMore = Boolean(
112396
113396
  (data && typeof data === "object" && data["has_more"]) ?? (parsed && typeof parsed === "object" && parsed["has_more"])
112397
113397
  );
112398
- pageToken = stringField(data, "page_token") ?? stringField(parsed, "page_token") ?? "";
113398
+ pageToken = stringField2(data, "page_token") ?? stringField2(parsed, "page_token") ?? "";
112399
113399
  if (!hasMore || !pageToken) break;
112400
113400
  }
112401
113401
  if (newlyLearned > 0) {
@@ -112654,6 +113654,403 @@ var init_channelClient = __esm({
112654
113654
  }
112655
113655
  });
112656
113656
 
113657
+ // src/tasklist/client.ts
113658
+ var client_exports = {};
113659
+ __export(client_exports, {
113660
+ TaskApiError: () => TaskApiError,
113661
+ TaskListClient: () => TaskListClient,
113662
+ TaskRequestTimeoutError: () => TaskRequestTimeoutError,
113663
+ isPermissionDeniedError: () => isPermissionDeniedError,
113664
+ isTaskNotFoundError: () => isTaskNotFoundError,
113665
+ isTaskRequestTimeoutError: () => isTaskRequestTimeoutError
113666
+ });
113667
+ function isTaskNotFoundError(err2) {
113668
+ if (err2 instanceof TaskApiError) {
113669
+ if (err2.status === 404) return true;
113670
+ if (typeof err2.message === "string" && /not.?found|不存在|resource_not_exist/i.test(err2.message)) {
113671
+ return true;
113672
+ }
113673
+ }
113674
+ return false;
113675
+ }
113676
+ function isPermissionDeniedError(err2) {
113677
+ if (err2 instanceof TaskApiError) {
113678
+ if (err2.status === 403) return true;
113679
+ if (typeof err2.message === "string" && /no permission|no.access|forbidden|access.denied|无权限|未授权/i.test(err2.message)) {
113680
+ return true;
113681
+ }
113682
+ }
113683
+ return false;
113684
+ }
113685
+ function parseDueMs(task) {
113686
+ const due = asRecord(task["due"]);
113687
+ const timestamp2 = due["timestamp"];
113688
+ if (typeof timestamp2 !== "string") return void 0;
113689
+ const ms = Number(timestamp2);
113690
+ if (!Number.isFinite(ms)) return void 0;
113691
+ return due["is_all_day"] === true ? ms + MS_PER_DAY : ms;
113692
+ }
113693
+ function asRecord(v) {
113694
+ return typeof v === "object" && v !== null ? v : {};
113695
+ }
113696
+ function wrapErr(label, err2) {
113697
+ if (err2 instanceof TaskApiError) throw err2;
113698
+ const rec = asRecord(err2);
113699
+ const response = asRecord(rec["response"]);
113700
+ const status = typeof response["status"] === "number" ? response["status"] : void 0;
113701
+ const body = asRecord(response["data"]);
113702
+ const code = typeof body["code"] === "number" ? body["code"] : void 0;
113703
+ const bodyMsg = typeof body["msg"] === "string" ? body["msg"] : void 0;
113704
+ const message = `[tasklist.client] ${label} failed: ${bodyMsg ?? err2?.message ?? String(err2)}`;
113705
+ throw new TaskApiError(message, { status, code, cause: err2 });
113706
+ }
113707
+ function isTaskRequestTimeoutError(err2) {
113708
+ if (err2 instanceof TaskRequestTimeoutError) return true;
113709
+ if (err2 instanceof TaskApiError && err2.cause instanceof TaskRequestTimeoutError) return true;
113710
+ return false;
113711
+ }
113712
+ function withTimeout2(promise, ms, label) {
113713
+ let timer;
113714
+ const timeout = new Promise((_, reject) => {
113715
+ timer = setTimeout(() => reject(new TaskRequestTimeoutError(`${label} timed out after ${ms}ms`)), ms);
113716
+ });
113717
+ return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
113718
+ }
113719
+ function isNotFoundLikeRaw(err2) {
113720
+ const rec = asRecord(err2);
113721
+ const response = asRecord(rec["response"]);
113722
+ if (response["status"] === 404) return true;
113723
+ const body = asRecord(response["data"]);
113724
+ const msg = typeof body["msg"] === "string" ? body["msg"] : "";
113725
+ return /not.?found|不存在|resource_not_exist/i.test(msg);
113726
+ }
113727
+ var DEFAULT_TIMEOUT_MS, TaskApiError, MS_PER_DAY, TaskRequestTimeoutError, TASK_V2_BASE, TaskListClient;
113728
+ var init_client = __esm({
113729
+ "src/tasklist/client.ts"() {
113730
+ "use strict";
113731
+ DEFAULT_TIMEOUT_MS = 12e3;
113732
+ TaskApiError = class extends Error {
113733
+ status;
113734
+ code;
113735
+ constructor(message, opts) {
113736
+ super(message);
113737
+ this.name = "TaskApiError";
113738
+ this.status = opts.status;
113739
+ this.code = opts.code;
113740
+ this.cause = opts.cause;
113741
+ }
113742
+ };
113743
+ MS_PER_DAY = 24 * 60 * 6e4;
113744
+ TaskRequestTimeoutError = class extends Error {
113745
+ };
113746
+ TASK_V2_BASE = "/open-apis/task/v2";
113747
+ TaskListClient = class {
113748
+ #requester;
113749
+ #timeoutMs;
113750
+ constructor(requester, opts = {}) {
113751
+ this.#requester = requester;
113752
+ this.#timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
113753
+ }
113754
+ /** Single choke point for every outbound call — applies the timeout race uniformly. */
113755
+ #request(config, label) {
113756
+ return withTimeout2(this.#requester.request(config), this.#timeoutMs, label);
113757
+ }
113758
+ async getTask(taskGuid) {
113759
+ let resp;
113760
+ try {
113761
+ resp = await this.#request(
113762
+ {
113763
+ method: "GET",
113764
+ url: `${TASK_V2_BASE}/tasks/${encodeURIComponent(taskGuid)}`,
113765
+ params: { user_id_type: "open_id" }
113766
+ },
113767
+ `getTask(${taskGuid})`
113768
+ );
113769
+ } catch (err2) {
113770
+ if (isNotFoundLikeRaw(err2)) return null;
113771
+ wrapErr(`getTask(${taskGuid})`, err2);
113772
+ }
113773
+ const task = resp.data?.task;
113774
+ if (!task) return null;
113775
+ return {
113776
+ guid: String(task["guid"] ?? task["id"] ?? taskGuid),
113777
+ summary: typeof task["summary"] === "string" ? task["summary"] : void 0,
113778
+ description: typeof task["description"] === "string" ? task["description"] : void 0,
113779
+ completedAt: typeof task["completed_at"] === "string" ? task["completed_at"] : void 0,
113780
+ dueMs: parseDueMs(task)
113781
+ };
113782
+ }
113783
+ /** Overwrite the task description (bridge-owned status-snapshot block only — see writeback.ts). */
113784
+ async patchDescription(taskGuid, description) {
113785
+ await this.#patchTask(taskGuid, { description }, ["description"]);
113786
+ }
113787
+ /** Mark the task complete "now". */
113788
+ async complete(taskGuid) {
113789
+ await this.#patchTask(taskGuid, { completed_at: String(Date.now()) }, ["completed_at"]);
113790
+ }
113791
+ /**
113792
+ * Clear completed_at (task v2 patch semantics: an `update_fields` entry with
113793
+ * no corresponding value in `task` clears that field — verified via
113794
+ * `lark-cli schema task tasks patch`).
113795
+ */
113796
+ async reopen(taskGuid) {
113797
+ await this.#patchTask(taskGuid, {}, ["completed_at"]);
113798
+ }
113799
+ async #patchTask(taskGuid, task, updateFields) {
113800
+ const label = `patchTask(${taskGuid}, fields=${updateFields.join(",")})`;
113801
+ try {
113802
+ await this.#request(
113803
+ {
113804
+ method: "PATCH",
113805
+ url: `${TASK_V2_BASE}/tasks/${encodeURIComponent(taskGuid)}`,
113806
+ params: { user_id_type: "open_id" },
113807
+ data: { task, update_fields: updateFields }
113808
+ },
113809
+ label
113810
+ );
113811
+ } catch (err2) {
113812
+ wrapErr(label, err2);
113813
+ }
113814
+ }
113815
+ async addComment(taskGuid, content) {
113816
+ const label = `addComment(${taskGuid})`;
113817
+ try {
113818
+ await this.#request(
113819
+ {
113820
+ method: "POST",
113821
+ url: `${TASK_V2_BASE}/comments`,
113822
+ data: { resource_type: "task", resource_id: taskGuid, content }
113823
+ },
113824
+ label
113825
+ );
113826
+ } catch (err2) {
113827
+ wrapErr(label, err2);
113828
+ }
113829
+ }
113830
+ async listComments(taskGuid, opts = {}) {
113831
+ const label = `listComments(${taskGuid})`;
113832
+ let resp;
113833
+ try {
113834
+ resp = await this.#request(
113835
+ {
113836
+ method: "GET",
113837
+ url: `${TASK_V2_BASE}/comments`,
113838
+ params: {
113839
+ resource_type: "task",
113840
+ resource_id: taskGuid,
113841
+ page_size: opts.pageSize ?? 50,
113842
+ // Spread-omit: the SDK serializes a literal `undefined` value into
113843
+ // the query string ("page_token=undefined"), which strict endpoints
113844
+ // reject with 1470400 — only send the key when we hold a real token.
113845
+ ...opts.pageToken ? { page_token: opts.pageToken } : {}
113846
+ }
113847
+ },
113848
+ label
113849
+ );
113850
+ } catch (err2) {
113851
+ wrapErr(label, err2);
113852
+ }
113853
+ const items = resp.data?.items ?? [];
113854
+ const comments = items.map((raw) => {
113855
+ const creator = asRecord(raw["creator"] ?? raw["commentator"]);
113856
+ return {
113857
+ id: String(raw["id"] ?? raw["comment_id"] ?? ""),
113858
+ content: typeof raw["content"] === "string" ? raw["content"] : "",
113859
+ // Real API response uses `created_at` (string ms). create_milli_time/
113860
+ // create_time are kept as fallbacks only in case a differently-versioned
113861
+ // tenant/response shape uses them — created_at is checked first since
113862
+ // it's the verified real field (2026-07 live check against docs/task-handle.md).
113863
+ createMillis: typeof raw["created_at"] === "string" ? raw["created_at"] : typeof raw["create_milli_time"] === "string" ? raw["create_milli_time"] : typeof raw["create_time"] === "string" ? raw["create_time"] : void 0,
113864
+ creatorId: typeof creator["id"] === "string" ? creator["id"] : void 0,
113865
+ creatorType: typeof creator["type"] === "string" ? creator["type"] : void 0
113866
+ };
113867
+ });
113868
+ return {
113869
+ comments,
113870
+ hasMore: Boolean(resp.data?.has_more),
113871
+ pageToken: resp.data?.page_token
113872
+ };
113873
+ }
113874
+ /**
113875
+ * Fetch a tasklist's current guid + member list (verified against live
113876
+ * `lark-cli schema task tasklists get`, 1.0.64 — `GET /tasklists/
113877
+ * :tasklist_guid`). Used by `tasklist-init` as a post-create/post-reuse
113878
+ * safety net: read the membership back and warn if the owner's open_id
113879
+ * didn't actually land as a member (a silently-dropped member is a real
113880
+ * platform failure mode this guards against — see tasklistInit.ts).
113881
+ */
113882
+ async getTasklist(tasklistGuid) {
113883
+ const label = `getTasklist(${tasklistGuid})`;
113884
+ let resp;
113885
+ try {
113886
+ resp = await this.#request(
113887
+ {
113888
+ method: "GET",
113889
+ url: `${TASK_V2_BASE}/tasklists/${encodeURIComponent(tasklistGuid)}`,
113890
+ params: { user_id_type: "open_id" }
113891
+ },
113892
+ label
113893
+ );
113894
+ } catch (err2) {
113895
+ if (isNotFoundLikeRaw(err2)) return null;
113896
+ wrapErr(label, err2);
113897
+ }
113898
+ const tasklist = resp.data?.tasklist;
113899
+ if (!tasklist) return null;
113900
+ const rawMembers = Array.isArray(tasklist["members"]) ? tasklist["members"] : [];
113901
+ const members = rawMembers.map((m) => ({
113902
+ id: String(m["id"] ?? ""),
113903
+ type: m["type"] === "user" || m["type"] === "chat" || m["type"] === "app" ? m["type"] : void 0,
113904
+ role: m["role"] === "assignee" || m["role"] === "follower" || m["role"] === "editor" || m["role"] === "viewer" ? m["role"] : "editor"
113905
+ }));
113906
+ return { guid: String(tasklist["guid"] ?? tasklistGuid), members };
113907
+ }
113908
+ async createTasklist(name, members = []) {
113909
+ const label = `createTasklist(${name})`;
113910
+ let resp;
113911
+ try {
113912
+ resp = await this.#request(
113913
+ {
113914
+ method: "POST",
113915
+ url: `${TASK_V2_BASE}/tasklists`,
113916
+ params: { user_id_type: "open_id" },
113917
+ data: { name, members }
113918
+ },
113919
+ label
113920
+ );
113921
+ } catch (err2) {
113922
+ wrapErr(label, err2);
113923
+ }
113924
+ const guid = resp.data?.tasklist?.["guid"];
113925
+ if (typeof guid !== "string" || guid.length === 0) {
113926
+ throw new TaskApiError(`[tasklist.client] createTasklist(${name}) returned no guid`, {});
113927
+ }
113928
+ return { guid };
113929
+ }
113930
+ /**
113931
+ * List tasks belonging to a tasklist (used by TasklistPoller,
113932
+ * src/tasklist/tasklistPoller.ts, for the v3 "候选注入" candidate
113933
+ * discovery). `GET /tasklists/:tasklist_guid/tasks` verified live
113934
+ * 2026-07-04 against a real tasklist (200 + items[]); note this endpoint
113935
+ * strictly validates `page_token` (a literal "undefined" query value is a
113936
+ * hard 1470400), hence the spread-omit below. Treat as best-effort like
113937
+ * every other tasklist call: TasklistPoller swallows failures and keeps
113938
+ * its previous candidate snapshot rather than propagating.
113939
+ */
113940
+ async listTasklistTasks(tasklistGuid, opts = {}) {
113941
+ const label = `listTasklistTasks(${tasklistGuid})`;
113942
+ let resp;
113943
+ try {
113944
+ resp = await this.#request(
113945
+ {
113946
+ method: "GET",
113947
+ url: `${TASK_V2_BASE}/tasklists/${encodeURIComponent(tasklistGuid)}/tasks`,
113948
+ params: {
113949
+ user_id_type: "open_id",
113950
+ page_size: opts.pageSize ?? 50,
113951
+ // Same spread-omit as listComments: "page_token=undefined" in the
113952
+ // query string is a hard 400 (1470400) on this endpoint — verified
113953
+ // live 2026-07-04 against a real tasklist.
113954
+ ...opts.pageToken ? { page_token: opts.pageToken } : {}
113955
+ }
113956
+ },
113957
+ label
113958
+ );
113959
+ } catch (err2) {
113960
+ wrapErr(label, err2);
113961
+ }
113962
+ const items = resp.data?.items ?? [];
113963
+ const tasks = items.map((task) => ({
113964
+ guid: String(task["guid"] ?? task["id"] ?? ""),
113965
+ summary: typeof task["summary"] === "string" ? task["summary"] : void 0,
113966
+ description: typeof task["description"] === "string" ? task["description"] : void 0,
113967
+ completedAt: typeof task["completed_at"] === "string" ? task["completed_at"] : void 0,
113968
+ dueMs: parseDueMs(task)
113969
+ }));
113970
+ return { tasks, hasMore: Boolean(resp.data?.has_more), pageToken: resp.data?.page_token };
113971
+ }
113972
+ /**
113973
+ * Add members to an existing tasklist (verified against `lark-cli schema
113974
+ * task tasklists add_members`, 1.0.64 — `POST /tasklists/:tasklist_guid/
113975
+ * members`). Used by `tasklist-init --team` and by a bot's own first-run
113976
+ * self-join (docs/task-handle.md §7 "同一 owner 的一组 bot… 把自己加为
113977
+ * editor") to add sibling bot apps as `editor` members of the shared
113978
+ * "Agent Team" list. Idempotent from the caller's perspective: re-adding an
113979
+ * existing member is a harmless no-op per the platform (not independently
113980
+ * verified against a live 409/duplicate-shaped error — callers should treat
113981
+ * any failure here as best-effort, same swallow-and-warn posture as the
113982
+ * rest of this feature).
113983
+ */
113984
+ async addTasklistMembers(tasklistGuid, members) {
113985
+ const label = `addTasklistMembers(${tasklistGuid})`;
113986
+ try {
113987
+ await this.#request(
113988
+ {
113989
+ method: "POST",
113990
+ url: `${TASK_V2_BASE}/tasklists/${encodeURIComponent(tasklistGuid)}/members`,
113991
+ params: { user_id_type: "open_id" },
113992
+ data: { members }
113993
+ },
113994
+ label
113995
+ );
113996
+ } catch (err2) {
113997
+ wrapErr(label, err2);
113998
+ }
113999
+ }
114000
+ };
114001
+ }
114002
+ });
114003
+
114004
+ // src/tasklist/teamRegistry.ts
114005
+ var teamRegistry_exports = {};
114006
+ __export(teamRegistry_exports, {
114007
+ claimTeamTasklistGuid: () => claimTeamTasklistGuid,
114008
+ overwriteTeamTasklistGuid: () => overwriteTeamTasklistGuid,
114009
+ readTeamTasklistGuid: () => readTeamTasklistGuid
114010
+ });
114011
+ import { rename as rename4, readFile as readFile6, writeFile as writeFile5, mkdir as mkdir4 } from "node:fs/promises";
114012
+ import { dirname } from "node:path";
114013
+ async function readTeamTasklistGuid(filePath) {
114014
+ try {
114015
+ const raw = await readFile6(filePath, "utf8");
114016
+ const parsed = JSON.parse(raw);
114017
+ if (typeof parsed !== "object" || parsed === null) return void 0;
114018
+ const guid = parsed["tasklistGuid"];
114019
+ return typeof guid === "string" && guid.length > 0 ? guid : void 0;
114020
+ } catch {
114021
+ return void 0;
114022
+ }
114023
+ }
114024
+ async function claimTeamTasklistGuid(filePath, tasklistGuid) {
114025
+ const existing = await readTeamTasklistGuid(filePath);
114026
+ if (existing) return existing;
114027
+ await writeTeamTasklistGuid(filePath, tasklistGuid);
114028
+ return tasklistGuid;
114029
+ }
114030
+ async function overwriteTeamTasklistGuid(filePath, tasklistGuid) {
114031
+ await writeTeamTasklistGuid(filePath, tasklistGuid);
114032
+ }
114033
+ async function writeTeamTasklistGuid(filePath, tasklistGuid) {
114034
+ try {
114035
+ const file = { version: 1, tasklistGuid };
114036
+ const json2 = JSON.stringify(file, null, 2);
114037
+ const tmpPath = `${filePath}.tmp`;
114038
+ await mkdir4(dirname(filePath), { recursive: true });
114039
+ await writeFile5(tmpPath, json2, "utf8");
114040
+ await rename4(tmpPath, filePath);
114041
+ } catch (err2) {
114042
+ console.warn(
114043
+ `[tasklist.teamRegistry] failed to persist tasklistGuid to ${filePath} (continuing, best-effort):`,
114044
+ err2
114045
+ );
114046
+ }
114047
+ }
114048
+ var init_teamRegistry = __esm({
114049
+ "src/tasklist/teamRegistry.ts"() {
114050
+ "use strict";
114051
+ }
114052
+ });
114053
+
112657
114054
  // node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/can-promise.js
112658
114055
  var require_can_promise = __commonJS({
112659
114056
  "node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/can-promise.js"(exports, module) {
@@ -116991,7 +118388,7 @@ var require_canvas = __commonJS({
116991
118388
  });
116992
118389
 
116993
118390
  // node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/browser.js
116994
- var require_browser = __commonJS({
118391
+ var require_browser2 = __commonJS({
116995
118392
  "node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/browser.js"(exports) {
116996
118393
  var canPromise = require_can_promise();
116997
118394
  var QRCode2 = require_qrcode();
@@ -117138,7 +118535,7 @@ var require_server = __commonJS({
117138
118535
  }
117139
118536
  }
117140
118537
  exports.create = QRCode2.create;
117141
- exports.toCanvas = require_browser().toCanvas;
118538
+ exports.toCanvas = require_browser2().toCanvas;
117142
118539
  exports.toString = function toString3(text, opts, cb) {
117143
118540
  const params = checkParams(text, opts, cb);
117144
118541
  const type2 = params.opts ? params.opts.type : void 0;
@@ -124196,10 +125593,81 @@ var ResponseSurfacePrototypeConfigSchema = external_exports.object({
124196
125593
  }).default(responseSurfacePrototypeConfigDefaults);
124197
125594
 
124198
125595
  // src/config/botLoader.ts
125596
+ var KNOWN_EFFORT_VALUES = /* @__PURE__ */ new Set(["low", "medium", "high", "max"]);
124199
125597
  var GitIdentitySchema = external_exports.object({
124200
125598
  name: external_exports.string().min(1),
124201
125599
  email: external_exports.string().email()
124202
125600
  });
125601
+ var TaskHandleConfigSchema = external_exports.object({
125602
+ /**
125603
+ * owner 的「Agent Team」共享清单 GUID。由 `larkway tasklist-init --team`
125604
+ * provisioning 子命令产出并手工填入,或经共享注册文件(task-team.json)被
125605
+ * 同组 bot 自动发现。缺失 = 功能休眠(main.ts 不会替你建)。
125606
+ */
125607
+ tasklistGuid: external_exports.string().min(1).optional(),
125608
+ /**
125609
+ * @deprecated v1 遗留字段,v2 已去掉「enabled 开关」语义 —— 真正门槛见上。
125610
+ * 仅为了不 break 现网已写 `enabled: true` 的 yaml(strict schema 否则会拒绝
125611
+ * 未知字段)而接受它;接受后从不读取,loadBots() 会打一条 deprecation warn
125612
+ * 提示删除。新 yaml 不要再写这个字段。
125613
+ */
125614
+ enabled: external_exports.boolean().optional(),
125615
+ /**
125616
+ * v3.1 停滞检测(docs/task-handle.md §12)全部可选,均有保守默认值——不配
125617
+ * 就用默认阈值,不是新的 enable 开关(检测本身跟 tasklistGuid 一样,只要有
125618
+ * 认领记录就跑;想彻底关掉停滞检测本身,见 stallDetectionDisabled)。
125619
+ */
125620
+ /** 绑定话题超过这个时长(ms)无活动 → 判定停滞,唤醒认领的 agent。默认 24h。 */
125621
+ stallThresholdMs: external_exports.number().positive().optional(),
125622
+ /** 若该话题最后一轮 turn 以失败/崩溃收场,改用这个更短的阈值(ms)。默认 30min。 */
125623
+ stallFastThresholdMs: external_exports.number().positive().optional(),
125624
+ /**
125625
+ * v3.2 交接断链检测(docs/task-handle.md §13):若最后一轮完成 turn 的回复
125626
+ * @ 了花名册上的另一个 bot,且对方的 bridge 在这个阈值(ms)内在同一话题没
125627
+ * 有收到任何事件,改用这个更短的阈值(优先级高于上面两个,取更短者)。
125628
+ * 默认 5min——协作断链比一般停滞紧急得多,任务多为小时级,理应分钟级发现。
125629
+ * **下限提示**:5min 对应本机 open 模式 bot 的 gap-fill 巡检周期(300s)+
125630
+ * 一个 patrol tick 缓冲——配更短不会被拦,但低于部署实际的 gap-fill 周期
125631
+ * 有跟补投撞车、同一事件被提醒两次的风险,见 docs/task-handle.md §13。只有
125632
+ * 明确 chats 白名单(非 open 模式)的 bot 没有周期性 gap-fill(只有断线重连
125633
+ * 触发),下限可以放宽到 2min。且只对**同一 bridge 进程内**的协作 bot 生
125634
+ * 效——跨 bridge 的 @ 天生观察不到对方的收到时间,走一般停滞阈值。
125635
+ * **收到只是暂缓,不是永久解除**——见 stallHandoffReceiptGraceMs。
125636
+ */
125637
+ stallHandoffThresholdMs: external_exports.number().positive().optional(),
125638
+ /**
125639
+ * v3.2 revision 3(docs/task-handle.md §13):对方"收到了事件"只在这个阈值
125640
+ * (ms)内被信任——收到之后这段时间里,如果对方**真的跑完一轮 turn**(不是
125641
+ * 又收到一次事件),才算真正解除交接断链;超过这个时长仍没有对方跑完 turn
125642
+ * 的记录,重新判定为断链、再次提醒。默认 30min(相对 handler.ts 自己注释的
125643
+ * 单轮 turn 5-15 分钟耗时留足余量)。防的是反向漏洞:如果"收到"永久解除
125644
+ * 断链判定,对方收到后真的崩了/卡死,就再也不会被判定断链了。
125645
+ */
125646
+ stallHandoffReceiptGraceMs: external_exports.number().positive().optional(),
125647
+ /** 同一任务两次提醒之间的最短间隔(ms),防止骚扰。默认 24h。 */
125648
+ stallNudgeCooldownMs: external_exports.number().positive().optional(),
125649
+ /** 连续几次提醒仍无进展后改为升级(任务评论通知人类,此后对该任务静默直到有新活动)。默认 2。 */
125650
+ stallEscalateAfterNudges: external_exports.number().int().positive().optional(),
125651
+ /** 彻底关闭停滞检测(默认 false = 开启,阈值保守)。 */
125652
+ stallDetectionDisabled: external_exports.boolean().optional(),
125653
+ /**
125654
+ * v3.3 候选黑洞提示(docs/task-handle.md §14):共享清单里一个未认领候选
125655
+ * 任务连续滞留超过这个时长(ms)仍没被任何话题绑定(自动或人工认领都算解除),
125656
+ * TasklistPoller 就在该任务下留一条机械提示评论(每个候选只提一次,绑定
125657
+ * 成功后从"已提示"集合清除,再次滞留可再提)。默认 1h。只对同一 tasklistGuid
125658
+ * 共享组里"首个解析出该 guid 的 bot"的配置生效(跟 TasklistPoller 本身
125659
+ * "一个 guid 一个实例"的粒度一致,见 clientOwnerBotId 的同款约定)。
125660
+ */
125661
+ candidateUnboundAlertMs: external_exports.number().positive().optional(),
125662
+ /**
125663
+ * v3.3 全局唤醒保险丝(docs/task-handle.md §14):这个 bot 的 StallDetector
125664
+ * 每小时(滚动窗口,非整点分桶)最多合成几次唤醒 turn(含通用/断链/过期各
125665
+ * 档,不含升级评论)。超限本轮抑制,只打一条点名哪个任务被抑制的 warn 日志,
125666
+ * 不改任何持久状态,下一轮正常重新评估。默认 6——纯粹是防止未知 bug 导致
125667
+ * 唤醒风暴烧推理额度的保险丝,正常路径永远碰不到它,不是需要调优的阈值。
125668
+ */
125669
+ stallNudgeHourlyCap: external_exports.number().int().positive().optional()
125670
+ }).strict();
124203
125671
  var GitCloneUrlSchema = external_exports.string().min(1).refine(
124204
125672
  (value) => {
124205
125673
  if (/^https?:\/\/\S+$/i.test(value)) return true;
@@ -124387,7 +125855,92 @@ var BotConfigSchema = external_exports.object({
124387
125855
  *
124388
125856
  * @default "claude"
124389
125857
  */
124390
- backend: external_exports.string().min(1).default("claude")
125858
+ backend: external_exports.string().min(1).default("claude"),
125859
+ /**
125860
+ * COT (思维链) 气泡:把 agent 的 thinking 思考过程 + 工具调用摘要实时推到飞书
125861
+ * 原生的可折叠思维链气泡(与最终答案卡片互不干扰,最终答案永远只走卡片)。
125862
+ *
125863
+ * - "off":完全不调用 message_cot API(零网络、零副作用)。
125864
+ * - "brief"(默认):思考过程 + 工具名摘要。
125865
+ * - "detailed":额外推工具入参(TOOL_CALL_ARGS)与截断后的工具结果。
125866
+ *
125867
+ * message_cot 是无公开文档的 API —— 任何一步失败都会在本次会话内自动降级、
125868
+ * 只记 warn,绝不影响卡片和最终答案(见 src/bridge/cotProgress.ts)。
125869
+ */
125870
+ cot: external_exports.enum(["off", "brief", "detailed"]).default("brief"),
125871
+ /**
125872
+ * COT 展示形态。`cot` 管密度(off/brief/detailed),这个管落点:
125873
+ * - "bubble"(默认):走飞书原生 message_cot 思维链气泡(src/bridge/cotProgress.ts)——
125874
+ * 客户端原生渲染(工具图标列表 + 可折叠头),真机体验最好。已知限制:话题内
125875
+ * 锚定不可用(气泡落群顶层,本租户 thread 通道判死),收尾 bug 已在 main 修复。
125876
+ * - "card":思考过程内嵌进答案卡片里的 collapsible_panel(懒创建、答案上方)。
125877
+ * ⚠️ 实验选项 —— 2026-07-05 真机实测:飞书客户端**不渲染 collapsible_panel 的
125878
+ * 折叠交互**(API 全 code=0,但面板被拍平成普通文本、无折叠箭头、终态也不可折叠;
125879
+ * CardKit 无 GET 读回、只能真机人眼验)。故 card 形态目前仅作纯文本内嵌展示用,
125880
+ * 体验不如气泡,不建议作默认。见 memory feishu-message-cot-api。
125881
+ * cot="off" 时本字段无意义(两种形态都不产出)。
125882
+ */
125883
+ cotSurface: external_exports.enum(["card", "bubble"]).default("bubble"),
125884
+ /**
125885
+ * 话题 ↔ 飞书任务句柄(docs/task-handle.md,v2)。省略此字段不代表关闭,但也
125886
+ * 不会触发任何自动建清单——main.ts 在 startup 时只做只读解析(yaml 里的
125887
+ * tasklistGuid,或共享注册文件里已有的 guid);清单本身只由
125888
+ * `larkway tasklist-init --team` 建一次。
125889
+ */
125890
+ taskHandle: TaskHandleConfigSchema.optional(),
125891
+ /**
125892
+ * Per-bot model override (perf plan 批 C 旋钮). Passed through verbatim to
125893
+ * the backend CLI as `--model <value>` (claude) / `turn/start.model`
125894
+ * (codex) — larkway does not validate or allowlist model ids, same
125895
+ * precedent as `backend` above. Omitted = unchanged host/backend default
125896
+ * behavior (byte-identical to before this field existed).
125897
+ */
125898
+ model: external_exports.string().min(1).optional(),
125899
+ /**
125900
+ * Per-bot reasoning-effort override (perf plan 批 C 旋钮). Passed through
125901
+ * verbatim as `--effort <value>` on the claude CLI (confirmed supported:
125902
+ * `claude --effort low|medium|high|max`, also non-interactive). Also
125903
+ * confirmed supported on codex via `turn/start.effort`, mapped through
125904
+ * codexEffortFromLarkway (src/codex/runner.ts) since codex's own value
125905
+ * space is low/medium/high/xhigh, not low/medium/high/max. Omitted =
125906
+ * unchanged default behavior.
125907
+ */
125908
+ effort: external_exports.string().min(1).optional(),
125909
+ /**
125910
+ * docs/larkway-perf-plan.md §4 — opt into a persistent warm process
125911
+ * instead of the default one-shot cold-start-per-turn behavior. Takes
125912
+ * effect for `backend: "codex"` (a single bot-level `codex app-server`
125913
+ * process — src/codex/pool.ts CodexProcessPool) and `backend: "claude"`
125914
+ * (one warm process per active thread — src/claude/pool.ts
125915
+ * ClaudeProcessPool). Any other backend value is a no-op (main.ts only
125916
+ * constructs a pool for these two; botLoader's advisory warn below flags
125917
+ * the mismatch upfront). Byte-identical behavior when unset, matching
125918
+ * every other perf-plan flag in this schema. `.optional()` (not
125919
+ * `.default(false)`, deliberately — B1 fix): a `.default()` would make
125920
+ * every future `larkway bot` yaml write-out include `warmProcess: false`
125921
+ * even when nobody asked for it, and — because this schema is `.strict()`
125922
+ * — would break loading that yaml back with an OLDER larkway build that
125923
+ * predates this field. `undefined` and `false` are treated identically at
125924
+ * both read sites (main.ts's `bot.warmProcess && …` and the advisory warn
125925
+ * below).
125926
+ */
125927
+ warmProcess: external_exports.boolean().optional(),
125928
+ /**
125929
+ * Idle threshold (ms) before a warm process (see `warmProcess` above) with
125930
+ * no in-flight turn gets SIGTERM'd. Only meaningful when `warmProcess` is
125931
+ * true. @default 10 * 60 * 1000 (10 min) — see DEFAULT_WARM_PROCESS_IDLE_MS
125932
+ * in src/codex/pool.ts (backend "codex") / src/claude/pool.ts (backend
125933
+ * "claude") — both use the same default value.
125934
+ */
125935
+ warmProcessIdleMs: external_exports.number().int().positive().optional(),
125936
+ /**
125937
+ * `backend: "claude"` only: cap on how many warm per-thread processes
125938
+ * ClaudeProcessPool keeps alive at once (LRU-evicts the longest-idle one
125939
+ * past this). Meaningless for `backend: "codex"`, which only ever holds
125940
+ * one bot-level process regardless. @default 6 — see DEFAULT_MAX_PROCESSES
125941
+ * in src/claude/pool.ts.
125942
+ */
125943
+ warmProcessMaxProcesses: external_exports.number().int().positive().optional()
124391
125944
  }).strict();
124392
125945
  async function loadBots(botsDir) {
124393
125946
  let entries;
@@ -124425,6 +125978,21 @@ async function loadBots(botsDir) {
124425
125978
  ${issues}`);
124426
125979
  }
124427
125980
  const bot = result.data;
125981
+ if (bot.effort && !KNOWN_EFFORT_VALUES.has(bot.effort)) {
125982
+ console.warn(
125983
+ `[botLoader] Bot "${bot.id}" effort "${bot.effort}" is not one of the known values (${[...KNOWN_EFFORT_VALUES].join(", ")}). Continuing \u2014 the value is still passed through to the backend CLI verbatim (claude: --effort; codex: mapped through codexEffortFromLarkway), but a typo here will silently fail the bot's spawn every turn.`
125984
+ );
125985
+ }
125986
+ if (bot.warmProcess && bot.backend !== "codex" && bot.backend !== "claude") {
125987
+ console.warn(
125988
+ `[botLoader] Bot "${bot.id}" sets warmProcess:true but backend is "${bot.backend}" (only "codex"/"claude" are supported as of this writing). warmProcess will be a no-op for this bot.`
125989
+ );
125990
+ }
125991
+ if (bot.taskHandle?.enabled !== void 0) {
125992
+ console.warn(
125993
+ `[botLoader] Bot "${bot.id}" has taskHandle.enabled set, but this field was removed in v2 (docs/task-handle.md \xA76.3 \u2014 the gate is now whether a tasklistGuid resolves, not a flag). The value is accepted for backward compat but never read; safe to delete it from the yaml.`
125994
+ );
125995
+ }
124428
125996
  if (bot.memory_file) {
124429
125997
  const memoryPath2 = path.join(botsDir, bot.memory_file);
124430
125998
  try {
@@ -124456,25 +126024,8 @@ ${issues}`);
124456
126024
  return bots;
124457
126025
  }
124458
126026
 
124459
- // src/config/paths.ts
124460
- import { homedir } from "node:os";
124461
- import { join, resolve } from "node:path";
124462
- function larkwayHome() {
124463
- const env = process.env.LARKWAY_HOME;
124464
- if (env && env.trim() !== "") return resolve(env);
124465
- return join(homedir(), ".larkway");
124466
- }
124467
- function assertSafePathSegment(label, value) {
124468
- if (!/^[A-Za-z0-9_-]+$/.test(value)) {
124469
- throw new Error(`${label} must be a safe path segment`);
124470
- }
124471
- }
124472
- function resolveAgentWorkspacePathFromHome(home, agentId) {
124473
- assertSafePathSegment("agentId", agentId);
124474
- return join(home, "agents", agentId, "workspace");
124475
- }
124476
-
124477
126027
  // src/cli/botsStore.ts
126028
+ init_paths();
124478
126029
  function resolveBotsDir() {
124479
126030
  const override = process.env.LARKWAY_BOTS_DIR;
124480
126031
  if (override && override.trim() !== "") return path2.resolve(override);
@@ -124637,6 +126188,7 @@ var import_dotenv = __toESM(require_main(), 1);
124637
126188
  import { readFile as readFile3 } from "node:fs/promises";
124638
126189
  import os from "node:os";
124639
126190
  import path3 from "node:path";
126191
+ init_paths();
124640
126192
  import_dotenv.default.config();
124641
126193
  import_dotenv.default.config({ path: path3.join(larkwayHome(), ".env") });
124642
126194
  var ConfigSchema = external_exports.object({
@@ -124696,6 +126248,7 @@ var ConfigJson = external_exports.object({
124696
126248
  var DEFAULT_CONFIG_PATH = path3.join(larkwayHome(), "config.json");
124697
126249
 
124698
126250
  // src/cli/hostConfig.ts
126251
+ init_paths();
124699
126252
  function resolveLarkwayHome() {
124700
126253
  return larkwayHome();
124701
126254
  }
@@ -125420,6 +126973,7 @@ async function ensureMemoryScaffold(workspacePath) {
125420
126973
  }
125421
126974
 
125422
126975
  // src/cli/commands/init.ts
126976
+ init_paths();
125423
126977
  var execFileAsync3 = promisify3(execFile3);
125424
126978
  function checkNodeVersion() {
125425
126979
  const version = process.versions.node;
@@ -125805,6 +127359,8 @@ async function runCreateBot(ctx, creds, basics) {
125805
127359
  response_surface_prototype: DEFAULT_RESPONSE_SURFACE_PROTOTYPE,
125806
127360
  runtime: "agent_workspace",
125807
127361
  backend: basics.backend,
127362
+ cot: "brief",
127363
+ cotSurface: "bubble",
125808
127364
  memory_file: memoryFile,
125809
127365
  ...basics.gitlabTokenEnv ? { gitlab_token_env: basics.gitlabTokenEnv } : {},
125810
127366
  ...basics.gitName && basics.gitEmail ? { git_identity: { name: basics.gitName, email: basics.gitEmail } } : {}
@@ -126103,7 +127659,7 @@ async function run(ctx, args) {
126103
127659
  }
126104
127660
 
126105
127661
  // src/cli/commands/doctor.ts
126106
- import { access as access6, readdir as readdir4, readFile as readFile6, rm } from "node:fs/promises";
127662
+ import { access as access6, readdir as readdir4, readFile as readFile7, rm } from "node:fs/promises";
126107
127663
  import { execFile as execFile5 } from "node:child_process";
126108
127664
  import path10 from "node:path";
126109
127665
  import { promisify as promisify5 } from "node:util";
@@ -126325,7 +127881,7 @@ async function checkWorktrees(ctx) {
126325
127881
  const gitPath = path10.join(worktreeDir, ".git");
126326
127882
  let gitContent = null;
126327
127883
  try {
126328
- const stat6 = await readFile6(gitPath, "utf-8").catch(() => null);
127884
+ const stat6 = await readFile7(gitPath, "utf-8").catch(() => null);
126329
127885
  gitContent = stat6;
126330
127886
  } catch {
126331
127887
  }
@@ -126471,6 +128027,94 @@ async function checkWsConnectivity(ctx, opts) {
126471
128027
  message: `WS \u63A2\u6D4B\u5931\u8D25: ${result.message ?? "\u672A\u77E5\u9519\u8BEF"}\u3002${opts.lint ? "(CI \u6A21\u5F0F: \u7F51\u7EDC/\u8D85\u65F6\u4E0D\u8BA1\u5165 error)" : "\u8BF7\u68C0\u67E5\u98DE\u4E66 app \u51ED\u636E\u53CA\u7F51\u7EDC\u53EF\u8FBE\u6027\u3002"}`
126472
128028
  };
126473
128029
  }
128030
+ async function probeTaskScope(appId, appSecret, tasklistGuid) {
128031
+ try {
128032
+ const { Client: LarkSdkClient2 } = await Promise.resolve().then(() => __toESM(require_lib2(), 1));
128033
+ const { TaskListClient: TaskListClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
128034
+ const sdkClient = new LarkSdkClient2({ appId, appSecret });
128035
+ const taskClient = new TaskListClient2({
128036
+ request: (config) => sdkClient.request(config)
128037
+ });
128038
+ await taskClient.listTasklistTasks(tasklistGuid, { pageSize: 1 });
128039
+ return { ok: true };
128040
+ } catch (err2) {
128041
+ return { ok: false, message: err2 instanceof Error ? err2.message : String(err2) };
128042
+ }
128043
+ }
128044
+ var TASK_HANDLE_REQUIRED_SCOPES = [
128045
+ "task:tasklist:read",
128046
+ "task:tasklist:write",
128047
+ "task:task:read",
128048
+ "task:comment:write"
128049
+ ];
128050
+ async function checkTaskHandle(ctx, opts) {
128051
+ const results = [];
128052
+ const botIds = await ctx.botsStore.listBots();
128053
+ if (botIds.length === 0) return results;
128054
+ const { resolveTaskTeamRegistryPath: resolveTaskTeamRegistryPath2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
128055
+ const { readTeamTasklistGuid: readTeamTasklistGuid2 } = await Promise.resolve().then(() => (init_teamRegistry(), teamRegistry_exports));
128056
+ const registryGuid = await readTeamTasklistGuid2(resolveTaskTeamRegistryPath2());
128057
+ let anyConfigured = false;
128058
+ for (const id of botIds) {
128059
+ let bot;
128060
+ try {
128061
+ bot = await ctx.botsStore.readBot(id);
128062
+ } catch {
128063
+ continue;
128064
+ }
128065
+ const guid = bot.taskHandle?.tasklistGuid ?? registryGuid;
128066
+ if (!guid) continue;
128067
+ anyConfigured = true;
128068
+ if (process.env["LARKWAY_SKIP_WS_PROBE"] === "1") {
128069
+ results.push({
128070
+ id: `task-handle-${id}`,
128071
+ label: `bot "${id}" \u8BDD\u9898\u2194\u4EFB\u52A1\u53E5\u67C4`,
128072
+ status: "ok",
128073
+ message: `\u5DF2\u914D\u7F6E(guid=${guid}),scope \u63A2\u6D4B\u5DF2\u8DF3\u8FC7(LARKWAY_SKIP_WS_PROBE=1)\u3002`
128074
+ });
128075
+ continue;
128076
+ }
128077
+ const appSecret = await ctx.hostConfig.readSecret(bot.app_secret_env);
128078
+ if (!appSecret) {
128079
+ results.push({
128080
+ id: `task-handle-${id}`,
128081
+ label: `bot "${id}" \u8BDD\u9898\u2194\u4EFB\u52A1\u53E5\u67C4`,
128082
+ status: "warn",
128083
+ message: `\u5DF2\u914D\u7F6E guid=${guid},\u4F46 AppSecret \u7F3A\u5931,\u8DF3\u8FC7 scope \u63A2\u6D4B(\u89C1\u98DE\u4E66\u51ED\u636E\u68C0\u67E5\u9879)\u3002`
128084
+ });
128085
+ continue;
128086
+ }
128087
+ const probe = await probeTaskScope(bot.app_id, appSecret, guid);
128088
+ if (probe.ok) {
128089
+ results.push({
128090
+ id: `task-handle-${id}`,
128091
+ label: `bot "${id}" \u8BDD\u9898\u2194\u4EFB\u52A1\u53E5\u67C4`,
128092
+ status: "ok",
128093
+ message: `\u5DF2\u914D\u7F6E(guid=${guid}),task scope \u63A2\u6D4B\u6210\u529F\u3002`
128094
+ });
128095
+ } else {
128096
+ const scopeLink = `https://open.feishu.cn/app/${bot.app_id}/auth?q=${TASK_HANDLE_REQUIRED_SCOPES.join(",")}&op_from=doctor&token_type=tenant`;
128097
+ const looksLikeScopeError = /scope|permission|无权限|未授权|access.denied/i.test(probe.message ?? "");
128098
+ results.push({
128099
+ id: `task-handle-${id}`,
128100
+ label: `bot "${id}" \u8BDD\u9898\u2194\u4EFB\u52A1\u53E5\u67C4`,
128101
+ status: opts.lint && !looksLikeScopeError ? "warn" : "error",
128102
+ message: `\u5DF2\u914D\u7F6E guid=${guid},\u4F46 task scope \u63A2\u6D4B\u5931\u8D25:${probe.message ?? "\u672A\u77E5\u9519\u8BEF"}
128103
+ \u9700\u8981\u4EE5\u4E0B scope:${TASK_HANDLE_REQUIRED_SCOPES.join(", ")}
128104
+ \u5F00\u901A\u94FE\u63A5:${scopeLink}`
128105
+ });
128106
+ }
128107
+ }
128108
+ if (!anyConfigured) {
128109
+ results.push({
128110
+ id: "task-handle-not-configured",
128111
+ label: "\u8BDD\u9898\u2194\u4EFB\u52A1\u53E5\u67C4 (\u53EF\u9009)",
128112
+ status: "ok",
128113
+ message: '\u672A\u914D\u7F6E\u4EFB\u4F55 bot \u7684\u8BDD\u9898\u2194\u4EFB\u52A1\u53E5\u67C4(\u53EF\u9009\u529F\u80FD)\u3002\u60F3\u542F\u7528:larkway tasklist-init --adopt "<\u6E05\u5355\u540D>" --team <bot1,bot2,\u2026>(\u89C1 docs/task-handle.md \xA77)\u3002'
128114
+ });
128115
+ }
128116
+ return results;
128117
+ }
126474
128118
  async function runAllChecks(ctx, opts) {
126475
128119
  const results = [];
126476
128120
  results.push(await checkClaude(ctx));
@@ -126483,6 +128127,8 @@ async function runAllChecks(ctx, opts) {
126483
128127
  results.push(await checkWsConnectivity(ctx, opts));
126484
128128
  const codexChecks = await checkCodex(ctx);
126485
128129
  results.push(...codexChecks);
128130
+ const taskHandleChecks = await checkTaskHandle(ctx, opts);
128131
+ results.push(...taskHandleChecks);
126486
128132
  return results;
126487
128133
  }
126488
128134
  function exitCodeFromResults(results) {
@@ -126664,6 +128310,7 @@ async function run2(ctx, args) {
126664
128310
 
126665
128311
  // src/cli/commands/bot.ts
126666
128312
  import path11 from "node:path";
128313
+ init_paths();
126667
128314
  async function run3(ctx, args) {
126668
128315
  const sub = args[0];
126669
128316
  const rest = args.slice(1);
@@ -127265,10 +128912,10 @@ function inferLarkwayHome(botsDir, fallback) {
127265
128912
  }
127266
128913
 
127267
128914
  // src/cli/commands/memory.ts
127268
- import { readFile as readFile7 } from "node:fs/promises";
128915
+ import { readFile as readFile8 } from "node:fs/promises";
127269
128916
  import { spawn } from "node:child_process";
127270
128917
  import { tmpdir } from "node:os";
127271
- import { mkdir as mkdir4, writeFile as writeFile5, rm as rm2 } from "node:fs/promises";
128918
+ import { mkdir as mkdir5, writeFile as writeFile6, rm as rm2 } from "node:fs/promises";
127272
128919
  import path12 from "node:path";
127273
128920
  import process2 from "node:process";
127274
128921
  function parseArgs(args) {
@@ -127289,10 +128936,10 @@ function parseArgs(args) {
127289
128936
  async function openInEditor(initial) {
127290
128937
  const editor = process2.env.EDITOR || process2.env.VISUAL || "vi";
127291
128938
  const tmpDir = path12.join(tmpdir(), `larkway-memory-${process2.pid}`);
127292
- await mkdir4(tmpDir, { recursive: true });
128939
+ await mkdir5(tmpDir, { recursive: true });
127293
128940
  const tmpFile = path12.join(tmpDir, "memory.md");
127294
128941
  try {
127295
- await writeFile5(tmpFile, initial, "utf-8");
128942
+ await writeFile6(tmpFile, initial, "utf-8");
127296
128943
  await new Promise((resolve2, reject) => {
127297
128944
  const child = spawn(editor, [tmpFile], {
127298
128945
  stdio: "inherit",
@@ -127305,7 +128952,7 @@ async function openInEditor(initial) {
127305
128952
  else resolve2();
127306
128953
  });
127307
128954
  });
127308
- return await readFile7(tmpFile, "utf-8");
128955
+ return await readFile8(tmpFile, "utf-8");
127309
128956
  } finally {
127310
128957
  await rm2(tmpDir, { recursive: true, force: true });
127311
128958
  }
@@ -127346,7 +128993,7 @@ async function subSet(ctx, id, file) {
127346
128993
  if (!await assertBotExists(ctx, id)) return 1;
127347
128994
  let content;
127348
128995
  try {
127349
- content = await readFile7(file, "utf-8");
128996
+ content = await readFile8(file, "utf-8");
127350
128997
  } catch (e) {
127351
128998
  const msg = `\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${file}": ${e instanceof Error ? e.message : String(e)}`;
127352
128999
  if (ctx.flags.json) {
@@ -127467,8 +129114,9 @@ async function run4(ctx, args) {
127467
129114
  }
127468
129115
 
127469
129116
  // src/cli/commands/perms.ts
127470
- import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile6 } from "node:fs/promises";
129117
+ import { mkdir as mkdir6, readFile as readFile9, writeFile as writeFile7 } from "node:fs/promises";
127471
129118
  import path13 from "node:path";
129119
+ init_paths();
127472
129120
  function parsePermsArgs(args) {
127473
129121
  const flags = {
127474
129122
  addChats: [],
@@ -127664,7 +129312,7 @@ async function writePermissionGrants(ctx, id, flags) {
127664
129312
  const grantedPath = path13.join(workspacePath, "permissions-granted.md");
127665
129313
  const grants = [];
127666
129314
  if (flags.grantFromRequest) {
127667
- const requestText = await readFile8(requestPath, "utf8").catch(() => "");
129315
+ const requestText = await readFile9(requestPath, "utf8").catch(() => "");
127668
129316
  grants.push(...extractRequestLines(requestText));
127669
129317
  }
127670
129318
  grants.push(...renderCapabilityGrantLines(splitPermissionList(flags.grantPermissions)));
@@ -127684,8 +129332,8 @@ async function writePermissionGrants(ctx, id, flags) {
127684
129332
  "Do not record secret values here.",
127685
129333
  ""
127686
129334
  ].filter((line) => line !== void 0).join("\n");
127687
- await mkdir5(path13.dirname(grantedPath), { recursive: true });
127688
- await writeFile6(grantedPath, body, "utf8");
129335
+ await mkdir6(path13.dirname(grantedPath), { recursive: true });
129336
+ await writeFile7(grantedPath, body, "utf8");
127689
129337
  return { filePath: grantedPath, grants };
127690
129338
  }
127691
129339
  async function editChatsInteractive(config, ctx) {
@@ -128036,13 +129684,14 @@ __export(bridgeControl_exports, {
128036
129684
  });
128037
129685
  import { spawn as spawn2, execFile as execFile6 } from "node:child_process";
128038
129686
  import { openSync, constants as fsConstants } from "node:fs";
128039
- import { readFile as readFile9, writeFile as writeFile7, unlink as unlink5, mkdir as mkdir6, readdir as readdir5, access as access7 } from "node:fs/promises";
129687
+ import { readFile as readFile10, writeFile as writeFile8, unlink as unlink5, mkdir as mkdir7, readdir as readdir5, access as access7 } from "node:fs/promises";
128040
129688
  import { promisify as promisify6 } from "node:util";
128041
129689
  import path15 from "node:path";
128042
129690
  import process3 from "node:process";
128043
129691
  import { stat as stat3 } from "node:fs/promises";
128044
129692
 
128045
129693
  // src/bridge/statusFile.ts
129694
+ init_paths();
128046
129695
  import fs2 from "node:fs/promises";
128047
129696
  import path14 from "node:path";
128048
129697
  var DEFAULT_STALE_MS = 9e4;
@@ -128113,7 +129762,7 @@ async function resolveDistMain() {
128113
129762
  async function readPid(larkwayDir) {
128114
129763
  const pidFile = bridgePidPath(larkwayDir);
128115
129764
  try {
128116
- const raw = (await readFile9(pidFile, "utf-8")).trim();
129765
+ const raw = (await readFile10(pidFile, "utf-8")).trim();
128117
129766
  const n = Number(raw);
128118
129767
  return Number.isInteger(n) && n > 0 ? n : null;
128119
129768
  } catch (e) {
@@ -128123,8 +129772,8 @@ async function readPid(larkwayDir) {
128123
129772
  }
128124
129773
  async function writePid(larkwayDir, pid) {
128125
129774
  const pidFile = bridgePidPath(larkwayDir);
128126
- await mkdir6(path15.dirname(pidFile), { recursive: true });
128127
- await writeFile7(pidFile, String(pid) + "\n", "utf-8");
129775
+ await mkdir7(path15.dirname(pidFile), { recursive: true });
129776
+ await writeFile8(pidFile, String(pid) + "\n", "utf-8");
128128
129777
  }
128129
129778
  async function removePid(larkwayDir) {
128130
129779
  const pidFile = bridgePidPath(larkwayDir);
@@ -128184,7 +129833,7 @@ async function listMainBridgePids(mainProcessPattern) {
128184
129833
  async function hasAnyFreshStatusJson(larkwayDir, nowMs, staleMs = DEFAULT_STALE_MS) {
128185
129834
  const checkFile2 = async (file) => {
128186
129835
  try {
128187
- const raw = await readFile9(file, "utf-8");
129836
+ const raw = await readFile10(file, "utf-8");
128188
129837
  const parsed = JSON.parse(raw);
128189
129838
  if (typeof parsed["updatedAt"] !== "string") return { fresh: false, pid: null };
128190
129839
  const updatedMs = Date.parse(parsed["updatedAt"]);
@@ -128271,7 +129920,7 @@ async function startBridge(larkwayDir, opts) {
128271
129920
  await stopBridge(larkwayDir, opts);
128272
129921
  }
128273
129922
  const logPath = bridgeLogPath(larkwayDir);
128274
- await mkdir6(path15.dirname(logPath), { recursive: true });
129923
+ await mkdir7(path15.dirname(logPath), { recursive: true });
128275
129924
  let supervisorExists = false;
128276
129925
  try {
128277
129926
  await access7(supervisorScript);
@@ -128382,7 +130031,7 @@ async function tailBridgeLog(larkwayDir, n = 80) {
128382
130031
  await fh.close();
128383
130032
  }
128384
130033
  } else {
128385
- content = await readFile9(logPath, "utf-8");
130034
+ content = await readFile10(logPath, "utf-8");
128386
130035
  }
128387
130036
  } catch (e) {
128388
130037
  if (e.code === "ENOENT") {
@@ -128645,7 +130294,7 @@ async function run6(ctx, args) {
128645
130294
 
128646
130295
  // src/cli/commands/update.ts
128647
130296
  import { spawn as spawn4 } from "node:child_process";
128648
- import { readFile as readFile10, access as access9 } from "node:fs/promises";
130297
+ import { readFile as readFile11, access as access9 } from "node:fs/promises";
128649
130298
  import "node:module";
128650
130299
  import path17 from "node:path";
128651
130300
  var DEFAULT_NPM_PACKAGE_SPEC = "larkway@latest";
@@ -128724,7 +130373,7 @@ async function findRepoRoot(startDir) {
128724
130373
  const pkgPath = path17.join(dir, "package.json");
128725
130374
  try {
128726
130375
  await access9(pkgPath);
128727
- const raw = await readFile10(pkgPath, "utf-8");
130376
+ const raw = await readFile11(pkgPath, "utf-8");
128728
130377
  const pkg = JSON.parse(raw);
128729
130378
  if (pkg.name === "larkway") return dir;
128730
130379
  } catch {
@@ -128961,15 +130610,16 @@ async function run7(ctx, args) {
128961
130610
  import http from "node:http";
128962
130611
  import { randomBytes } from "node:crypto";
128963
130612
  import { existsSync } from "node:fs";
128964
- import { readFile as readFile13, stat as stat5 } from "node:fs/promises";
130613
+ import { readFile as readFile14, stat as stat5 } from "node:fs/promises";
128965
130614
  import path22 from "node:path";
128966
130615
  import { fileURLToPath as fileURLToPath2 } from "node:url";
128967
130616
 
128968
130617
  // src/web/api.ts
128969
- import { readdir as readdir6, readFile as readFile12, stat as stat4 } from "node:fs/promises";
130618
+ import { readdir as readdir6, readFile as readFile13, stat as stat4 } from "node:fs/promises";
128970
130619
  import { execFile as execFileCallback2 } from "node:child_process";
128971
130620
  import path21 from "node:path";
128972
130621
  import { promisify as promisify8 } from "node:util";
130622
+ init_paths();
128973
130623
 
128974
130624
  // src/version.ts
128975
130625
  import { readFileSync } from "node:fs";
@@ -129036,8 +130686,9 @@ function isRuntimeEventRecord(value) {
129036
130686
  // src/web/onboardSession.ts
129037
130687
  var QRCode = __toESM(require_lib3(), 1);
129038
130688
  import { randomUUID } from "node:crypto";
129039
- import { mkdir as mkdir7, writeFile as writeFile8, rename as rename4, chmod as chmod2, readFile as readFile11 } from "node:fs/promises";
130689
+ import { mkdir as mkdir8, writeFile as writeFile9, rename as rename5, chmod as chmod2, readFile as readFile12 } from "node:fs/promises";
129040
130690
  import path20 from "node:path";
130691
+ init_paths();
129041
130692
  async function defaultRegisterApp(opts) {
129042
130693
  const sdk = await Promise.resolve().then(() => __toESM(require_lib2(), 1));
129043
130694
  return sdk.registerApp(opts);
@@ -129069,10 +130720,10 @@ function quoteIfNeeded2(v) {
129069
130720
  return /[\s#"'=]/.test(v) ? `"${v.replace(/"/g, '\\"')}"` : v;
129070
130721
  }
129071
130722
  async function atomicWrite3(file, content) {
129072
- await mkdir7(path20.dirname(file), { recursive: true });
130723
+ await mkdir8(path20.dirname(file), { recursive: true });
129073
130724
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
129074
- await writeFile8(tmp, content, "utf-8");
129075
- await rename4(tmp, file);
130725
+ await writeFile9(tmp, content, "utf-8");
130726
+ await rename5(tmp, file);
129076
130727
  }
129077
130728
  async function writeSecretTo(envPath, envName, value) {
129078
130729
  if (!ENV_NAME_RE.test(envName)) {
@@ -129080,7 +130731,7 @@ async function writeSecretTo(envPath, envName, value) {
129080
130731
  }
129081
130732
  const map2 = /* @__PURE__ */ new Map();
129082
130733
  try {
129083
- const raw = await readFile11(envPath, "utf-8");
130734
+ const raw = await readFile12(envPath, "utf-8");
129084
130735
  for (const line of raw.split("\n")) {
129085
130736
  const m = ENV_LINE_RE.exec(line.trim());
129086
130737
  if (m) map2.set(m[1], stripQuotes2(m[2]));
@@ -129095,7 +130746,7 @@ async function writeSecretTo(envPath, envName, value) {
129095
130746
  }
129096
130747
  async function botYamlExists(botsDir, id) {
129097
130748
  try {
129098
- await readFile11(path20.join(botsDir, `${id}.yaml`), "utf-8");
130749
+ await readFile12(path20.join(botsDir, `${id}.yaml`), "utf-8");
129099
130750
  return true;
129100
130751
  } catch {
129101
130752
  return false;
@@ -129164,6 +130815,8 @@ async function createBotFromCreds(opts) {
129164
130815
  response_surface_prototype: DEFAULT_RESPONSE_SURFACE_PROTOTYPE,
129165
130816
  runtime: "agent_workspace",
129166
130817
  backend: form.backend && form.backend.trim() ? form.backend.trim() : "codex",
130818
+ cot: "brief",
130819
+ cotSurface: "bubble",
129167
130820
  // Persist the Feishu avatar URL so the Web 管理面 can show an avatar before
129168
130821
  // the bridge writes status.json (pre-bridge / central roster). Best-effort:
129169
130822
  // only set when resolveBotIdentity returned a valid url.
@@ -129524,7 +131177,7 @@ async function readBotFromDir(dir, id) {
129524
131177
  const memFile = path21.join(dir, `${id}.memory.md`);
129525
131178
  let raw;
129526
131179
  try {
129527
- raw = await readFile12(yamlFile, "utf-8");
131180
+ raw = await readFile13(yamlFile, "utf-8");
129528
131181
  } catch (e) {
129529
131182
  if (e.code === "ENOENT") {
129530
131183
  throw new Error(`Bot "${id}" not found`);
@@ -129539,7 +131192,7 @@ async function readBotFromDir(dir, id) {
129539
131192
  }
129540
131193
  let memory = null;
129541
131194
  try {
129542
- memory = await readFile12(memFile, "utf-8");
131195
+ memory = await readFile13(memFile, "utf-8");
129543
131196
  } catch {
129544
131197
  }
129545
131198
  return { config, memory };
@@ -129696,8 +131349,8 @@ async function loadChatNameMap(profile) {
129696
131349
  try {
129697
131350
  const { stdout: stdout2 } = await eventExecFile("lark-cli", args, { timeout: 8e3 });
129698
131351
  for (const chat of extractLarkCliChats(stdout2)) {
129699
- const chatId = stringField2(chat, "chat_id");
129700
- const name = stringField2(chat, "name");
131352
+ const chatId = stringField3(chat, "chat_id");
131353
+ const name = stringField3(chat, "name");
129701
131354
  if (chatId && name) names.set(chatId, name);
129702
131355
  }
129703
131356
  } catch {
@@ -129724,7 +131377,7 @@ function extractLarkCliChats(stdout2) {
129724
131377
  function isLarkOpenChatId(value) {
129725
131378
  return typeof value === "string" && value.startsWith("oc_");
129726
131379
  }
129727
- function stringField2(value, key) {
131380
+ function stringField3(value, key) {
129728
131381
  if (!value || typeof value !== "object") return void 0;
129729
131382
  const out = value[key];
129730
131383
  return typeof out === "string" ? out : void 0;
@@ -129764,6 +131417,13 @@ var putBot = async (req) => {
129764
131417
  for (const k of ["name", "description", "chats", "repos", "turn_taking_limit", "backend"]) {
129765
131418
  if (k in bodyWithoutTokenFields) merged[k] = bodyWithoutTokenFields[k];
129766
131419
  }
131420
+ for (const k of ["model", "effort"]) {
131421
+ if (k in bodyWithoutTokenFields) {
131422
+ const v = bodyWithoutTokenFields[k];
131423
+ if (typeof v === "string" && v.trim().length > 0) merged[k] = v.trim();
131424
+ else delete merged[k];
131425
+ }
131426
+ }
129767
131427
  if (tokenValue !== void 0) {
129768
131428
  if (tokenValue.trim().length > 0) {
129769
131429
  const envName = gitlabTokenEnvNameForBot(id);
@@ -129786,6 +131446,10 @@ var putBot = async (req) => {
129786
131446
  backend: "codex",
129787
131447
  ...bodyWithoutTokenFields
129788
131448
  };
131449
+ for (const k of ["model", "effort"]) {
131450
+ const v = draft[k];
131451
+ if (typeof v === "string" && v.trim().length === 0) delete draft[k];
131452
+ }
129789
131453
  if (tokenValue !== void 0) {
129790
131454
  if (tokenValue.trim().length > 0) {
129791
131455
  const envName = gitlabTokenEnvNameForBot(id);
@@ -130418,7 +132082,7 @@ async function handleStatic(res, pathname, token) {
130418
132082
  }
130419
132083
  let buf;
130420
132084
  try {
130421
- buf = await readFile13(target);
132085
+ buf = await readFile14(target);
130422
132086
  } catch {
130423
132087
  writeText(res, 404, "not found");
130424
132088
  return;
@@ -130531,7 +132195,8 @@ async function run8(ctx, args) {
130531
132195
  }
130532
132196
 
130533
132197
  // src/cli/commands/dogfood.ts
130534
- import { access as access10, readFile as readFile14, readdir as readdir7 } from "node:fs/promises";
132198
+ init_paths();
132199
+ import { access as access10, readFile as readFile15, readdir as readdir7 } from "node:fs/promises";
130535
132200
  import path24 from "node:path";
130536
132201
 
130537
132202
  // src/agent/permissionGate.ts
@@ -131113,7 +132778,7 @@ async function listMarkdownFiles(root) {
131113
132778
  }
131114
132779
  async function readTextOrNull(filePath) {
131115
132780
  try {
131116
- return await readFile14(filePath, "utf8");
132781
+ return await readFile15(filePath, "utf8");
131117
132782
  } catch {
131118
132783
  return null;
131119
132784
  }
@@ -131124,6 +132789,563 @@ function exitCodeFor(checks) {
131124
132789
  return 0;
131125
132790
  }
131126
132791
 
132792
+ // src/cli/commands/tasklistInit.ts
132793
+ var import_node_sdk2 = __toESM(require_lib2(), 1);
132794
+ init_client();
132795
+ init_paths();
132796
+ init_teamRegistry();
132797
+
132798
+ // src/cli/ownerIdentity.ts
132799
+ import { spawnSync } from "node:child_process";
132800
+ function resolveOwnerOpenId(profile, _spawnSync = spawnSync) {
132801
+ try {
132802
+ const result = _spawnSync("lark-cli", ["auth", "status", "--profile", profile, "--json"], {
132803
+ encoding: "utf-8",
132804
+ timeout: 1e4
132805
+ });
132806
+ if (result.status !== 0) return void 0;
132807
+ const stdout2 = typeof result.stdout === "string" ? result.stdout : "";
132808
+ if (stdout2.trim().length === 0) return void 0;
132809
+ const parsed = JSON.parse(stdout2);
132810
+ if (typeof parsed !== "object" || parsed === null) return void 0;
132811
+ const identities = parsed["identities"];
132812
+ if (typeof identities !== "object" || identities === null) return void 0;
132813
+ const user = identities["user"];
132814
+ if (typeof user !== "object" || user === null) return void 0;
132815
+ const openId = user["openId"];
132816
+ return typeof openId === "string" && openId.length > 0 ? openId : void 0;
132817
+ } catch {
132818
+ return void 0;
132819
+ }
132820
+ }
132821
+
132822
+ // src/lark/profileBootstrap.ts
132823
+ import { spawnSync as spawnSync2 } from "node:child_process";
132824
+ function deriveLarkCliProfile(explicitProfile, appId) {
132825
+ return explicitProfile ?? appId;
132826
+ }
132827
+
132828
+ // src/cli/userTasklistOps.ts
132829
+ import { spawnSync as spawnSync3 } from "node:child_process";
132830
+ var SPAWN_TIMEOUT_MS = 15e3;
132831
+ function runLarkCliJson(args, spawnSyncFn) {
132832
+ let result;
132833
+ try {
132834
+ result = spawnSyncFn("lark-cli", args, { encoding: "utf-8", timeout: SPAWN_TIMEOUT_MS });
132835
+ } catch (err2) {
132836
+ return {
132837
+ ok: false,
132838
+ error: `\u65E0\u6CD5\u6267\u884C lark-cli(${err2 instanceof Error ? err2.message : String(err2)})\u2014\u2014 \u786E\u8BA4 lark-cli \u5DF2\u5B89\u88C5\u4E14\u5728 PATH \u91CC\u3002`
132839
+ };
132840
+ }
132841
+ const stdout2 = typeof result.stdout === "string" ? result.stdout : "";
132842
+ const stderr = typeof result.stderr === "string" ? result.stderr : "";
132843
+ if (stdout2.trim().length === 0) {
132844
+ return {
132845
+ ok: false,
132846
+ error: `lark-cli \u6CA1\u6709\u8F93\u51FA\u4EFB\u4F55\u5185\u5BB9(exit=${result.status ?? "unknown"})\u3002${stderr ? `stderr: ${stderr.slice(0, 500)}` : ""}`
132847
+ };
132848
+ }
132849
+ let parsed;
132850
+ try {
132851
+ parsed = JSON.parse(stdout2);
132852
+ } catch {
132853
+ return { ok: false, error: `lark-cli \u8F93\u51FA\u4E0D\u662F\u5408\u6CD5 JSON(exit=${result.status ?? "unknown"}): ${stdout2.slice(0, 500)}` };
132854
+ }
132855
+ if (typeof parsed === "object" && parsed !== null && parsed["ok"] === false) {
132856
+ const errObj = parsed["error"];
132857
+ const rec = typeof errObj === "object" && errObj !== null ? errObj : {};
132858
+ const message = typeof rec["message"] === "string" ? rec["message"] : "\u672A\u77E5\u9519\u8BEF";
132859
+ const hint = typeof rec["hint"] === "string" && rec["hint"] ? `
132860
+ \u63D0\u793A:${rec["hint"]}` : "";
132861
+ return { ok: false, error: `${message}${hint}` };
132862
+ }
132863
+ return { ok: true, data: parsed };
132864
+ }
132865
+ function listUserTasklists(profile, spawnSyncFn = spawnSync3) {
132866
+ const result = runLarkCliJson(
132867
+ ["task", "tasklists", "list", "--as", "user", "--profile", profile, "--page-all", "--json"],
132868
+ spawnSyncFn
132869
+ );
132870
+ if (!result.ok) return result;
132871
+ const data = result.data;
132872
+ const items = typeof data === "object" && data !== null ? data["items"] : void 0;
132873
+ if (!Array.isArray(items)) {
132874
+ return { ok: false, error: `lark-cli \u8FD4\u56DE\u7684\u6E05\u5355\u5217\u8868\u5F62\u72B6\u4E0D\u5BF9(\u7F3A\u5C11 items \u6570\u7EC4):${JSON.stringify(data).slice(0, 300)}` };
132875
+ }
132876
+ const tasklists = [];
132877
+ for (const raw of items) {
132878
+ if (typeof raw !== "object" || raw === null) continue;
132879
+ const guid = raw["guid"];
132880
+ const name = raw["name"];
132881
+ if (typeof guid === "string" && guid.length > 0) {
132882
+ tasklists.push({ guid, name: typeof name === "string" ? name : "(\u65E0\u6807\u9898)" });
132883
+ }
132884
+ }
132885
+ return { ok: true, data: tasklists };
132886
+ }
132887
+ function addTasklistMembersAsUser(profile, tasklistGuid, members, spawnSyncFn = spawnSync3) {
132888
+ const dataJson = JSON.stringify({ members });
132889
+ return runLarkCliJson(
132890
+ [
132891
+ "task",
132892
+ "tasklists",
132893
+ "add_members",
132894
+ "--as",
132895
+ "user",
132896
+ "--profile",
132897
+ profile,
132898
+ "--tasklist-guid",
132899
+ tasklistGuid,
132900
+ "--data",
132901
+ dataJson,
132902
+ "--json"
132903
+ ],
132904
+ spawnSyncFn
132905
+ );
132906
+ }
132907
+ function getUserTasklistMembers(profile, tasklistGuid, spawnSyncFn = spawnSync3) {
132908
+ const result = runLarkCliJson(
132909
+ ["task", "tasklists", "get", "--as", "user", "--profile", profile, "--tasklist-guid", tasklistGuid, "--json"],
132910
+ spawnSyncFn
132911
+ );
132912
+ if (!result.ok) return result;
132913
+ const data = result.data;
132914
+ const tasklist = typeof data === "object" && data !== null ? data["tasklist"] : void 0;
132915
+ const members = typeof tasklist === "object" && tasklist !== null ? tasklist["members"] : void 0;
132916
+ if (!Array.isArray(members)) {
132917
+ return { ok: false, error: `lark-cli \u8FD4\u56DE\u7684\u6E05\u5355\u8BE6\u60C5\u5F62\u72B6\u4E0D\u5BF9(\u7F3A\u5C11 tasklist.members \u6570\u7EC4):${JSON.stringify(data).slice(0, 300)}` };
132918
+ }
132919
+ return {
132920
+ ok: true,
132921
+ data: members.filter((m) => typeof m === "object" && m !== null).map((m) => ({
132922
+ id: String(m["id"] ?? ""),
132923
+ type: typeof m["type"] === "string" ? m["type"] : void 0,
132924
+ role: typeof m["role"] === "string" ? m["role"] : void 0
132925
+ }))
132926
+ };
132927
+ }
132928
+
132929
+ // src/cli/commands/tasklistInit.ts
132930
+ function parseArgs2(args) {
132931
+ let team = [];
132932
+ let name = "Agent Team";
132933
+ let owner;
132934
+ let force = false;
132935
+ let adopt;
132936
+ let adoptGuid;
132937
+ for (let i = 0; i < args.length; i++) {
132938
+ const arg = args[i];
132939
+ if (arg === "--team" && i + 1 < args.length) {
132940
+ team = args[++i].split(",").map((s) => s.trim()).filter(Boolean);
132941
+ } else if (arg.startsWith("--team=")) {
132942
+ team = arg.slice("--team=".length).split(",").map((s) => s.trim()).filter(Boolean);
132943
+ } else if (arg === "--name" && i + 1 < args.length) {
132944
+ name = args[++i];
132945
+ } else if (arg.startsWith("--name=")) {
132946
+ name = arg.slice("--name=".length);
132947
+ } else if (arg === "--owner" && i + 1 < args.length) {
132948
+ owner = args[++i];
132949
+ } else if (arg.startsWith("--owner=")) {
132950
+ owner = arg.slice("--owner=".length);
132951
+ } else if (arg === "--force") {
132952
+ force = true;
132953
+ } else if (arg === "--adopt" && i + 1 < args.length) {
132954
+ adopt = args[++i];
132955
+ } else if (arg.startsWith("--adopt=")) {
132956
+ adopt = arg.slice("--adopt=".length);
132957
+ } else if (arg === "--adopt-guid" && i + 1 < args.length) {
132958
+ adoptGuid = args[++i];
132959
+ } else if (arg.startsWith("--adopt-guid=")) {
132960
+ adoptGuid = arg.slice("--adopt-guid=".length);
132961
+ }
132962
+ }
132963
+ return { team, name, owner, force, adopt, adoptGuid };
132964
+ }
132965
+ var USAGE2 = `larkway tasklist-init [--team <bot1,bot2,\u2026>] [--name <\u6E05\u5355\u540D>] [--owner <open_id>] [--force]
132966
+ [--adopt "<\u6E05\u5355\u540D>" | --adopt-guid <guid>]
132967
+
132968
+ \u5BF9\u7740 Claude Code / Codex \u8BF4\u4E00\u53E5\u300C\u5E2E\u6211\u914D\u7F6E\u4E00\u4E0B Agent Team\u300D\u5C31\u591F\u4E86 \u2014\u2014 \u4E00\u4E2A\u6CA1\u8BFB\u8FC7\u8FD9\u4EFD
132969
+ \u5E2E\u52A9\u7684 agent,\u7B2C\u4E00\u6B65\u5FC5\u7136\u4F1A\u8DD1 \`larkway tasklist-init --help\`(\u5C31\u662F\u4F60\u6B63\u5728\u770B\u7684\u8FD9\u6BB5),
132970
+ \u4ECE\u8FD9\u91CC\u5C31\u80FD\u5B66\u4F1A\u600E\u4E48\u628A\u672C\u673A\u6240\u6709\u5DF2\u914D\u7F6E\u7684 bot \u90FD\u914D\u6210\u6E05\u5355\u6210\u5458,\u4E0D\u9700\u8981\u4F60\u518D\u624B\u52A8\u4F20\u4EFB\u4F55\u53C2\u6570\u3002
132971
+
132972
+ \u96F6\u53C2\u6570\u4E09\u6B65 Quickstart:
132973
+ 1. (\u53EF\u9009)\u5148\u5728\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u624B\u52A8\u5EFA\u4E00\u4E2A\u6E05\u5355,\u540D\u5B57\u968F\u4FBF\u53D6,\u6BD4\u5982 "Agent Team"\u3002
132974
+ 2. \u8DD1:larkway tasklist-init
132975
+ \u2014\u2014 \u4F1A\u81EA\u52A8:--team = \u672C\u673A bots/ \u76EE\u5F55\u4E0B\u5168\u90E8\u5DF2\u914D\u7F6E bot;--name = "Agent Team";
132976
+ \u7528\u4F60\u7684 lark-cli \u7528\u6237\u8EAB\u4EFD\u6309\u540D\u5B57\u67E5\u4E00\u904D\u80FD\u770B\u5230\u7684\u6E05\u5355 \u2014\u2014 \u67E5\u5230\u540C\u540D\u7684\u5C31 adopt(\u628A\u8FD9\u4E9B
132977
+ bot \u52A0\u4E3A editor);\u67E5\u4E0D\u5230(\u6216\u6CA1\u767B\u5F55/\u7F3A scope)\u5C31\u9759\u9ED8\u56DE\u9000\u5230\u65E7\u7684\u521B\u5EFA\u8DEF\u5F84(\u7528\u7B2C\u4E00\u4E2A
132978
+ bot \u7684 app \u8EAB\u4EFD\u65B0\u5EFA\u4E00\u4E2A\u6E05\u5355,\u5E76\u81EA\u52A8\u628A\u4F60\u52A0\u4E3A owner)\u3002
132979
+ 3. \u53BB\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u786E\u8BA4\u8FD9\u4E2A\u6E05\u5355\u5728\u4F60\u7684\u5217\u8868\u91CC\u53EF\u89C1,\u4E4B\u540E\u53F3\u952E\u8BDD\u9898\u6D88\u606F\u9009"\u8F6C\u4EFB\u52A1"\u5373\u53EF\u3002
132980
+
132981
+ \u4EE5\u4E0A\u5168\u90E8\u662F\u7F3A\u7701\u884C\u4E3A,\u4EE5\u4E0B flag \u4EC5\u7528\u4E8E\u8986\u76D6:
132982
+
132983
+ --team <bot1,bot2,\u2026> \u663E\u5F0F\u6307\u5B9A\u8981\u52A0\u5165\u6E05\u5355\u7684 bot(\u9ED8\u8BA4 = \u5168\u90E8\u5DF2\u914D\u7F6E bot)
132984
+ --name <\u6E05\u5355\u540D> \u663E\u5F0F\u6307\u5B9A\u6E05\u5355\u540D(\u9ED8\u8BA4 "Agent Team"),\u4EC5\u5728\u81EA\u52A8/\u663E\u5F0F create
132985
+ \u8DEF\u5F84\u91CC\u751F\u6548;--adopt/--adopt-guid \u4F1A\u5FFD\u7565\u5B83,\u6309\u540D\u5B57/guid \u67E5
132986
+ --adopt "<\u6E05\u5355\u540D>" \u5F3A\u5236\u8D70 adopt \u6A21\u5F0F\u5E76\u7528\u8FD9\u4E2A\u540D\u5B57\u67E5\u627E\u2014\u2014\u67E5\u4E0D\u5230/\u67E5\u51FA\u591A\u4E2A\u90FD\u4F1A
132987
+ \u76F4\u63A5\u62A5\u9519\u9000\u51FA,\u4E0D\u4F1A\u9759\u9ED8\u56DE\u9000\u5230\u521B\u5EFA\u8DEF\u5F84(\u4F60\u5DF2\u7ECF\u660E\u786E\u8981 adopt\u4E86)
132988
+ --adopt-guid <guid> \u8DF3\u8FC7\u6309\u540D\u5B57\u67E5\u627E,\u76F4\u63A5 adopt \u8FD9\u4E2A guid \u7684\u6E05\u5355(\u5355\u72EC\u4F7F\u7528\u4E5F
132989
+ \u7B49\u4EF7\u4E8E\u5F3A\u5236 adopt \u6A21\u5F0F);\u591A\u4E2A\u540C\u540D\u6E05\u5355\u65F6\u7528\u5B83\u6D88\u6B67
132990
+ --owner <open_id> \u663E\u5F0F\u6307\u5B9A\u4EBA\u7C7B owner(\u4EC5\u521B\u5EFA\u8DEF\u5F84\u7528\u5230,adopt \u6A21\u5F0F\u4E0B\u4F60\u672C\u6765\u5C31\u662F
132991
+ owner,\u4E0D\u9700\u8981\u8FD9\u4E2A)
132992
+ --force \u8986\u76D6\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6\u91CC\u5DF2\u6709\u7684 guid(\u9ED8\u8BA4\u4E0D\u8986\u76D6,\u907F\u514D\u8BEF\u64CD\u4F5C\u628A
132993
+ \u6574\u4E2A\u56E2\u961F\u5207\u5230\u4E00\u4E2A\u65B0\u677F)
132994
+
132995
+ \u2500\u2500 adopt \u6A21\u5F0F\u5185\u90E8\u6B65\u9AA4(\u81EA\u52A8\u9009\u4E2D,\u6216\u663E\u5F0F --adopt/--adopt-guid \u89E6\u53D1)\u2500\u2500
132996
+ 1. \u4EE5\u4F60(\u64CD\u4F5C\u8005)\u7684\u7528\u6237\u8EAB\u4EFD\u6309\u540D\u5B57\u7CBE\u786E\u5339\u914D\u4F60\u80FD\u770B\u5230\u7684\u6E05\u5355(\u91CD\u540D \u2192 \u62A5\u9519\u5217\u51FA\u5168\u90E8
132997
+ guid,\u8BA9\u4F60\u52A0 --adopt-guid <guid> \u6D88\u6B67\u91CD\u8DD1;\u4E00\u4E2A\u90FD\u627E\u4E0D\u5230 \u2192 \u81EA\u52A8\u6A21\u5F0F\u4E0B\u9759\u9ED8\u56DE\u9000
132998
+ \u521B\u5EFA\u8DEF\u5F84,\u663E\u5F0F --adopt \u4E0B\u62A5\u9519\u63D0\u793A\u5148\u53BB\u4EFB\u52A1\u4E2D\u5FC3\u5EFA\u4E00\u4E2A)
132999
+ 2. \u628A\u6BCF\u4E2A bot \u7684 app \u52A0\u4E3A\u6E05\u5355\u6210\u5458(role=editor,\u5E42\u7B49,\u5DF2\u662F\u6210\u5458\u7684\u8DF3\u8FC7/\u65E0\u5BB3\u91CD\u590D)
133000
+ 3. \u5199\u5165\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6 <LARKWAY_HOME>/task-team.json(\u4E0E\u521B\u5EFA\u8DEF\u5F84\u540C\u4E00\u5957
133001
+ first-writer-wins / --force \u8986\u76D6\u8BED\u4E49)
133002
+ 4. \u8BFB\u56DE\u6210\u5458\u5217\u8868,\u6838\u5B9E\u6BCF\u4E2A bot \u771F\u7684\u52A0\u5165\u6210\u529F
133003
+
133004
+ \u524D\u7F6E\u6761\u4EF6(\u4EC5 adopt \u9700\u8981):\u8FD9\u4E2A profile \u5FC5\u987B\u5DF2\u7ECF\u7528**\u7528\u6237\u8EAB\u4EFD**\u767B\u5F55\u8FC7 lark-cli \u4E14
133005
+ \u7533\u8BF7\u4E86 task \u57DF\u6743\u9650:
133006
+ lark-cli auth login --profile <profile> --domain task
133007
+ (\u7528\u54EA\u4E2A profile:\u7B2C\u4E00\u4E2A\u56E2\u961F bot \u7684 lark_cli_profile,\u7F3A\u7701\u65F6\u7528\u5B83\u7684 app_id)\u3002
133008
+ \u6CA1\u6709\u4E8B\u5148\u767B\u5F55/\u7F3A scope \u4F1A\u5728 adopt \u6B65\u9AA4 1 \u62A5\u9519,\u62A5\u9519\u4FE1\u606F\u4F1A\u5E26\u4E0A\u9762\u8FD9\u6761\u547D\u4EE4\u4F5C\u4E3A\u63D0\u793A\u3002
133009
+
133010
+ \u2500\u2500 \u521B\u5EFA\u8DEF\u5F84\u5185\u90E8\u6B65\u9AA4(\u81EA\u52A8\u56DE\u9000,\u6216\u672A\u5339\u914D\u5230\u540C\u540D\u6E05\u5355\u65F6\u89E6\u53D1)\u2500\u2500
133011
+ 1. \u5148\u67E5\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6:\u5DF2\u6709 guid \u4E14\u672A\u4F20 --force \u2192 \u590D\u7528;\u5426\u5219\u5EFA\u4E00\u4E2A\u65B0\u6E05\u5355
133012
+ 2. \u628A\u4F60(owner)\u52A0\u4E3A\u4EBA\u7C7B\u6210\u5458(role=editor)\u2014\u2014 \u5426\u5219\u4F60\u5728\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u770B\u4E0D\u5230\u8FD9\u4E2A\u6E05\u5355
133013
+ 3. \u628A\u6BCF\u4E2A bot \u7684 app \u90FD\u52A0\u4E3A\u6E05\u5355\u6210\u5458(role=editor)
133014
+ 4. \u8BFB\u56DE\u6210\u5458\u5217\u8868,\u82E5 owner \u672A\u6210\u529F\u52A0\u5165\u4F1A\u6253\u5370\u4E00\u6761 warning(\u4E0D\u4F1A\u8BA9\u547D\u4EE4\u5931\u8D25)
133015
+ 5. (\u4EC5\u65B0\u5EFA\u65F6)\u628A guid \u5199\u8FDB\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6
133016
+
133017
+ owner open_id \u89E3\u6790\u987A\u5E8F(\u4EC5\u521B\u5EFA\u8DEF\u5F84\u7528\u5230):\u663E\u5F0F --owner \u4F18\u5148;\u7701\u7565\u65F6\u5C1D\u8BD5\u4ECE lark-cli
133018
+ \u5F53\u524D\u767B\u5F55\u7684\u7528\u6237\u8EAB\u4EFD\u81EA\u52A8\u89E3\u6790(\u9700\u8981\u4F60\u4E4B\u524D\u5BF9\u8BE5 profile \u8DD1\u8FC7 \`lark-cli auth login\`);
133019
+ \u4E24\u8005\u90FD\u62FF\u4E0D\u5230\u4F1A\u76F4\u63A5\u62A5\u9519\u9000\u51FA,\u4E0D\u5EFA/\u4E0D\u52A8\u4EFB\u4F55\u6E05\u5355\u3002
133020
+
133021
+ \u4E0D\u4F1A\u628A\u4EFB\u4F55\u7FA4\u52A0\u4E3A\u6210\u5458 \u2014\u2014 v2 \u6E05\u5355\u9ED8\u8BA4\u53EA\u5BF9 owner \u79C1\u6709;\u5206\u4EAB\u7ED9\u540C\u4E8B\u770B,\u8981\u7ED9 editor \u6743\u9650
133022
+ \u5BF9\u65B9\u624D\u80FD\u628A\u8BDD\u9898\u8F6C\u4EFB\u52A1\u8FDB\u6765,\u7ED9 viewer \u53EA\u80FD\u770B\u4E0D\u80FD\u8F6C(\u89C1 docs/task-handle.md \xA75.3)\u3002
133023
+
133024
+ \u5168\u7A0B\u975E\u4EA4\u4E92:\u4EFB\u4F55\u6B67\u4E49\u90FD\u76F4\u63A5\u975E\u96F6\u9000\u51FA\u5E76\u628A\u5019\u9009 + \u4E0B\u4E00\u6B65\u6307\u5F15\u6253\u5230 stderr,\u4E0D\u4F1A\u5F39\u4EFB\u4F55
133025
+ \u4EA4\u4E92\u5F0F\u95EE\u9898(\u8FD9\u4E2A\u547D\u4EE4\u672C\u6765\u5C31\u8BBE\u8BA1\u7ED9 agent \u5728 headless \u73AF\u5883\u91CC\u8DD1)\u3002
133026
+
133027
+ \u26A0\uFE0F \u9996\u6B21\u8DD1\u5B8C\u540E,\u8BF7\u5728\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u786E\u8BA4\u8FD9\u4E2A\u6E05\u5355\u786E\u5B9E\u53EF\u89C1(\u89C1 docs/task-handle.md \xA77 \u2014
133028
+ \u672C\u5B9E\u73B0\u65E0\u6CD5\u5728\u65E0\u7F51\u7EDC\u73AF\u5883\u4E0B\u7AEF\u5230\u7AEF\u9A8C\u8BC1 editor \u6210\u5458\u7684\u53EF\u89C1\u6027,\u7559\u7ED9\u8FD9\u4E00\u6B65\u4EBA\u5DE5\u786E\u8BA4)\u3002
133029
+
133030
+ \u793A\u4F8B:
133031
+ larkway tasklist-init
133032
+ larkway tasklist-init --adopt "Agent Team"
133033
+ larkway tasklist-init --adopt "Agent Team" --adopt-guid abc-123-guid
133034
+ larkway tasklist-init --team larkway-devops,larkway-marketing --name "Agent Team"
133035
+ larkway tasklist-init --team larkway-devops --owner ou_1234567890abcdef
133036
+ larkway tasklist-init --team larkway-devops --force`;
133037
+ async function run10(ctx, args) {
133038
+ if (args.includes("help")) {
133039
+ ctx.ui.print(USAGE2);
133040
+ return 0;
133041
+ }
133042
+ const { team, name, owner: explicitOwner, force, adopt, adoptGuid: explicitAdoptGuid } = parseArgs2(args);
133043
+ let team_ = team;
133044
+ if (team_.length === 0) {
133045
+ team_ = await ctx.botsStore.listBots();
133046
+ if (team_.length === 0) {
133047
+ const msg = "\u672C\u673A\u6CA1\u6709\u914D\u7F6E\u4EFB\u4F55 bot\u3002\u5148\u8FD0\u884C `larkway bot add` \u6DFB\u52A0\u81F3\u5C11\u4E00\u4E2A bot,\u518D\u91CD\u8DD1\u8FD9\u4E2A\u547D\u4EE4\u3002";
133048
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: msg });
133049
+ else {
133050
+ ctx.ui.failure(msg);
133051
+ ctx.ui.print(USAGE2);
133052
+ }
133053
+ return 1;
133054
+ }
133055
+ }
133056
+ const bots = [];
133057
+ for (const botId of team_) {
133058
+ let bot;
133059
+ try {
133060
+ bot = await ctx.botsStore.readBot(botId);
133061
+ } catch (err2) {
133062
+ const msg = err2 instanceof Error ? err2.message : String(err2);
133063
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: msg });
133064
+ else ctx.ui.failure(msg);
133065
+ return 1;
133066
+ }
133067
+ const appSecret = await ctx.hostConfig.readSecret(bot.app_secret_env);
133068
+ if (!appSecret) {
133069
+ const msg = `Bot "${botId}" \u7684 app_secret_env "${bot.app_secret_env}" \u5728 ~/.larkway/.env \u4E2D\u672A\u8BBE\u7F6E\u3002`;
133070
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: msg });
133071
+ else ctx.ui.failure(msg);
133072
+ return 1;
133073
+ }
133074
+ bots.push({ id: bot.id, app_id: bot.app_id, appSecret, lark_cli_profile: bot.lark_cli_profile });
133075
+ }
133076
+ const creator = bots[0];
133077
+ const creatorProfile = deriveLarkCliProfile(creator.lark_cli_profile, creator.app_id);
133078
+ const loginHint = `lark-cli auth login --profile ${creatorProfile} --domain task`;
133079
+ if (adopt !== void 0 || explicitAdoptGuid !== void 0) {
133080
+ const adoptName = adopt ?? name;
133081
+ if (adoptName.trim().length === 0) {
133082
+ const msg = '--adopt \u9700\u8981\u4E00\u4E2A\u975E\u7A7A\u7684\u6E05\u5355\u540D\u5B57,\u4F8B\u5982 --adopt "Agent Team"\u3002';
133083
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: msg });
133084
+ else {
133085
+ ctx.ui.failure(msg);
133086
+ ctx.ui.print(USAGE2);
133087
+ }
133088
+ return 1;
133089
+ }
133090
+ const target = resolveAdoptTarget(creatorProfile, adoptName, explicitAdoptGuid, loginHint);
133091
+ if (!target.ok) {
133092
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: target.message });
133093
+ else {
133094
+ ctx.ui.failure(target.message);
133095
+ if (target.reason === "list-failed") ctx.ui.print(USAGE2);
133096
+ }
133097
+ return 1;
133098
+ }
133099
+ return runAdoptWithGuid(ctx, {
133100
+ guid: target.guid,
133101
+ matchedName: target.matchedName,
133102
+ bots,
133103
+ force,
133104
+ creatorProfile,
133105
+ loginHint
133106
+ });
133107
+ }
133108
+ const autoTarget = resolveAdoptTarget(creatorProfile, name, void 0, loginHint);
133109
+ if (autoTarget.ok) {
133110
+ return runAdoptWithGuid(ctx, {
133111
+ guid: autoTarget.guid,
133112
+ matchedName: autoTarget.matchedName,
133113
+ bots,
133114
+ force,
133115
+ creatorProfile,
133116
+ loginHint
133117
+ });
133118
+ }
133119
+ if (autoTarget.reason === "ambiguous") {
133120
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: autoTarget.message });
133121
+ else ctx.ui.failure(autoTarget.message);
133122
+ return 1;
133123
+ }
133124
+ if (!ctx.flags.json) {
133125
+ const why = autoTarget.reason === "list-failed" ? "\u65E0\u6CD5\u4EE5\u4F60\u7684\u7528\u6237\u8EAB\u4EFD\u67E5\u8BE2\u6E05\u5355" : `\u6CA1\u627E\u5230\u540C\u540D\u6E05\u5355 "${name}"`;
133126
+ ctx.ui.print(ctx.ui.dim(`(${why},\u56DE\u9000\u5230\u7528 bot app \u8EAB\u4EFD\u521B\u5EFA/\u590D\u7528\u6E05\u5355\u2014\u2014\u5982\u9700\u5F3A\u5236 adopt \u89C1 --adopt)`));
133127
+ }
133128
+ const ownerOpenId = explicitOwner ?? resolveOwnerOpenId(creatorProfile);
133129
+ if (!ownerOpenId) {
133130
+ const msg = `\u65E0\u6CD5\u786E\u5B9A\u6E05\u5355\u7684\u4EBA\u7C7B owner:\u672A\u4F20 --owner,\u4E14\u65E0\u6CD5\u4ECE lark-cli \u5F53\u524D\u767B\u5F55\u7684\u7528\u6237\u8EAB\u4EFD\u81EA\u52A8\u89E3\u6790(\u5C1D\u8BD5\u7684 profile: "${creatorProfile}")\u3002\u8BF7\u663E\u5F0F\u4F20\u5165 --owner <open_id>(\u53EF\u7528 \`lark-cli auth status --profile ${creatorProfile} --json\` \u67E5\u770B\u662F\u5426\u5DF2\u767B\u5F55\u7528\u6237\u8EAB\u4EFD,\u6216\u7528 lark-cli contact \u6309\u59D3\u540D/\u90AE\u7BB1\u67E5 open_id)\u3002\u4E0D\u4F1A\u5EFA/\u4E0D\u4F1A\u52A8\u4EFB\u4F55\u6E05\u5355\u3002`;
133131
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: msg });
133132
+ else {
133133
+ ctx.ui.failure(msg);
133134
+ ctx.ui.print(USAGE2);
133135
+ }
133136
+ return 1;
133137
+ }
133138
+ const sdkClient = new import_node_sdk2.Client({ appId: creator.app_id, appSecret: creator.appSecret });
133139
+ const requester = {
133140
+ request: (config) => sdkClient.request(config)
133141
+ };
133142
+ const taskClient = new TaskListClient(requester);
133143
+ const members = [
133144
+ { id: ownerOpenId, type: "user", role: "editor" },
133145
+ ...bots.map((b) => ({ id: b.app_id, type: "app", role: "editor" }))
133146
+ ];
133147
+ const registryPath = resolveTaskTeamRegistryPath();
133148
+ const existingGuid = await readTeamTasklistGuid(registryPath);
133149
+ let tasklistGuid;
133150
+ let reused;
133151
+ if (existingGuid && !force) {
133152
+ tasklistGuid = existingGuid;
133153
+ reused = true;
133154
+ try {
133155
+ await taskClient.addTasklistMembers(tasklistGuid, members);
133156
+ } catch (err2) {
133157
+ const msg = `\u590D\u7528\u5DF2\u6709\u6E05\u5355 ${tasklistGuid} \u65F6\u8865\u5145\u6210\u5458\u5931\u8D25: ${err2 instanceof Error ? err2.message : String(err2)}`;
133158
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: msg });
133159
+ else {
133160
+ ctx.ui.failure(msg);
133161
+ ctx.ui.print("\u5E38\u89C1\u539F\u56E0:app \u672A\u5728\u5F00\u653E\u5E73\u53F0\u540E\u53F0\u52FE\u9009 task:tasklist:write / task:task:write scope(\u89C1 docs/task-handle.md \xA77)\u3002");
133162
+ }
133163
+ return 1;
133164
+ }
133165
+ } else {
133166
+ reused = false;
133167
+ try {
133168
+ const created = await taskClient.createTasklist(name, members);
133169
+ tasklistGuid = created.guid;
133170
+ } catch (err2) {
133171
+ const msg = `\u5EFA\u6E05\u5355\u5931\u8D25: ${err2 instanceof Error ? err2.message : String(err2)}`;
133172
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: msg });
133173
+ else {
133174
+ ctx.ui.failure(msg);
133175
+ ctx.ui.print("\u5E38\u89C1\u539F\u56E0:app \u672A\u5728\u5F00\u653E\u5E73\u53F0\u540E\u53F0\u52FE\u9009 task:tasklist:write / task:task:write scope(\u89C1 docs/task-handle.md \xA77)\u3002");
133176
+ }
133177
+ return 1;
133178
+ }
133179
+ if (force && existingGuid) {
133180
+ await overwriteTeamTasklistGuid(registryPath, tasklistGuid);
133181
+ } else {
133182
+ await claimTeamTasklistGuid(registryPath, tasklistGuid);
133183
+ }
133184
+ }
133185
+ let ownerConfirmedMember;
133186
+ let membershipCheckError;
133187
+ try {
133188
+ const snapshot = await taskClient.getTasklist(tasklistGuid);
133189
+ ownerConfirmedMember = snapshot?.members.some((m) => m.id === ownerOpenId) ?? void 0;
133190
+ } catch (err2) {
133191
+ membershipCheckError = err2 instanceof Error ? err2.message : String(err2);
133192
+ }
133193
+ if (ctx.flags.json) {
133194
+ ctx.ui.emitJson({
133195
+ ok: true,
133196
+ team: team_,
133197
+ name,
133198
+ ownerOpenId,
133199
+ tasklistGuid,
133200
+ reused,
133201
+ ownerConfirmedMember: ownerConfirmedMember ?? null,
133202
+ membershipCheckError: membershipCheckError ?? null
133203
+ });
133204
+ return 0;
133205
+ }
133206
+ if (ownerConfirmedMember === false) {
133207
+ ctx.ui.warning(
133208
+ `owner(${ownerOpenId})\u672A\u51FA\u73B0\u5728\u6E05\u5355 ${tasklistGuid} \u7684\u6210\u5458\u5217\u8868\u91CC\u2014\u2014\u53EF\u80FD\u88AB\u9759\u9ED8\u4E22\u5F03,owner \u5927\u6982\u7387\u770B\u4E0D\u5230\u8FD9\u4E2A\u677F\u3002\u8BF7\u68C0\u67E5 open_id \u662F\u5426\u6B63\u786E\u3001\u662F\u5426\u8DE8\u79DF\u6237,\u6216\u624B\u52A8\u5728\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u628A owner \u52A0\u4E3A\u8BE5\u6E05\u5355\u6210\u5458\u3002`
133209
+ );
133210
+ } else if (membershipCheckError) {
133211
+ ctx.ui.warning(
133212
+ `\u65E0\u6CD5\u8BFB\u56DE\u6E05\u5355 ${tasklistGuid} \u7684\u6210\u5458\u5217\u8868\u4EE5\u6838\u5B9E owner \u662F\u5426\u52A0\u5165\u6210\u529F(\u7EE7\u7EED,\u4E0D\u5F71\u54CD\u672C\u6B21\u7ED3\u679C): ${membershipCheckError}`
133213
+ );
133214
+ }
133215
+ if (reused) {
133216
+ ctx.ui.success(`\u590D\u7528\u5DF2\u6709\u6E05\u5355: ${tasklistGuid}`);
133217
+ } else {
133218
+ ctx.ui.success(`\u6E05\u5355 "${name}" \u5DF2\u521B\u5EFA: ${tasklistGuid}`);
133219
+ }
133220
+ ctx.ui.print(`owner \u6210\u5458: ${ownerOpenId}${explicitOwner ? "" : "(\u4ECE lark-cli \u5F53\u524D\u767B\u5F55\u7528\u6237\u81EA\u52A8\u89E3\u6790)"}`);
133221
+ ctx.ui.print(`\u5DF2\u52A0\u5165\u6210\u5458(editor): ${bots.map((b) => b.id).join(", ")}`);
133222
+ ctx.ui.print("");
133223
+ if (!reused) {
133224
+ ctx.ui.print(
133225
+ `\u5DF2\u5199\u5165\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6 ${registryPath} \u2014\u2014 \u56E2\u961F\u91CC\u7684 bot \u4E0B\u6B21\u91CD\u542F\u4F1A\u81EA\u52A8\u53D1\u73B0\u5E76\u4F7F\u7528\u8FD9\u4E2A guid,\u4E0D\u9700\u8981\u624B\u5DE5\u6539 yaml\u3002`
133226
+ );
133227
+ ctx.ui.print("");
133228
+ }
133229
+ ctx.ui.print(
133230
+ ctx.ui.bold("\u53EF\u9009:") + " \u5982\u679C\u60F3\u56FA\u5B9A\u7ED1\u5B9A\u3001\u4E0D\u4F9D\u8D56\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6\u7684\u81EA\u52A8\u53D1\u73B0,\u4E5F\u53EF\u4EE5\u628A guid \u624B\u5199\u8FDB\u5404 bot \u7684 bots/<id>.yaml:"
133231
+ );
133232
+ ctx.ui.print(` taskHandle:`);
133233
+ ctx.ui.print(` tasklistGuid: "${tasklistGuid}"`);
133234
+ ctx.ui.print("");
133235
+ ctx.ui.print(
133236
+ ctx.ui.bold("\u26A0\uFE0F \u8BF7\u624B\u52A8\u786E\u8BA4:") + " \u53BB\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u786E\u8BA4\u8FD9\u4E2A\u6E05\u5355\u786E\u5B9E\u53EF\u89C1(\u89C1 docs/task-handle.md \xA77)\u3002"
133237
+ );
133238
+ return 0;
133239
+ }
133240
+ function resolveAdoptTarget(creatorProfile, name, explicitGuid, loginHint) {
133241
+ if (explicitGuid) {
133242
+ return { ok: true, guid: explicitGuid, matchedName: name };
133243
+ }
133244
+ const listResult = listUserTasklists(creatorProfile);
133245
+ if (!listResult.ok) {
133246
+ return {
133247
+ ok: false,
133248
+ reason: "list-failed",
133249
+ message: `\u65E0\u6CD5\u4EE5\u4F60\u7684\u7528\u6237\u8EAB\u4EFD\u5217\u51FA\u6E05\u5355:${listResult.error}
133250
+
133251
+ \u8BF7\u5148\u786E\u8BA4\u5DF2\u7528\u8FD9\u4E2A profile \u767B\u5F55\u8FC7\u7528\u6237\u8EAB\u4EFD\u3001\u4E14\u7533\u8BF7\u4E86 task \u57DF\u6743\u9650:
133252
+ ${loginHint}
133253
+ (\u53EF\u7528 \`lark-cli auth status --profile ${creatorProfile} --json\` \u68C0\u67E5\u5F53\u524D\u72B6\u6001)`
133254
+ };
133255
+ }
133256
+ const matches = listResult.data.filter((t) => t.name === name);
133257
+ if (matches.length === 0) {
133258
+ return {
133259
+ ok: false,
133260
+ reason: "not-found",
133261
+ message: `\u5728\u4F60\u80FD\u770B\u5230\u7684\u6E05\u5355\u91CC\u6CA1\u627E\u5230\u540D\u4E3A "${name}" \u7684\u6E05\u5355\u3002\u8BF7\u5148\u53BB\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u65B0\u5EFA\u4E00\u4E2A\u540C\u540D\u6E05\u5355,\u518D\u91CD\u8DD1\u8FD9\u4E2A\u547D\u4EE4;\u6216\u8005\u68C0\u67E5\u540D\u5B57\u662F\u5426\u5B8C\u5168\u4E00\u81F4(\u6CE8\u610F\u5168\u534A\u89D2\u5B57\u7B26\u3001\u591A\u4F59\u7A7A\u683C)\u3002`
133262
+ };
133263
+ }
133264
+ if (matches.length > 1) {
133265
+ return {
133266
+ ok: false,
133267
+ reason: "ambiguous",
133268
+ message: `\u627E\u5230 ${matches.length} \u4E2A\u540C\u540D\u6E05\u5355 "${name}",\u65E0\u6CD5\u786E\u5B9A\u8981 adopt \u54EA\u4E00\u4E2A:
133269
+ ` + matches.map((t) => ` - ${t.guid}`).join("\n") + "\n\u8BF7\u52A0 --adopt-guid <guid> \u663E\u5F0F\u6307\u5B9A\u5176\u4E2D\u4E00\u4E2A\u91CD\u8DD1,\u6216\u53BB\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u628A\u5176\u4E2D\u4E00\u4E2A\u6539\u540D\u540E\u518D\u91CD\u8DD1\u3002",
133270
+ matches
133271
+ };
133272
+ }
133273
+ return { ok: true, guid: matches[0].guid, matchedName: matches[0].name };
133274
+ }
133275
+ async function runAdoptWithGuid(ctx, opts) {
133276
+ const { guid: tasklistGuid, matchedName, bots, force, creatorProfile, loginHint } = opts;
133277
+ const members = bots.map((b) => ({ id: b.app_id, type: "app", role: "editor" }));
133278
+ const addResult = addTasklistMembersAsUser(creatorProfile, tasklistGuid, members);
133279
+ if (!addResult.ok) {
133280
+ const msg = `\u628A bot app \u52A0\u4E3A\u6E05\u5355 ${tasklistGuid} \u7684 editor \u5931\u8D25:${addResult.error}
133281
+
133282
+ \u5E38\u89C1\u539F\u56E0:\u8FD9\u4E2A profile \u7684\u7528\u6237\u8EAB\u4EFD\u7F3A task:tasklist:write scope \u2014\u2014 \u91CD\u65B0\u767B\u5F55\u5E76\u7533\u8BF7 task \u57DF\u6743\u9650:
133283
+ ${loginHint}`;
133284
+ if (ctx.flags.json) ctx.ui.emitJson({ ok: false, error: msg });
133285
+ else ctx.ui.failure(msg);
133286
+ return 1;
133287
+ }
133288
+ const registryPath = resolveTaskTeamRegistryPath();
133289
+ let registeredGuid;
133290
+ if (force) {
133291
+ await overwriteTeamTasklistGuid(registryPath, tasklistGuid);
133292
+ registeredGuid = tasklistGuid;
133293
+ } else {
133294
+ registeredGuid = await claimTeamTasklistGuid(registryPath, tasklistGuid);
133295
+ }
133296
+ const registryMismatch = registeredGuid !== tasklistGuid;
133297
+ const membersResult = getUserTasklistMembers(creatorProfile, tasklistGuid);
133298
+ const missingBots = [];
133299
+ let membershipCheckError;
133300
+ if (membersResult.ok) {
133301
+ for (const b of bots) {
133302
+ if (!membersResult.data.some((m) => m.id === b.app_id)) missingBots.push(b.id);
133303
+ }
133304
+ } else {
133305
+ membershipCheckError = membersResult.error;
133306
+ }
133307
+ if (ctx.flags.json) {
133308
+ ctx.ui.emitJson({
133309
+ ok: true,
133310
+ mode: "adopt",
133311
+ adoptedName: matchedName,
133312
+ tasklistGuid,
133313
+ addedMembers: bots.map((b) => b.id),
133314
+ missingBots,
133315
+ membershipCheckError: membershipCheckError ?? null,
133316
+ registeredGuid,
133317
+ registryMismatch
133318
+ });
133319
+ return 0;
133320
+ }
133321
+ ctx.ui.success(`\u5DF2 adopt \u6E05\u5355 "${matchedName}": ${tasklistGuid}`);
133322
+ ctx.ui.print(`\u5DF2\u52A0\u5165\u6210\u5458(editor): ${bots.map((b) => b.id).join(", ")}`);
133323
+ ctx.ui.print("");
133324
+ if (missingBots.length > 0) {
133325
+ ctx.ui.warning(
133326
+ `\u4EE5\u4E0B bot \u7684 app \u672A\u51FA\u73B0\u5728\u6E05\u5355\u6210\u5458\u5217\u8868\u91CC\u2014\u2014\u53EF\u80FD\u88AB\u9759\u9ED8\u4E22\u5F03:${missingBots.join(", ")}\u3002\u8BF7\u68C0\u67E5 app_id \u662F\u5426\u6B63\u786E,\u6216\u624B\u52A8\u5728\u98DE\u4E66\u4EFB\u52A1\u4E2D\u5FC3\u628A\u5B83\u4EEC\u52A0\u4E3A\u8BE5\u6E05\u5355\u7684 editor\u3002`
133327
+ );
133328
+ } else if (membershipCheckError) {
133329
+ ctx.ui.warning(`\u65E0\u6CD5\u8BFB\u56DE\u6E05\u5355 ${tasklistGuid} \u7684\u6210\u5458\u5217\u8868\u4EE5\u6838\u5B9E\u662F\u5426\u52A0\u5165\u6210\u529F(\u7EE7\u7EED,\u4E0D\u5F71\u54CD\u672C\u6B21\u7ED3\u679C): ${membershipCheckError}`);
133330
+ }
133331
+ if (registryMismatch) {
133332
+ ctx.ui.warning(
133333
+ `\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6\u91CC\u5DF2\u7ECF\u7ED1\u5B9A\u4E86\u53E6\u4E00\u4E2A\u6E05\u5355(${registeredGuid}),\u8FD9\u6B21 adopt \u7684\u6E05\u5355(${tasklistGuid})\u672A\u5199\u5165\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6\u2014\u2014\u907F\u514D\u7834\u574F\u73B0\u6709\u56E2\u961F\u7ED1\u5B9A\u3002\u5982\u679C\u786E\u5B9E\u60F3\u5207\u6362\u5230\u8FD9\u4E2A\u65B0\u6E05\u5355,\u52A0 --force \u91CD\u8DD1\u3002`
133334
+ );
133335
+ } else {
133336
+ ctx.ui.print(
133337
+ `\u5DF2\u5199\u5165\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6 ${registryPath} \u2014\u2014 \u56E2\u961F\u91CC\u7684 bot \u4E0B\u6B21\u91CD\u542F\u4F1A\u81EA\u52A8\u53D1\u73B0\u5E76\u4F7F\u7528\u8FD9\u4E2A guid,\u4E0D\u9700\u8981\u624B\u5DE5\u6539 yaml\u3002`
133338
+ );
133339
+ }
133340
+ ctx.ui.print("");
133341
+ ctx.ui.print(
133342
+ ctx.ui.bold("\u53EF\u9009:") + " \u5982\u679C\u60F3\u56FA\u5B9A\u7ED1\u5B9A\u3001\u4E0D\u4F9D\u8D56\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6\u7684\u81EA\u52A8\u53D1\u73B0,\u4E5F\u53EF\u4EE5\u628A guid \u624B\u5199\u8FDB\u5404 bot \u7684 bots/<id>.yaml:"
133343
+ );
133344
+ ctx.ui.print(` taskHandle:`);
133345
+ ctx.ui.print(` tasklistGuid: "${tasklistGuid}"`);
133346
+ return 0;
133347
+ }
133348
+
131127
133349
  // src/cli/index.ts
131128
133350
  var _require2 = createRequire3(import.meta.url);
131129
133351
  var _pkg = _require2("../../package.json");
@@ -131140,9 +133362,10 @@ var COMMANDS = {
131140
133362
  logs: run6,
131141
133363
  update: run7,
131142
133364
  dogfood: run9,
131143
- ui: run8
133365
+ ui: run8,
133366
+ "tasklist-init": run10
131144
133367
  };
131145
- var USAGE2 = `larkway \u2014 Feishu \u2194 local CLI agent bridge host manager
133368
+ var USAGE3 = `larkway \u2014 Feishu \u2194 local CLI agent bridge host manager
131146
133369
 
131147
133370
  \u7528\u6CD5:
131148
133371
  larkway \u76F4\u63A5\u6253\u5F00\u7F51\u9875\u7BA1\u7406\u9762(\u63A8\u8350 \u2014\u2014 \u626B\u7801\u7ED1\u5B9A + \u914D\u7F6E\u5168\u5728\u6D4F\u89C8\u5668\u91CC\u70B9\u70B9\u70B9)
@@ -131158,6 +133381,7 @@ var USAGE2 = `larkway \u2014 Feishu \u2194 local CLI agent bridge host manager
131158
133381
  update pull + install + restart
131159
133382
  dogfood preflight|guide [id] v0.3 Phase 1 dogfood \u524D\u7F6E\u9A8C\u6536 / \u4E0B\u4E00\u6B65\u6307\u5F15
131160
133383
  ui \u542F\u52A8\u8F7B\u91CF Web UI \u7BA1\u7406\u9762(127.0.0.1 + token)
133384
+ tasklist-init \u4E3A\u8BDD\u9898\u2194\u4EFB\u52A1\u53E5\u67C4 feature provisioning \u5171\u4EAB\u6E05\u5355(docs/task-handle.md)
131161
133385
 
131162
133386
  \u5168\u5C40 flags:
131163
133387
  --json \u673A\u5668\u53EF\u8BFB\u8F93\u51FA(\u4E0E\u5176\u4ED6 flag \u6B63\u4EA4)
@@ -131222,29 +133446,32 @@ async function main(argv) {
131222
133446
  return 0;
131223
133447
  }
131224
133448
  if (help) {
131225
- print(USAGE2);
133449
+ if (command === "tasklist-init") {
133450
+ return run10(buildContext(flags), ["help"]);
133451
+ }
133452
+ print(USAGE3);
131226
133453
  return 0;
131227
133454
  }
131228
133455
  if (command === void 0) {
131229
133456
  if (flags.json || flags.nonInteractive) {
131230
- print(USAGE2);
133457
+ print(USAGE3);
131231
133458
  return 1;
131232
133459
  }
131233
133460
  print("\u542F\u52A8\u7F51\u9875\u7BA1\u7406\u9762\u2026(\u9996\u6B21\u4F7F\u7528:\u5728\u6D4F\u89C8\u5668\u91CC\u626B\u7801\u7ED1\u5B9A + \u914D\u7F6E,\u5168\u7A0B\u70B9\u70B9\u70B9)");
131234
133461
  print(dim("\u65E0\u6D4F\u89C8\u5668 / \u670D\u52A1\u5668\u573A\u666F\u8BF7\u6539\u7528 `larkway init`(CLI \u5411\u5BFC)\u3002"));
131235
133462
  return run8(buildContext(flags), []);
131236
133463
  }
131237
- const run10 = COMMANDS[command];
131238
- if (!run10) {
133464
+ const run11 = COMMANDS[command];
133465
+ if (!run11) {
131239
133466
  failure(`\u672A\u77E5\u547D\u4EE4: ${command}`);
131240
133467
  print("");
131241
- print(USAGE2);
133468
+ print(USAGE3);
131242
133469
  return 1;
131243
133470
  }
131244
133471
  const ctx = buildContext(flags);
131245
133472
  const isLifecycle = command === "start" || command === "stop" || command === "status" || command === "logs";
131246
133473
  const runArgs = isLifecycle ? [command, ...args] : args;
131247
- return run10(ctx, runArgs);
133474
+ return run11(ctx, runArgs);
131248
133475
  }
131249
133476
  main(process4.argv.slice(2)).then((code) => process4.exit(code)).catch((e) => {
131250
133477
  const message = e instanceof Error ? e.message : String(e);