@ycodium-ai/plugin-team 0.2.3681 → 0.2.3710

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/assets/ui.js +7 -7
  2. package/dist/index.js +1472 -1964
  3. package/package.json +2 -3
package/dist/index.js CHANGED
@@ -475,134 +475,6 @@ var compareSets = /* @__PURE__ */ makeCompareSet(compareBoth);
475
475
  var isEqual = (u) => hasProperty(u, symbol2);
476
476
  var asEquivalence = () => equals;
477
477
 
478
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Reducer.js
479
- function make(combine2, initialValue, combineAll) {
480
- return {
481
- combine: combine2,
482
- initialValue,
483
- combineAll: combineAll ?? ((collection) => {
484
- let out = initialValue;
485
- for (const value of collection) {
486
- out = combine2(out, value);
487
- }
488
- return out;
489
- })
490
- };
491
- }
492
-
493
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Equivalence.js
494
- var make2 = (isEquivalent) => (self, that) => self === that || isEquivalent(self, that);
495
-
496
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/array.js
497
- var isArrayNonEmpty = (self) => self.length > 0;
498
-
499
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/doNotation.js
500
- var let_ = (map7) => dual(3, (self, name, f) => map7(self, (a) => ({
501
- ...a,
502
- [name]: f(a)
503
- })));
504
- var bindTo = (map7) => dual(2, (self, name) => map7(self, (a) => ({
505
- [name]: a
506
- })));
507
- var bind = (map7, flatMap4) => dual(3, (self, name, f) => flatMap4(self, (a) => map7(f(a), (b) => ({
508
- ...a,
509
- [name]: b
510
- }))));
511
-
512
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/record.js
513
- function assignProperty(self, key, value) {
514
- if (key === "__proto__") {
515
- Object.defineProperty(self, key, {
516
- value,
517
- writable: true,
518
- enumerable: true,
519
- configurable: true
520
- });
521
- } else {
522
- ;
523
- self[key] = value;
524
- }
525
- }
526
- function assignProperties(self, source) {
527
- for (const key of Reflect.ownKeys(source)) {
528
- if (Object.prototype.propertyIsEnumerable.call(source, key)) {
529
- assignProperty(self, key, source[key]);
530
- }
531
- }
532
- }
533
-
534
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Option.js
535
- var Option_exports = {};
536
- __export(Option_exports, {
537
- Do: () => Do,
538
- all: () => all,
539
- andThen: () => andThen,
540
- as: () => as,
541
- asVoid: () => asVoid,
542
- bind: () => bind2,
543
- bindTo: () => bindTo2,
544
- composeK: () => composeK,
545
- contains: () => contains,
546
- containsWith: () => containsWith,
547
- exists: () => exists,
548
- filter: () => filter,
549
- filterMap: () => filterMap,
550
- firstSomeOf: () => firstSomeOf,
551
- flatMap: () => flatMap,
552
- flatMapNullishOr: () => flatMapNullishOr,
553
- flatten: () => flatten,
554
- fromIterable: () => fromIterable,
555
- fromNullOr: () => fromNullOr,
556
- fromNullishOr: () => fromNullishOr,
557
- fromUndefinedOr: () => fromUndefinedOr,
558
- gen: () => gen,
559
- getFailure: () => getFailure2,
560
- getOrElse: () => getOrElse,
561
- getOrNull: () => getOrNull,
562
- getOrThrow: () => getOrThrow,
563
- getOrThrowWith: () => getOrThrowWith,
564
- getOrUndefined: () => getOrUndefined,
565
- getSuccess: () => getSuccess2,
566
- isNone: () => isNone2,
567
- isOption: () => isOption2,
568
- isSome: () => isSome2,
569
- let: () => let_2,
570
- lift2: () => lift2,
571
- liftNullishOr: () => liftNullishOr,
572
- liftPredicate: () => liftPredicate,
573
- liftThrowable: () => liftThrowable,
574
- makeCombinerFailFast: () => makeCombinerFailFast,
575
- makeEquivalence: () => makeEquivalence,
576
- makeOrder: () => makeOrder,
577
- makeReducer: () => makeReducer,
578
- makeReducerFailFast: () => makeReducerFailFast,
579
- map: () => map,
580
- match: () => match,
581
- none: () => none2,
582
- orElse: () => orElse,
583
- orElseResult: () => orElseResult,
584
- orElseSome: () => orElseSome,
585
- partitionMap: () => partitionMap,
586
- product: () => product,
587
- productMany: () => productMany,
588
- reduceCompact: () => reduceCompact,
589
- some: () => some2,
590
- tap: () => tap,
591
- toArray: () => toArray,
592
- toRefinement: () => toRefinement,
593
- void: () => void_,
594
- zipLeft: () => zipLeft,
595
- zipRight: () => zipRight,
596
- zipWith: () => zipWith
597
- });
598
-
599
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Combiner.js
600
- function make3(combine2) {
601
- return {
602
- combine: combine2
603
- };
604
- }
605
-
606
478
  // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Redactable.js
607
479
  var symbolRedactable = /* @__PURE__ */ Symbol.for("~effect/Redactable");
608
480
  var isRedactable = (u) => hasProperty(u, symbolRedactable);
@@ -825,6 +697,28 @@ var pickInternalCall = () => {
825
697
  };
826
698
  var internalCall = /* @__PURE__ */ pickInternalCall();
827
699
 
700
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/record.js
701
+ function assignProperty(self, key, value) {
702
+ if (key === "__proto__") {
703
+ Object.defineProperty(self, key, {
704
+ value,
705
+ writable: true,
706
+ enumerable: true,
707
+ configurable: true
708
+ });
709
+ } else {
710
+ ;
711
+ self[key] = value;
712
+ }
713
+ }
714
+ function assignProperties(self, source) {
715
+ for (const key of Reflect.ownKeys(source)) {
716
+ if (Object.prototype.propertyIsEnumerable.call(source, key)) {
717
+ assignProperty(self, key, source[key]);
718
+ }
719
+ }
720
+ }
721
+
828
722
  // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/core.js
829
723
  var EffectTypeId = `~effect/Effect`;
830
724
  var ExitTypeId = `~effect/Exit`;
@@ -1168,103 +1062,229 @@ var DoneVoid = {
1168
1062
  value: void 0
1169
1063
  };
1170
1064
 
1171
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/option.js
1172
- var TypeId = "~effect/data/Option";
1173
- var CommonProto = {
1174
- [TypeId]: {
1175
- _A: (_) => _
1176
- },
1177
- ...PipeInspectableProto,
1178
- [Symbol.iterator]() {
1179
- return new SingleShotGen(this);
1180
- }
1181
- };
1182
- var SomeProto = /* @__PURE__ */ Object.defineProperty(/* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(CommonProto), {
1183
- _tag: "Some",
1184
- _op: "Some",
1185
- [symbol2](that) {
1186
- return isOption(that) && isSome(that) && equals(this.value, that.value);
1187
- },
1188
- [symbol]() {
1189
- return combine(hash(this._tag))(hash(this.value));
1190
- },
1191
- toString() {
1192
- return `some(${format(this.value)})`;
1193
- },
1194
- toJSON() {
1195
- return {
1196
- _id: "Option",
1197
- _tag: this._tag,
1198
- value: toJson(this.value)
1199
- };
1200
- }
1201
- }), "valueOrUndefined", {
1202
- get() {
1203
- return this.value;
1204
- }
1205
- });
1206
- var NoneHash = /* @__PURE__ */ hash("None");
1207
- var NoneProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(CommonProto), {
1208
- _tag: "None",
1209
- _op: "None",
1210
- valueOrUndefined: void 0,
1211
- [symbol2](that) {
1212
- return isOption(that) && isNone(that);
1213
- },
1214
- [symbol]() {
1215
- return NoneHash;
1216
- },
1217
- toString() {
1218
- return `none()`;
1219
- },
1220
- toJSON() {
1221
- return {
1222
- _id: "Option",
1223
- _tag: this._tag
1224
- };
1225
- }
1065
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Effectable.js
1066
+ var Prototype2 = (options) => makePrimitiveProto({
1067
+ op: options.label,
1068
+ [evaluate]: options.evaluate
1226
1069
  });
1227
- var isOption = (input) => hasProperty(input, TypeId);
1228
- var isNone = (fa) => fa._tag === "None";
1229
- var isSome = (fa) => fa._tag === "Some";
1230
- var none = /* @__PURE__ */ Object.create(NoneProto);
1231
- var some = (value) => {
1232
- const a = Object.create(SomeProto);
1233
- a.value = value;
1234
- return a;
1235
- };
1236
1070
 
1237
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/result.js
1238
- var TypeId2 = "~effect/data/Result";
1239
- var CommonProto2 = {
1240
- [TypeId2]: {
1241
- /* v8 ignore next 2 */
1242
- _A: (_) => _,
1243
- _E: (_) => _
1244
- },
1245
- ...PipeInspectableProto,
1246
- [Symbol.iterator]() {
1247
- return new SingleShotGen(this);
1071
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/stackTraceLimit.js
1072
+ var isStackTraceLimitWritable = () => {
1073
+ const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit");
1074
+ if (desc === void 0) {
1075
+ return Object.isExtensible(Error);
1248
1076
  }
1077
+ return Object.hasOwn(desc, "writable") ? desc.writable === true : desc.set !== void 0;
1249
1078
  };
1250
- var SuccessProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(CommonProto2), {
1251
- _tag: "Success",
1252
- _op: "Success",
1253
- [symbol2](that) {
1254
- return isResult(that) && isSuccess(that) && equals(this.success, that.success);
1255
- },
1256
- [symbol]() {
1257
- return combine(hash(this._tag))(hash(this.success));
1258
- },
1259
- toString() {
1260
- return `success(${format(this.success)})`;
1261
- },
1262
- toJSON() {
1263
- return {
1264
- _id: "Result",
1265
- _tag: this._tag,
1266
- value: toJson(this.success)
1267
- };
1079
+ var canWriteStackTraceLimit = /* @__PURE__ */ isStackTraceLimitWritable();
1080
+ var getStackTraceLimit = () => Error.stackTraceLimit;
1081
+ var setStackTraceLimit = (value) => {
1082
+ if (canWriteStackTraceLimit) {
1083
+ ;
1084
+ Error.stackTraceLimit = value;
1085
+ }
1086
+ };
1087
+
1088
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Option.js
1089
+ var Option_exports = {};
1090
+ __export(Option_exports, {
1091
+ Do: () => Do,
1092
+ all: () => all,
1093
+ andThen: () => andThen,
1094
+ as: () => as,
1095
+ asVoid: () => asVoid,
1096
+ bind: () => bind2,
1097
+ bindTo: () => bindTo2,
1098
+ composeK: () => composeK,
1099
+ contains: () => contains,
1100
+ containsWith: () => containsWith,
1101
+ exists: () => exists,
1102
+ filter: () => filter,
1103
+ filterMap: () => filterMap,
1104
+ firstSomeOf: () => firstSomeOf,
1105
+ flatMap: () => flatMap,
1106
+ flatMapNullishOr: () => flatMapNullishOr,
1107
+ flatten: () => flatten,
1108
+ fromIterable: () => fromIterable,
1109
+ fromNullOr: () => fromNullOr,
1110
+ fromNullishOr: () => fromNullishOr,
1111
+ fromUndefinedOr: () => fromUndefinedOr,
1112
+ gen: () => gen,
1113
+ getFailure: () => getFailure2,
1114
+ getOrElse: () => getOrElse,
1115
+ getOrNull: () => getOrNull,
1116
+ getOrThrow: () => getOrThrow,
1117
+ getOrThrowWith: () => getOrThrowWith,
1118
+ getOrUndefined: () => getOrUndefined,
1119
+ getSuccess: () => getSuccess2,
1120
+ isNone: () => isNone2,
1121
+ isOption: () => isOption2,
1122
+ isSome: () => isSome2,
1123
+ let: () => let_2,
1124
+ lift2: () => lift2,
1125
+ liftNullishOr: () => liftNullishOr,
1126
+ liftPredicate: () => liftPredicate,
1127
+ liftThrowable: () => liftThrowable,
1128
+ makeCombinerFailFast: () => makeCombinerFailFast,
1129
+ makeEquivalence: () => makeEquivalence,
1130
+ makeOrder: () => makeOrder,
1131
+ makeReducer: () => makeReducer,
1132
+ makeReducerFailFast: () => makeReducerFailFast,
1133
+ map: () => map,
1134
+ match: () => match,
1135
+ none: () => none2,
1136
+ orElse: () => orElse,
1137
+ orElseResult: () => orElseResult,
1138
+ orElseSome: () => orElseSome,
1139
+ partitionMap: () => partitionMap,
1140
+ product: () => product,
1141
+ productMany: () => productMany,
1142
+ reduceCompact: () => reduceCompact,
1143
+ some: () => some2,
1144
+ tap: () => tap,
1145
+ toArray: () => toArray,
1146
+ toRefinement: () => toRefinement,
1147
+ void: () => void_,
1148
+ zipLeft: () => zipLeft,
1149
+ zipRight: () => zipRight,
1150
+ zipWith: () => zipWith
1151
+ });
1152
+
1153
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Combiner.js
1154
+ function make(combine2) {
1155
+ return {
1156
+ combine: combine2
1157
+ };
1158
+ }
1159
+
1160
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Reducer.js
1161
+ function make2(combine2, initialValue, combineAll) {
1162
+ return {
1163
+ combine: combine2,
1164
+ initialValue,
1165
+ combineAll: combineAll ?? ((collection) => {
1166
+ let out = initialValue;
1167
+ for (const value of collection) {
1168
+ out = combine2(out, value);
1169
+ }
1170
+ return out;
1171
+ })
1172
+ };
1173
+ }
1174
+
1175
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Equivalence.js
1176
+ var make3 = (isEquivalent) => (self, that) => self === that || isEquivalent(self, that);
1177
+
1178
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/doNotation.js
1179
+ var let_ = (map7) => dual(3, (self, name, f) => map7(self, (a) => ({
1180
+ ...a,
1181
+ [name]: f(a)
1182
+ })));
1183
+ var bindTo = (map7) => dual(2, (self, name) => map7(self, (a) => ({
1184
+ [name]: a
1185
+ })));
1186
+ var bind = (map7, flatMap4) => dual(3, (self, name, f) => flatMap4(self, (a) => map7(f(a), (b) => ({
1187
+ ...a,
1188
+ [name]: b
1189
+ }))));
1190
+
1191
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/option.js
1192
+ var TypeId = "~effect/data/Option";
1193
+ var CommonProto = {
1194
+ [TypeId]: {
1195
+ _A: (_) => _
1196
+ },
1197
+ ...PipeInspectableProto,
1198
+ [Symbol.iterator]() {
1199
+ return new SingleShotGen(this);
1200
+ }
1201
+ };
1202
+ var SomeProto = /* @__PURE__ */ Object.defineProperty(/* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(CommonProto), {
1203
+ _tag: "Some",
1204
+ _op: "Some",
1205
+ [symbol2](that) {
1206
+ return isOption(that) && isSome(that) && equals(this.value, that.value);
1207
+ },
1208
+ [symbol]() {
1209
+ return combine(hash(this._tag))(hash(this.value));
1210
+ },
1211
+ toString() {
1212
+ return `some(${format(this.value)})`;
1213
+ },
1214
+ toJSON() {
1215
+ return {
1216
+ _id: "Option",
1217
+ _tag: this._tag,
1218
+ value: toJson(this.value)
1219
+ };
1220
+ }
1221
+ }), "valueOrUndefined", {
1222
+ get() {
1223
+ return this.value;
1224
+ }
1225
+ });
1226
+ var NoneHash = /* @__PURE__ */ hash("None");
1227
+ var NoneProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(CommonProto), {
1228
+ _tag: "None",
1229
+ _op: "None",
1230
+ valueOrUndefined: void 0,
1231
+ [symbol2](that) {
1232
+ return isOption(that) && isNone(that);
1233
+ },
1234
+ [symbol]() {
1235
+ return NoneHash;
1236
+ },
1237
+ toString() {
1238
+ return `none()`;
1239
+ },
1240
+ toJSON() {
1241
+ return {
1242
+ _id: "Option",
1243
+ _tag: this._tag
1244
+ };
1245
+ }
1246
+ });
1247
+ var isOption = (input) => hasProperty(input, TypeId);
1248
+ var isNone = (fa) => fa._tag === "None";
1249
+ var isSome = (fa) => fa._tag === "Some";
1250
+ var none = /* @__PURE__ */ Object.create(NoneProto);
1251
+ var some = (value) => {
1252
+ const a = Object.create(SomeProto);
1253
+ a.value = value;
1254
+ return a;
1255
+ };
1256
+
1257
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/result.js
1258
+ var TypeId2 = "~effect/data/Result";
1259
+ var CommonProto2 = {
1260
+ [TypeId2]: {
1261
+ /* v8 ignore next 2 */
1262
+ _A: (_) => _,
1263
+ _E: (_) => _
1264
+ },
1265
+ ...PipeInspectableProto,
1266
+ [Symbol.iterator]() {
1267
+ return new SingleShotGen(this);
1268
+ }
1269
+ };
1270
+ var SuccessProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(CommonProto2), {
1271
+ _tag: "Success",
1272
+ _op: "Success",
1273
+ [symbol2](that) {
1274
+ return isResult(that) && isSuccess(that) && equals(this.success, that.success);
1275
+ },
1276
+ [symbol]() {
1277
+ return combine(hash(this._tag))(hash(this.success));
1278
+ },
1279
+ toString() {
1280
+ return `success(${format(this.success)})`;
1281
+ },
1282
+ toJSON() {
1283
+ return {
1284
+ _id: "Result",
1285
+ _tag: this._tag,
1286
+ value: toJson(this.success)
1287
+ };
1268
1288
  }
1269
1289
  });
1270
1290
  var FailureProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(CommonProto2), {
@@ -1443,7 +1463,7 @@ var filterMap = /* @__PURE__ */ dual(2, (self, f) => {
1443
1463
  return isSuccess(next) ? some2(next.success) : none2();
1444
1464
  });
1445
1465
  var filter = /* @__PURE__ */ dual(2, (self, predicate) => isNone2(self) ? none2() : predicate(self.value) ? some2(self.value) : none2());
1446
- var makeEquivalence = (isEquivalent) => make2((x, y) => isNone2(x) ? isNone2(y) : isNone2(y) ? false : isEquivalent(x.value, y.value));
1466
+ var makeEquivalence = (isEquivalent) => make3((x, y) => isNone2(x) ? isNone2(y) : isNone2(y) ? false : isEquivalent(x.value, y.value));
1447
1467
  var makeOrder = (O) => make4((self, that) => isSome2(self) ? isSome2(that) ? O(self.value, that.value) : 1 : -1);
1448
1468
  var lift2 = (f) => dual(2, (self, that) => zipWith(self, that, f));
1449
1469
  var liftPredicate = /* @__PURE__ */ dual(2, (b, predicate) => predicate(b) ? some2(b) : none2());
@@ -1468,14 +1488,14 @@ var gen = (...args2) => {
1468
1488
  return some2(state.value);
1469
1489
  };
1470
1490
  function makeReducer(combiner) {
1471
- return make((self, that) => {
1491
+ return make2((self, that) => {
1472
1492
  if (isNone2(self)) return that;
1473
1493
  if (isNone2(that)) return self;
1474
1494
  return some2(combiner.combine(self.value, that.value));
1475
1495
  }, none2());
1476
1496
  }
1477
1497
  function makeCombinerFailFast(combiner) {
1478
- return make3((self, that) => {
1498
+ return make((self, that) => {
1479
1499
  if (isNone2(self) || isNone2(that)) return none2();
1480
1500
  return some2(combiner.combine(self.value, that.value));
1481
1501
  });
@@ -1483,7 +1503,7 @@ function makeCombinerFailFast(combiner) {
1483
1503
  function makeReducerFailFast(reducer) {
1484
1504
  const combine2 = makeCombinerFailFast(reducer).combine;
1485
1505
  const initialValue = some2(reducer.initialValue);
1486
- return make(combine2, initialValue, (collection) => {
1506
+ return make2(combine2, initialValue, (collection) => {
1487
1507
  let out = initialValue;
1488
1508
  for (const value of collection) {
1489
1509
  out = combine2(out, value);
@@ -1493,100 +1513,20 @@ function makeReducerFailFast(reducer) {
1493
1513
  });
1494
1514
  }
1495
1515
 
1496
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Result.js
1497
- var succeed2 = succeed;
1498
- var fail2 = fail;
1499
- var isFailure2 = isFailure;
1500
- var match2 = /* @__PURE__ */ dual(2, (self, {
1501
- onFailure,
1502
- onSuccess
1503
- }) => isFailure2(self) ? onFailure(self.failure) : onSuccess(self.success));
1504
-
1505
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Array.js
1506
- var Array2 = globalThis.Array;
1507
- var fromIterable2 = (collection) => Array2.isArray(collection) ? collection : Array2.from(collection);
1508
- var append = /* @__PURE__ */ dual(2, (self, last) => [...self, last]);
1509
- var appendAll = /* @__PURE__ */ dual(2, (self, that) => fromIterable2(self).concat(fromIterable2(that)));
1510
- var isArray = Array2.isArray;
1511
- var isArrayNonEmpty2 = isArrayNonEmpty;
1512
- var isReadonlyArrayNonEmpty = isArrayNonEmpty;
1513
- var hashBucketsAdd = (buckets, value) => {
1514
- const hash2 = hash(value);
1515
- const bucket = buckets.get(hash2);
1516
- if (bucket === void 0) {
1517
- buckets.set(hash2, [value]);
1518
- return true;
1516
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Context.js
1517
+ var ServiceTypeId = "~effect/Context/Service";
1518
+ var Service = function() {
1519
+ const prevLimit = getStackTraceLimit();
1520
+ setStackTraceLimit(2);
1521
+ const err = new Error();
1522
+ setStackTraceLimit(prevLimit);
1523
+ function KeyClass() {
1519
1524
  }
1520
- for (const previous of bucket) {
1521
- if (equals(previous, value)) {
1522
- return false;
1523
- }
1524
- }
1525
- bucket.push(value);
1526
- return true;
1527
- };
1528
- var union = /* @__PURE__ */ dual(2, (self, that) => {
1529
- const a = fromIterable2(self);
1530
- const b = fromIterable2(that);
1531
- if (isReadonlyArrayNonEmpty(a)) {
1532
- return isReadonlyArrayNonEmpty(b) ? dedupe(appendAll(a, b)) : a;
1533
- }
1534
- return b;
1535
- });
1536
- var empty = () => [];
1537
- var map2 = /* @__PURE__ */ dual(2, (self, f) => self.map(f));
1538
- var dedupe = (self) => {
1539
- const input = fromIterable2(self);
1540
- if (input.length < 2) {
1541
- return [...input];
1542
- }
1543
- const buckets = /* @__PURE__ */ new Map();
1544
- const out = [];
1545
- for (const value of input) {
1546
- if (hashBucketsAdd(buckets, value)) {
1547
- out.push(value);
1548
- }
1549
- }
1550
- return out;
1551
- };
1552
-
1553
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Effectable.js
1554
- var Prototype2 = (options) => makePrimitiveProto({
1555
- op: options.label,
1556
- [evaluate]: options.evaluate
1557
- });
1558
-
1559
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/stackTraceLimit.js
1560
- var isStackTraceLimitWritable = () => {
1561
- const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit");
1562
- if (desc === void 0) {
1563
- return Object.isExtensible(Error);
1564
- }
1565
- return Object.hasOwn(desc, "writable") ? desc.writable === true : desc.set !== void 0;
1566
- };
1567
- var canWriteStackTraceLimit = /* @__PURE__ */ isStackTraceLimitWritable();
1568
- var getStackTraceLimit = () => Error.stackTraceLimit;
1569
- var setStackTraceLimit = (value) => {
1570
- if (canWriteStackTraceLimit) {
1571
- ;
1572
- Error.stackTraceLimit = value;
1573
- }
1574
- };
1575
-
1576
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Context.js
1577
- var ServiceTypeId = "~effect/Context/Service";
1578
- var Service = function() {
1579
- const prevLimit = getStackTraceLimit();
1580
- setStackTraceLimit(2);
1581
- const err = new Error();
1582
- setStackTraceLimit(prevLimit);
1583
- function KeyClass() {
1584
- }
1585
- const self = KeyClass;
1586
- Object.setPrototypeOf(self, ServiceProto);
1587
- Object.defineProperty(self, "stack", {
1588
- get() {
1589
- return err.stack;
1525
+ const self = KeyClass;
1526
+ Object.setPrototypeOf(self, ServiceProto);
1527
+ Object.defineProperty(self, "stack", {
1528
+ get() {
1529
+ return err.stack;
1590
1530
  }
1591
1531
  });
1592
1532
  const init = (key, options) => {
@@ -1711,7 +1651,7 @@ var Proto = {
1711
1651
  var hasSameCache = (self, that) => self.cacheRoot === that.cacheRoot;
1712
1652
  var isContext = (u) => hasProperty(u, TypeId3);
1713
1653
  var isReference = (u) => !!u[ReferenceTypeId];
1714
- var empty2 = () => emptyContext2;
1654
+ var empty = () => emptyContext2;
1715
1655
  var emptyContext2 = /* @__PURE__ */ makeUnsafe(/* @__PURE__ */ new Map());
1716
1656
  var make5 = (key, service2) => makeUnsafe(/* @__PURE__ */ new Map([[key.key, service2]]));
1717
1657
  var add = /* @__PURE__ */ dual(3, (self, key, service2) => {
@@ -1959,7 +1899,7 @@ var minutes = (minutes2) => make6(minutes2 * 6e4);
1959
1899
  var hours = (hours2) => make6(hours2 * 36e5);
1960
1900
  var days = (days2) => make6(days2 * 864e5);
1961
1901
  var weeks = (weeks2) => make6(weeks2 * 6048e5);
1962
- var toMillis = (self) => match3(fromInputUnsafe(self), {
1902
+ var toMillis = (self) => match2(fromInputUnsafe(self), {
1963
1903
  onMillis: identity,
1964
1904
  onNanos: (nanos2) => Number(nanos2) / 1e6,
1965
1905
  onInfinity: () => Infinity,
@@ -1977,7 +1917,7 @@ var toNanosUnsafe = (input) => {
1977
1917
  return roundMillisToNanos(self.value.millis);
1978
1918
  }
1979
1919
  };
1980
- var match3 = /* @__PURE__ */ dual(2, (self, options) => {
1920
+ var match2 = /* @__PURE__ */ dual(2, (self, options) => {
1981
1921
  switch (self.value._tag) {
1982
1922
  case "Millis":
1983
1923
  return options.onMillis(self.value.millis);
@@ -2004,6 +1944,66 @@ var Equivalence = (self, that) => matchPair(self, that, {
2004
1944
  });
2005
1945
  var equals2 = /* @__PURE__ */ dual(2, (self, that) => Equivalence(self, that));
2006
1946
 
1947
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/internal/array.js
1948
+ var isArrayNonEmpty = (self) => self.length > 0;
1949
+
1950
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Result.js
1951
+ var succeed2 = succeed;
1952
+ var fail2 = fail;
1953
+ var isFailure2 = isFailure;
1954
+ var match3 = /* @__PURE__ */ dual(2, (self, {
1955
+ onFailure,
1956
+ onSuccess
1957
+ }) => isFailure2(self) ? onFailure(self.failure) : onSuccess(self.success));
1958
+
1959
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Array.js
1960
+ var Array2 = globalThis.Array;
1961
+ var fromIterable2 = (collection) => Array2.isArray(collection) ? collection : Array2.from(collection);
1962
+ var append = /* @__PURE__ */ dual(2, (self, last) => [...self, last]);
1963
+ var appendAll = /* @__PURE__ */ dual(2, (self, that) => fromIterable2(self).concat(fromIterable2(that)));
1964
+ var isArray = Array2.isArray;
1965
+ var isArrayNonEmpty2 = isArrayNonEmpty;
1966
+ var isReadonlyArrayNonEmpty = isArrayNonEmpty;
1967
+ var hashBucketsAdd = (buckets, value) => {
1968
+ const hash2 = hash(value);
1969
+ const bucket = buckets.get(hash2);
1970
+ if (bucket === void 0) {
1971
+ buckets.set(hash2, [value]);
1972
+ return true;
1973
+ }
1974
+ for (const previous of bucket) {
1975
+ if (equals(previous, value)) {
1976
+ return false;
1977
+ }
1978
+ }
1979
+ bucket.push(value);
1980
+ return true;
1981
+ };
1982
+ var union = /* @__PURE__ */ dual(2, (self, that) => {
1983
+ const a = fromIterable2(self);
1984
+ const b = fromIterable2(that);
1985
+ if (isReadonlyArrayNonEmpty(a)) {
1986
+ return isReadonlyArrayNonEmpty(b) ? dedupe(appendAll(a, b)) : a;
1987
+ }
1988
+ return b;
1989
+ });
1990
+ var empty2 = () => [];
1991
+ var map2 = /* @__PURE__ */ dual(2, (self, f) => self.map(f));
1992
+ var dedupe = (self) => {
1993
+ const input = fromIterable2(self);
1994
+ if (input.length < 2) {
1995
+ return [...input];
1996
+ }
1997
+ const buckets = /* @__PURE__ */ new Map();
1998
+ const out = [];
1999
+ for (const value of input) {
2000
+ if (hashBucketsAdd(buckets, value)) {
2001
+ out.push(value);
2002
+ }
2003
+ }
2004
+ return out;
2005
+ };
2006
+
2007
2007
  // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Scheduler.js
2008
2008
  var Scheduler = /* @__PURE__ */ Reference("effect/Scheduler", {
2009
2009
  fiberCached: true,
@@ -2354,7 +2354,7 @@ var FiberImpl = class {
2354
2354
  }
2355
2355
  this._stack.length = 0;
2356
2356
  this._children = void 0;
2357
- this.context = empty2();
2357
+ this.context = empty();
2358
2358
  }
2359
2359
  runLoop(effect) {
2360
2360
  const prevFiber = globalThis[currentFiberTypeId];
@@ -2493,7 +2493,7 @@ var fiberAwaitAll = (self) => callback((resume) => {
2493
2493
  });
2494
2494
  var fiberInterruptAll = (fibers) => withFiber((parent) => {
2495
2495
  const annotations = fiberStackAnnotations(parent);
2496
- let fiberArr = empty();
2496
+ let fiberArr = empty2();
2497
2497
  for (const fiber2 of fibers) {
2498
2498
  fiber2.interruptUnsafe(parent.id, annotations);
2499
2499
  fiberArr.push(fiber2);
@@ -2517,7 +2517,7 @@ var suspend = /* @__PURE__ */ makePrimitive({
2517
2517
  return this[args]();
2518
2518
  }
2519
2519
  });
2520
- var fromResult = /* @__PURE__ */ match2({
2520
+ var fromResult = /* @__PURE__ */ match3({
2521
2521
  onFailure: fail3,
2522
2522
  onSuccess: succeed3
2523
2523
  });
@@ -3013,7 +3013,7 @@ var runPromiseWith = (context2) => {
3013
3013
  return exit3.value;
3014
3014
  });
3015
3015
  };
3016
- var runPromise = /* @__PURE__ */ runPromiseWith(/* @__PURE__ */ empty2());
3016
+ var runPromise = /* @__PURE__ */ runPromiseWith(/* @__PURE__ */ empty());
3017
3017
  var runSyncExitWith = (context2) => {
3018
3018
  const runFork3 = runForkWith(context2);
3019
3019
  return (effect) => {
@@ -3026,7 +3026,7 @@ var runSyncExitWith = (context2) => {
3026
3026
  return fiber2._exit ?? exitDie(new AsyncFiberError(fiber2));
3027
3027
  };
3028
3028
  };
3029
- var runSyncExit = /* @__PURE__ */ runSyncExitWith(/* @__PURE__ */ empty2());
3029
+ var runSyncExit = /* @__PURE__ */ runSyncExitWith(/* @__PURE__ */ empty());
3030
3030
  var ClockRef = /* @__PURE__ */ Reference("effect/Clock", {
3031
3031
  defaultValue: () => new ClockImpl()
3032
3032
  });
@@ -3175,13 +3175,6 @@ var logLevelColors = {
3175
3175
  Fatal: [colors.bgBrightRed, colors.black]
3176
3176
  };
3177
3177
 
3178
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Cause.js
3179
- var isFailReason2 = isFailReason;
3180
- var map4 = causeMap;
3181
-
3182
- // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Data.js
3183
- var TaggedError2 = TaggedError;
3184
-
3185
3178
  // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Exit.js
3186
3179
  var succeed4 = exitSucceed;
3187
3180
  var failCause2 = exitFailCause;
@@ -3189,6 +3182,13 @@ var fail4 = exitFail;
3189
3182
  var void_3 = exitVoid;
3190
3183
  var isSuccess3 = exitIsSuccess;
3191
3184
 
3185
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Cause.js
3186
+ var isFailReason2 = isFailReason;
3187
+ var map4 = causeMap;
3188
+
3189
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Data.js
3190
+ var TaggedError2 = TaggedError;
3191
+
3192
3192
  // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Clock.js
3193
3193
  var currentTimeMillis2 = currentTimeMillis;
3194
3194
 
@@ -4501,7 +4501,7 @@ var Objects = class _Objects extends Base2 {
4501
4501
  }
4502
4502
  }
4503
4503
  } else if (parseIndexes) {
4504
- const keyPairs = empty();
4504
+ const keyPairs = empty2();
4505
4505
  for (let i = 0; i < indexCount; i++) {
4506
4506
  const index = indexes[i];
4507
4507
  const keys = getIndexSignatureKeys(record2, index.is.parameter, options);
@@ -6908,7 +6908,7 @@ var File = /* @__PURE__ */ instanceOf(globalThis.File, {
6908
6908
  name: String4,
6909
6909
  lastModified: Int
6910
6910
  }), transformOrFail2({
6911
- decode: (e) => match2(decodeBase64(e.data), {
6911
+ decode: (e) => match3(decodeBase64(e.data), {
6912
6912
  onFailure: () => fail5(new InvalidValue({
6913
6913
  message: "Expected a valid Base64 string"
6914
6914
  })),
@@ -7538,19 +7538,11 @@ var WorkIdentity = Struct({
7538
7538
  principalId: optionalKey2(String4),
7539
7539
  threadId: optionalKey2(String4),
7540
7540
  pluginId: optionalKey2(String4),
7541
- /** 指派给项目成员时的成员号(引擎成员号);名字见快照的 `memberNames`。 */
7541
+ /** 指派给项目成员时的成员号(引擎成员号)。 */
7542
7542
  memberId: optionalKey2(String4),
7543
7543
  /** 人的稳定键(见 personKey),界面拿它到快照的人名表里找名字。 */
7544
7544
  personKey: optionalKey2(String4)
7545
7545
  });
7546
- var workIdentity = (value) => ({
7547
- kind: value.kind,
7548
- ...value.principalId === void 0 ? {} : { principalId: value.principalId },
7549
- ...value.threadId === void 0 ? {} : { threadId: value.threadId },
7550
- ...value.memberId === void 0 ? {} : { memberId: value.memberId },
7551
- ...value.pluginId === void 0 ? {} : { pluginId: value.pluginId },
7552
- ...value.principal === void 0 ? {} : { personKey: personKey(value.principal) }
7553
- });
7554
7546
  var BoardPerson = Struct({ key: String4, displayName: String4 });
7555
7547
  var BoardMachine = Struct({
7556
7548
  principalId: String4,
@@ -7568,53 +7560,20 @@ var BoardRemoteWork = Struct({
7568
7560
  })
7569
7561
  )
7570
7562
  });
7571
- var MemberName = Struct({
7572
- memberId: String4,
7573
- name: String4
7574
- });
7575
- var AssignableMember = Struct({
7576
- memberId: String4,
7577
- name: String4,
7578
- kind: Literals(["human", "agent"])
7579
- });
7580
7563
  var BoardItem = Struct({
7581
7564
  workItemId: String4,
7582
7565
  sequenceId: Number5,
7583
- title: NullOr(String4),
7584
- state: Struct({ stateId: String4, name: String4 }),
7585
- assignees: ArraySchema(WorkIdentity),
7586
- blockedByTotal: Number5,
7587
- version: Number5,
7588
- /** 件所在的线;没分线(旧项目级目标的件、手动建的件)是 null。 */
7589
- workLineId: NullOr(String4),
7590
- assigneeSession: optionalKey2(
7591
- Struct({
7592
- threadId: String4,
7593
- status: Literals(["running", "failed", "waiting", "stopped"]),
7594
- stoppedAt: optionalKey2(String4)
7595
- })
7596
- ),
7597
- queued: optionalKey2(Boolean3)
7566
+ title: NullOr(String4)
7598
7567
  });
7599
7568
  var BoardGoalItem = Struct({
7600
7569
  workItemId: String4,
7601
7570
  sequenceId: Number5,
7602
7571
  state: Struct({ stateId: String4, name: String4, group: String4 })
7603
7572
  });
7604
- var BoardLane = Struct({
7605
- workLineId: NullOr(String4),
7606
- name: NullOr(String4),
7607
- archived: Boolean3,
7608
- /** 这条线的目标一句话;没分线的一道取旧项目级目标。 */
7609
- goal: optionalKey2(String4),
7610
- /** 线的群会话;界面据此给「打开团队」。 */
7611
- threadId: optionalKey2(String4)
7612
- });
7613
7573
  var BoardSnapshot = Struct({
7614
7574
  states: ArraySchema(BoardState),
7575
+ // 远端成员待处理摘要里的件(按号读到的那些)。
7615
7576
  items: ArraySchema(BoardItem),
7616
- // 只在项目层(读取没给 workLineId)出现;团队层的件都在同一条线上。
7617
- lanes: optionalKey2(ArraySchema(BoardLane)),
7618
7577
  // 当前目标与 goal 命令同一份形状;目标栏与看板都从这里读,同一个门铃刷新。
7619
7578
  goal: ReadGoalResult,
7620
7579
  goalItem: optionalKey2(BoardGoalItem),
@@ -7623,18 +7582,12 @@ var BoardSnapshot = Struct({
7623
7582
  rosterMembers: ArraySchema(TeamMember),
7624
7583
  // 导入旧名单时记下的「旧成员号 → 引擎成员号」;目标和手册里记的还是旧号,名字靠它认回来。
7625
7584
  rosterAliases: optionalKey2(Record(String4, String4)),
7626
- // 看板上被指派的项目成员(含人和已移除的)的名字;没有成员指派时省略,读不出来也省略,界面退回通用名。
7627
- memberNames: optionalKey2(ArraySchema(MemberName)),
7628
- // 指派菜单的选项:项目里还在队的成员(人和 agent)。引擎里的成员读不出来时省略并标 membersUnavailable。
7629
- assignableMembers: optionalKey2(ArraySchema(AssignableMember)),
7630
- membersUnavailable: optionalKey2(Literal2(true)),
7631
7585
  machines: ArraySchema(BoardMachine),
7632
7586
  machinesUnavailable: optionalKey2(Literal2(true)),
7633
7587
  remoteWork: ArraySchema(BoardRemoteWork),
7634
7588
  viewerKey: optionalKey2(String4),
7635
7589
  // 负责人要用看板与项目记忆两组工具;开关由宿主控件拨,这里只读来提示。
7636
- toolkits: Struct({ board: Boolean3, memory: Boolean3 }),
7637
- truncated: Boolean3
7590
+ toolkits: Struct({ board: Boolean3, memory: Boolean3 })
7638
7591
  });
7639
7592
  var ReadBoardInput = Struct({
7640
7593
  projectId: String4,
@@ -7660,7 +7613,7 @@ var WORK_EVENTS = /* @__PURE__ */ new Set([
7660
7613
  "work.relation-removed",
7661
7614
  "work.comment-posted",
7662
7615
  "work.states-seeded",
7663
- // 线建、改名、归档、恢复:项目层看板的泳道随之变。
7616
+ // 线建、改名、归档、恢复:团队层快照要核线还在。
7664
7617
  "work.line-created",
7665
7618
  "work.line-updated",
7666
7619
  "work.line-archived",
@@ -7684,7 +7637,7 @@ var kindsOf = (type) => [
7684
7637
  ...HANDBOOK_EVENTS.has(type) ? ["handbook"] : [],
7685
7638
  ...MEMBER_EVENTS.has(type) ? ["members"] : []
7686
7639
  ];
7687
- function registerTeamWatch(ctx, hasMember, options) {
7640
+ function registerTeamWatch(ctx, options) {
7688
7641
  const after = options.after ?? ((delay2, notify) => {
7689
7642
  const timer = NodeTimers.setTimeout(notify, delay2);
7690
7643
  return () => NodeTimers.clearTimeout(timer);
@@ -7693,7 +7646,6 @@ function registerTeamWatch(ctx, hasMember, options) {
7693
7646
  const cursors = /* @__PURE__ */ new Map();
7694
7647
  const timers = /* @__PURE__ */ new Map();
7695
7648
  const pending = /* @__PURE__ */ new Map();
7696
- const subscriptions = /* @__PURE__ */ new Map();
7697
7649
  let disposed = false;
7698
7650
  const invalidate = (projectId, kinds, sequence = 0) => {
7699
7651
  if (projectId === null || disposed || !listeners.has(projectId)) return;
@@ -7724,59 +7676,13 @@ function registerTeamWatch(ctx, hasMember, options) {
7724
7676
  const stops = [.../* @__PURE__ */ new Set([...WORK_EVENTS, ...HANDBOOK_EVENTS, ...MEMBER_EVENTS])].map(
7725
7677
  (type) => ctx.events.onRaw(type, (event) => changed(event, kindsOf(type)))
7726
7678
  );
7727
- const subscribe = (projectId) => {
7728
- if (subscriptions.has(projectId)) return;
7729
- const controller = new AbortController();
7730
- const { signal } = controller;
7731
- const known = /* @__PURE__ */ new Set();
7732
- const active = () => !disposed && !signal.aborted && listeners.has(projectId);
7733
- const done2 = (async () => {
7734
- while (active()) {
7735
- try {
7736
- await ctx.threads.subscribeList(
7737
- { projectId },
7738
- async (frame) => {
7739
- if (!active() || frame._tag === "synchronized") return;
7740
- const ids = frame._tag === "snapshot" ? [.../* @__PURE__ */ new Set([...known, ...frame.value.items.map((row) => row.threadId)])] : frame._tag === "refresh" ? frame.threads.map((row) => row.threadId) : [
7741
- frame.change._tag === "removed" ? frame.change.threadId : frame.change.thread.threadId
7742
- ];
7743
- if (frame._tag === "snapshot") {
7744
- known.clear();
7745
- for (const row of frame.value.items) known.add(row.threadId);
7746
- } else if (frame._tag === "delta") {
7747
- if (frame.change._tag === "removed") known.delete(frame.change.threadId);
7748
- else known.add(frame.change.thread.threadId);
7749
- }
7750
- if (ids.length === 0) return;
7751
- const matches = await hasMember(projectId, ids).catch(() => false);
7752
- if (matches && active()) invalidate(projectId, ["board"]);
7753
- },
7754
- signal
7755
- );
7756
- } catch {
7757
- }
7758
- if (!active()) return;
7759
- await new Promise((resolve2) => {
7760
- const finish = () => {
7761
- cancel();
7762
- signal.removeEventListener("abort", finish);
7763
- resolve2();
7764
- };
7765
- const cancel = after(1e3, finish);
7766
- signal.addEventListener("abort", finish, { once: true });
7767
- if (signal.aborted) finish();
7768
- });
7769
- }
7770
- })();
7771
- subscriptions.set(projectId, { controller, done: done2 });
7772
- };
7773
7679
  stops.push(
7774
7680
  ctx.events.onRaw("plugin.event-appended", (event) => {
7775
7681
  const fact = decodeFact(event.payload);
7776
7682
  if (isNone2(fact)) return;
7777
7683
  const name = fact.value.name;
7778
7684
  const kinds = [];
7779
- if (name === "team/goal-started" || name === "team/roster-set" || name === "team/member-woken" || name === "team/member-bound")
7685
+ if (name === "team/goal-started" || name === "team/roster-set" || name === "team/member-bound")
7780
7686
  kinds.push("board");
7781
7687
  if (name === "team/goal-started" || name === "team/member-bound" || name === "team/handbook-acknowledged" || name === "team/handbook-publication-completed")
7782
7688
  kinds.push("handbook");
@@ -7799,7 +7705,6 @@ function registerTeamWatch(ctx, hasMember, options) {
7799
7705
  };
7800
7706
  listeners.set(projectId, subscribers);
7801
7707
  subscribers.add(ring);
7802
- subscribe(projectId);
7803
7708
  try {
7804
7709
  ring(cursors.get(projectId) ?? emptyChanges());
7805
7710
  await new Promise((resolve2) => {
@@ -7810,30 +7715,22 @@ function registerTeamWatch(ctx, hasMember, options) {
7810
7715
  subscribers.delete(ring);
7811
7716
  if (subscribers.size === 0) {
7812
7717
  listeners.delete(projectId);
7813
- const subscription = subscriptions.get(projectId);
7814
- subscriptions.delete(projectId);
7815
- subscription?.controller.abort();
7816
7718
  timers.get(projectId)?.();
7817
7719
  timers.delete(projectId);
7818
7720
  pending.delete(projectId);
7819
7721
  cursors.delete(projectId);
7820
- await subscription?.done;
7821
7722
  }
7822
7723
  }
7823
7724
  }
7824
7725
  });
7825
- ctx.onDispose(async () => {
7726
+ ctx.onDispose(() => {
7826
7727
  disposed = true;
7827
- const owned = [...subscriptions.values()];
7828
- for (const subscription of owned) subscription.controller.abort();
7829
7728
  for (const stop of stops) stop();
7830
7729
  for (const cancel of timers.values()) cancel();
7831
7730
  timers.clear();
7832
7731
  listeners.clear();
7833
7732
  cursors.clear();
7834
7733
  pending.clear();
7835
- subscriptions.clear();
7836
- await Promise.all(owned.map((subscription) => subscription.done));
7837
7734
  });
7838
7735
  }
7839
7736
 
@@ -7848,6 +7745,9 @@ async function readMachines(ctx, projectId) {
7848
7745
  }
7849
7746
  var memberEnvironment = (member) => member.agent?.environmentId ?? null;
7850
7747
  var isRemoteMember = (member, localEnvironmentId) => member.kind === "agent" && memberEnvironment(member) !== localEnvironmentId;
7748
+ var readyProviders = (machine) => machine.providers.filter(
7749
+ (provider) => provider.readiness === void 0 || provider.readiness === "ready"
7750
+ );
7851
7751
 
7852
7752
  // src/workReads.ts
7853
7753
  var PAGE_SIZE = 200;
@@ -7873,45 +7773,160 @@ async function readPages(maximum, read, keep = "oldest") {
7873
7773
  return { items, truncated };
7874
7774
  }
7875
7775
 
7876
- // src/boardLanes.ts
7877
- function boardLanes(lines, goalOf, hasUnlinedItems) {
7878
- const laneOf = (line) => {
7879
- const goal = goalOf(line.workLineId);
7776
+ // src/domain/goalItem.ts
7777
+ var teamGoalItemId = (projectId, goalId) => `team-goal:${projectId}:${goalId}`;
7778
+ var GOAL_ITEM_TITLE_MAX = 120;
7779
+ var goalItemTitle = (goal) => {
7780
+ const line = (goal.titleSeed ?? goal.goal).split(/\r?\n/u, 1)[0].trim();
7781
+ return line.length <= GOAL_ITEM_TITLE_MAX ? line : `${line.slice(0, GOAL_ITEM_TITLE_MAX - 1)}\u2026`;
7782
+ };
7783
+
7784
+ // src/board.ts
7785
+ var PEOPLE_LIMIT = 200;
7786
+ var ITEMS_GET_MAX = 50;
7787
+ var decodeRead2 = decodeUnknownSync(ReadBoardInput);
7788
+ function registerBoardCommands(ctx, goals, rosters, options = {}) {
7789
+ const readTeam = async (projectId, workLineId) => {
7790
+ const view = await rosters.activeView(projectId).catch(() => null);
7791
+ const roster = view?.team ?? null;
7792
+ const members = view?.active ?? null;
7793
+ const read = await goals.read(projectId, {
7794
+ roster,
7795
+ ...members === null ? {} : { known: members },
7796
+ ...workLineId === void 0 ? {} : { forLine: workLineId }
7797
+ });
7880
7798
  return {
7881
- workLineId: line.workLineId,
7882
- name: line.name,
7883
- archived: line.archivedAt !== null,
7884
- ...goal === void 0 ? {} : { goal },
7885
- ...line.threadId === null ? {} : { threadId: line.threadId }
7799
+ roster,
7800
+ // 线上没有目标时 read 会退回项目级目标;团队层只认这条线自己的。
7801
+ goal: read !== null && workLineId !== void 0 && read.workLineId !== workLineId ? null : read
7886
7802
  };
7887
7803
  };
7888
- const unlinedGoal = goalOf(null);
7889
- return [
7890
- ...lines.filter((line) => line.archivedAt === null).map(laneOf),
7891
- ...hasUnlinedItems || unlinedGoal !== void 0 ? [
7892
- {
7893
- workLineId: null,
7894
- name: null,
7895
- archived: false,
7896
- ...unlinedGoal === void 0 ? {} : { goal: unlinedGoal }
7897
- }
7898
- ] : [],
7899
- ...lines.filter((line) => line.archivedAt !== null).map(laneOf)
7900
- ];
7804
+ ctx.command({
7805
+ name: "board",
7806
+ scope: "read",
7807
+ payloadSchema: payloadSchemaOf(ReadBoardInput),
7808
+ handle: async (payload, invocation) => {
7809
+ const { projectId, workLineId } = decodeRead2(payload);
7810
+ const [stateList, { goal, roster }, toolkitStates, people, machines, viewer] = await Promise.all([
7811
+ ctx.work.states.list({ projectId }),
7812
+ readTeam(projectId, workLineId),
7813
+ ctx.projects.toolkits.get({ projectId }),
7814
+ // 人名只用来显示作者;本机所有者排第一,后面是这个项目认识的账号。
7815
+ readPages(PEOPLE_LIMIT, (page) => ctx.communication.people.list({ projectId, ...page })),
7816
+ // 项目里的别的机器:远端作者的名字与在线状态;读不出来退回空,不连累快照。
7817
+ readMachines(ctx, projectId),
7818
+ // 认出当前读者只为把作者显示成「你」:读不出来就当没有。
7819
+ invocation.viewer?.communication.people.current({}).catch(() => null),
7820
+ // 团队层核这条线还在(读不到照抛,界面显示读取失败)。
7821
+ workLineId === void 0 ? void 0 : ctx.work.lines.get({ projectId, workLineId })
7822
+ ]);
7823
+ const goalItemId = goal === null ? null : teamGoalItemId(projectId, goal.goalId);
7824
+ const [remoteWork, goalItem] = await Promise.all([
7825
+ Promise.all(
7826
+ (roster?.members ?? []).flatMap(
7827
+ (member) => member.principal === void 0 ? [] : [
7828
+ ctx.work.remoteAgentPending.get({ projectId, principalId: member.principal.principalId }).then(
7829
+ // 一台机器上可能有好几个成员:只算派给这个成员的(与直接派给那台机器的)。
7830
+ ({ work }) => {
7831
+ const items2 = work.items.filter(
7832
+ (item) => item.memberId === void 0 || item.memberId === member.memberId
7833
+ ).map(({ workItemId, blocked }) => ({ workItemId, blocked }));
7834
+ return { count: items2.length, items: items2 };
7835
+ },
7836
+ () => null
7837
+ ).then((pending) => ({ memberId: member.memberId, pending }))
7838
+ ]
7839
+ )
7840
+ ),
7841
+ // 目标那件按号单读。
7842
+ goalItemId === null ? void 0 : ctx.work.items.get({ projectId, workItemIds: [goalItemId] }).then(({ items: items2 }) => items2.find((item) => item.workItemId === goalItemId))
7843
+ ]);
7844
+ const pendingIds = [
7845
+ ...new Set(
7846
+ remoteWork.flatMap(({ pending }) => pending?.items.map((row) => row.workItemId) ?? [])
7847
+ )
7848
+ ];
7849
+ const items = (await Promise.all(
7850
+ Array.from(
7851
+ { length: Math.ceil(pendingIds.length / ITEMS_GET_MAX) },
7852
+ (_, index) => ctx.work.items.get({
7853
+ projectId,
7854
+ workItemIds: pendingIds.slice(index * ITEMS_GET_MAX, (index + 1) * ITEMS_GET_MAX)
7855
+ }).then(
7856
+ ({ items: items2 }) => items2,
7857
+ () => []
7858
+ )
7859
+ )
7860
+ )).flat().map(({ workItemId, sequenceId, title }) => ({ workItemId, sequenceId, title }));
7861
+ const enabled = (toolkit) => toolkitStates.toolkits.some((row) => row.toolkit === toolkit && row.enabled);
7862
+ return {
7863
+ states: stateList.states.map(({ stateId, name, sequence, defaultKey }) => ({
7864
+ stateId,
7865
+ name,
7866
+ sequence,
7867
+ ...defaultKey === void 0 ? {} : { defaultKey }
7868
+ })).sort((a, b) => a.sequence - b.sequence),
7869
+ items,
7870
+ goal,
7871
+ ...goalItem === void 0 ? {} : {
7872
+ goalItem: {
7873
+ workItemId: goalItem.workItemId,
7874
+ sequenceId: goalItem.sequenceId,
7875
+ state: {
7876
+ stateId: goalItem.state.stateId,
7877
+ name: goalItem.state.name,
7878
+ group: goalItem.state.group
7879
+ }
7880
+ }
7881
+ },
7882
+ people: people.items.map((row) => ({
7883
+ key: personKey(row.person),
7884
+ displayName: row.displayName
7885
+ })),
7886
+ rosterMembers: roster?.members ?? [],
7887
+ ...roster?.aliases === void 0 ? {} : { rosterAliases: roster.aliases },
7888
+ ...machines === null ? { machinesUnavailable: true } : {},
7889
+ machines: [...machines?.values() ?? []].map(
7890
+ ({ principalId, environmentId, name, online, lastSeenAt }) => ({
7891
+ principalId,
7892
+ environmentId,
7893
+ name,
7894
+ online,
7895
+ ...lastSeenAt === void 0 ? {} : { lastSeenAt }
7896
+ })
7897
+ ),
7898
+ remoteWork,
7899
+ ...viewer?.person == null ? {} : { viewerKey: personKey(viewer.person) },
7900
+ toolkits: { board: enabled("board"), memory: enabled("memory") }
7901
+ };
7902
+ }
7903
+ });
7904
+ registerTeamWatch(ctx, options);
7901
7905
  }
7902
7906
 
7903
- // src/domain/execution.ts
7904
- function goalExecution(goal) {
7907
+ // src/runtime/serial.ts
7908
+ function createSerial(closedMessage = "team-serial-closed") {
7909
+ let pending = Promise.resolve();
7910
+ let closed = false;
7905
7911
  return {
7906
- leadMemberId: goal?.leadMemberId ?? null,
7907
- members: goal === null ? [] : [...goal.threads, ...goal.remoteMembers ?? []]
7912
+ run(execute) {
7913
+ const result2 = pending.then(() => {
7914
+ if (closed) throw new Error(closedMessage);
7915
+ return execute();
7916
+ });
7917
+ pending = result2.then(
7918
+ () => void 0,
7919
+ () => void 0
7920
+ );
7921
+ return result2;
7922
+ },
7923
+ /** 不再开始新任务,并等已经在跑和排在前面的任务落定。 */
7924
+ async close() {
7925
+ closed = true;
7926
+ await pending;
7927
+ }
7908
7928
  };
7909
7929
  }
7910
- function findGoalMember(members, identity2) {
7911
- return members.find(
7912
- (member) => "threadId" in identity2 ? "threadId" in member && member.threadId === identity2.threadId : "principalId" in member && member.principalId === identity2.principalId
7913
- );
7914
- }
7915
7930
 
7916
7931
  // src/domain/facts.ts
7917
7932
  function foldFact(state, event, decode4, keyOf) {
@@ -7933,786 +7948,171 @@ var factEnvelope = (name, data) => Struct({ pluginId: Literal2("team"), name: Li
7933
7948
  var nowIso = map5(now2, formatIso2);
7934
7949
  var factTime = () => runPromise2(nowIso);
7935
7950
 
7936
- // src/domain/recipient.ts
7937
- var recipientOf = (principal) => principal.kind === "agent" && principal.threadId !== void 0 ? { threadId: principal.threadId } : principal.kind === "remote-agent" && principal.principalId !== void 0 ? { principalId: principal.principalId } : void 0;
7938
- var recipientKey = (who) => "threadId" in who ? `thread:${who.threadId}` : `principal:${who.principalId}`;
7939
-
7940
- // src/domain/binding.ts
7941
- var MemberBoundFact = Struct({
7942
- version: Literal2(1),
7943
- projectId: String4,
7944
- memberId: String4,
7945
- specKey: String4,
7946
- goalId: String4,
7947
- threadId: optionalKey2(String4),
7948
- principalId: optionalKey2(String4),
7949
- replaces: optionalKey2(String4)
7950
- }).check(
7951
- makeFilter2((fact) => fact.threadId === void 0 !== (fact.principalId === void 0))
7952
- );
7953
- var MemberBoundEnvelope = factEnvelope("member-bound", MemberBoundFact);
7954
- var bindingOfFact = (fact) => fact.threadId !== void 0 ? { threadId: fact.threadId } : { principalId: fact.principalId };
7955
- function bindingsOf(goal) {
7956
- const bindings = {};
7957
- for (const member of goalExecution(goal).members)
7958
- bindings[member.memberId] = "threadId" in member ? { threadId: member.threadId } : { principalId: member.principalId };
7959
- return bindings;
7960
- }
7961
- function withBoundBinding(bindings, fact) {
7962
- const next = { ...bindings };
7963
- if (fact.replaces !== void 0) delete next[fact.replaces];
7964
- next[fact.memberId] = bindingOfFact(fact);
7965
- return next;
7966
- }
7967
- var leadAfterBinding = (leadMemberId, fact) => fact.replaces !== void 0 && leadMemberId === fact.replaces ? fact.memberId : leadMemberId;
7968
- function withBoundMember(goal, fact) {
7969
- if (fact.projectId !== goal.projectId || fact.goalId !== goal.goalId) return goal;
7970
- const { remoteMembers: _before, ...rest } = goal;
7971
- const superseded = (memberId) => memberId === fact.memberId || memberId === fact.replaces;
7972
- const threads = goal.threads.filter((row) => !superseded(row.memberId));
7973
- const remoteMembers = (goal.remoteMembers ?? []).filter((row) => !superseded(row.memberId));
7974
- const binding = bindingOfFact(fact);
7975
- const next = "threadId" in binding ? {
7976
- threads: [...threads, { memberId: fact.memberId, threadId: binding.threadId }],
7977
- remoteMembers
7978
- } : {
7979
- threads,
7980
- remoteMembers: [
7981
- ...remoteMembers,
7982
- { memberId: fact.memberId, principalId: binding.principalId }
7983
- ]
7984
- };
7985
- return {
7986
- ...rest,
7987
- leadMemberId: leadAfterBinding(goal.leadMemberId, fact),
7988
- threads: next.threads,
7989
- ...next.remoteMembers.length === 0 ? {} : { remoteMembers: next.remoteMembers }
7990
- };
7951
+ // src/domain/service.ts
7952
+ var HISTORY_LIMIT = 500;
7953
+ var ACTIVE_LIMIT = 2e3;
7954
+ var MEMBER_NAMES_LIMIT = 50;
7955
+ var FactEnvelope = Struct({
7956
+ pluginId: Literal2("team"),
7957
+ name: Literal2("team/roster-set"),
7958
+ data: RosterSetFact
7959
+ });
7960
+ var decodeFact2 = decodeUnknownOption2(FactEnvelope);
7961
+ var decodeSetLead = decodeUnknownOption2(SetLeadInput);
7962
+ var decodeRosterFact = (payload) => map(decodeFact2(payload), (fact) => fact.data);
7963
+ var factProject = (fact) => fact.roster.projectId;
7964
+ function foldRoster(state, event) {
7965
+ return foldFact(state, event, decodeRosterFact, factProject);
7991
7966
  }
7992
- function targetOf(bindings, assignee, pinned) {
7993
- if (assignee.kind !== "member") return recipientOf(assignee);
7994
- const { memberId } = assignee;
7995
- if (memberId === void 0) return void 0;
7996
- const fixed = pinned !== void 0 && Object.hasOwn(pinned, memberId) ? pinned[memberId] : void 0;
7997
- if (fixed !== void 0) return { threadId: fixed };
7998
- return bindings !== void 0 && Object.hasOwn(bindings, memberId) ? bindings[memberId] : void 0;
7967
+ async function listMembers(ctx, projectId) {
7968
+ const { items, truncated } = await readPages(
7969
+ ACTIVE_LIMIT,
7970
+ (page) => ctx.members.list({ projectId, ...page })
7971
+ );
7972
+ if (truncated) throw new Error("team-members-truncated");
7973
+ return items;
7999
7974
  }
8000
- function assignedToGoalMember(goal, assignees) {
8001
- const execution = goalExecution(goal);
8002
- const members = execution.members.filter((member) => member.memberId !== execution.leadMemberId);
8003
- const bindings = bindingsOf(goal);
8004
- return assignees.some((assignee) => {
8005
- const who = targetOf(bindings, assignee);
8006
- return who !== void 0 && findGoalMember(members, who) !== void 0;
8007
- });
7975
+ async function listMemberHistory(ctx, projectId) {
7976
+ const { items } = await readPages(
7977
+ HISTORY_LIMIT,
7978
+ (page) => ctx.members.list({ projectId, includeRemoved: true, ...page }),
7979
+ "newest"
7980
+ );
7981
+ return items;
8008
7982
  }
8009
-
8010
- // src/runtime/goalScopes.ts
8011
- var isLinePaused = (project2, workLineId) => project2.linePauses?.[workLineId] === true;
8012
- var isMemberPausedOnLine = (project2, workLineId, memberId) => (project2.pausedMembers?.[workLineId] ?? []).includes(memberId);
8013
- var isLineStopped = (project2, workLineId) => workLineId !== void 0 && project2.lineStops?.[workLineId] !== void 0;
8014
- var isLineGated = (project2, workLineId) => isLineStopped(project2, workLineId) || workLineId !== void 0 && isLinePaused(project2, workLineId);
8015
- var scopesOf = (project2) => [
8016
- project2,
8017
- ...Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([, scope2]) => scope2)
8018
- ];
8019
- var scopeForLine = (project2, workLineId) => (workLineId === void 0 ? void 0 : project2.lineScopes?.[workLineId]) ?? project2;
8020
- var scopeOfThread = (project2, threadId) => scopesOf(project2).find((scope2) => scope2.threads.includes(threadId));
8021
- var teamSessions = (scope2) => [
8022
- .../* @__PURE__ */ new Set([...scope2.threads, ...Object.keys(scope2.memberIds ?? {})])
8023
- ];
8024
- var lineScopeOfThread = (project2, threadId) => {
8025
- for (const [workLineId, scope2] of Object.entries(project2.lineScopes ?? {}))
8026
- if (teamSessions(scope2).includes(threadId)) return { workLineId, scope: scope2 };
8027
- return void 0;
8028
- };
8029
- var scopeOfPrincipal = (project2, principalId) => scopesOf(project2).find((scope2) => scope2.remoteMembers.includes(principalId));
8030
- var isGoalThread = (project2, threadId) => scopeOfThread(project2, threadId) !== void 0;
8031
- var isLeadThread = (project2, threadId) => scopesOf(project2).some((scope2) => scope2.leadThreads?.includes(threadId) === true);
8032
- var isRemoteMember2 = (project2, principalId) => scopeOfPrincipal(project2, principalId) !== void 0;
8033
- var leadOfThread = (project2, threadId) => scopeOfThread(project2, threadId)?.leadThreads?.[0];
8034
- var placeOfRecipient = (project2, who, workLineId) => {
8035
- if ("threadId" in who) {
8036
- const line = lineScopeOfThread(project2, who.threadId);
8037
- if (line !== void 0) return line;
8038
- return project2.threads.includes(who.threadId) ? { scope: project2 } : void 0;
7983
+ async function findMember(ctx, projectId, memberId, window) {
7984
+ try {
7985
+ const { member } = await ctx.members.get({ projectId, memberId, ...window });
7986
+ return member;
7987
+ } catch (error) {
7988
+ const reason = typeof error === "object" && error !== null && "reason" in error ? error.reason : null;
7989
+ if (reason === "member-not-found") return null;
7990
+ throw error;
8039
7991
  }
8040
- const own = workLineId === void 0 ? void 0 : project2.lineScopes?.[workLineId];
8041
- if (workLineId !== void 0 && own?.remoteMembers.includes(who.principalId) === true)
8042
- return { workLineId, scope: own };
8043
- if (project2.remoteMembers.includes(who.principalId)) return { scope: project2 };
8044
- const found = Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).find(([, scope2]) => scope2.remoteMembers.includes(who.principalId));
8045
- return found === void 0 ? void 0 : { workLineId: found[0], scope: found[1] };
8046
- };
8047
- var scopeOfRecipient = (project2, who, workLineId) => placeOfRecipient(project2, who, workLineId)?.scope;
8048
- var memberOfRecipientIn = (scope2, who) => {
8049
- if ("threadId" in who) return scope2.memberIds?.[who.threadId];
8050
- for (const [memberId, recipient] of Object.entries(scope2.bindings ?? {}))
8051
- if ("principalId" in recipient && recipient.principalId === who.principalId) return memberId;
8052
- return void 0;
8053
- };
8054
- var isRecipientGated = (project2, who, workLineId) => {
8055
- const place = placeOfRecipient(project2, who, workLineId);
8056
- if (place === void 0 || place.workLineId === void 0) return false;
8057
- if (isLineGated(project2, place.workLineId)) return true;
8058
- const member = memberOfRecipientIn(place.scope, who);
8059
- return member !== void 0 && isMemberPausedOnLine(project2, place.workLineId, member);
8060
- };
8061
- var isThreadGated = (project2, threadId) => isRecipientGated(project2, { threadId });
8062
- var teamLeadCandidates = (project2, memberId) => Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).flatMap(([workLineId, scope2]) => {
8063
- const leadThreadId = scope2.leadThreads?.[0];
8064
- return scope2.bindings?.[memberId] !== void 0 && leadThreadId !== void 0 && !isLineStopped(project2, workLineId) ? [{ workLineId, leadThreadId }] : [];
8065
- });
8066
-
8067
- // src/runtime/wakeQueries.ts
8068
- function memberWorktreeFor(goals, project2, projectId, recipient) {
8069
- if (!("threadId" in recipient) || project2 === void 0) return;
8070
- const { threadId } = recipient;
8071
- const scope2 = scopeOfThread(project2, threadId);
8072
- if (scope2 === void 0 || scope2.leadThreads?.includes(threadId)) return;
8073
- const leadThreadId = scope2.leadThreads?.[0];
8074
- if (leadThreadId === void 0) return;
7992
+ }
7993
+ var isActiveAgent = (member) => member.kind === "agent" && member.removedAt === null;
7994
+ var toTeamMember = (member, where) => {
7995
+ if (!isRemoteMember(member, where.local)) return { memberId: member.memberId, name: member.name };
7996
+ const machine = member.agent === null ? void 0 : where.machines?.get(member.agent.environmentId);
8075
7997
  return {
8076
- leadThreadId,
8077
- failure: project2.worktreeFailures?.[threadId],
8078
- // 只在要建树或报失败时读目标取名字,叫醒的热路径不多读。
8079
- name: async () => (await goals.read(projectId, { forThread: threadId }))?.members.find(
8080
- (member) => member.threadId === threadId
8081
- )?.name ?? scope2.memberIds?.[threadId] ?? threadId
7998
+ memberId: member.memberId,
7999
+ name: member.name,
8000
+ remote: true,
8001
+ ...machine === void 0 ? {} : { principal: { kind: "remote-agent", principalId: machine.principalId } }
8082
8002
  };
8083
- }
8084
- var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
8085
- var compareWakeItems = (a, b) => a.sequenceId - b.sequenceId || compare(a.workItemId, b.workItemId);
8086
- var OPEN_ITEM_GROUPS = ["backlog", "unstarted", "started"];
8087
- var isClosed = (item) => item?.stateGroup === "completed" || item?.stateGroup === "cancelled";
8088
- var hasOpenBlocker = (project2, workItemId) => (readFact(project2.blockers, workItemId) ?? []).some(
8089
- (blocker) => !isClosed(readFact(project2.items, blocker))
8090
- );
8091
- var isAssignedOpen = (project2, workItemId, threadId) => {
8092
- const item = readFact(project2.items, workItemId);
8093
- return item !== null && item !== void 0 && !isClosed(item) && item.assigneeThreads.includes(threadId);
8094
- };
8095
- var isWorkable = (project2, workItemId) => !isClosed(readFact(project2.items, workItemId)) && !hasOpenBlocker(project2, workItemId);
8096
- var isOpenWorkFor = (project2, workItemId, threadId) => isAssignedOpen(project2, workItemId, threadId) && !hasOpenBlocker(project2, workItemId);
8097
- var isHandedOver = (item, threadId) => {
8098
- if (item.stateGroup === "started") return true;
8099
- const status = threadId === void 0 ? void 0 : item.wakeStatus?.[threadId];
8100
- return status === "active" || status === "released";
8101
- };
8102
- var memberOfThread = (item, threadId) => {
8103
- for (const [memberId, thread] of Object.entries(item.memberThreads ?? {}))
8104
- if (thread === threadId) return memberId;
8105
- return void 0;
8106
- };
8107
- var isPinnedTo = (item, threadId) => !isClosed(item) && memberOfThread(item, threadId) !== void 0 && isHandedOver(item, threadId);
8108
- var hasPinnedOpenItem = (project2, threadId) => Object.values(project2.items).some((item) => isPinnedTo(item, threadId));
8109
- var pinnedThreadsOf = (item) => Object.values(item.memberThreads ?? {}).filter((threadId) => isPinnedTo(item, threadId));
8110
- var pinnedOpenThreads = (project2) => {
8111
- const threads = /* @__PURE__ */ new Set();
8112
- for (const item of Object.values(project2.items))
8113
- for (const threadId of pinnedThreadsOf(item)) threads.add(threadId);
8114
- return threads;
8115
- };
8116
- var memberLookup = (project2) => {
8117
- let pinned;
8118
- return (threadId) => isGoalThread(project2, threadId) || (pinned ??= pinnedOpenThreads(project2)).has(threadId);
8119
8003
  };
8120
- var isMember = (project2, recipient) => "threadId" in recipient ? isGoalThread(project2, recipient.threadId) || hasPinnedOpenItem(project2, recipient.threadId) : isRemoteMember2(project2, recipient.principalId);
8121
- var isQueuedUnclaimed = (item, threadId) => item.wakeStatus?.[threadId] === "pending" && !item.claimedBy.includes(threadId);
8122
- var isDeferredWake = (project2, item, threadId) => isGoalThread(project2, threadId) && !isLeadThread(project2, threadId) && item.assigneeThreads.includes(threadId) && isQueuedUnclaimed(item, threadId) && !isClosed(item);
8123
- function countDeferredWakes(project2, threadId) {
8124
- let count2 = 0;
8125
- for (const workItemId in project2.items) {
8126
- const item = readFact(project2.items, workItemId);
8127
- if (item !== null && isDeferredWake(project2, item, threadId)) count2++;
8128
- }
8129
- return count2;
8130
- }
8131
- function deferredWakes(project2, threadId) {
8132
- return Object.entries(project2.items).filter(([, item]) => isDeferredWake(project2, item, threadId)).map(([workItemId, item]) => ({ workItemId, sequenceId: item.sequenceId })).sort(
8133
- (a, b) => Number(hasOpenBlocker(project2, a.workItemId)) - Number(hasOpenBlocker(project2, b.workItemId)) || compareWakeItems(a, b)
8134
- );
8135
- }
8136
- var exemptFromTrial = (project2, recipient, workItemId) => {
8137
- if (project2 === void 0 || !("threadId" in recipient)) return false;
8138
- if (isLeadThread(project2, recipient.threadId)) return true;
8139
- if (workItemId === void 0) return false;
8140
- const item = readFact(project2.items, workItemId);
8141
- return item !== null && item !== void 0 && pinnedThreadsOf(item).includes(recipient.threadId);
8142
- };
8143
- function memberOfRecipient(project2, recipient) {
8144
- if (project2 === void 0) return void 0;
8145
- const key = recipientKey(recipient);
8146
- for (const scope2 of scopesOf(project2))
8147
- for (const [memberId, bound] of Object.entries(scope2.bindings ?? {}))
8148
- if (recipientKey(bound) === key) return memberId;
8149
- return void 0;
8150
- }
8151
- function executorOf(project2, workItemId, memberId) {
8152
- if (project2 === void 0) return void 0;
8153
- const item = readFact(project2.items, workItemId);
8154
- const target = targetOf(
8155
- scopeForLine(project2, item?.workLineId).bindings,
8156
- { kind: "member", memberId },
8157
- item?.memberThreads
8158
- );
8159
- if (target === void 0) return void 0;
8160
- if ("threadId" in target && isLeadThread(project2, target.threadId)) return void 0;
8161
- return isMember(project2, target) ? target : void 0;
8162
- }
8163
-
8164
- // src/boardProgress.ts
8165
- var sessionStatus = (thread) => {
8166
- const { status, threadId } = thread;
8167
- switch (status) {
8168
- case "running":
8169
- case "failed":
8170
- return { threadId, status };
8171
- case "needs-input":
8172
- case "needs-approval":
8173
- return { threadId, status: "waiting" };
8174
- case "idle":
8175
- case "stopped": {
8176
- const stoppedAt = thread.summary?.latestTurn?.completedAt;
8177
- return { threadId, status: "stopped", ...stoppedAt == null ? {} : { stoppedAt } };
8178
- }
8179
- default:
8180
- return void 0;
8181
- }
8182
- };
8183
- var cardSessions = (members, wakes, pinned = wakes === void 0 ? void 0 : memberLookup(wakes)) => {
8184
- return (threadId) => findGoalMember(members, { threadId }) !== void 0 || pinned?.(threadId) === true;
8185
- };
8186
- function boardProgress(rows, goalOf, threads, wakes) {
8187
- const sessions = new Map(threads.map((thread) => [thread.threadId, thread]));
8188
- const pinned = wakes === void 0 ? void 0 : memberLookup(wakes);
8189
- const scopes = /* @__PURE__ */ new Map();
8190
- const scopeOf = (goal) => {
8191
- let scope2 = scopes.get(goal);
8192
- if (scope2 === void 0) {
8193
- scope2 = {
8194
- isMember: cardSessions(goalExecution(goal).members, wakes, pinned),
8195
- bindings: bindingsOf(goal)
8196
- };
8197
- scopes.set(goal, scope2);
8198
- }
8199
- return scope2;
8004
+ function resolveTeam(stored, members, where) {
8005
+ if (stored === null) return { team: null, problem: null };
8006
+ if (stored.version === 1) return { team: stored.roster, problem: null };
8007
+ const { roster: lead } = stored;
8008
+ const team = {
8009
+ projectId: lead.projectId,
8010
+ members: members.filter(isActiveAgent).map((member) => toTeamMember(member, where)),
8011
+ leadMemberId: lead.leadMemberId,
8012
+ revision: lead.revision,
8013
+ updatedAt: lead.updatedAt,
8014
+ ...lead.aliases === void 0 ? {} : { aliases: lead.aliases }
8200
8015
  };
8201
- const threadsOf = (item) => item.assignees.flatMap((assignee) => {
8202
- const who = targetOf(
8203
- scopeOf(goalOf(item)).bindings,
8204
- assignee,
8205
- readFact(wakes?.items ?? {}, item.workItemId)?.memberThreads
8206
- );
8207
- return who !== void 0 && "threadId" in who ? [who.threadId] : [];
8016
+ const problem = teamRuleError(team);
8017
+ return problem === null ? { team, problem: null } : { team: null, problem };
8018
+ }
8019
+ function createRosterService(ctx, options = {}) {
8020
+ const rosters = ctx.derivedState({
8021
+ key: "rosters",
8022
+ version: 2,
8023
+ events: ["plugin.event-appended"],
8024
+ init: () => ({}),
8025
+ apply: foldRoster
8208
8026
  });
8209
- const started = /* @__PURE__ */ new Map();
8210
- const countStarted = (threadId, workItemId) => {
8211
- const items = started.get(threadId) ?? /* @__PURE__ */ new Set();
8212
- items.add(workItemId);
8213
- started.set(threadId, items);
8214
- };
8215
- for (const item of rows) {
8216
- if (item.state.group !== "started") continue;
8217
- for (const threadId of threadsOf(item)) countStarted(threadId, item.workItemId);
8218
- }
8219
- for (const [workItemId, item] of Object.entries(wakes?.items ?? {})) {
8220
- if (item.stateGroup === "started") {
8221
- for (const threadId of item.assigneeThreads) countStarted(threadId, workItemId);
8222
- }
8223
- }
8224
- const queuedByMember = /* @__PURE__ */ new Map();
8225
- const queuedFor = (threadId) => {
8226
- let items = queuedByMember.get(threadId);
8227
- if (items === void 0) {
8228
- items = new Set(
8229
- wakes === void 0 ? [] : deferredWakes(wakes, threadId).map((item) => item.workItemId)
8230
- );
8231
- queuedByMember.set(threadId, items);
8232
- }
8233
- return items;
8027
+ const now3 = options.now ?? factTime;
8028
+ const storedOf = async (projectId) => readFact(await rosters.get(), projectId);
8029
+ const machinesOf = async (projectId) => ({
8030
+ local: ctx.environment.id,
8031
+ machines: await readMachines(ctx, projectId)
8032
+ });
8033
+ const activeView = async (projectId) => {
8034
+ const [stored, active, where] = await Promise.all([
8035
+ storedOf(projectId),
8036
+ listMembers(ctx, projectId),
8037
+ machinesOf(projectId)
8038
+ ]);
8039
+ return { stored, active, machines: where.machines, ...resolveTeam(stored, active, where) };
8234
8040
  };
8235
- return (item) => {
8236
- const assignees = threadsOf(item).filter(scopeOf(goalOf(item)).isMember);
8237
- const queued = assignees.some((threadId) => queuedFor(threadId).has(item.workItemId));
8238
- const assignee = item.state.group === "started" ? assignees.find(
8239
- (threadId) => (started.get(threadId)?.size ?? 0) <= 1 || wakes?.items[item.workItemId]?.wakeStatus?.[threadId] === "active"
8240
- ) : void 0;
8241
- const thread = assignee === void 0 ? void 0 : sessions.get(assignee);
8242
- const session = thread === void 0 ? void 0 : sessionStatus(thread);
8041
+ const inspect = async (projectId) => {
8042
+ const [view, history] = await Promise.all([
8043
+ activeView(projectId),
8044
+ listMemberHistory(ctx, projectId)
8045
+ ]);
8046
+ const shown = new Set(history.map((member) => member.memberId));
8243
8047
  return {
8244
- ...session === void 0 ? {} : { assigneeSession: session },
8245
- ...queued ? { queued: true } : {}
8048
+ ...view,
8049
+ members: [...view.active.filter((member) => !shown.has(member.memberId)), ...history]
8246
8050
  };
8247
8051
  };
8248
- }
8249
-
8250
- // src/boardSessions.ts
8251
- var MEMBER_THREAD_LIMIT = 50;
8252
- var PINNED_SESSION_LIMIT = 20;
8253
- async function readGoalSessions(ctx, projectId, goal) {
8254
- const leadThreadId = goal?.threads.find(
8255
- ({ memberId }) => memberId === goal.leadMemberId
8256
- )?.threadId;
8257
- if (leadThreadId === void 0) return [];
8258
- const [memberThreads, leadThread] = await Promise.all([
8259
- // 成员都是负责人的子会话;人数很少,只读摘要,按小上限防游标出错。
8260
- readPages(
8261
- MEMBER_THREAD_LIMIT,
8262
- (page) => ctx.threads.list({
8263
- ...page,
8264
- projectId,
8265
- parentThreadId: leadThreadId,
8266
- include: ["summary"],
8267
- limit: MEMBER_THREAD_LIMIT
8268
- })
8269
- ).catch(() => null),
8270
- // 负责人自己也可能领件;与成员分开读,一边失败不连累另一边。
8271
- ctx.threads.get({ threadId: leadThreadId }).catch(() => null)
8272
- ]);
8273
- return [
8274
- ...memberThreads?.items ?? [],
8275
- ...leadThread === null ? [] : [
8276
- {
8277
- threadId: leadThread.id,
8278
- status: leadThread.status,
8279
- summary: { latestTurn: leadThread.latestTurn }
8280
- }
8281
- ]
8282
- ];
8283
- }
8284
- async function readPinnedSessions(ctx, rows, wakeProject, listed) {
8285
- const threadIds = [
8286
- ...new Set(
8287
- rows.flatMap((row) => {
8288
- const wakeItem = wakeProject?.items[row.workItemId];
8289
- return wakeItem === void 0 ? [] : pinnedThreadsOf(wakeItem);
8290
- })
8291
- )
8292
- ].filter((threadId) => !listed.has(threadId)).slice(0, PINNED_SESSION_LIMIT);
8293
- const pinned = await Promise.all(
8294
- threadIds.map((threadId) => ctx.threads.get({ threadId }).catch(() => null))
8295
- );
8296
- return pinned.flatMap(
8297
- (thread) => thread === null ? [] : [
8298
- {
8299
- threadId: thread.id,
8300
- status: thread.status,
8301
- summary: { latestTurn: thread.latestTurn }
8052
+ const aliases = async (projectId) => {
8053
+ const stored = await storedOf(projectId);
8054
+ return stored?.version === 2 ? stored.roster.aliases ?? {} : {};
8055
+ };
8056
+ const storedLeadMemberId = async (projectId) => {
8057
+ const stored = await storedOf(projectId);
8058
+ return stored?.version === 2 ? stored.roster.leadMemberId : null;
8059
+ };
8060
+ const readTeam = async (projectId) => {
8061
+ const stored = await storedOf(projectId);
8062
+ if (stored === null) return { team: null };
8063
+ if (stored.version === 1) return { team: stored.roster };
8064
+ const [active, where] = await Promise.all([listMembers(ctx, projectId), machinesOf(projectId)]);
8065
+ return { team: resolveTeam(stored, active, where).team, active };
8066
+ };
8067
+ const read = async (projectId) => (await readTeam(projectId)).team;
8068
+ const append2 = async (projectId, expectedRevision, lead) => {
8069
+ const revision = expectedRevision + 1;
8070
+ const receipt = await ctx.events.append({
8071
+ commandId: `roster:${projectId}:${revision}`,
8072
+ name: "roster-set",
8073
+ aggregate: { kind: "project", id: projectId },
8074
+ data: {
8075
+ version: 2,
8076
+ roster: {
8077
+ projectId,
8078
+ leadMemberId: lead.leadMemberId,
8079
+ revision,
8080
+ updatedAt: await now3(),
8081
+ ...lead.aliases === void 0 ? {} : { aliases: lead.aliases }
8082
+ }
8302
8083
  }
8303
- ]
8304
- );
8305
- }
8306
-
8307
- // src/domain/goalItem.ts
8308
- var teamGoalItemId = (projectId, goalId) => `team-goal:${projectId}:${goalId}`;
8309
- var GOAL_ITEM_TITLE_MAX = 120;
8310
- var goalItemTitle = (goal) => {
8311
- const line = (goal.titleSeed ?? goal.goal).split(/\r?\n/u, 1)[0].trim();
8312
- return line.length <= GOAL_ITEM_TITLE_MAX ? line : `${line.slice(0, GOAL_ITEM_TITLE_MAX - 1)}\u2026`;
8313
- };
8314
-
8315
- // src/board.ts
8316
- var ITEM_LIMIT = 500;
8317
- var PEOPLE_LIMIT = 200;
8318
- var decodeRead2 = decodeUnknownSync(ReadBoardInput);
8319
- function registerBoardCommands(ctx, goals, rosters, wakes, options = {}) {
8320
- const hasMember = async (projectId, threadIds) => {
8321
- const members = (await goals.allOf(projectId)).flatMap((goal) => goalExecution(goal).members);
8322
- if (threadIds.some(cardSessions(members, void 0))) return true;
8323
- const wake = (await wakes.get())[projectId];
8324
- return wake !== void 0 && threadIds.some(cardSessions(members, wake));
8084
+ });
8085
+ await rosters.get({ throughSequence: receipt.sequence });
8086
+ return { revision };
8325
8087
  };
8326
- const readTeam = async (projectId, workLineId) => {
8327
- const view = await rosters.activeView(projectId).catch(() => null);
8328
- const roster = view?.team ?? null;
8329
- const members = view?.active ?? null;
8330
- const aliases = view?.stored?.version === 2 ? view.stored.roster.aliases : roster?.aliases;
8331
- const read = await goals.read(projectId, {
8332
- roster,
8333
- ...members === null ? {} : { known: members },
8334
- ...workLineId === void 0 ? {} : { forLine: workLineId }
8088
+ const { run: serial } = createSerial();
8089
+ const setLead = async (payload) => {
8090
+ const decoded = decodeSetLead(payload);
8091
+ if (isNone2(decoded)) return rejectRoster("invalid-payload");
8092
+ const { projectId, leadMemberId, expectedRevision } = decoded.value;
8093
+ const { stored, active } = await activeView(projectId);
8094
+ if (stored?.version === 1) return rejectRoster("legacy-roster");
8095
+ if (expectedRevision !== (stored?.roster.revision ?? 0)) return rejectRoster("stale-revision");
8096
+ const lead = active.find((member) => member.memberId === leadMemberId);
8097
+ if (lead === void 0 || !isActiveAgent(lead)) return rejectRoster("lead-not-member");
8098
+ if (isRemoteMember(lead, ctx.environment.id)) return rejectRoster("lead-remote");
8099
+ return append2(projectId, expectedRevision, {
8100
+ leadMemberId,
8101
+ ...stored?.roster.aliases === void 0 ? {} : { aliases: stored.roster.aliases }
8335
8102
  });
8336
- return {
8337
- roster,
8338
- members,
8339
- aliases,
8340
- // 线上没有目标时 read 会退回项目级目标;团队层只认这条线自己的。
8341
- goal: read !== null && workLineId !== void 0 && read.workLineId !== workLineId ? null : read
8342
- };
8343
8103
  };
8344
- ctx.command({
8345
- name: "board",
8346
- scope: "read",
8347
- payloadSchema: payloadSchemaOf(ReadBoardInput),
8348
- handle: async (payload, invocation) => {
8349
- const { projectId, workLineId } = decodeRead2(payload);
8350
- const [
8351
- stateList,
8352
- { goal, roster, members, aliases },
8353
- toolkitStates,
8354
- people,
8355
- machines,
8356
- viewer,
8357
- lines,
8358
- everyGoal
8359
- ] = await Promise.all([
8360
- ctx.work.states.list({ projectId }),
8361
- // 团队只读一次:目标上的姓名和下面的 rosterMembers 用同一份。它随成员事件与 team/roster-set 门铃一起刷新。
8362
- readTeam(projectId, workLineId),
8363
- ctx.projects.toolkits.get({ projectId }),
8364
- // 人名只用来显示作者;本机所有者排第一,后面是这个项目认识的账号。
8365
- readPages(PEOPLE_LIMIT, (page) => ctx.communication.people.list({ projectId, ...page })),
8366
- // 项目里的别的机器:远端作者(那台机器上的 agent)的名字与在线状态;读不出来退回空,不连累看板。
8367
- readMachines(ctx, projectId),
8368
- // 认出当前读者只为把作者显示成「你」:读不出来就当没有,不连累整张看板。
8369
- invocation.viewer?.communication.people.current({}).catch(() => null),
8370
- // 团队层只核这条线还在(读不到照抛,界面显示读取失败);项目层要全部线(含已归档)来分道,线不设上限。
8371
- workLineId === void 0 ? readPages(
8372
- Number.POSITIVE_INFINITY,
8373
- (page) => ctx.work.lines.list({ projectId, includeArchived: true, ...page })
8374
- ).then(({ items: items2 }) => items2) : ctx.work.lines.get({ projectId, workLineId }).then(({ line }) => [line]),
8375
- // 项目层各线的目标只用来在道内解析成员绑定、写道头一句话,不补姓名;团队层只要这一条线,上面已读好。
8376
- workLineId === void 0 ? goals.allOf(projectId) : []
8377
- ]);
8378
- const lineGoals = new Map(
8379
- everyGoal.flatMap((row) => row.workLineId === void 0 ? [] : [[row.workLineId, row]])
8380
- );
8381
- if (workLineId !== void 0 && goal !== null) lineGoals.set(workLineId, goal);
8382
- const goalOfLane = (laneId) => (laneId === null ? void 0 : lineGoals.get(laneId)) ?? (workLineId === void 0 ? goal : null);
8383
- const archivedLines = new Set(
8384
- lines.flatMap((line) => line.archivedAt === null ? [] : [line.workLineId])
8385
- );
8386
- const sessionGoals = workLineId === void 0 ? [
8387
- goal,
8388
- ...[...lineGoals].filter(([laneId]) => !archivedLines.has(laneId)).map(([, row]) => row)
8389
- ] : [goal];
8390
- const goalItemId = goal === null ? null : teamGoalItemId(projectId, goal.goalId);
8391
- const [remoteWork, { items: rows, truncated }, goalItem, sessionGroups, wakeState] = await Promise.all([
8392
- Promise.all(
8393
- (roster?.members ?? []).flatMap(
8394
- (member) => member.principal === void 0 ? [] : [
8395
- ctx.work.remoteAgentPending.get({ projectId, principalId: member.principal.principalId }).then(
8396
- // 一台机器上可能有好几个成员:只算派给这个成员的(与直接派给那台机器的)。
8397
- ({ work }) => {
8398
- const items2 = work.items.filter(
8399
- (item) => item.memberId === void 0 || item.memberId === member.memberId
8400
- ).map(({ workItemId, blocked }) => ({ workItemId, blocked }));
8401
- return { count: items2.length, items: items2 };
8402
- },
8403
- () => null
8404
- ).then((pending) => ({ memberId: member.memberId, pending }))
8405
- ]
8406
- )
8407
- ),
8408
- readPages(
8409
- ITEM_LIMIT,
8410
- (page) => ctx.work.items.list({
8411
- projectId,
8412
- ...workLineId === void 0 ? {} : { workLineIds: [workLineId] },
8413
- ...page
8414
- })
8415
- ),
8416
- // 目标那件按号单读:看板只列最新的 500 件,目标那件可能不在里面。
8417
- goalItemId === null ? void 0 : ctx.work.items.get({ projectId, workItemIds: [goalItemId] }).then(({ items: items2 }) => items2.find((item) => item.workItemId === goalItemId)),
8418
- Promise.all(sessionGoals.map((row) => readGoalSessions(ctx, projectId, row))),
8419
- wakes.get().catch(() => null)
8420
- ]);
8421
- const sessions = sessionGroups.flat();
8422
- const wakeProject = wakeState?.[projectId];
8423
- sessions.push(
8424
- ...await readPinnedSessions(
8425
- ctx,
8426
- rows,
8427
- wakeProject,
8428
- new Set(sessions.map((session) => session.threadId))
8429
- )
8430
- );
8431
- const progress = boardProgress(
8432
- rows,
8433
- (item) => goalOfLane(item.workLineId ?? null),
8434
- sessions,
8435
- wakeProject
8436
- );
8437
- const assignedMemberIds = [
8438
- ...new Set(
8439
- rows.flatMap(
8440
- (item) => item.assignees.flatMap(
8441
- (assignee) => assignee.kind === "member" ? [assignee.memberId] : []
8442
- )
8443
- )
8444
- )
8445
- ];
8446
- const known = [
8447
- ...members ?? roster?.members ?? [],
8448
- ...(goal?.members ?? []).flatMap(
8449
- ({ memberId, name }) => name === null ? [] : [{ memberId: aliasedMemberId(aliases, memberId) ?? memberId, name }]
8450
- )
8451
- ];
8452
- const memberNames = assignedMemberIds.length === 0 ? [] : await rosters.memberNames(projectId, assignedMemberIds, known).catch(() => []);
8453
- const enabled = (toolkit) => toolkitStates.toolkits.some((row) => row.toolkit === toolkit && row.enabled);
8454
- const items = rows.map((item) => ({
8455
- workItemId: item.workItemId,
8456
- sequenceId: item.sequenceId,
8457
- title: item.title,
8458
- state: { stateId: item.state.stateId, name: item.state.name },
8459
- assignees: item.assignees.map(workIdentity),
8460
- blockedByTotal: item.blockedByTotal,
8461
- version: item.version,
8462
- workLineId: item.workLineId ?? null,
8463
- ...progress(item)
8464
- }));
8465
- return {
8466
- states: stateList.states.map(({ stateId, name, sequence, defaultKey }) => ({
8467
- stateId,
8468
- name,
8469
- sequence,
8470
- ...defaultKey === void 0 ? {} : { defaultKey }
8471
- })).sort((a, b) => a.sequence - b.sequence),
8472
- items,
8473
- // 项目里没有线(还没开过团队)保持原来的一张平板看板,不出泳道。
8474
- ...workLineId === void 0 && lines.length > 0 ? {
8475
- lanes: boardLanes(
8476
- lines,
8477
- (laneId) => laneId === null ? goal?.goal : lineGoals.get(laneId)?.goal,
8478
- rows.some((row) => row.workLineId == null)
8479
- )
8480
- } : {},
8481
- goal,
8482
- ...goalItem === void 0 ? {} : {
8483
- goalItem: {
8484
- workItemId: goalItem.workItemId,
8485
- sequenceId: goalItem.sequenceId,
8486
- state: {
8487
- stateId: goalItem.state.stateId,
8488
- name: goalItem.state.name,
8489
- group: goalItem.state.group
8490
- }
8491
- }
8492
- },
8493
- people: people.items.map((row) => ({
8494
- key: personKey(row.person),
8495
- displayName: row.displayName
8496
- })),
8497
- rosterMembers: roster?.members ?? [],
8498
- ...memberNames.length === 0 ? {} : { memberNames },
8499
- ...members === null ? { membersUnavailable: true } : {
8500
- assignableMembers: members.map(({ memberId, name, kind }) => ({
8501
- memberId,
8502
- name,
8503
- kind
8504
- }))
8505
- },
8506
- ...roster?.aliases === void 0 ? {} : { rosterAliases: roster.aliases },
8507
- ...machines === null ? { machinesUnavailable: true } : {},
8508
- machines: [...machines?.values() ?? []].map(
8509
- ({ principalId, environmentId, name, online, lastSeenAt }) => ({
8510
- principalId,
8511
- environmentId,
8512
- name,
8513
- online,
8514
- ...lastSeenAt === void 0 ? {} : { lastSeenAt }
8515
- })
8516
- ),
8517
- remoteWork,
8518
- ...viewer?.person == null ? {} : { viewerKey: personKey(viewer.person) },
8519
- toolkits: { board: enabled("board"), memory: enabled("memory") },
8520
- truncated
8521
- };
8522
- }
8523
- });
8524
- registerTeamWatch(ctx, hasMember, options);
8525
- }
8526
-
8527
- // src/runtime/serial.ts
8528
- function createSerial(closedMessage = "team-serial-closed") {
8529
- let pending = Promise.resolve();
8530
- let closed = false;
8531
- return {
8532
- run(execute) {
8533
- const result2 = pending.then(() => {
8534
- if (closed) throw new Error(closedMessage);
8535
- return execute();
8536
- });
8537
- pending = result2.then(
8538
- () => void 0,
8539
- () => void 0
8540
- );
8541
- return result2;
8542
- },
8543
- /** 不再开始新任务,并等已经在跑和排在前面的任务落定。 */
8544
- async close() {
8545
- closed = true;
8546
- await pending;
8547
- }
8548
- };
8549
- }
8550
-
8551
- // src/domain/service.ts
8552
- var HISTORY_LIMIT = 500;
8553
- var ACTIVE_LIMIT = 2e3;
8554
- var MEMBER_NAMES_LIMIT = 50;
8555
- var FactEnvelope = Struct({
8556
- pluginId: Literal2("team"),
8557
- name: Literal2("team/roster-set"),
8558
- data: RosterSetFact
8559
- });
8560
- var decodeFact2 = decodeUnknownOption2(FactEnvelope);
8561
- var decodeSetLead = decodeUnknownOption2(SetLeadInput);
8562
- var decodeRosterFact = (payload) => map(decodeFact2(payload), (fact) => fact.data);
8563
- var factProject = (fact) => fact.roster.projectId;
8564
- function foldRoster(state, event) {
8565
- return foldFact(state, event, decodeRosterFact, factProject);
8566
- }
8567
- async function listMembers(ctx, projectId) {
8568
- const { items, truncated } = await readPages(
8569
- ACTIVE_LIMIT,
8570
- (page) => ctx.members.list({ projectId, ...page })
8571
- );
8572
- if (truncated) throw new Error("team-members-truncated");
8573
- return items;
8574
- }
8575
- async function listMemberHistory(ctx, projectId) {
8576
- const { items } = await readPages(
8577
- HISTORY_LIMIT,
8578
- (page) => ctx.members.list({ projectId, includeRemoved: true, ...page }),
8579
- "newest"
8580
- );
8581
- return items;
8582
- }
8583
- async function findMember(ctx, projectId, memberId, window) {
8584
- try {
8585
- const { member } = await ctx.members.get({ projectId, memberId, ...window });
8586
- return member;
8587
- } catch (error) {
8588
- const reason = typeof error === "object" && error !== null && "reason" in error ? error.reason : null;
8589
- if (reason === "member-not-found") return null;
8590
- throw error;
8591
- }
8592
- }
8593
- var isActiveAgent = (member) => member.kind === "agent" && member.removedAt === null;
8594
- var toTeamMember = (member, where) => {
8595
- if (!isRemoteMember(member, where.local)) return { memberId: member.memberId, name: member.name };
8596
- const machine = member.agent === null ? void 0 : where.machines?.get(member.agent.environmentId);
8597
- return {
8598
- memberId: member.memberId,
8599
- name: member.name,
8600
- remote: true,
8601
- ...machine === void 0 ? {} : { principal: { kind: "remote-agent", principalId: machine.principalId } }
8602
- };
8603
- };
8604
- function resolveTeam(stored, members, where) {
8605
- if (stored === null) return { team: null, problem: null };
8606
- if (stored.version === 1) return { team: stored.roster, problem: null };
8607
- const { roster: lead } = stored;
8608
- const team = {
8609
- projectId: lead.projectId,
8610
- members: members.filter(isActiveAgent).map((member) => toTeamMember(member, where)),
8611
- leadMemberId: lead.leadMemberId,
8612
- revision: lead.revision,
8613
- updatedAt: lead.updatedAt,
8614
- ...lead.aliases === void 0 ? {} : { aliases: lead.aliases }
8615
- };
8616
- const problem = teamRuleError(team);
8617
- return problem === null ? { team, problem: null } : { team: null, problem };
8618
- }
8619
- function createRosterService(ctx, options = {}) {
8620
- const rosters = ctx.derivedState({
8621
- key: "rosters",
8622
- version: 2,
8623
- events: ["plugin.event-appended"],
8624
- init: () => ({}),
8625
- apply: foldRoster
8626
- });
8627
- const now3 = options.now ?? factTime;
8628
- const storedOf = async (projectId) => readFact(await rosters.get(), projectId);
8629
- const machinesOf = async (projectId) => ({
8630
- local: ctx.environment.id,
8631
- machines: await readMachines(ctx, projectId)
8632
- });
8633
- const activeView = async (projectId) => {
8634
- const [stored, active, where] = await Promise.all([
8635
- storedOf(projectId),
8636
- listMembers(ctx, projectId),
8637
- machinesOf(projectId)
8638
- ]);
8639
- return { stored, active, machines: where.machines, ...resolveTeam(stored, active, where) };
8640
- };
8641
- const inspect = async (projectId) => {
8642
- const [view, history] = await Promise.all([
8643
- activeView(projectId),
8644
- listMemberHistory(ctx, projectId)
8645
- ]);
8646
- const shown = new Set(history.map((member) => member.memberId));
8647
- return {
8648
- ...view,
8649
- members: [...view.active.filter((member) => !shown.has(member.memberId)), ...history]
8650
- };
8651
- };
8652
- const aliases = async (projectId) => {
8653
- const stored = await storedOf(projectId);
8654
- return stored?.version === 2 ? stored.roster.aliases ?? {} : {};
8655
- };
8656
- const storedLeadMemberId = async (projectId) => {
8657
- const stored = await storedOf(projectId);
8658
- return stored?.version === 2 ? stored.roster.leadMemberId : null;
8659
- };
8660
- const readTeam = async (projectId) => {
8661
- const stored = await storedOf(projectId);
8662
- if (stored === null) return { team: null };
8663
- if (stored.version === 1) return { team: stored.roster };
8664
- const [active, where] = await Promise.all([listMembers(ctx, projectId), machinesOf(projectId)]);
8665
- return { team: resolveTeam(stored, active, where).team, active };
8666
- };
8667
- const read = async (projectId) => (await readTeam(projectId)).team;
8668
- const append2 = async (projectId, expectedRevision, lead) => {
8669
- const revision = expectedRevision + 1;
8670
- const receipt = await ctx.events.append({
8671
- commandId: `roster:${projectId}:${revision}`,
8672
- name: "roster-set",
8673
- aggregate: { kind: "project", id: projectId },
8674
- data: {
8675
- version: 2,
8676
- roster: {
8677
- projectId,
8678
- leadMemberId: lead.leadMemberId,
8679
- revision,
8680
- updatedAt: await now3(),
8681
- ...lead.aliases === void 0 ? {} : { aliases: lead.aliases }
8682
- }
8683
- }
8684
- });
8685
- await rosters.get({ throughSequence: receipt.sequence });
8686
- return { revision };
8687
- };
8688
- const { run: serial } = createSerial();
8689
- const setLead = async (payload) => {
8690
- const decoded = decodeSetLead(payload);
8691
- if (isNone2(decoded)) return rejectRoster("invalid-payload");
8692
- const { projectId, leadMemberId, expectedRevision } = decoded.value;
8693
- const { stored, active } = await activeView(projectId);
8694
- if (stored?.version === 1) return rejectRoster("legacy-roster");
8695
- if (expectedRevision !== (stored?.roster.revision ?? 0)) return rejectRoster("stale-revision");
8696
- const lead = active.find((member) => member.memberId === leadMemberId);
8697
- if (lead === void 0 || !isActiveAgent(lead)) return rejectRoster("lead-not-member");
8698
- if (isRemoteMember(lead, ctx.environment.id)) return rejectRoster("lead-remote");
8699
- return append2(projectId, expectedRevision, {
8700
- leadMemberId,
8701
- ...stored?.roster.aliases === void 0 ? {} : { aliases: stored.roster.aliases }
8702
- });
8703
- };
8704
- const adoptImported = async (input) => {
8705
- const stored = await storedOf(input.projectId);
8706
- if (stored?.version !== 1 || stored.roster.revision !== input.expectedRevision)
8707
- return rejectRoster("stale-revision");
8708
- return append2(input.projectId, input.expectedRevision, input);
8709
- };
8710
- const memberNames = async (projectId, memberIds, known = []) => (await Promise.all(
8711
- memberIds.slice(0, MEMBER_NAMES_LIMIT).map(async (memberId) => {
8712
- const named = known.find((row) => row.memberId === memberId);
8713
- if (named !== void 0) return [{ memberId, name: named.name }];
8714
- const member = await findMember(ctx, projectId, memberId, { textLimit: 2 }).catch(
8715
- () => null
8104
+ const adoptImported = async (input) => {
8105
+ const stored = await storedOf(input.projectId);
8106
+ if (stored?.version !== 1 || stored.roster.revision !== input.expectedRevision)
8107
+ return rejectRoster("stale-revision");
8108
+ return append2(input.projectId, input.expectedRevision, input);
8109
+ };
8110
+ const memberNames = async (projectId, memberIds, known = []) => (await Promise.all(
8111
+ memberIds.slice(0, MEMBER_NAMES_LIMIT).map(async (memberId) => {
8112
+ const named = known.find((row) => row.memberId === memberId);
8113
+ if (named !== void 0) return [{ memberId, name: named.name }];
8114
+ const member = await findMember(ctx, projectId, memberId, { textLimit: 2 }).catch(
8115
+ () => null
8716
8116
  );
8717
8117
  return member === null ? [] : [{ memberId, name: member.name }];
8718
8118
  })
@@ -8975,33 +8375,120 @@ var specOfMember = (member) => ({
8975
8375
  });
8976
8376
  var trialIdOf = (projectId, memberId, specKey, attempt) => `team-trial:${stableHash(JSON.stringify([projectId, memberId, specKey, attempt]))}`;
8977
8377
 
8978
- // src/domain/defaultModel.ts
8979
- var isSelectionUnavailable = isTagged("ProviderSelectionError");
8980
- var MODEL_UNAVAILABLE_REASONS = /* @__PURE__ */ new Set([
8981
- "instance-not-found",
8982
- "instance-unavailable",
8983
- "model-not-in-catalog"
8984
- ]);
8985
- var UNAVAILABLE_MESSAGE_KEYS = {
8986
- "instance-not-found": "instanceNotFound",
8987
- "instance-unavailable": "instanceUnavailable",
8988
- "model-not-in-catalog": "modelNotInCatalog"
8989
- };
8990
- var unavailableFrom = (error) => {
8991
- const reason = Reflect.get(error, "reason");
8992
- return typeof reason === "string" && MODEL_UNAVAILABLE_REASONS.has(reason) ? { code: "model-unavailable", reason } : { code: "model-unavailable" };
8993
- };
8994
- async function resolveModel(ctx, base, named = false) {
8995
- try {
8996
- return {
8997
- selection: await ctx.providers.selection.resolve(
8998
- named && base !== null ? { base, instanceId: base.instanceId, model: base.model } : { base }
8999
- )
9000
- };
9001
- } catch (error) {
9002
- if (isSelectionUnavailable(error)) return unavailableFrom(error);
9003
- throw error;
9004
- }
8378
+ // src/domain/execution.ts
8379
+ function goalExecution(goal) {
8380
+ return {
8381
+ leadMemberId: goal?.leadMemberId ?? null,
8382
+ members: goal === null ? [] : [...goal.threads, ...goal.remoteMembers ?? []]
8383
+ };
8384
+ }
8385
+ function findGoalMember(members, identity2) {
8386
+ return members.find(
8387
+ (member) => "threadId" in identity2 ? "threadId" in member && member.threadId === identity2.threadId : "principalId" in member && member.principalId === identity2.principalId
8388
+ );
8389
+ }
8390
+
8391
+ // src/domain/recipient.ts
8392
+ var recipientOf = (principal) => principal.kind === "agent" && principal.threadId !== void 0 ? { threadId: principal.threadId } : principal.kind === "remote-agent" && principal.principalId !== void 0 ? { principalId: principal.principalId } : void 0;
8393
+ var recipientKey = (who) => "threadId" in who ? `thread:${who.threadId}` : `principal:${who.principalId}`;
8394
+
8395
+ // src/domain/binding.ts
8396
+ var MemberBoundFact = Struct({
8397
+ version: Literal2(1),
8398
+ projectId: String4,
8399
+ memberId: String4,
8400
+ specKey: String4,
8401
+ goalId: String4,
8402
+ threadId: optionalKey2(String4),
8403
+ principalId: optionalKey2(String4),
8404
+ replaces: optionalKey2(String4)
8405
+ }).check(
8406
+ makeFilter2((fact) => fact.threadId === void 0 !== (fact.principalId === void 0))
8407
+ );
8408
+ var MemberBoundEnvelope = factEnvelope("member-bound", MemberBoundFact);
8409
+ var bindingOfFact = (fact) => fact.threadId !== void 0 ? { threadId: fact.threadId } : { principalId: fact.principalId };
8410
+ function bindingsOf(goal) {
8411
+ const bindings = {};
8412
+ for (const member of goalExecution(goal).members)
8413
+ bindings[member.memberId] = "threadId" in member ? { threadId: member.threadId } : { principalId: member.principalId };
8414
+ return bindings;
8415
+ }
8416
+ function withBoundBinding(bindings, fact) {
8417
+ const next = { ...bindings };
8418
+ if (fact.replaces !== void 0) delete next[fact.replaces];
8419
+ next[fact.memberId] = bindingOfFact(fact);
8420
+ return next;
8421
+ }
8422
+ var leadAfterBinding = (leadMemberId, fact) => fact.replaces !== void 0 && leadMemberId === fact.replaces ? fact.memberId : leadMemberId;
8423
+ function withBoundMember(goal, fact) {
8424
+ if (fact.projectId !== goal.projectId || fact.goalId !== goal.goalId) return goal;
8425
+ const { remoteMembers: _before, ...rest } = goal;
8426
+ const superseded = (memberId) => memberId === fact.memberId || memberId === fact.replaces;
8427
+ const threads = goal.threads.filter((row) => !superseded(row.memberId));
8428
+ const remoteMembers = (goal.remoteMembers ?? []).filter((row) => !superseded(row.memberId));
8429
+ const binding = bindingOfFact(fact);
8430
+ const next = "threadId" in binding ? {
8431
+ threads: [...threads, { memberId: fact.memberId, threadId: binding.threadId }],
8432
+ remoteMembers
8433
+ } : {
8434
+ threads,
8435
+ remoteMembers: [
8436
+ ...remoteMembers,
8437
+ { memberId: fact.memberId, principalId: binding.principalId }
8438
+ ]
8439
+ };
8440
+ return {
8441
+ ...rest,
8442
+ leadMemberId: leadAfterBinding(goal.leadMemberId, fact),
8443
+ threads: next.threads,
8444
+ ...next.remoteMembers.length === 0 ? {} : { remoteMembers: next.remoteMembers }
8445
+ };
8446
+ }
8447
+ function targetOf(bindings, assignee, pinned) {
8448
+ if (assignee.kind !== "member") return recipientOf(assignee);
8449
+ const { memberId } = assignee;
8450
+ if (memberId === void 0) return void 0;
8451
+ const fixed = pinned !== void 0 && Object.hasOwn(pinned, memberId) ? pinned[memberId] : void 0;
8452
+ if (fixed !== void 0) return { threadId: fixed };
8453
+ return bindings !== void 0 && Object.hasOwn(bindings, memberId) ? bindings[memberId] : void 0;
8454
+ }
8455
+ function assignedToGoalMember(goal, assignees) {
8456
+ const execution = goalExecution(goal);
8457
+ const members = execution.members.filter((member) => member.memberId !== execution.leadMemberId);
8458
+ const bindings = bindingsOf(goal);
8459
+ return assignees.some((assignee) => {
8460
+ const who = targetOf(bindings, assignee);
8461
+ return who !== void 0 && findGoalMember(members, who) !== void 0;
8462
+ });
8463
+ }
8464
+
8465
+ // src/domain/defaultModel.ts
8466
+ var isSelectionUnavailable = isTagged("ProviderSelectionError");
8467
+ var MODEL_UNAVAILABLE_REASONS = /* @__PURE__ */ new Set([
8468
+ "instance-not-found",
8469
+ "instance-unavailable",
8470
+ "model-not-in-catalog"
8471
+ ]);
8472
+ var UNAVAILABLE_MESSAGE_KEYS = {
8473
+ "instance-not-found": "instanceNotFound",
8474
+ "instance-unavailable": "instanceUnavailable",
8475
+ "model-not-in-catalog": "modelNotInCatalog"
8476
+ };
8477
+ var unavailableFrom = (error) => {
8478
+ const reason = Reflect.get(error, "reason");
8479
+ return typeof reason === "string" && MODEL_UNAVAILABLE_REASONS.has(reason) ? { code: "model-unavailable", reason } : { code: "model-unavailable" };
8480
+ };
8481
+ async function resolveModel(ctx, base, named = false) {
8482
+ try {
8483
+ return {
8484
+ selection: await ctx.providers.selection.resolve(
8485
+ named && base !== null ? { base, instanceId: base.instanceId, model: base.model } : { base }
8486
+ )
8487
+ };
8488
+ } catch (error) {
8489
+ if (isSelectionUnavailable(error)) return unavailableFrom(error);
8490
+ throw error;
8491
+ }
9005
8492
  }
9006
8493
  async function resolveDefaultSelection(ctx, projectId) {
9007
8494
  const project2 = await ctx.projects.get({ projectId });
@@ -9601,7 +9088,7 @@ function createGoalService(ctx, rosterService, options = {}) {
9601
9088
  rosterMemberName(roster, member.memberId)
9602
9089
  ])
9603
9090
  );
9604
- const others = options2.teamOnly === true && goal.workLineId === void 0 ? /* @__PURE__ */ new Map() : await engineNames(
9091
+ const others = await engineNames(
9605
9092
  projectId,
9606
9093
  execution.members.flatMap(
9607
9094
  ({ memberId }) => teamNames.get(memberId) === null ? [memberId] : []
@@ -10395,7 +9882,7 @@ function membersSnapshot(view, trialOf = () => null, viewer = null) {
10395
9882
  name: machine.name,
10396
9883
  online: machine.online,
10397
9884
  ...machine.lastSeenAt === void 0 ? {} : { lastSeenAt: machine.lastSeenAt },
10398
- providers: machine.providers.map((provider) => ({
9885
+ providers: readyProviders(machine).map((provider) => ({
10399
9886
  instanceId: provider.instanceId,
10400
9887
  displayName: provider.displayName,
10401
9888
  models: provider.models.map(({ slug, name }) => ({ slug, name })),
@@ -10952,7 +10439,7 @@ function createMemberWriter(ctx, rosters, queue = createSerial()) {
10952
10439
  const machines = missing2.some(({ member }) => member.principal !== void 0) ? [...(await readMachines(ctx, projectId))?.values() ?? []] : [];
10953
10440
  const remoteAgentOf = (principalId) => {
10954
10441
  const machine = machines.find((entry) => entry.principalId === principalId);
10955
- const provider = machine?.providers[0];
10442
+ const provider = machine === void 0 ? void 0 : readyProviders(machine)[0];
10956
10443
  const model = provider?.defaultModel ?? provider?.models[0]?.slug;
10957
10444
  return machine === void 0 || provider === void 0 || model === void 0 ? void 0 : {
10958
10445
  environmentId: machine.environmentId,
@@ -10988,90 +10475,6 @@ function createMemberWriter(ctx, rosters, queue = createSerial()) {
10988
10475
  return { create, update, remove, restore, importLegacy };
10989
10476
  }
10990
10477
 
10991
- // src/runtime/commentWakes.ts
10992
- var COMMENT_MENTIONS_FACT = "comment-mentions";
10993
- var COMMENT_MENTIONS_EVENT = `team/${COMMENT_MENTIONS_FACT}`;
10994
- var CommentMentionsFact = Struct({
10995
- version: Literal2(1),
10996
- projectId: String4,
10997
- workItemId: String4,
10998
- commentId: String4,
10999
- recipients: ArraySchema(
11000
- Union2([
11001
- Struct({ threadId: String4 }),
11002
- Struct({ principalId: String4 })
11003
- ])
11004
- )
11005
- });
11006
- var commentPeople = (comment) => [
11007
- ...comment.parentAuthor === void 0 ? [] : [comment.parentAuthor],
11008
- ...comment.mentions
11009
- ];
11010
- var recipientsOf = (people) => {
11011
- const recipients = /* @__PURE__ */ new Map();
11012
- for (const person of people) {
11013
- const recipient = recipientOf(person);
11014
- if (recipient !== void 0) recipients.set(recipientKey(recipient), recipient);
11015
- }
11016
- return recipients;
11017
- };
11018
- var reachedKeys = (actor, people) => new Set(recipientsOf([actor, ...people]).keys());
11019
- var commentRecipients = (project2, actor, people) => {
11020
- const author = recipientOf(actor);
11021
- const authorKey = author === void 0 ? void 0 : recipientKey(author);
11022
- return [...recipientsOf(people)].filter(([key]) => key !== authorKey).map(([, recipient]) => recipient).filter((recipient) => isMember(project2, recipient));
11023
- };
11024
- var commentWakeKey = (commentId, recipient) => "threadId" in recipient ? `comment:${commentId}:${recipient.threadId}` : `comment:${commentId}:principal:${recipient.principalId}`;
11025
- var queueCommentWakes = (project2, comment, recipients) => [
11026
- ...project2.pendingComments,
11027
- ...recipients.map((recipient) => ({
11028
- ...recipient,
11029
- key: commentWakeKey(comment.commentId, recipient),
11030
- projectId: comment.projectId,
11031
- workItemId: comment.workItemId,
11032
- commentId: comment.commentId
11033
- })).filter((queued) => !project2.pendingComments.some((pending) => pending.key === queued.key))
11034
- ];
11035
-
11036
- // src/domain/mentionText.ts
11037
- var isAtSign = (character) => character === "@" || character === "\uFF20";
11038
- var MENTION_BOUNDARY = /[\s\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\u3000-\u303f\uff01-\uff0f\uff1a-\uff20\uff3b-\uff40\uff5b-\uff65]/u;
11039
- var isMentionStart = (text, index) => {
11040
- if (!isAtSign(text[index])) return false;
11041
- const before = Array.from(text.slice(Math.max(0, index - 2), index)).at(-1);
11042
- return before === void 0 || MENTION_BOUNDARY.test(before);
11043
- };
11044
-
11045
- // src/runtime/mentionedMembers.ts
11046
- var mentionPrincipal = (member) => member.threadId !== void 0 ? member.principalId === void 0 ? { kind: "agent", threadId: member.threadId } : null : member.principalId !== void 0 ? { kind: "remote-agent", principalId: member.principalId } : null;
11047
- var ASCII_WORD = /[A-Za-z0-9_-]/;
11048
- function mentionedMembers(body, members) {
11049
- const aliases = members.flatMap((member) => {
11050
- const principal = mentionPrincipal(member);
11051
- const recipient = principal === null ? void 0 : recipientOf(principal);
11052
- if (recipient === void 0) return [];
11053
- const id = "threadId" in recipient ? recipient.threadId : recipient.principalId;
11054
- return [id, member.memberId, member.name].flatMap(
11055
- (text) => text === void 0 || text === null || text === "" ? [] : [{ text: text.toLowerCase(), key: recipientKey(recipient), recipient }]
11056
- );
11057
- }).sort((a, b) => b.text.length - a.text.length);
11058
- const found = /* @__PURE__ */ new Map();
11059
- for (let at = 0; at < body.length; at++) {
11060
- if (!isMentionStart(body, at)) continue;
11061
- const rest = body.slice(at + 1);
11062
- const hits = aliases.filter((alias) => {
11063
- if (rest.slice(0, alias.text.length).toLowerCase() !== alias.text) return false;
11064
- const next = rest[alias.text.length];
11065
- return next === void 0 || !(ASCII_WORD.test(alias.text.slice(-1)) && ASCII_WORD.test(next));
11066
- });
11067
- const longest = hits.filter((hit) => hit.text.length === hits[0]?.text.length);
11068
- const keys = new Set(longest.map((hit) => hit.key));
11069
- const [only] = longest;
11070
- if (only !== void 0 && keys.size === 1) found.set(only.key, only.recipient);
11071
- }
11072
- return [...found.values()];
11073
- }
11074
-
11075
10478
  // src/domain/pause.ts
11076
10479
  var LinePausedEnvelope = factEnvelope(
11077
10480
  "line-paused",
@@ -11119,11 +10522,6 @@ var decodeEnvelope2 = decodeUnknownOption2(
11119
10522
  name: Literal2("team/member-woken"),
11120
10523
  data: MemberWokenFact
11121
10524
  }),
11122
- Struct({
11123
- pluginId: Literal2("team"),
11124
- name: Literal2(COMMENT_MENTIONS_EVENT),
11125
- data: CommentMentionsFact
11126
- }),
11127
10525
  MemberBoundEnvelope,
11128
10526
  LinePausedEnvelope,
11129
10527
  LineMemberPausedEnvelope,
@@ -11226,87 +10624,6 @@ var assigneeMembers = (principals) => [
11226
10624
  ...new Set(principals.flatMap((p) => p.kind === "member" ? [p.memberId] : []))
11227
10625
  ];
11228
10626
 
11229
- // src/runtime/commentMentions.ts
11230
- var COMMENT_MENTION_RETRIES = 2;
11231
- function startCommentMentionRuntime(ctx, goals) {
11232
- const handle = async (event) => {
11233
- const decoded = decodeWorkEvent(event);
11234
- if (isNone2(decoded) || decoded.value.type !== "work.comment-posted") return;
11235
- const comment = decoded.value.payload;
11236
- const { projectId, workItemId, commentId } = comment;
11237
- const resolve2 = async () => {
11238
- const { items } = await ctx.work.items.get({ projectId, workItemIds: [workItemId] });
11239
- const forLine = items.find((row) => row.workItemId === workItemId)?.workLineId ?? null;
11240
- const goal = await goals.read(projectId, { teamOnly: true, forLine });
11241
- if (goal === null) return;
11242
- const { comment: posted } = await ctx.work.comments.get({ projectId, commentId });
11243
- if (posted.body === null) return;
11244
- const reached = reachedKeys(comment.actor, commentPeople(comment));
11245
- const { workLineId } = goal;
11246
- const onTeam = workLineId === void 0 ? goal.members : (await Promise.all(
11247
- goal.members.map(
11248
- async (member) => await memberOnLine(ctx, projectId, workLineId, member.memberId) ? [member] : []
11249
- )
11250
- )).flat();
11251
- const recipients = mentionedMembers(posted.body, onTeam).filter(
11252
- (recipient) => !reached.has(recipientKey(recipient))
11253
- );
11254
- if (recipients.length === 0) return;
11255
- await ctx.events.append({
11256
- commandId: `team-comment-mentions:${commentId}`,
11257
- name: COMMENT_MENTIONS_FACT,
11258
- aggregate: { kind: "project", id: projectId },
11259
- data: { version: 1, projectId, workItemId, commentId, recipients }
11260
- });
11261
- };
11262
- for (let attempt = 1; ; attempt++) {
11263
- try {
11264
- return await resolve2();
11265
- } catch (error) {
11266
- if (attempt > COMMENT_MENTION_RETRIES) {
11267
- ctx.log.warn("team comment mentions failed", {
11268
- projectId,
11269
- workItemId,
11270
- commentId,
11271
- eventId: event.eventId,
11272
- attempts: attempt,
11273
- error
11274
- });
11275
- return;
11276
- }
11277
- }
11278
- }
11279
- };
11280
- ctx.reactor({ name: "team-comment-mentions", events: ["work.comment-posted"], handle });
11281
- }
11282
-
11283
- // src/runtime/dispatchClaim.ts
11284
- var allow = { allowed: true };
11285
- var claim = (detail) => ({ allowed: false, detail });
11286
- function registerDispatchClaim(ctx, wakes) {
11287
- const check = async (input) => {
11288
- const project2 = readFact(
11289
- await wakes.get({ throughSequence: input.throughSequence }),
11290
- input.projectId
11291
- );
11292
- if (project2 === null) return allow;
11293
- if (input.workLineId !== null && project2.lineScopes?.[input.workLineId] !== void 0)
11294
- return claim("the team on this work line dispatches its members");
11295
- if (project2.goalId === void 0) return allow;
11296
- if (readFact(project2.bindings ?? {}, input.memberId) !== null)
11297
- return claim("the project's team goal dispatches this member");
11298
- if (isClosed(readFact(project2.items, teamGoalItemId(input.projectId, project2.goalId))))
11299
- return allow;
11300
- const lines = await ctx.work.lines.list({
11301
- projectId: input.projectId,
11302
- includeArchived: true,
11303
- limit: 1
11304
- });
11305
- return lines.items.length === 0 ? claim("the project's team goal takes this member in and dispatches it") : allow;
11306
- };
11307
- return ctx.operations.guard({ operations: ["work.dispatch"], check });
11308
- }
11309
-
11310
10627
  // src/runtime/deliveryPacing.ts
11311
10628
  function foldProgress(state, event) {
11312
10629
  const decoded = decodeWorkEvent(event);
@@ -11375,148 +10692,478 @@ function createDeliveryPacing(progress, waitingOnSomeone2) {
11375
10692
  return { observe, allows };
11376
10693
  }
11377
10694
 
11378
- // src/runtime/memberWaiting.ts
11379
- var readMemberExecution = async (ctx, threadId, known) => {
11380
- const shell = await ctx.threads.get({ threadId });
11381
- const summary = known ?? await ctx.threads.execution.summary({ threadId, limit: 1 });
11382
- return { ...shell, queuePaused: summary.queuePaused, summary };
11383
- };
11384
- var waitingOnSomeone = (execution) => execution.summary.queuePaused || execution.summary.pendingApprovalCount > 0 || execution.summary.pendingUserInputCount > 0 || (execution.pendingAsyncQuestionCount ?? 0) > 0 || (execution.awaitingPersonReplyCount ?? 0) > 0;
11385
-
11386
- // src/runtime/groupDelivery.ts
11387
- var decodeFact5 = decodeUnknownOption2(
11388
- Union2([MemberBoundEnvelope, LinePausedEnvelope, LineMemberPausedEnvelope])
11389
- );
11390
- var decodeProjectEvent = decodeUnknownOption2(Struct({ projectId: String4 }));
11391
- var SWEEP_MS = 6e4;
11392
- function startGroupDeliveryRuntime(ctx, wakes, now3 = () => runPromise2(map5(now2, toEpochMillis2))) {
10695
+ // src/runtime/goalScopes.ts
10696
+ var isLinePaused = (project2, workLineId) => project2.linePauses?.[workLineId] === true;
10697
+ var isMemberPausedOnLine = (project2, workLineId, memberId) => (project2.pausedMembers?.[workLineId] ?? []).includes(memberId);
10698
+ var isLineStopped = (project2, workLineId) => workLineId !== void 0 && project2.lineStops?.[workLineId] !== void 0;
10699
+ var isLineGated = (project2, workLineId) => isLineStopped(project2, workLineId) || workLineId !== void 0 && isLinePaused(project2, workLineId);
10700
+ var scopesOf = (project2) => [
10701
+ project2,
10702
+ ...Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([, scope2]) => scope2)
10703
+ ];
10704
+ var scopeForLine = (project2, workLineId) => (workLineId === void 0 ? void 0 : project2.lineScopes?.[workLineId]) ?? project2;
10705
+ var scopeOfThread = (project2, threadId) => scopesOf(project2).find((scope2) => scope2.threads.includes(threadId));
10706
+ var teamSessions = (scope2) => [
10707
+ .../* @__PURE__ */ new Set([...scope2.threads, ...Object.keys(scope2.memberIds ?? {})])
10708
+ ];
10709
+ var lineScopeOfThread = (project2, threadId) => {
10710
+ for (const [workLineId, scope2] of Object.entries(project2.lineScopes ?? {}))
10711
+ if (teamSessions(scope2).includes(threadId)) return { workLineId, scope: scope2 };
10712
+ return void 0;
10713
+ };
10714
+ var scopeOfPrincipal = (project2, principalId) => scopesOf(project2).find((scope2) => scope2.remoteMembers.includes(principalId));
10715
+ var isGoalThread = (project2, threadId) => scopeOfThread(project2, threadId) !== void 0;
10716
+ var isLeadThread = (project2, threadId) => scopesOf(project2).some((scope2) => scope2.leadThreads?.includes(threadId) === true);
10717
+ var isRemoteMember2 = (project2, principalId) => scopeOfPrincipal(project2, principalId) !== void 0;
10718
+ var leadOfThread = (project2, threadId) => scopeOfThread(project2, threadId)?.leadThreads?.[0];
10719
+ var placeOfRecipient = (project2, who, workLineId) => {
10720
+ if ("threadId" in who) {
10721
+ const line = lineScopeOfThread(project2, who.threadId);
10722
+ if (line !== void 0) return line;
10723
+ return project2.threads.includes(who.threadId) ? { scope: project2 } : void 0;
10724
+ }
10725
+ const own = workLineId === void 0 ? void 0 : project2.lineScopes?.[workLineId];
10726
+ if (workLineId !== void 0 && own?.remoteMembers.includes(who.principalId) === true)
10727
+ return { workLineId, scope: own };
10728
+ if (project2.remoteMembers.includes(who.principalId)) return { scope: project2 };
10729
+ const found = Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).find(([, scope2]) => scope2.remoteMembers.includes(who.principalId));
10730
+ return found === void 0 ? void 0 : { workLineId: found[0], scope: found[1] };
10731
+ };
10732
+ var scopeOfRecipient = (project2, who, workLineId) => placeOfRecipient(project2, who, workLineId)?.scope;
10733
+ var memberOfRecipientIn = (scope2, who) => {
10734
+ if ("threadId" in who) return scope2.memberIds?.[who.threadId];
10735
+ for (const [memberId, recipient] of Object.entries(scope2.bindings ?? {}))
10736
+ if ("principalId" in recipient && recipient.principalId === who.principalId) return memberId;
10737
+ return void 0;
10738
+ };
10739
+ var isRecipientGated = (project2, who, workLineId) => {
10740
+ const place = placeOfRecipient(project2, who, workLineId);
10741
+ if (place === void 0 || place.workLineId === void 0) return false;
10742
+ if (isLineGated(project2, place.workLineId)) return true;
10743
+ const member = memberOfRecipientIn(place.scope, who);
10744
+ return member !== void 0 && isMemberPausedOnLine(project2, place.workLineId, member);
10745
+ };
10746
+ var isThreadGated = (project2, threadId) => isRecipientGated(project2, { threadId });
10747
+ var teamLeadCandidates = (project2, memberId) => Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).flatMap(([workLineId, scope2]) => {
10748
+ const leadThreadId = scope2.leadThreads?.[0];
10749
+ return scope2.bindings?.[memberId] !== void 0 && leadThreadId !== void 0 && !isLineStopped(project2, workLineId) ? [{ workLineId, leadThreadId }] : [];
10750
+ });
10751
+
10752
+ // src/runtime/memberWaiting.ts
10753
+ var readMemberExecution = async (ctx, threadId, known) => {
10754
+ const shell = await ctx.threads.get({ threadId });
10755
+ const summary = known ?? await ctx.threads.execution.summary({ threadId, limit: 1 });
10756
+ return { ...shell, queuePaused: summary.queuePaused, summary };
10757
+ };
10758
+ var waitingOnSomeone = (execution) => execution.summary.queuePaused || execution.summary.pendingApprovalCount > 0 || execution.summary.pendingUserInputCount > 0 || (execution.pendingAsyncQuestionCount ?? 0) > 0 || (execution.awaitingPersonReplyCount ?? 0) > 0;
10759
+
10760
+ // src/runtime/wakeQueries.ts
10761
+ function memberWorktreeFor(goals, project2, projectId, recipient) {
10762
+ if (!("threadId" in recipient) || project2 === void 0) return;
10763
+ const { threadId } = recipient;
10764
+ const scope2 = scopeOfThread(project2, threadId);
10765
+ if (scope2 === void 0 || scope2.leadThreads?.includes(threadId)) return;
10766
+ const leadThreadId = scope2.leadThreads?.[0];
10767
+ if (leadThreadId === void 0) return;
10768
+ return {
10769
+ leadThreadId,
10770
+ failure: project2.worktreeFailures?.[threadId],
10771
+ // 只在要建树或报失败时读目标取名字,叫醒的热路径不多读。
10772
+ name: async () => (await goals.read(projectId, { forThread: threadId }))?.members.find(
10773
+ (member) => member.threadId === threadId
10774
+ )?.name ?? scope2.memberIds?.[threadId] ?? threadId
10775
+ };
10776
+ }
10777
+ var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
10778
+ var compareWakeItems = (a, b) => a.sequenceId - b.sequenceId || compare(a.workItemId, b.workItemId);
10779
+ var OPEN_ITEM_GROUPS = ["backlog", "unstarted", "started"];
10780
+ var isClosed = (item) => item?.stateGroup === "completed" || item?.stateGroup === "cancelled";
10781
+ var hasOpenBlocker = (project2, workItemId) => (readFact(project2.blockers, workItemId) ?? []).some(
10782
+ (blocker) => !isClosed(readFact(project2.items, blocker))
10783
+ );
10784
+ var isAssignedOpen = (project2, workItemId, threadId) => {
10785
+ const item = readFact(project2.items, workItemId);
10786
+ return item !== null && item !== void 0 && !isClosed(item) && item.assigneeThreads.includes(threadId);
10787
+ };
10788
+ var isWorkable = (project2, workItemId) => !isClosed(readFact(project2.items, workItemId)) && !hasOpenBlocker(project2, workItemId);
10789
+ var isOpenWorkFor = (project2, workItemId, threadId) => isAssignedOpen(project2, workItemId, threadId) && !hasOpenBlocker(project2, workItemId);
10790
+ var isHandedOver = (item, threadId) => {
10791
+ if (item.stateGroup === "started") return true;
10792
+ const status = threadId === void 0 ? void 0 : item.wakeStatus?.[threadId];
10793
+ return status === "active" || status === "released";
10794
+ };
10795
+ var memberOfThread = (item, threadId) => {
10796
+ for (const [memberId, thread] of Object.entries(item.memberThreads ?? {}))
10797
+ if (thread === threadId) return memberId;
10798
+ return void 0;
10799
+ };
10800
+ var isPinnedTo = (item, threadId) => !isClosed(item) && memberOfThread(item, threadId) !== void 0 && isHandedOver(item, threadId);
10801
+ var hasPinnedOpenItem = (project2, threadId) => Object.values(project2.items).some((item) => isPinnedTo(item, threadId));
10802
+ var pinnedThreadsOf = (item) => Object.values(item.memberThreads ?? {}).filter((threadId) => isPinnedTo(item, threadId));
10803
+ var pinnedOpenThreads = (project2) => {
10804
+ const threads = /* @__PURE__ */ new Set();
10805
+ for (const item of Object.values(project2.items))
10806
+ for (const threadId of pinnedThreadsOf(item)) threads.add(threadId);
10807
+ return threads;
10808
+ };
10809
+ var memberLookup = (project2) => {
10810
+ let pinned;
10811
+ return (threadId) => isGoalThread(project2, threadId) || (pinned ??= pinnedOpenThreads(project2)).has(threadId);
10812
+ };
10813
+ var isMember = (project2, recipient) => "threadId" in recipient ? isGoalThread(project2, recipient.threadId) || hasPinnedOpenItem(project2, recipient.threadId) : isRemoteMember2(project2, recipient.principalId);
10814
+ var isQueuedUnclaimed = (item, threadId) => item.wakeStatus?.[threadId] === "pending" && !item.claimedBy.includes(threadId);
10815
+ var isDeferredWake = (project2, item, threadId) => isGoalThread(project2, threadId) && !isLeadThread(project2, threadId) && item.assigneeThreads.includes(threadId) && isQueuedUnclaimed(item, threadId) && !isClosed(item);
10816
+ function countDeferredWakes(project2, threadId) {
10817
+ let count2 = 0;
10818
+ for (const workItemId in project2.items) {
10819
+ const item = readFact(project2.items, workItemId);
10820
+ if (item !== null && isDeferredWake(project2, item, threadId)) count2++;
10821
+ }
10822
+ return count2;
10823
+ }
10824
+ var exemptFromTrial = (project2, recipient, workItemId) => {
10825
+ if (project2 === void 0 || !("threadId" in recipient)) return false;
10826
+ if (isLeadThread(project2, recipient.threadId)) return true;
10827
+ if (workItemId === void 0) return false;
10828
+ const item = readFact(project2.items, workItemId);
10829
+ return item !== null && item !== void 0 && pinnedThreadsOf(item).includes(recipient.threadId);
10830
+ };
10831
+ function memberOfRecipient(project2, recipient) {
10832
+ if (project2 === void 0) return void 0;
10833
+ const key = recipientKey(recipient);
10834
+ for (const scope2 of scopesOf(project2))
10835
+ for (const [memberId, bound] of Object.entries(scope2.bindings ?? {}))
10836
+ if (recipientKey(bound) === key) return memberId;
10837
+ return void 0;
10838
+ }
10839
+ function executorOf(project2, workItemId, memberId) {
10840
+ if (project2 === void 0) return void 0;
10841
+ const item = readFact(project2.items, workItemId);
10842
+ const target = targetOf(
10843
+ scopeForLine(project2, item?.workLineId).bindings,
10844
+ { kind: "member", memberId },
10845
+ item?.memberThreads
10846
+ );
10847
+ if (target === void 0) return void 0;
10848
+ if ("threadId" in target && isLeadThread(project2, target.threadId)) return void 0;
10849
+ return isMember(project2, target) ? target : void 0;
10850
+ }
10851
+
10852
+ // src/runtime/reviewWakes.ts
10853
+ var decodeCompletion = decodeUnknownOption2(
10854
+ Struct({
10855
+ projectId: String4,
10856
+ workItemId: String4,
10857
+ state: Struct({ group: String4 }),
10858
+ actor: Union2([
10859
+ Struct({ kind: Literal2("agent"), threadId: String4 }),
10860
+ Struct({ kind: Literal2("remote-agent"), principalId: String4 })
10861
+ ])
10862
+ })
10863
+ );
10864
+ var openLeadItemsBlockedBy = (project2, workItemId, lead) => lead === void 0 ? [] : Object.entries(project2.items).filter(
10865
+ ([id]) => isAssignedOpen(project2, id, lead) && readFact(project2.blockers, id)?.includes(workItemId) === true
10866
+ );
10867
+ function reviewOnUpdate(previous, next, event) {
10868
+ const decoded = decodeCompletion(event.payload);
10869
+ if (isNone2(decoded) || decoded.value.state.group !== "completed") return null;
10870
+ const { projectId, workItemId, actor } = decoded.value;
10871
+ if (readFact(previous.items, workItemId)?.stateGroup === "completed") return null;
10872
+ const by = actor.kind === "agent" ? { threadId: actor.threadId } : { principalId: actor.principalId };
10873
+ const lead = scopeOfRecipient(next, by, readFact(next.items, workItemId)?.workLineId)?.leadThreads?.[0] ?? next.leadThreads?.[0];
10874
+ if (lead === void 0) return null;
10875
+ if (!isMember(next, by) || "threadId" in by && isLeadThread(next, by.threadId)) return null;
10876
+ const goals = openLeadItemsBlockedBy(next, workItemId, lead);
10877
+ if (goals.length === 0 || goals.some(([id]) => isWorkable(next, id))) return null;
10878
+ return {
10879
+ key: `review:${workItemId}:${lead}#${event.sequence}`,
10880
+ projectId,
10881
+ workItemId,
10882
+ threadId: lead,
10883
+ by
10884
+ };
10885
+ }
10886
+ var isLiveReview = (project2, review) => isLeadThread(project2, review.threadId) && readFact(project2.items, review.workItemId)?.stateGroup === "completed" && openLeadItemsBlockedBy(project2, review.workItemId, review.threadId).length > 0;
10887
+ var isReviewDue = (project2, key) => project2.pendingReviews?.some((review) => review.key === key && isLiveReview(project2, review)) === true;
10888
+ var dueReviews = (project2) => (project2.pendingReviews ?? []).filter((review) => isLiveReview(project2, review)).map((review) => {
10889
+ const item = readFact(project2.items, review.workItemId);
10890
+ return {
10891
+ ...review,
10892
+ kind: "review",
10893
+ lead: true,
10894
+ ...item === null ? {} : { sequenceId: item.sequenceId }
10895
+ };
10896
+ });
10897
+ function pruneReviews(previous, state) {
10898
+ let next = state;
10899
+ for (const [projectId, project2] of Object.entries(state)) {
10900
+ const pending = project2.pendingReviews;
10901
+ if (pending === void 0 || pending.length === 0) continue;
10902
+ const before = previous[projectId];
10903
+ if (before !== void 0 && before.leadThreads === project2.leadThreads && before.lineScopes === project2.lineScopes && before.items === project2.items && before.blockers === project2.blockers)
10904
+ continue;
10905
+ const live = pending.filter((review) => isLiveReview(project2, review));
10906
+ if (live.length !== pending.length)
10907
+ next = { ...next, [projectId]: { ...project2, pendingReviews: live } };
10908
+ }
10909
+ return next;
10910
+ }
10911
+ function reviewFacts(project2, queues, lead) {
10912
+ const { busy, waiting } = queues;
10913
+ const team = scopeOfThread(project2, lead)?.threads ?? project2.threads;
10914
+ return {
10915
+ idle: team.filter(
10916
+ (threadId) => !isLeadThread(project2, threadId) && !busy.has(threadId) && (waiting.get(threadId) ?? []).length === 0
10917
+ ),
10918
+ queued: [...busy].filter((threadId) => team.includes(threadId)).flatMap(
10919
+ (threadId) => [...waiting.get(threadId) ?? []].sort(compareWakeItems).map(({ workItemId, sequenceId }) => ({ workItemId, sequenceId, threadId }))
10920
+ )
10921
+ };
10922
+ }
10923
+
10924
+ // src/runtime/wakeDue.ts
10925
+ var itemWakeKey = (workItemId, threadId, generation) => generation === 0 ? `item:${workItemId}:${threadId}` : `item:${workItemId}:${threadId}#${generation}`;
10926
+ var unblockedWakeKey = (workItemId, threadId, generation) => `unblocked:${workItemId}:${threadId}#${generation}`;
10927
+ var compare2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
10928
+ var itemWakeCandidate = (projectId, project2, workItemId, item, threadId, woken, isMemberThread) => {
10929
+ if (!isWorkable(project2, workItemId) || !isMemberThread(threadId)) return null;
10930
+ if (isThreadGated(project2, threadId)) return null;
10931
+ const unblocked = readFact(project2.blockedAfterClaim, workItemId) ?? 0;
10932
+ const finishing = unblocked > 0 && item.claimedBy.includes(threadId);
10933
+ if (!finishing && !item.assigneeThreads.includes(threadId)) return null;
10934
+ const lead = isLeadThread(project2, threadId);
10935
+ const leadThreadId = leadOfThread(project2, threadId);
10936
+ const key = finishing ? unblockedWakeKey(workItemId, threadId, unblocked) : itemWakeKey(workItemId, threadId, readFact(project2.rearms, `${workItemId}:${threadId}`) ?? 0);
10937
+ if (woken.has(key) || !finishing && !lead && item.wakeStatus?.[threadId] === "released")
10938
+ return null;
10939
+ const memberId = finishing ? void 0 : memberOfThread(item, threadId);
10940
+ return {
10941
+ key,
10942
+ projectId,
10943
+ threadId,
10944
+ kind: finishing ? "unblocked" : "item",
10945
+ workItemId,
10946
+ sequenceId: item.sequenceId,
10947
+ ...memberId === void 0 ? {} : { memberId },
10948
+ // 成员的叫醒话里点名负责人(卡住时找谁);带队自己和解挡续做不带。
10949
+ ...lead ? { lead: true } : finishing || leadThreadId === void 0 ? {} : { leadThreadId }
10950
+ };
10951
+ };
10952
+ var engineDispatches = (project2, wake) => wake.kind === "item" && wake.memberId !== void 0 && "threadId" in wake && wake.key === itemWakeKey(wake.workItemId, wake.threadId, 0) && lineScopeOfThread(project2, wake.threadId) !== void 0;
10953
+ var wakeGated = (project2, wake) => isRecipientGated(project2, wake, readFact(project2.items, wake.workItemId)?.workLineId);
10954
+ var needsMemberSlot = (wake) => wake.kind === "item" && wake.lead !== true;
10955
+ function memberQueues(projectId, project2, woken = new Set(project2.woken), onlyThreadId) {
10956
+ const isMemberThread = memberLookup(project2);
10957
+ const busy = /* @__PURE__ */ new Set();
10958
+ const waiting = /* @__PURE__ */ new Map();
10959
+ for (const [workItemId, item] of Object.entries(project2.items)) {
10960
+ if (onlyThreadId !== void 0 && !item.assigneeThreads.includes(onlyThreadId)) continue;
10961
+ if (!isWorkable(project2, workItemId)) continue;
10962
+ for (const threadId of item.assigneeThreads) {
10963
+ if (onlyThreadId !== void 0 && threadId !== onlyThreadId || isLeadThread(project2, threadId) || !isMemberThread(threadId))
10964
+ continue;
10965
+ if (item.wakeStatus?.[threadId] === "active") busy.add(threadId);
10966
+ const candidate = itemWakeCandidate(
10967
+ projectId,
10968
+ project2,
10969
+ workItemId,
10970
+ item,
10971
+ threadId,
10972
+ woken,
10973
+ isMemberThread
10974
+ );
10975
+ if (candidate === null || !needsMemberSlot(candidate)) continue;
10976
+ const queue = waiting.get(threadId);
10977
+ if (queue === void 0) waiting.set(threadId, [candidate]);
10978
+ else queue.push(candidate);
10979
+ }
10980
+ }
10981
+ return { busy, waiting };
10982
+ }
10983
+ function memberItemSlots(projectId, project2, woken, onlyThreadId) {
10984
+ const { busy, waiting } = memberQueues(projectId, project2, woken, onlyThreadId);
10985
+ const slots = /* @__PURE__ */ new Map();
10986
+ for (const [threadId, candidates] of waiting)
10987
+ if (!busy.has(threadId))
10988
+ slots.set(
10989
+ threadId,
10990
+ candidates.reduce(
10991
+ (first, candidate) => compareWakeItems(candidate, first) < 0 ? candidate : first
10992
+ )
10993
+ );
10994
+ return slots;
10995
+ }
10996
+ function isWakeDue(state, wake) {
10997
+ const project2 = readFact(state, wake.projectId);
10998
+ if (project2 === null || engineDispatches(project2, wake)) return false;
10999
+ if (wake.kind === "review")
11000
+ return isReviewDue(project2, wake.key) && !wakeGated(project2, wake);
11001
+ if (!("threadId" in wake)) return false;
11002
+ const item = readFact(project2.items, wake.workItemId);
11003
+ if (item === null) return false;
11004
+ const woken = new Set(project2.woken);
11005
+ const candidate = itemWakeCandidate(
11006
+ wake.projectId,
11007
+ project2,
11008
+ wake.workItemId,
11009
+ item,
11010
+ wake.threadId,
11011
+ woken,
11012
+ memberLookup(project2)
11013
+ );
11014
+ if (candidate?.key !== wake.key) return false;
11015
+ return !needsMemberSlot(candidate) || memberItemSlots(wake.projectId, project2, woken, wake.threadId).get(wake.threadId)?.key === wake.key;
11016
+ }
11017
+ function dueWakes(state) {
11018
+ const due = [];
11019
+ for (const [projectId, project2] of Object.entries(state)) {
11020
+ const woken = new Set(project2.woken);
11021
+ const isMemberThread = memberLookup(project2);
11022
+ const slots = memberItemSlots(projectId, project2, woken);
11023
+ for (const [workItemId, item] of Object.entries(project2.items)) {
11024
+ const unblocked = readFact(project2.blockedAfterClaim, workItemId) ?? 0;
11025
+ const recipients = /* @__PURE__ */ new Set([
11026
+ ...item.assigneeThreads,
11027
+ ...unblocked > 0 ? item.claimedBy : []
11028
+ ]);
11029
+ for (const threadId of recipients) {
11030
+ const candidate = itemWakeCandidate(
11031
+ projectId,
11032
+ project2,
11033
+ workItemId,
11034
+ item,
11035
+ threadId,
11036
+ woken,
11037
+ isMemberThread
11038
+ );
11039
+ if (candidate === null || engineDispatches(project2, candidate)) continue;
11040
+ if (needsMemberSlot(candidate) && slots.get(threadId)?.key !== candidate.key) continue;
11041
+ due.push(candidate);
11042
+ }
11043
+ }
11044
+ due.push(...dueReviews(project2).filter((review) => !wakeGated(project2, review)));
11045
+ }
11046
+ return due.sort((a, b) => compare2(a.projectId, b.projectId) || compare2(a.key, b.key));
11047
+ }
11048
+ function teamItemDecision(project2, projectId, workItemId, threadId) {
11049
+ const item = readFact(project2.items, workItemId);
11050
+ if (item === null) return { kind: "held", reason: "the team has not seen this work item yet" };
11051
+ if (isClosed(item)) return { kind: "held", reason: "the work item is closed" };
11052
+ if (hasOpenBlocker(project2, workItemId))
11053
+ return { kind: "held", reason: "the work item is blocked by unfinished work items" };
11054
+ if (isThreadGated(project2, threadId))
11055
+ return { kind: "held", reason: "the team is stopped or paused" };
11056
+ const woken = new Set(project2.woken);
11057
+ const candidate = itemWakeCandidate(
11058
+ projectId,
11059
+ project2,
11060
+ workItemId,
11061
+ item,
11062
+ threadId,
11063
+ woken,
11064
+ memberLookup(project2)
11065
+ );
11066
+ if (candidate === null)
11067
+ return {
11068
+ kind: "held",
11069
+ reason: item.assigneeThreads.includes(threadId) ? "the team already handed this work item over" : "the team runs this member in another session"
11070
+ };
11071
+ if (needsMemberSlot(candidate)) {
11072
+ const slot = memberItemSlots(projectId, project2, woken, threadId).get(threadId);
11073
+ if (slot?.key !== candidate.key)
11074
+ return {
11075
+ kind: "held",
11076
+ reason: slot === void 0 ? "the member is still working on another work item" : `the member gets W${slot.sequenceId} first`
11077
+ };
11078
+ }
11079
+ return { kind: "due", wake: candidate };
11080
+ }
11081
+
11082
+ // src/runtime/dispatchGate.ts
11083
+ var allow = { allowed: true };
11084
+ var hold = (detail) => ({ allowed: false, detail });
11085
+ var allowIn = (threadId, picked) => threadId === picked ? allow : { allowed: true, threadId };
11086
+ var JOINING = "the member is joining this team; its team session opens within a couple of minutes";
11087
+ function registerDispatchGate(ctx, deps) {
11393
11088
  const pacing = createDeliveryPacing(
11394
11089
  ctx.derivedState(deliveryPacingStateDefinition),
11395
11090
  async (threadId) => waitingOnSomeone(await readMemberExecution(ctx, threadId))
11396
11091
  );
11397
- const bind4 = async (projectId, target, to) => {
11398
- try {
11399
- await ctx.communication.deliveries.bind({
11400
- projectId,
11401
- recordId: target.recordId,
11402
- targetKey: target.targetKey,
11403
- to
11404
- });
11405
- } catch (error) {
11406
- const refusal = decodeHostRefusal(error);
11407
- if (isSome2(refusal) && refusal.value.rejection.code === "project-not-writable") return;
11408
- ctx.log.warn("team group delivery bind failed", {
11409
- recordId: target.recordId,
11410
- targetKey: target.targetKey,
11411
- error
11412
- });
11413
- }
11414
- };
11415
- const scopeUsable = async (projectId, project2, workLineId, memberId) => project2 !== void 0 && !isLineGated(project2, workLineId) && !isMemberPausedOnLine(project2, workLineId, memberId) && await memberOnLine(ctx, projectId, workLineId, memberId).catch(() => false);
11416
- const boundRecipient = async (project2, workLineId, memberId) => {
11417
- const scope2 = project2.lineScopes?.[workLineId];
11418
- const recipient = scope2?.bindings?.[memberId];
11419
- if (recipient === void 0) return null;
11420
- const execution = "threadId" in recipient ? await ctx.projections.getThreadExecution(recipient.threadId) : null;
11421
- if ("threadId" in recipient && scope2?.leadThreads?.includes(recipient.threadId) === true && (execution?.session?.status === "error" || execution?.latestTurn?.state === "error"))
11422
- return null;
11423
- return { recipient, execution };
11424
- };
11425
- const candidate = async (projectId, project2, workLineId, memberId) => await scopeUsable(projectId, project2, workLineId, memberId) ? boundRecipient(project2, workLineId, memberId) : null;
11426
- const resolve2 = async (projectId, project2, target) => {
11427
- const scopeLineId = target.scope?.workLineId;
11428
- if (scopeLineId !== void 0 && !await scopeUsable(projectId, project2, scopeLineId, target.member))
11429
- return null;
11430
- const member = await findMember(ctx, projectId, target.member);
11431
- if (member === null) return null;
11432
- if (member.kind === "human")
11433
- return member.identity?.kind === "human" ? { person: member.identity.principal } : null;
11434
- if (scopeLineId !== void 0)
11435
- return (await boundRecipient(project2, scopeLineId, target.member))?.recipient ?? null;
11436
- let best = null;
11437
- for (const workLineId of Object.keys(project2?.lineScopes ?? {}).sort()) {
11438
- const found = await candidate(projectId, project2, workLineId, target.member);
11439
- if (found === null) continue;
11440
- const at = Date.parse(found.execution?.latestTurn?.startedAt ?? "") || 0;
11441
- if (best === null || at > best.at) best = { recipient: found.recipient, at };
11442
- }
11443
- return best?.recipient ?? null;
11092
+ ctx.reactor({
11093
+ name: "team-delivery-pacing",
11094
+ events: ["thread.session-set"],
11095
+ handle: (event) => pacing.observe(event)
11096
+ });
11097
+ const legacyProjectGoal = async (project2, input) => {
11098
+ if (project2.goalId === void 0) return allow;
11099
+ if (readFact(project2.bindings ?? {}, input.memberId) !== null)
11100
+ return hold("the project's older team goal dispatches this member");
11101
+ if (isClosed(readFact(project2.items, teamGoalItemId(input.projectId, project2.goalId))))
11102
+ return allow;
11103
+ const lines = await ctx.work.lines.list({
11104
+ projectId: input.projectId,
11105
+ includeArchived: true,
11106
+ limit: 1
11107
+ });
11108
+ return lines.items.length === 0 ? hold("the project's older team goal takes this member in and dispatches it") : allow;
11444
11109
  };
11445
- const sweep = async (projectId, projectRow) => {
11446
- if ((projectRow ?? await ctx.projects.get({ projectId })).archivedAt != null) return;
11447
- const { items: targets } = await readPages(
11448
- Number.POSITIVE_INFINITY,
11449
- (page) => ctx.communication.deliveries.listUnbound({ projectId, ...page })
11450
- );
11451
- if (targets.length === 0) return;
11452
- const project2 = (await wakes.get())[projectId];
11453
- const froms = /* @__PURE__ */ new Map();
11454
- for (const target of targets) {
11455
- try {
11456
- const recipient = await resolve2(projectId, project2, target);
11457
- if (recipient === null) continue;
11458
- if ("threadId" in recipient) {
11459
- if (!froms.has(target.recordId)) {
11460
- const [record2] = await ctx.communication.records.get({ recordIds: [target.recordId] });
11461
- froms.set(target.recordId, record2?.from);
11462
- }
11463
- const from = froms.get(target.recordId);
11464
- const fromPerson = from?.kind === "user" || from?.kind === "local-owner";
11465
- if (!fromPerson && !await pacing.allows(recipient.threadId, await now3())) continue;
11466
- }
11467
- await bind4(projectId, target, recipient);
11468
- } catch (error) {
11469
- ctx.log.warn("team group delivery sweep target failed", {
11470
- recordId: target.recordId,
11471
- targetKey: target.targetKey,
11472
- error
11473
- });
11110
+ const memberThreadsOf = (scope2, memberId) => teamSessions(scope2).filter((threadId) => scope2.memberIds?.[threadId] === memberId);
11111
+ const itemGate = async (project2, scope2, input) => {
11112
+ const workItemId = input.workItemId;
11113
+ const binding = readFact(scope2.bindings ?? {}, input.memberId);
11114
+ if (binding === null) return hold(JOINING);
11115
+ if (!("threadId" in binding)) return hold("the team runs this member on another machine");
11116
+ const lead = scope2.leadThreads?.includes(binding.threadId) === true;
11117
+ const executor = lead ? { threadId: binding.threadId } : executorOf(project2, workItemId, input.memberId);
11118
+ if (executor === void 0 || !("threadId" in executor)) return hold(JOINING);
11119
+ const threadId = executor.threadId;
11120
+ const decision = teamItemDecision(project2, input.projectId, workItemId, threadId);
11121
+ if (decision.kind === "held") return hold(decision.reason);
11122
+ const isReady2 = await deps.readiness();
11123
+ if (!exemptFromTrial(project2, { threadId }, workItemId) && !isReady2(input.projectId, input.memberId, scope2.goalId))
11124
+ return hold("the member has not passed its trial run yet");
11125
+ const worktree = lead ? void 0 : memberWorktreeFor(deps.goals, project2, input.projectId, { threadId });
11126
+ if (worktree !== void 0 && worktree.failure === void 0) {
11127
+ const execution = await ctx.projections.getThreadExecution(threadId);
11128
+ if (execution !== null && !execution.worktreePath) {
11129
+ deps.requestWorktree(input.projectId, threadId);
11130
+ return hold("the member's own worktree is being prepared");
11474
11131
  }
11475
11132
  }
11133
+ return allowIn(threadId, input.threadId);
11476
11134
  };
11477
- const sweepAll = async () => {
11478
- const projects = await ctx.projects.list({});
11479
- for (const project2 of projects) {
11480
- await sweep(project2.id, project2).catch(
11481
- (error) => ctx.log.warn("team group delivery sweep failed", { projectId: project2.id, error })
11482
- );
11483
- }
11135
+ const messageGate = async (project2, scope2, input) => {
11136
+ const binding = readFact(scope2.bindings ?? {}, input.memberId);
11137
+ if (binding === null) return hold(JOINING);
11138
+ if (!("threadId" in binding)) return allow;
11139
+ const threadId = input.threadId !== null && memberThreadsOf(scope2, input.memberId).includes(input.threadId) ? input.threadId : binding.threadId;
11140
+ const isReady2 = await deps.readiness();
11141
+ if (!exemptFromTrial(project2, { threadId }, input.workItemId ?? void 0) && !isReady2(input.projectId, input.memberId, scope2.goalId))
11142
+ return hold("the member has not passed its trial run yet");
11143
+ if (input.reason === "group" && input.recordId !== void 0) {
11144
+ const [record2] = await ctx.communication.records.get({ recordIds: [input.recordId] });
11145
+ const fromAgent = record2 !== void 0 && record2.from.kind !== "user" && record2.from.kind !== "local-owner";
11146
+ if (fromAgent && !await pacing.allows(threadId, await deps.now()))
11147
+ return hold("slowed down: the member made no progress in its last turns");
11148
+ }
11149
+ return allowIn(threadId, input.threadId);
11484
11150
  };
11485
- ctx.reactor({
11486
- name: "team-group-delivery",
11487
- // 成员建出或改了(人点「这是我」绑上)也补扫:停在待绑定的人目标立刻投到人。
11488
- events: [
11489
- "communication.group-message-sent",
11490
- "work.line-restored",
11491
- // 线或成员恢复:暂停期间没绑的目标补扫出去。
11492
- "work.line-resumed",
11493
- "plugin.event-appended",
11494
- "member.created",
11495
- "member.updated",
11496
- "member.restored"
11497
- ],
11498
- handle: (event) => {
11499
- const projectId = decodeProjectEvent(event.payload);
11500
- if (isSome2(projectId)) return sweep(projectId.value.projectId);
11501
- const fact = decodeFact5(event.payload);
11502
- return isSome2(fact) ? sweep(fact.value.data.projectId) : Promise.resolve();
11503
- }
11504
- });
11505
- ctx.reactor({
11506
- name: "team-group-delivery-pacing",
11507
- events: ["thread.session-set"],
11508
- handle: async (event) => {
11509
- const threadId = event.payload?.threadId;
11510
- if (typeof threadId !== "string" || !Object.values(await wakes.get()).some((project2) => isGoalThread(project2, threadId)))
11511
- return;
11512
- return pacing.observe(event);
11151
+ const check = async (input) => {
11152
+ const project2 = readFact(
11153
+ await deps.wakes.get({ throughSequence: input.throughSequence }),
11154
+ input.projectId
11155
+ );
11156
+ if (project2 === null) return allow;
11157
+ const scope2 = input.workLineId === null ? void 0 : project2.lineScopes?.[input.workLineId];
11158
+ if (scope2 === void 0) {
11159
+ if (input.reason === "item") return legacyProjectGoal(project2, input);
11160
+ return project2.goalId !== void 0 && readFact(project2.bindings ?? {}, input.memberId) !== null ? messageGate(project2, project2, input) : allow;
11513
11161
  }
11514
- });
11515
- ctx.interval({ name: "team-group-delivery", everyMs: SWEEP_MS, handle: sweepAll });
11516
- const ready = sweepAll().catch(
11517
- (error) => ctx.log.warn("team group delivery catch-up failed", { error })
11518
- );
11519
- return { ready };
11162
+ if (project2.lineStops?.[input.workLineId] !== void 0)
11163
+ return hold("the team on this work line is stopped");
11164
+ return input.reason === "item" ? itemGate(project2, scope2, input) : messageGate(project2, scope2, input);
11165
+ };
11166
+ return ctx.operations.guard({ operations: ["work.dispatch"], check });
11520
11167
  }
11521
11168
 
11522
11169
  // src/runtime/memberBinding.ts
@@ -12182,268 +11829,92 @@ function foldRecovery(state, event, previous) {
12182
11829
  }
12183
11830
  if (retriedItemIds.length === member.retriedItemIds.length && pending === member.pending && remindedItemIds.length === (member.remindedItemIds?.length ?? 0) && escalatedItemIds.length === (member.escalatedItemIds?.length ?? 0) && unprogressedRetryItemIds.length === (member.unprogressedRetryItemIds?.length ?? 0))
12184
11831
  continue;
12185
- changed = true;
12186
- recovery[threadId] = {
12187
- ...member,
12188
- retriedItemIds,
12189
- remindedItemIds,
12190
- escalatedItemIds,
12191
- unprogressedRetryItemIds,
12192
- pending,
12193
- handledTurnIds: pending === null && member.pending !== null ? [.../* @__PURE__ */ new Set([...member.handledTurnIds, member.pending.turnId])] : member.handledTurnIds
12194
- };
12195
- }
12196
- return changed ? { ...state, [work.projectId]: { ...project2, recovery } } : state;
12197
- }
12198
- return state;
12199
- }
12200
- function dueRecoveries(state, changedThreadId) {
12201
- return Object.entries(state).flatMap(
12202
- ([projectId, project2]) => Object.entries(project2.recovery ?? {}).flatMap(([threadId, member]) => {
12203
- const scope2 = scopeOfThread(project2, threadId);
12204
- const memberId = scope2?.memberIds?.[threadId];
12205
- const leadThreadId = scope2?.leadThreads?.[0];
12206
- if (member.pending === null || changedThreadId !== void 0 && threadId !== changedThreadId && member.pending.plan?.recipientThreadId !== changedThreadId || memberId === void 0 || leadThreadId === void 0 || threadId === leadThreadId || isThreadGated(project2, threadId) || !isMember(project2, { threadId }))
12207
- return [];
12208
- const stalled = member.pending.kind === "stall";
12209
- if (member.handledTurnIds.includes(member.pending.turnId)) return [];
12210
- const items = Object.entries(project2.items).filter(
12211
- ([workItemId, item]) => (stalled ? item.stateGroup === "started" && item.assigneeThreads.includes(threadId) : isOpenWorkFor(project2, workItemId, threadId) && !isQueuedUnclaimed(item, threadId)) && (!stalled || (member.pending?.plan?.workItemIds.includes(workItemId) || !(member.escalatedItemIds ?? []).includes(workItemId)) && !hasOpenBlocker(project2, workItemId))
12212
- ).map(([workItemId, item]) => ({
12213
- workItemId,
12214
- sequenceId: item.sequenceId,
12215
- lastCommentId: item.lastCommentId,
12216
- waitingForReply: item.lastActionCommentThreadId === threadId
12217
- })).sort((a, b) => a.sequenceId - b.sequenceId);
12218
- const deferredWakeCount = countDeferredWakes(project2, threadId);
12219
- return items.length === 0 ? [] : [
12220
- {
12221
- ...member.pending,
12222
- projectId,
12223
- threadId,
12224
- memberId,
12225
- leadThreadId,
12226
- key: JSON.stringify([threadId, member.pending.turnId]),
12227
- alreadyRetried: items.some(
12228
- (item) => member.retriedItemIds.includes(item.workItemId) || (member.remindedItemIds ?? []).includes(item.workItemId) || (member.escalatedItemIds ?? []).includes(item.workItemId) || (member.pending?.continuedItemIds ?? []).includes(item.workItemId)
12229
- ),
12230
- items,
12231
- ...deferredWakeCount > 0 ? { deferredWakeCount } : {}
12232
- }
12233
- ];
12234
- })
12235
- );
12236
- }
12237
-
12238
- // src/runtime/reviewWakes.ts
12239
- var decodeCompletion = decodeUnknownOption2(
12240
- Struct({
12241
- projectId: String4,
12242
- workItemId: String4,
12243
- state: Struct({ group: String4 }),
12244
- actor: Union2([
12245
- Struct({ kind: Literal2("agent"), threadId: String4 }),
12246
- Struct({ kind: Literal2("remote-agent"), principalId: String4 })
12247
- ])
12248
- })
12249
- );
12250
- var openLeadItemsBlockedBy = (project2, workItemId, lead) => lead === void 0 ? [] : Object.entries(project2.items).filter(
12251
- ([id]) => isAssignedOpen(project2, id, lead) && readFact(project2.blockers, id)?.includes(workItemId) === true
12252
- );
12253
- function reviewOnUpdate(previous, next, event) {
12254
- const decoded = decodeCompletion(event.payload);
12255
- if (isNone2(decoded) || decoded.value.state.group !== "completed") return null;
12256
- const { projectId, workItemId, actor } = decoded.value;
12257
- if (readFact(previous.items, workItemId)?.stateGroup === "completed") return null;
12258
- const by = actor.kind === "agent" ? { threadId: actor.threadId } : { principalId: actor.principalId };
12259
- const lead = scopeOfRecipient(next, by, readFact(next.items, workItemId)?.workLineId)?.leadThreads?.[0] ?? next.leadThreads?.[0];
12260
- if (lead === void 0) return null;
12261
- if (!isMember(next, by) || "threadId" in by && isLeadThread(next, by.threadId)) return null;
12262
- const goals = openLeadItemsBlockedBy(next, workItemId, lead);
12263
- if (goals.length === 0 || goals.some(([id]) => isWorkable(next, id))) return null;
12264
- return {
12265
- key: `review:${workItemId}:${lead}#${event.sequence}`,
12266
- projectId,
12267
- workItemId,
12268
- threadId: lead,
12269
- by
12270
- };
12271
- }
12272
- var isLiveReview = (project2, review) => isLeadThread(project2, review.threadId) && readFact(project2.items, review.workItemId)?.stateGroup === "completed" && openLeadItemsBlockedBy(project2, review.workItemId, review.threadId).length > 0;
12273
- var isReviewDue = (project2, key) => project2.pendingReviews?.some((review) => review.key === key && isLiveReview(project2, review)) === true;
12274
- var dueReviews = (project2) => (project2.pendingReviews ?? []).filter((review) => isLiveReview(project2, review)).map((review) => {
12275
- const item = readFact(project2.items, review.workItemId);
12276
- return {
12277
- ...review,
12278
- kind: "review",
12279
- lead: true,
12280
- ...item === null ? {} : { sequenceId: item.sequenceId }
12281
- };
12282
- });
12283
- function pruneReviews(previous, state) {
12284
- let next = state;
12285
- for (const [projectId, project2] of Object.entries(state)) {
12286
- const pending = project2.pendingReviews;
12287
- if (pending === void 0 || pending.length === 0) continue;
12288
- const before = previous[projectId];
12289
- if (before !== void 0 && before.leadThreads === project2.leadThreads && before.lineScopes === project2.lineScopes && before.items === project2.items && before.blockers === project2.blockers)
12290
- continue;
12291
- const live = pending.filter((review) => isLiveReview(project2, review));
12292
- if (live.length !== pending.length)
12293
- next = { ...next, [projectId]: { ...project2, pendingReviews: live } };
12294
- }
12295
- return next;
12296
- }
12297
- function reviewFacts(project2, queues, lead) {
12298
- const { busy, waiting } = queues;
12299
- const team = scopeOfThread(project2, lead)?.threads ?? project2.threads;
12300
- return {
12301
- idle: team.filter(
12302
- (threadId) => !isLeadThread(project2, threadId) && !busy.has(threadId) && (waiting.get(threadId) ?? []).length === 0
12303
- ),
12304
- queued: [...busy].filter((threadId) => team.includes(threadId)).flatMap(
12305
- (threadId) => [...waiting.get(threadId) ?? []].sort(compareWakeItems).map(({ workItemId, sequenceId }) => ({ workItemId, sequenceId, threadId }))
12306
- )
12307
- };
12308
- }
12309
-
12310
- // src/runtime/wakeDue.ts
12311
- var itemWakeKey = (workItemId, threadId, generation) => generation === 0 ? `item:${workItemId}:${threadId}` : `item:${workItemId}:${threadId}#${generation}`;
12312
- var unblockedWakeKey = (workItemId, threadId, generation) => `unblocked:${workItemId}:${threadId}#${generation}`;
12313
- var compare2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
12314
- var itemWakeCandidate = (projectId, project2, workItemId, item, threadId, woken, isMemberThread) => {
12315
- if (!isWorkable(project2, workItemId) || !isMemberThread(threadId)) return null;
12316
- if (isThreadGated(project2, threadId)) return null;
12317
- const unblocked = readFact(project2.blockedAfterClaim, workItemId) ?? 0;
12318
- const finishing = unblocked > 0 && item.claimedBy.includes(threadId);
12319
- if (!finishing && !item.assigneeThreads.includes(threadId)) return null;
12320
- const lead = isLeadThread(project2, threadId);
12321
- const leadThreadId = leadOfThread(project2, threadId);
12322
- const key = finishing ? unblockedWakeKey(workItemId, threadId, unblocked) : itemWakeKey(workItemId, threadId, readFact(project2.rearms, `${workItemId}:${threadId}`) ?? 0);
12323
- if (woken.has(key) || !finishing && !lead && item.wakeStatus?.[threadId] === "released")
12324
- return null;
12325
- const memberId = finishing ? void 0 : memberOfThread(item, threadId);
12326
- return {
12327
- key,
12328
- projectId,
12329
- threadId,
12330
- kind: finishing ? "unblocked" : "item",
12331
- workItemId,
12332
- sequenceId: item.sequenceId,
12333
- ...memberId === void 0 ? {} : { memberId },
12334
- // 成员的叫醒话里点名负责人(卡住时找谁);带队自己和解挡续做不带。
12335
- ...lead ? { lead: true } : finishing || leadThreadId === void 0 ? {} : { leadThreadId }
12336
- };
12337
- };
12338
- var wakeGated = (project2, wake) => isRecipientGated(project2, wake, readFact(project2.items, wake.workItemId)?.workLineId);
12339
- var needsMemberSlot = (wake) => wake.kind === "item" && wake.lead !== true;
12340
- function memberQueues(projectId, project2, woken = new Set(project2.woken), onlyThreadId) {
12341
- const isMemberThread = memberLookup(project2);
12342
- const busy = /* @__PURE__ */ new Set();
12343
- const waiting = /* @__PURE__ */ new Map();
12344
- for (const [workItemId, item] of Object.entries(project2.items)) {
12345
- if (onlyThreadId !== void 0 && !item.assigneeThreads.includes(onlyThreadId)) continue;
12346
- if (!isWorkable(project2, workItemId)) continue;
12347
- for (const threadId of item.assigneeThreads) {
12348
- if (onlyThreadId !== void 0 && threadId !== onlyThreadId || isLeadThread(project2, threadId) || !isMemberThread(threadId))
12349
- continue;
12350
- if (item.wakeStatus?.[threadId] === "active") busy.add(threadId);
12351
- const candidate = itemWakeCandidate(
12352
- projectId,
12353
- project2,
12354
- workItemId,
12355
- item,
12356
- threadId,
12357
- woken,
12358
- isMemberThread
12359
- );
12360
- if (candidate === null || !needsMemberSlot(candidate)) continue;
12361
- const queue = waiting.get(threadId);
12362
- if (queue === void 0) waiting.set(threadId, [candidate]);
12363
- else queue.push(candidate);
11832
+ changed = true;
11833
+ recovery[threadId] = {
11834
+ ...member,
11835
+ retriedItemIds,
11836
+ remindedItemIds,
11837
+ escalatedItemIds,
11838
+ unprogressedRetryItemIds,
11839
+ pending,
11840
+ handledTurnIds: pending === null && member.pending !== null ? [.../* @__PURE__ */ new Set([...member.handledTurnIds, member.pending.turnId])] : member.handledTurnIds
11841
+ };
12364
11842
  }
11843
+ return changed ? { ...state, [work.projectId]: { ...project2, recovery } } : state;
12365
11844
  }
12366
- return { busy, waiting };
12367
- }
12368
- function memberItemSlots(projectId, project2, woken, onlyThreadId) {
12369
- const { busy, waiting } = memberQueues(projectId, project2, woken, onlyThreadId);
12370
- const slots = /* @__PURE__ */ new Map();
12371
- for (const [threadId, candidates] of waiting)
12372
- if (!busy.has(threadId))
12373
- slots.set(
12374
- threadId,
12375
- candidates.reduce(
12376
- (first, candidate) => compareWakeItems(candidate, first) < 0 ? candidate : first
12377
- )
12378
- );
12379
- return slots;
12380
- }
12381
- function isWakeDue(state, wake) {
12382
- const project2 = readFact(state, wake.projectId);
12383
- if (project2 === null) return false;
12384
- if (wake.kind === "comment")
12385
- return project2.pendingComments.some(
12386
- (comment) => comment.key === wake.key && !wakeGated(project2, comment)
12387
- );
12388
- if (wake.kind === "review")
12389
- return isReviewDue(project2, wake.key) && !wakeGated(project2, wake);
12390
- if (!("threadId" in wake)) return false;
12391
- const item = readFact(project2.items, wake.workItemId);
12392
- if (item === null) return false;
12393
- const woken = new Set(project2.woken);
12394
- const candidate = itemWakeCandidate(
12395
- wake.projectId,
12396
- project2,
12397
- wake.workItemId,
12398
- item,
12399
- wake.threadId,
12400
- woken,
12401
- memberLookup(project2)
12402
- );
12403
- if (candidate?.key !== wake.key) return false;
12404
- return !needsMemberSlot(candidate) || memberItemSlots(wake.projectId, project2, woken, wake.threadId).get(wake.threadId)?.key === wake.key;
11845
+ return state;
12405
11846
  }
12406
- function dueWakes(state) {
12407
- const due = [];
12408
- for (const [projectId, project2] of Object.entries(state)) {
12409
- const woken = new Set(project2.woken);
12410
- const isMemberThread = memberLookup(project2);
12411
- const slots = memberItemSlots(projectId, project2, woken);
12412
- for (const [workItemId, item] of Object.entries(project2.items)) {
12413
- const unblocked = readFact(project2.blockedAfterClaim, workItemId) ?? 0;
12414
- const recipients = /* @__PURE__ */ new Set([
12415
- ...item.assigneeThreads,
12416
- ...unblocked > 0 ? item.claimedBy : []
12417
- ]);
12418
- for (const threadId of recipients) {
12419
- const candidate = itemWakeCandidate(
11847
+ function dueRecoveries(state, changedThreadId) {
11848
+ return Object.entries(state).flatMap(
11849
+ ([projectId, project2]) => Object.entries(project2.recovery ?? {}).flatMap(([threadId, member]) => {
11850
+ const scope2 = scopeOfThread(project2, threadId);
11851
+ const memberId = scope2?.memberIds?.[threadId];
11852
+ const leadThreadId = scope2?.leadThreads?.[0];
11853
+ if (member.pending === null || changedThreadId !== void 0 && threadId !== changedThreadId && member.pending.plan?.recipientThreadId !== changedThreadId || memberId === void 0 || leadThreadId === void 0 || threadId === leadThreadId || isThreadGated(project2, threadId) || !isMember(project2, { threadId }))
11854
+ return [];
11855
+ const stalled = member.pending.kind === "stall";
11856
+ if (member.handledTurnIds.includes(member.pending.turnId)) return [];
11857
+ const items = Object.entries(project2.items).filter(
11858
+ ([workItemId, item]) => (stalled ? item.stateGroup === "started" && item.assigneeThreads.includes(threadId) : isOpenWorkFor(project2, workItemId, threadId) && !isQueuedUnclaimed(item, threadId)) && (!stalled || (member.pending?.plan?.workItemIds.includes(workItemId) || !(member.escalatedItemIds ?? []).includes(workItemId)) && !hasOpenBlocker(project2, workItemId))
11859
+ ).map(([workItemId, item]) => ({
11860
+ workItemId,
11861
+ sequenceId: item.sequenceId,
11862
+ lastCommentId: item.lastCommentId,
11863
+ waitingForReply: item.lastActionCommentThreadId === threadId
11864
+ })).sort((a, b) => a.sequenceId - b.sequenceId);
11865
+ const deferredWakeCount = countDeferredWakes(project2, threadId);
11866
+ return items.length === 0 ? [] : [
11867
+ {
11868
+ ...member.pending,
12420
11869
  projectId,
12421
- project2,
12422
- workItemId,
12423
- item,
12424
11870
  threadId,
12425
- woken,
12426
- isMemberThread
12427
- );
12428
- if (candidate === null) continue;
12429
- if (needsMemberSlot(candidate) && slots.get(threadId)?.key !== candidate.key) continue;
12430
- due.push(candidate);
12431
- }
12432
- }
12433
- due.push(...dueReviews(project2).filter((review) => !wakeGated(project2, review)));
12434
- for (const comment of project2.pendingComments) {
12435
- if (wakeGated(project2, comment)) continue;
12436
- const item = readFact(project2.items, comment.workItemId);
12437
- due.push({
12438
- ...comment,
12439
- kind: "comment",
12440
- ...item === null ? {} : { sequenceId: item.sequenceId }
12441
- });
12442
- }
12443
- }
12444
- return due.sort((a, b) => compare2(a.projectId, b.projectId) || compare2(a.key, b.key));
11871
+ memberId,
11872
+ leadThreadId,
11873
+ key: JSON.stringify([threadId, member.pending.turnId]),
11874
+ alreadyRetried: items.some(
11875
+ (item) => member.retriedItemIds.includes(item.workItemId) || (member.remindedItemIds ?? []).includes(item.workItemId) || (member.escalatedItemIds ?? []).includes(item.workItemId) || (member.pending?.continuedItemIds ?? []).includes(item.workItemId)
11876
+ ),
11877
+ items,
11878
+ ...deferredWakeCount > 0 ? { deferredWakeCount } : {}
11879
+ }
11880
+ ];
11881
+ })
11882
+ );
12445
11883
  }
12446
11884
 
11885
+ // src/runtime/wakeTexts.ts
11886
+ var workLabel = (workItemId, sequenceId) => sequenceId === void 0 ? `the work item with workItemId ${workItemId}` : `work item W${sequenceId} (workItemId ${workItemId})`;
11887
+ var titled = (workItemId, sequenceId, title) => `${workLabel(workItemId, sequenceId)}${title ? `: ${title}` : ""}`;
11888
+ var ITEM_SUMMARY_MAX_CHARS = 300;
11889
+ var descriptionStart = (description) => {
11890
+ const first = description?.trim().split(/\n\s*\n/u)[0]?.trim() ?? "";
11891
+ if (first === "") return "";
11892
+ const points = Array.from(first);
11893
+ const summary = points.length > ITEM_SUMMARY_MAX_CHARS ? `${points.slice(0, ITEM_SUMMARY_MAX_CHARS).join("")}\u2026` : first;
11894
+ return `Its description starts: ${summary}`;
11895
+ };
11896
+ var itemHeadline = (sequenceId, item) => {
11897
+ const title = item?.title ? `${sequenceId === void 0 ? "Work item" : `W${sequenceId}`}: ${item.title}.` : "";
11898
+ const parts = [title, descriptionStart(item?.description)].filter((part2) => part2 !== "");
11899
+ return parts.length === 0 ? "" : `${parts.join(" ")} `;
11900
+ };
11901
+ var MEMBER_INSTRUCTIONS_LIMIT = 2e3;
11902
+ var startClause = (member) => member === void 0 ? "Otherwise claim it with work_claim_item before you change any file." : "Otherwise start it before you change any file: use work_update_item to move it into an in-progress state (see work_list_states) and leave its assignees as they are. It is assigned to you as a team member, so do not claim it with work_claim_item; claiming is for work nobody holds.";
11903
+ var memberInstructionsClause = (member) => member?.instructions == null || member.instructions.trim() === "" ? "" : ` Your standing work instructions as a member of this team: ${member.instructions}${member.nextOffset === null ? "" : ` (This is the first part only. Read the rest with work_read_member, passing memberId "${member.memberId}" and textOffset ${member.nextOffset}.)`}`;
11904
+ var itemText = (workItemId, sequenceId, item, leadThreadId, member) => `${itemHeadline(sequenceId, item)}You have ${workLabel(workItemId, sequenceId)}. ${handbookMessage("memberBrief")} Then read it with work_read_items, passing workItemIds: ["${workItemId}"]. If it is blocked by items that are not done yet, do not start it; you will be told again when they are done. ${startClause(member)}${memberInstructionsClause(member)} Before you change anything, search this repository's rules and checks with memory_search and follow what applies. Then work on it. ${MEMBER_READ_RANGES_RULE} ${MEMBER_NO_STASH_RULE} ${MEMBER_ASK_BEFORE_OUTSIDE_RULE} ${MEMBER_CHECKS_RULE} ${MEMBER_PROGRESS_RULE} ${memberStuckRule(leadThreadId)}${memberGroupRule(leadThreadId)} ${PERSON_MESSAGE_RULE}`;
11905
+ var leadItemText = (workItemId, sequenceId, title) => `You have ${titled(workItemId, sequenceId, title)}. You lead this team, so this item is the goal itself: do not claim it or do its work yourself. Read it with work_read_items, passing workItemIds: ["${workItemId}"]. If it has no work items yet, split it as your team brief says. ${LEAD_SPLIT_RULE} ${LEAD_OWN_ITEM_RULE} Record dependencies and assign each item to one member as the brief describes. ${LEAD_NO_MEMBER_TASKS} ${PERSON_MESSAGE_RULE}`;
11906
+ var leadUnblockedText = (workItemId, sequenceId, title) => `Everything blocking ${titled(workItemId, sequenceId, title)} is done now. Read those items with work_read_items and check the results. ${LEAD_ACCEPT_RULE} ${LEAD_SEND_BACK_RULE} When all of them are acceptable, mark this item done with work_update_item. ${LEAD_NO_MEMBER_TASKS}`;
11907
+ var unblockedText = (workItemId, sequenceId, title) => `Everything blocking ${titled(workItemId, sequenceId, title)} is done now. Read those items with work_read_items, finish this one, and mark it done with work_update_item.`;
11908
+ var leadReviewText = (workItemId, sequenceId, title, by, facts) => [
11909
+ `${by} marked ${titled(workItemId, sequenceId, title)} done. Read it with work_read_items, passing workItemIds: ["${workItemId}"], and check it now.`,
11910
+ LEAD_ACCEPT_RULE,
11911
+ LEAD_SEND_BACK_RULE,
11912
+ ...facts.idle.length === 0 ? [] : [`Nothing in progress now: ${facts.idle.join("; ")}.`],
11913
+ ...facts.queued.length === 0 ? [] : [`Waiting in line, not blocked: ${facts.queued.join("; ")}.`],
11914
+ ...facts.idle.length > 0 && facts.queued.length > 0 ? [LEAD_REBALANCE_RULE] : [],
11915
+ LEAD_NO_MEMBER_TASKS
11916
+ ].join(" ");
11917
+
12447
11918
  // src/runtime/memberAssignments.ts
12448
11919
  var threadOf = (bindings, memberId) => {
12449
11920
  const binding = bindings === void 0 ? null : readFact(bindings, memberId);
@@ -12583,6 +12054,8 @@ var WAKE_EVENTS = [
12583
12054
  "work.relation-added",
12584
12055
  "work.relation-removed",
12585
12056
  "work.comment-posted",
12057
+ // 看板把件交到了成员会话上(引擎叫醒了它):当成这一次「你有活」已经叫过,占名额、固定执行者都照旧。
12058
+ "work.item-dispatched",
12586
12059
  "plugin.event-appended",
12587
12060
  "thread.session-set",
12588
12061
  // 线与线上成员的暂停:引擎的看板事实,派发闸读它(见 `linePauses` / `pausedMembers`)。
@@ -12599,7 +12072,6 @@ var emptyProject = () => ({
12599
12072
  remoteMembers: [],
12600
12073
  items: {},
12601
12074
  blockers: {},
12602
- pendingComments: [],
12603
12075
  woken: [],
12604
12076
  rearms: {},
12605
12077
  blockedAfterClaim: {}
@@ -12665,6 +12137,24 @@ var withoutInitialLeadWake = (project2, projectId, workItemId) => {
12665
12137
  woken: [...project2.woken, ...leads.map((threadId) => itemWakeKey(workItemId, threadId, 0))]
12666
12138
  };
12667
12139
  };
12140
+ var decodeDispatched = decodeUnknownOption2(
12141
+ Struct({ projectId: String4, workItemId: String4, threadId: String4 })
12142
+ );
12143
+ var markWoken = (project2, key) => {
12144
+ if (project2.woken.includes(key)) return project2;
12145
+ const items = { ...project2.items };
12146
+ for (const [workItemId, item] of Object.entries(items)) {
12147
+ for (const threadId of item.assigneeThreads) {
12148
+ if (item.wakeStatus?.[threadId] !== "released" && isWorkable(project2, workItemId) && key === itemWakeKey(
12149
+ workItemId,
12150
+ threadId,
12151
+ readFact(project2.rearms, `${workItemId}:${threadId}`) ?? 0
12152
+ ))
12153
+ items[workItemId] = { ...item, wakeStatus: { ...item.wakeStatus, [threadId]: "active" } };
12154
+ }
12155
+ }
12156
+ return { ...project2, items, woken: [...project2.woken, key] };
12157
+ };
12668
12158
  var markBlockedAfterClaim = (project2, workItemId) => ({
12669
12159
  ...project2,
12670
12160
  blockedAfterClaim: {
@@ -12692,14 +12182,7 @@ function foldWorkWakes(state, event) {
12692
12182
  )
12693
12183
  };
12694
12184
  }
12695
- const next2 = rebindItems({ ...project4, ...scope2 });
12696
- return {
12697
- ...state,
12698
- [goal.projectId]: {
12699
- ...next2,
12700
- pendingComments: project4.pendingComments.filter((comment) => isMember(next2, comment))
12701
- }
12702
- };
12185
+ return { ...state, [goal.projectId]: rebindItems({ ...project4, ...scope2 }) };
12703
12186
  }
12704
12187
  if (fact.name === "team/member-bound") {
12705
12188
  const { projectId: projectId3, memberId } = fact.data;
@@ -12714,17 +12197,17 @@ function foldWorkWakes(state, event) {
12714
12197
  lineId === void 0 ? project4 : project4.lineScopes[lineId],
12715
12198
  fact.data
12716
12199
  );
12717
- const next2 = rebindItems(
12200
+ const next3 = rebindItems(
12718
12201
  lineId === void 0 ? { ...project4, ...bound } : { ...project4, lineScopes: { ...project4.lineScopes, [lineId]: bound } },
12719
12202
  memberId,
12720
12203
  lineId
12721
12204
  );
12722
- return { ...state, [projectId3]: next2 };
12205
+ return { ...state, [projectId3]: next3 };
12723
12206
  }
12724
12207
  if (fact.name === "team/line-paused" || fact.name === "team/line-member-paused") {
12725
12208
  const { projectId: projectId3, workLineId, status } = fact.data;
12726
12209
  const project4 = readFact(state, projectId3) ?? emptyProject();
12727
- const next2 = withPause(
12210
+ const next3 = withPause(
12728
12211
  project4,
12729
12212
  {
12730
12213
  projectId: projectId3,
@@ -12734,22 +12217,7 @@ function foldWorkWakes(state, event) {
12734
12217
  },
12735
12218
  event.eventId
12736
12219
  );
12737
- return next2 === project4 ? state : { ...state, [projectId3]: next2 };
12738
- }
12739
- if (fact.name === COMMENT_MENTIONS_EVENT) {
12740
- const { projectId: projectId3, workItemId: workItemId2, commentId, recipients } = fact.data;
12741
- const project4 = readFact(state, projectId3) ?? emptyProject();
12742
- return {
12743
- ...state,
12744
- [projectId3]: {
12745
- ...project4,
12746
- pendingComments: queueCommentWakes(
12747
- project4,
12748
- { projectId: projectId3, workItemId: workItemId2, commentId },
12749
- recipients.filter((recipient) => isMember(project4, recipient))
12750
- )
12751
- }
12752
- };
12220
+ return next3 === project4 ? state : { ...state, [projectId3]: next3 };
12753
12221
  }
12754
12222
  if (fact.name === "team/member-worktree-failed" || fact.name === "team/member-worktree-reported") {
12755
12223
  const { projectId: projectId3, threadId } = fact.data;
@@ -12767,15 +12235,6 @@ function foldWorkWakes(state, event) {
12767
12235
  }
12768
12236
  const { projectId: projectId2, key } = fact.data;
12769
12237
  const project3 = readFact(state, projectId2) ?? emptyProject();
12770
- if (project3.pendingComments.some((comment) => comment.key === key)) {
12771
- return {
12772
- ...state,
12773
- [projectId2]: {
12774
- ...project3,
12775
- pendingComments: project3.pendingComments.filter((comment) => comment.key !== key)
12776
- }
12777
- };
12778
- }
12779
12238
  if (project3.pendingReviews?.some((review) => review.key === key)) {
12780
12239
  return {
12781
12240
  ...state,
@@ -12785,19 +12244,22 @@ function foldWorkWakes(state, event) {
12785
12244
  }
12786
12245
  };
12787
12246
  }
12788
- if (project3.woken.includes(key)) return state;
12789
- const items = { ...project3.items };
12790
- for (const [workItemId2, item] of Object.entries(items)) {
12791
- for (const threadId of item.assigneeThreads) {
12792
- if (item.wakeStatus?.[threadId] !== "released" && isWorkable(project3, workItemId2) && key === itemWakeKey(
12793
- workItemId2,
12794
- threadId,
12795
- readFact(project3.rearms, `${workItemId2}:${threadId}`) ?? 0
12796
- ))
12797
- items[workItemId2] = { ...item, wakeStatus: { ...item.wakeStatus, [threadId]: "active" } };
12798
- }
12799
- }
12800
- return { ...state, [projectId2]: { ...project3, items, woken: [...project3.woken, key] } };
12247
+ const next2 = markWoken(project3, key);
12248
+ return next2 === project3 ? state : { ...state, [projectId2]: next2 };
12249
+ }
12250
+ if (event.type === "work.item-dispatched") {
12251
+ const decoded2 = decodeDispatched(event.payload);
12252
+ if (isNone2(decoded2)) return state;
12253
+ const { projectId: projectId2, workItemId: workItemId2, threadId } = decoded2.value;
12254
+ const project3 = readFact(state, projectId2);
12255
+ if (project3 === null) return state;
12256
+ const key = itemWakeKey(
12257
+ workItemId2,
12258
+ threadId,
12259
+ readFact(project3.rearms, `${workItemId2}:${threadId}`) ?? 0
12260
+ );
12261
+ const next2 = markWoken(project3, key);
12262
+ return next2 === project3 ? state : { ...state, [projectId2]: next2 };
12801
12263
  }
12802
12264
  const decoded = decodeWorkEvent(event);
12803
12265
  if (isNone2(decoded)) return state;
@@ -12928,20 +12390,11 @@ function foldWorkWakes(state, event) {
12928
12390
  }
12929
12391
  break;
12930
12392
  }
12393
+ // 评论里 @ 谁、回复谁由引擎投递(提案 0085 切片 3);这里只记最近一条评论,恢复与问进度要用。
12931
12394
  case "work.comment-posted": {
12932
- const { actor, parentAuthor, mentions, commentId } = work.payload;
12933
- const recipients = commentRecipients(
12934
- project2,
12935
- actor,
12936
- commentPeople({ parentAuthor, mentions })
12937
- );
12395
+ const { actor, commentId } = work.payload;
12938
12396
  next = {
12939
12397
  ...project2,
12940
- pendingComments: queueCommentWakes(
12941
- project2,
12942
- { projectId, workItemId, commentId },
12943
- recipients
12944
- ),
12945
12398
  items: currentItem === null ? project2.items : {
12946
12399
  ...project2.items,
12947
12400
  [workItemId]: {
@@ -13020,38 +12473,6 @@ function foldWakes(state, event) {
13020
12473
  }
13021
12474
 
13022
12475
  // src/runtime/wake.ts
13023
- var workLabel = (workItemId, sequenceId) => sequenceId === void 0 ? `the work item with workItemId ${workItemId}` : `work item W${sequenceId} (workItemId ${workItemId})`;
13024
- var titled = (workItemId, sequenceId, title) => `${workLabel(workItemId, sequenceId)}${title ? `: ${title}` : ""}`;
13025
- var ITEM_SUMMARY_MAX_CHARS = 300;
13026
- var descriptionStart = (description) => {
13027
- const first = description?.trim().split(/\n\s*\n/u)[0]?.trim() ?? "";
13028
- if (first === "") return "";
13029
- const points = Array.from(first);
13030
- const summary = points.length > ITEM_SUMMARY_MAX_CHARS ? `${points.slice(0, ITEM_SUMMARY_MAX_CHARS).join("")}\u2026` : first;
13031
- return `Its description starts: ${summary}`;
13032
- };
13033
- var itemHeadline = (sequenceId, item) => {
13034
- const title = item?.title ? `${sequenceId === void 0 ? "Work item" : `W${sequenceId}`}: ${item.title}.` : "";
13035
- const parts = [title, descriptionStart(item?.description)].filter((part2) => part2 !== "");
13036
- return parts.length === 0 ? "" : `${parts.join(" ")} `;
13037
- };
13038
- var MEMBER_INSTRUCTIONS_LIMIT = 2e3;
13039
- var startClause = (member) => member === void 0 ? "Otherwise claim it with work_claim_item before you change any file." : "Otherwise start it before you change any file: use work_update_item to move it into an in-progress state (see work_list_states) and leave its assignees as they are. It is assigned to you as a team member, so do not claim it with work_claim_item; claiming is for work nobody holds.";
13040
- var memberInstructionsClause = (member) => member?.instructions == null || member.instructions.trim() === "" ? "" : ` Your standing work instructions as a member of this team: ${member.instructions}${member.nextOffset === null ? "" : ` (This is the first part only. Read the rest with work_read_member, passing memberId "${member.memberId}" and textOffset ${member.nextOffset}.)`}`;
13041
- var itemText = (workItemId, sequenceId, item, leadThreadId, member) => `${itemHeadline(sequenceId, item)}You have ${workLabel(workItemId, sequenceId)}. ${handbookMessage("memberBrief")} Then read it with work_read_items, passing workItemIds: ["${workItemId}"]. If it is blocked by items that are not done yet, do not start it; you will be told again when they are done. ${startClause(member)}${memberInstructionsClause(member)} Before you change anything, search this repository's rules and checks with memory_search and follow what applies. Then work on it. ${MEMBER_READ_RANGES_RULE} ${MEMBER_NO_STASH_RULE} ${MEMBER_ASK_BEFORE_OUTSIDE_RULE} ${MEMBER_CHECKS_RULE} ${MEMBER_PROGRESS_RULE} ${memberStuckRule(leadThreadId)}${memberGroupRule(leadThreadId)} ${PERSON_MESSAGE_RULE}`;
13042
- var leadItemText = (workItemId, sequenceId, title) => `You have ${titled(workItemId, sequenceId, title)}. You lead this team, so this item is the goal itself: do not claim it or do its work yourself. Read it with work_read_items, passing workItemIds: ["${workItemId}"]. If it has no work items yet, split it as your team brief says. ${LEAD_SPLIT_RULE} ${LEAD_OWN_ITEM_RULE} Record dependencies and assign each item to one member as the brief describes. ${LEAD_NO_MEMBER_TASKS} ${PERSON_MESSAGE_RULE}`;
13043
- var leadUnblockedText = (workItemId, sequenceId, title) => `Everything blocking ${titled(workItemId, sequenceId, title)} is done now. Read those items with work_read_items and check the results. ${LEAD_ACCEPT_RULE} ${LEAD_SEND_BACK_RULE} When all of them are acceptable, mark this item done with work_update_item. ${LEAD_NO_MEMBER_TASKS}`;
13044
- var unblockedText = (workItemId, sequenceId, title) => `Everything blocking ${titled(workItemId, sequenceId, title)} is done now. Read those items with work_read_items, finish this one, and mark it done with work_update_item.`;
13045
- var leadReviewText = (workItemId, sequenceId, title, by, facts) => [
13046
- `${by} marked ${titled(workItemId, sequenceId, title)} done. Read it with work_read_items, passing workItemIds: ["${workItemId}"], and check it now.`,
13047
- LEAD_ACCEPT_RULE,
13048
- LEAD_SEND_BACK_RULE,
13049
- ...facts.idle.length === 0 ? [] : [`Nothing in progress now: ${facts.idle.join("; ")}.`],
13050
- ...facts.queued.length === 0 ? [] : [`Waiting in line, not blocked: ${facts.queued.join("; ")}.`],
13051
- ...facts.idle.length > 0 && facts.queued.length > 0 ? [LEAD_REBALANCE_RULE] : [],
13052
- LEAD_NO_MEMBER_TASKS
13053
- ].join(" ");
13054
- var commentText = (workItemId, sequenceId) => `There is a new comment for you on ${workLabel(workItemId, sequenceId)}. Read it with work_list_comments, passing workItemId: "${workItemId}", and reply there if needed.`;
13055
12476
  function startWakeRuntime(ctx, goals, options = {}) {
13056
12477
  const wakes = ctx.derivedState({
13057
12478
  key: WAKE_STATE_KEY,
@@ -13078,8 +12499,6 @@ function startWakeRuntime(ctx, goals, options = {}) {
13078
12499
  };
13079
12500
  };
13080
12501
  const baseText = async (wake, state) => {
13081
- if ("principalId" in wake || wake.kind === "comment")
13082
- return commentText(wake.workItemId, wake.sequenceId);
13083
12502
  const item = await readItem(wake.projectId, wake.workItemId);
13084
12503
  const title = item?.title;
13085
12504
  if (wake.kind === "review") {
@@ -13090,7 +12509,7 @@ function startWakeRuntime(ctx, goals, options = {}) {
13090
12509
  const key = "threadId" in id ? id.threadId : id.principalId;
13091
12510
  return `${member?.name ?? key} (${"threadId" in id ? "threadId" : "principalId"} ${key})`;
13092
12511
  };
13093
- const facts = project2 === void 0 ? { idle: [], queued: [] } : reviewFacts(project2, memberQueues(wake.projectId, project2), wake.threadId);
12512
+ const facts = project2 === void 0 || !("threadId" in wake) ? { idle: [], queued: [] } : reviewFacts(project2, memberQueues(wake.projectId, project2), wake.threadId);
13094
12513
  return leadReviewText(
13095
12514
  wake.workItemId,
13096
12515
  wake.sequenceId,
@@ -13118,6 +12537,42 @@ function startWakeRuntime(ctx, goals, options = {}) {
13118
12537
  title
13119
12538
  );
13120
12539
  };
12540
+ const compose = async (wake, state) => {
12541
+ const team = "threadId" in wake ? lineScopeOfThread(state[wake.projectId], wake.threadId) : void 0;
12542
+ const memberId = team === void 0 || !("threadId" in wake) ? void 0 : team.scope.memberIds?.[wake.threadId];
12543
+ const digest = team === void 0 || memberId === void 0 || !("threadId" in wake) || team.scope.leadThreads?.includes(wake.threadId) === true || options.groupDigest === void 0 ? null : await options.groupDigest.prepare({
12544
+ projectId: wake.projectId,
12545
+ workLineId: team.workLineId,
12546
+ memberId,
12547
+ threadId: wake.threadId,
12548
+ leadThreadId: team.scope.leadThreads?.[0] ?? "",
12549
+ wakeKey: wake.key,
12550
+ // 群记录的发件人取成员名(会话号 → 成员号 → 名单),读不到就退回 Lead / Member。
12551
+ // 名单在这一次摘要里只读一次:惰性缓存同一个 Promise,不随每条记录重读。
12552
+ nameOfThread: /* @__PURE__ */ (() => {
12553
+ let roster;
12554
+ return async (threadId) => {
12555
+ if (team.scope.memberIds?.[threadId] === void 0) return null;
12556
+ roster ??= goals.read(wake.projectId, { forLine: team.workLineId });
12557
+ return findGoalMember((await roster)?.members ?? [], { threadId })?.name ?? null;
12558
+ };
12559
+ })()
12560
+ });
12561
+ const waiting = team === void 0 || !("threadId" in wake) || team.scope.leadThreads?.includes(wake.threadId) !== true || options.teamWaiting === void 0 ? null : await (async () => {
12562
+ const members = (await goals.read(wake.projectId, { forThread: wake.threadId }))?.members ?? [];
12563
+ return options.teamWaiting(
12564
+ team.scope.threads.filter((id) => id !== wake.threadId),
12565
+ (id) => findGoalMember(members, { threadId: id })?.name ?? id
12566
+ );
12567
+ })().catch(() => null);
12568
+ return {
12569
+ digest,
12570
+ text: async () => {
12571
+ const body = await baseText(wake, state);
12572
+ return [digest?.body, body, waiting].filter((part2) => part2 != null).join("\n\n");
12573
+ }
12574
+ };
12575
+ };
13121
12576
  const execute = async () => {
13122
12577
  let state = await wakes.get();
13123
12578
  const isReady2 = options.readiness === void 0 ? () => true : await options.readiness();
@@ -13137,33 +12592,7 @@ function startWakeRuntime(ctx, goals, options = {}) {
13137
12592
  try {
13138
12593
  const id = hash2(wake.key);
13139
12594
  const worktree = memberWorktreeFor(goals, state[wake.projectId], wake.projectId, wake);
13140
- const team = "threadId" in wake ? lineScopeOfThread(state[wake.projectId], wake.threadId) : void 0;
13141
- const memberId2 = team === void 0 || !("threadId" in wake) ? void 0 : team.scope.memberIds?.[wake.threadId];
13142
- const digest = team === void 0 || memberId2 === void 0 || !("threadId" in wake) || team.scope.leadThreads?.includes(wake.threadId) === true || options.groupDigest === void 0 ? null : await options.groupDigest.prepare({
13143
- projectId: wake.projectId,
13144
- workLineId: team.workLineId,
13145
- memberId: memberId2,
13146
- threadId: wake.threadId,
13147
- leadThreadId: team.scope.leadThreads?.[0] ?? "",
13148
- wakeKey: wake.key,
13149
- // 群记录的发件人取成员名(会话号 → 成员号 → 名单),读不到就退回 Lead / Member。
13150
- // 名单在这一次摘要里只读一次:惰性缓存同一个 Promise,不随每条记录重读。
13151
- nameOfThread: /* @__PURE__ */ (() => {
13152
- let roster;
13153
- return async (threadId) => {
13154
- if (team.scope.memberIds?.[threadId] === void 0) return null;
13155
- roster ??= goals.read(wake.projectId, { forLine: team.workLineId });
13156
- return findGoalMember((await roster)?.members ?? [], { threadId })?.name ?? null;
13157
- };
13158
- })()
13159
- });
13160
- const waiting = team === void 0 || !("threadId" in wake) || team.scope.leadThreads?.includes(wake.threadId) !== true || options.teamWaiting === void 0 ? null : await (async () => {
13161
- const members = (await goals.read(wake.projectId, { forThread: wake.threadId }))?.members ?? [];
13162
- return options.teamWaiting(
13163
- team.scope.threads.filter((id2) => id2 !== wake.threadId),
13164
- (id2) => findGoalMember(members, { threadId: id2 })?.name ?? id2
13165
- );
13166
- })().catch(() => null);
12595
+ const { digest, text } = await compose(wake, state);
13167
12596
  const delivered = await deliverWake(ctx, {
13168
12597
  projectId: wake.projectId,
13169
12598
  recipient: wake,
@@ -13171,12 +12600,7 @@ function startWakeRuntime(ctx, goals, options = {}) {
13171
12600
  messageId: `team-wake-msg:${id}`,
13172
12601
  wake: { reason: "work", sequenceId: wake.sequenceId },
13173
12602
  memberWorktree: worktree === void 0 ? void 0 : { ...worktree, lane: worktrees },
13174
- // 评论在收件人正在跑时插进当前这一轮,不排到这一轮结束(试跑 3:一轮 97 分钟,检查点落空)。
13175
- ...wake.kind === "comment" ? { steer: true } : {},
13176
- text: async () => {
13177
- const body = await baseText(wake, state);
13178
- return [digest?.body, body, waiting].filter((part2) => part2 != null).join("\n\n");
13179
- }
12603
+ text
13180
12604
  });
13181
12605
  if (!delivered) continue;
13182
12606
  if (digest !== null) await options.groupDigest.confirm(wake.projectId, digest);
@@ -13212,7 +12636,73 @@ function startWakeRuntime(ctx, goals, options = {}) {
13212
12636
  });
13213
12637
  const ready = flush();
13214
12638
  void ready.catch((error) => ctx.log?.warn("team wake catch-up failed", { error }));
13215
- return { flush, ready, state: wakes, worktrees };
12639
+ return { flush, ready, state: wakes, worktrees, compose };
12640
+ }
12641
+
12642
+ // src/runtime/wakeSupplement.ts
12643
+ var decodeDispatched2 = decodeUnknownOption2(
12644
+ Struct({ projectId: String4, threadId: String4 })
12645
+ );
12646
+ function registerWakeSupplement(ctx, wakes, groupDigest) {
12647
+ const add3 = async (input) => {
12648
+ const state = await wakes.state.get({ throughSequence: input.throughSequence });
12649
+ const project2 = readFact(state, input.projectId);
12650
+ if (project2 === null || lineScopeOfThread(project2, input.threadId) === void 0) return null;
12651
+ const decision = teamItemDecision(project2, input.projectId, input.workItemId, input.threadId);
12652
+ if (decision.kind === "held")
12653
+ throw new Error(`the team cannot hand this work item over now: ${decision.reason}`);
12654
+ const { text } = await wakes.compose(decision.wake, state);
12655
+ return text();
12656
+ };
12657
+ ctx.reactor({
12658
+ name: "team-board-handover",
12659
+ events: ["work.item-dispatched"],
12660
+ handle: async (event) => {
12661
+ const decoded = decodeDispatched2(event.payload);
12662
+ if (isNone2(decoded) || groupDigest === void 0) return;
12663
+ await groupDigest.confirmPrepared(decoded.value.projectId, decoded.value.threadId);
12664
+ }
12665
+ });
12666
+ return ctx.operations.supplement({ operations: ["work.dispatch"], add: add3 });
12667
+ }
12668
+
12669
+ // src/runtime/worktreeSetup.ts
12670
+ var WORKTREE_SETUP_TEXT = "You joined this team. Before your first work item, get your own worktree ready. The work item arrives in a separate message, so do not look for work yet.";
12671
+ function startWorktreeSetupRuntime(ctx, deps) {
12672
+ const requested = /* @__PURE__ */ new Map();
12673
+ let running = Promise.resolve();
12674
+ const setUp = async (projectId, threadId) => {
12675
+ const project2 = (await deps.wakes.get())[projectId];
12676
+ const worktree = memberWorktreeFor(deps.goals, project2, projectId, { threadId });
12677
+ if (worktree === void 0 || worktree.failure !== void 0) return;
12678
+ const execution = await ctx.projections.getThreadExecution(threadId);
12679
+ if (execution === null || execution.archivedAt !== null || execution.worktreePath) return;
12680
+ const id = stableHash(threadId);
12681
+ await deliverWake(ctx, {
12682
+ projectId,
12683
+ recipient: { threadId },
12684
+ requestId: `team-worktree-setup:${id}`,
12685
+ messageId: `team-worktree-setup-msg:${id}`,
12686
+ wake: { reason: "notice" },
12687
+ memberWorktree: { ...worktree, lane: deps.lane },
12688
+ text: WORKTREE_SETUP_TEXT
12689
+ });
12690
+ };
12691
+ const drain = async () => {
12692
+ for (const [key, entry] of requested) {
12693
+ requested.delete(key);
12694
+ try {
12695
+ await setUp(entry.projectId, entry.threadId);
12696
+ } catch (error) {
12697
+ ctx.log.warn("team member worktree setup failed", { ...entry, error });
12698
+ }
12699
+ }
12700
+ };
12701
+ const request = (projectId, threadId) => {
12702
+ requested.set(`${projectId}|${threadId}`, { projectId, threadId });
12703
+ running = running.then(drain, drain);
12704
+ };
12705
+ return { request, idle: () => running };
13216
12706
  }
13217
12707
 
13218
12708
  // src/runtime/recovery.ts
@@ -13863,7 +13353,7 @@ var emptyHandbookProject = () => ({
13863
13353
  acknowledgements: {},
13864
13354
  reminded: {}
13865
13355
  });
13866
- var decodeFact6 = decodeUnknownOption2(
13356
+ var decodeFact5 = decodeUnknownOption2(
13867
13357
  Union2([
13868
13358
  factEnvelope("goal-started", GoalStartedFact),
13869
13359
  factEnvelope("member-bound", MemberBoundFact),
@@ -13947,7 +13437,7 @@ function foldHandbook(state, event) {
13947
13437
  };
13948
13438
  }
13949
13439
  if (event.type !== "plugin.event-appended") return state;
13950
- const decoded = decodeFact6(event.payload);
13440
+ const decoded = decodeFact5(event.payload);
13951
13441
  if (isNone2(decoded)) return state;
13952
13442
  const fact = decoded.value;
13953
13443
  const projectId = fact.name === "team/goal-started" ? fact.data.goal.projectId : fact.name === "team/roster-set" ? fact.data.roster.projectId : fact.data.projectId;
@@ -15977,6 +15467,14 @@ function createGroupDigest(ctx, state) {
15977
15467
  await state.get({ throughSequence: receipt.sequence });
15978
15468
  return { digestId, threadId: input.threadId, body };
15979
15469
  },
15470
+ /**
15471
+ * 引擎叫醒了这条会话(看板交件):随那次叫醒补充出去的那一份算已投递,水位前进。没有装配好的就什么也不做。
15472
+ */
15473
+ async confirmPrepared(projectId, threadId) {
15474
+ const prepared = readFact(await state.get(), threadId)?.prepared;
15475
+ if (prepared == null) return;
15476
+ await this.confirm(projectId, { digestId: prepared.digestId, threadId, body: prepared.body });
15477
+ },
15980
15478
  /** 叫醒已被宿主受理:水位前进。同一份只落一次。 */
15981
15479
  async confirm(projectId, digest) {
15982
15480
  const receipt = await ctx.events.append({
@@ -17224,7 +16722,7 @@ function startTrialRuntime(ctx, deps, options = {}) {
17224
16722
  // src/index.ts
17225
16723
  var index_default = definePlugin({
17226
16724
  id: "team",
17227
- apiVersion: 52,
16725
+ apiVersion: 53,
17228
16726
  activate(ctx) {
17229
16727
  const trialState = ctx.derivedState(trialStateDefinition);
17230
16728
  const rosters = createRosterService(ctx);
@@ -17256,7 +16754,19 @@ var index_default = definePlugin({
17256
16754
  },
17257
16755
  memberQueue
17258
16756
  );
17259
- registerDispatchClaim(ctx, wakes.state);
16757
+ const worktreeSetup = startWorktreeSetupRuntime(ctx, {
16758
+ wakes: wakes.state,
16759
+ goals,
16760
+ lane: wakes.worktrees
16761
+ });
16762
+ registerDispatchGate(ctx, {
16763
+ wakes: wakes.state,
16764
+ goals,
16765
+ readiness,
16766
+ requestWorktree: worktreeSetup.request,
16767
+ now: () => runPromise2(map5(now2, toEpochMillis2))
16768
+ });
16769
+ registerWakeSupplement(ctx, wakes, groupDigest);
17260
16770
  registerPauseCommands(ctx, { wakes: wakes.state, queue: memberQueue });
17261
16771
  startArchiveMirror(
17262
16772
  ctx,
@@ -17268,9 +16778,7 @@ var index_default = definePlugin({
17268
16778
  rebindTargets: rebindReader(trialState)
17269
16779
  });
17270
16780
  startMemberRemovalRuntime(ctx, goals, wakes.state);
17271
- startCommentMentionRuntime(ctx, goals);
17272
- startGroupDeliveryRuntime(ctx, wakes.state);
17273
- registerBoardCommands(ctx, goals, rosters, wakes.state);
16781
+ registerBoardCommands(ctx, goals, rosters);
17274
16782
  startRecoveryRuntime(ctx, goals, wakes.state, wakes.worktrees);
17275
16783
  startLeadCheckinRuntime(ctx, wakes.state, goals);
17276
16784
  startProgressCheckinRuntime(ctx, goals, wakes.state);