@ycodium-ai/plugin-team 0.2.3681 → 0.2.3766

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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 });
@@ -9027,6 +8514,10 @@ async function defaultAgent(ctx, projectId) {
9027
8514
 
9028
8515
  // locales/en.json
9029
8516
  var en_default = {
8517
+ presentation: {
8518
+ name: "Teams",
8519
+ description: "Give a project team a goal: the lead plans it on the board and hands work to the members."
8520
+ },
9030
8521
  handbook: {
9031
8522
  requestConflict: "This requestId was already used for a different handbook request. Use a new requestId.",
9032
8523
  requestVoided: "This requestId was abandoned before the handbook received it. Use a new requestId.",
@@ -9071,8 +8562,8 @@ var en_default = {
9071
8562
  versionConflict: "The team was changed at the same moment. Read it again and retry with a new requestId."
9072
8563
  },
9073
8564
  coordinator: {
9074
- createTool: `Add an AI teammate to this project's team as the lead of the current goal. Pass a new requestId, and reuse it only to retry the same request. The member runs on this machine with the provider instanceId and model you give (both or neither); without them it uses the project's default model. The new member joins the current goal and gets its own conversation under yours, normally within moments (if that fails you are told why, and it is retried every couple of minutes); in a team opened with team_open, that starts once you add the member with team_set_members; once it is ready you can assign work to it with work_update_item and assignees [{ "kind": "member", "memberId": <the memberId this tool returns> }]. Check who is on the team first with work_list_members.`,
9075
- updateTool: "Change a teammate as the lead of the current goal: name, title, responsibility, work instructions (an empty string clears them) or provider instanceId and model (both or neither); a new model applies to conversations opened afterwards, and a conversation already running keeps its model. Pass the member's version from work_read_member as expectedVersion; if it has changed you get stale-version, so read the member again. You cannot change your own provider or model. Reuse requestId only to retry the same request.",
8565
+ createTool: `Add an AI teammate to this project's team as the lead of the current goal. Pass a new requestId, and reuse it only to retry the same request. The member runs on this machine unless you give environmentId: another machine that joined this project (work_list_machines lists them with the providers each one offers). Give the provider instanceId and model together (both or neither); on this machine they default to the project's default model, while on another machine they are required and must be a ready provider instance that machine reported. The new member joins the current goal and gets its own conversation under yours, normally within moments (if that fails you are told why, and it is retried every couple of minutes); in a team opened with team_open, that starts once you add the member with team_set_members; once it is ready you can assign work to it with work_update_item and assignees [{ "kind": "member", "memberId": <the memberId this tool returns> }]. Check who is on the team first with work_list_members.`,
8566
+ updateTool: "Change a teammate as the lead of the current goal: name, title, responsibility, work instructions (an empty string clears them), the machine it runs on (environmentId: this machine's id or another machine from work_list_machines), or provider instanceId and model (both or neither). Without environmentId the member stays on its machine. Moving it to another machine keeps its provider instance only if that machine reported the same ready instance; otherwise give instanceId and model from that machine too. A new machine or model applies to conversations opened afterwards, and a conversation already running keeps its setup. Pass the member's version from work_read_member as expectedVersion; if it has changed you get stale-version, so read the member again. You cannot change your own machine, provider or model. Reuse requestId only to retry the same request.",
9076
8567
  removeTool: "Remove a teammate from the team as the lead of the current goal. The record is kept and the member can be restored. Work assigned to the member that has not reached it yet is taken back and you are told; work already in its hands stays with it until you decide. You cannot remove yourself. Reuse requestId only to retry the same request.",
9077
8568
  restoreTool: "Bring a removed teammate back as the lead of the current goal. Reuse requestId only to retry the same request.",
9078
8569
  noProject: "This call is not tied to a project, so there is no team to change.",
@@ -9090,7 +8581,12 @@ var en_default = {
9090
8581
  memberInOtherTeam: "Another team is using this member. You can only change or remove members who are on no team, or only on yours. See which teams each member is on with team_roster_overview.",
9091
8582
  memberNotFound: "No such member in this project. List the members with work_list_members.",
9092
8583
  cannotRemoveLead: "You cannot remove yourself, the lead of the current goal.",
9093
- cannotReplaceLead: "You cannot change your own provider or model."
8584
+ cannotReplaceLead: "You cannot change your own machine, provider or model.",
8585
+ machineNotInProject: "Machine {{environmentId}} is not in this project. Call work_list_machines to see the machines that joined it, or leave out environmentId to use this machine.",
8586
+ machineNeedsModel: "A member on machine {{environmentId}} needs a provider from that machine. Give instanceId and model from what work_list_machines reports for it.",
8587
+ machineInstanceNotFound: "Machine {{environmentId}} did not report a provider instance {{instanceId}}. Call work_list_machines (with includeModels) and give an instanceId and model that machine reported. The list is what that machine reported when it last connected: if the provider was set up there since, ask a person to reconnect that machine (or restart its Ycodium) to refresh the list, then call work_list_machines again.",
8588
+ machineInstanceNotReady: "Provider instance {{instanceId}} on machine {{environmentId}} is not ready to take work. Call work_list_machines and pick a ready instance, or ask a person to fix that provider on that machine. The readiness is what that machine reported when it last connected: if it was fixed since, ask a person to reconnect that machine (or restart its Ycodium) to refresh the list, then call work_list_machines again.",
8589
+ machinesUnavailable: "Could not read this project's machines right now. Try again in a moment."
9094
8590
  },
9095
8591
  board: {
9096
8592
  openTeam: "Open this team on the board",
@@ -9276,7 +8772,8 @@ var en_default = {
9276
8772
  thisMachine: "This machine",
9277
8773
  machineOffline: "{{name}} (offline)",
9278
8774
  machineRemoved: "A machine that left this project",
9279
- noMachine: "No machine chosen"
8775
+ noMachine: "No machine chosen",
8776
+ machineJoining: "Adding {{name}} to this project\u2026"
9280
8777
  },
9281
8778
  handbookPanel: {
9282
8779
  title: "Handbook",
@@ -9601,7 +9098,7 @@ function createGoalService(ctx, rosterService, options = {}) {
9601
9098
  rosterMemberName(roster, member.memberId)
9602
9099
  ])
9603
9100
  );
9604
- const others = options2.teamOnly === true && goal.workLineId === void 0 ? /* @__PURE__ */ new Map() : await engineNames(
9101
+ const others = await engineNames(
9605
9102
  projectId,
9606
9103
  execution.members.flatMap(
9607
9104
  ({ memberId }) => teamNames.get(memberId) === null ? [memberId] : []
@@ -9900,6 +9397,7 @@ var ImportResult = Union2([
9900
9397
 
9901
9398
  // src/coordinator/contracts.ts
9902
9399
  var agentFields = {
9400
+ environmentId: optionalKey2(String4),
9903
9401
  instanceId: optionalKey2(String4),
9904
9402
  model: optionalKey2(String4)
9905
9403
  };
@@ -9955,8 +9453,15 @@ var MemberChangeCompleted = Struct({
9955
9453
  ])
9956
9454
  });
9957
9455
 
9456
+ // src/messages.ts
9457
+ var interpolateMessage = (template, values) => template.replace(
9458
+ /{{(\w+)}}/g,
9459
+ (whole, name) => Object.hasOwn(values, name) ? String(values[name]) : whole
9460
+ );
9461
+
9958
9462
  // src/coordinator/messages.ts
9959
9463
  var coordinatorMessage = (key) => en_default.coordinator[key];
9464
+ var machineMessage = (key, values) => interpolateMessage(en_default.coordinator[key], values);
9960
9465
 
9961
9466
  // src/coordinator/entry.ts
9962
9467
  var decodeCreate = decodeUnknownSync(CreateMemberToolInput);
@@ -10094,8 +9599,17 @@ var GUARD_MESSAGES = {
10094
9599
  "cannot-replace-lead": "cannotReplaceLead",
10095
9600
  "invalid-agent": "invalidAgent",
10096
9601
  "model-unavailable": "modelUnavailable",
10097
- "no-default-model": "noDefaultModel"
9602
+ "no-default-model": "noDefaultModel",
9603
+ "machine-not-in-project": "machineNotInProject",
9604
+ "machine-needs-model": "machineNeedsModel",
9605
+ "machine-instance-not-found": "machineInstanceNotFound",
9606
+ "machine-instance-not-ready": "machineInstanceNotReady"
10098
9607
  };
9608
+ var refuseMachine = (code, values) => ({
9609
+ status: "rejected",
9610
+ code,
9611
+ detail: machineMessage(GUARD_MESSAGES[code], values)
9612
+ });
10099
9613
  var refuse = (code, reason) => ({
10100
9614
  status: "rejected",
10101
9615
  code,
@@ -10213,22 +9727,39 @@ function startCoordinatorRuntime(ctx, deps) {
10213
9727
  return refuse("cannot-remove-lead");
10214
9728
  if ((change.op === "remove" || change.op === "update") && await usedByOtherTeam(lead, target.memberId))
10215
9729
  return refuse("member-in-other-team");
10216
- if (change.op === "update" && (change.input.instanceId !== void 0 || change.input.model !== void 0) && await isLead(lead, target.memberId))
9730
+ if (change.op === "update" && (change.input.instanceId !== void 0 || change.input.model !== void 0 || change.input.environmentId !== void 0 && change.input.environmentId !== (target.agent?.environmentId ?? null)) && await isLead(lead, target.memberId))
10217
9731
  return refuse("cannot-replace-lead");
10218
9732
  return null;
10219
9733
  };
10220
9734
  const agentFor = async (change, projectId, target) => {
10221
9735
  const { instanceId, model } = change.input;
10222
- if (instanceId === void 0 && model === void 0) {
10223
- if (change.op === "update") return {};
9736
+ if (instanceId === void 0 !== (model === void 0)) return refuse("invalid-agent");
9737
+ const current = target?.agent ?? null;
9738
+ const environmentId = change.input.environmentId ?? current?.environmentId ?? ctx.environment.id;
9739
+ const named = instanceId !== void 0 && model !== void 0;
9740
+ if (!named && change.op === "update" && (change.input.environmentId === void 0 || environmentId === current?.environmentId))
9741
+ return {};
9742
+ const selection = named ? { instanceId, model } : current?.modelSelection ?? null;
9743
+ if (environmentId !== ctx.environment.id) {
9744
+ if (selection === null) return refuseMachine("machine-needs-model", { environmentId });
9745
+ const machines = await readMachines(ctx, projectId);
9746
+ if (machines === null) throw new Error(coordinatorMessage("machinesUnavailable"));
9747
+ const machine = machines.get(environmentId);
9748
+ if (machine === void 0) return refuseMachine("machine-not-in-project", { environmentId });
9749
+ if (machine.providers.length > 0) {
9750
+ const values = { environmentId, instanceId: selection.instanceId };
9751
+ if (!machine.providers.some((row) => row.instanceId === selection.instanceId))
9752
+ return refuseMachine("machine-instance-not-found", values);
9753
+ if (!readyProviders(machine).some((row) => row.instanceId === selection.instanceId))
9754
+ return refuseMachine("machine-instance-not-ready", values);
9755
+ }
9756
+ return { agent: { environmentId, modelSelection: selection } };
9757
+ }
9758
+ if (selection === null) {
10224
9759
  const fallback = await defaultAgent(ctx, projectId);
10225
9760
  return "code" in fallback ? refuse(fallback.code, "reason" in fallback ? fallback.reason : void 0) : { agent: fallback.agent };
10226
9761
  }
10227
- if (instanceId === void 0 || model === void 0) return refuse("invalid-agent");
10228
- const environmentId = target?.agent?.environmentId ?? ctx.environment.id;
10229
- if (environmentId !== ctx.environment.id)
10230
- return { agent: { environmentId, modelSelection: { instanceId, model } } };
10231
- const resolved = await resolveModel(ctx, { instanceId, model }, true);
9762
+ const resolved = await resolveModel(ctx, selection, named);
10232
9763
  return "code" in resolved ? refuse(resolved.code, "reason" in resolved ? resolved.reason : void 0) : { agent: { environmentId, modelSelection: resolved.selection.modelSelection } };
10233
9764
  };
10234
9765
  const plan = async (change, requestId, projectId, target) => {
@@ -10395,7 +9926,7 @@ function membersSnapshot(view, trialOf = () => null, viewer = null) {
10395
9926
  name: machine.name,
10396
9927
  online: machine.online,
10397
9928
  ...machine.lastSeenAt === void 0 ? {} : { lastSeenAt: machine.lastSeenAt },
10398
- providers: machine.providers.map((provider) => ({
9929
+ providers: readyProviders(machine).map((provider) => ({
10399
9930
  instanceId: provider.instanceId,
10400
9931
  displayName: provider.displayName,
10401
9932
  models: provider.models.map(({ slug, name }) => ({ slug, name })),
@@ -10952,7 +10483,7 @@ function createMemberWriter(ctx, rosters, queue = createSerial()) {
10952
10483
  const machines = missing2.some(({ member }) => member.principal !== void 0) ? [...(await readMachines(ctx, projectId))?.values() ?? []] : [];
10953
10484
  const remoteAgentOf = (principalId) => {
10954
10485
  const machine = machines.find((entry) => entry.principalId === principalId);
10955
- const provider = machine?.providers[0];
10486
+ const provider = machine === void 0 ? void 0 : readyProviders(machine)[0];
10956
10487
  const model = provider?.defaultModel ?? provider?.models[0]?.slug;
10957
10488
  return machine === void 0 || provider === void 0 || model === void 0 ? void 0 : {
10958
10489
  environmentId: machine.environmentId,
@@ -10988,90 +10519,6 @@ function createMemberWriter(ctx, rosters, queue = createSerial()) {
10988
10519
  return { create, update, remove, restore, importLegacy };
10989
10520
  }
10990
10521
 
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
10522
  // src/domain/pause.ts
11076
10523
  var LinePausedEnvelope = factEnvelope(
11077
10524
  "line-paused",
@@ -11119,11 +10566,6 @@ var decodeEnvelope2 = decodeUnknownOption2(
11119
10566
  name: Literal2("team/member-woken"),
11120
10567
  data: MemberWokenFact
11121
10568
  }),
11122
- Struct({
11123
- pluginId: Literal2("team"),
11124
- name: Literal2(COMMENT_MENTIONS_EVENT),
11125
- data: CommentMentionsFact
11126
- }),
11127
10569
  MemberBoundEnvelope,
11128
10570
  LinePausedEnvelope,
11129
10571
  LineMemberPausedEnvelope,
@@ -11226,87 +10668,6 @@ var assigneeMembers = (principals) => [
11226
10668
  ...new Set(principals.flatMap((p) => p.kind === "member" ? [p.memberId] : []))
11227
10669
  ];
11228
10670
 
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
10671
  // src/runtime/deliveryPacing.ts
11311
10672
  function foldProgress(state, event) {
11312
10673
  const decoded = decodeWorkEvent(event);
@@ -11375,148 +10736,478 @@ function createDeliveryPacing(progress, waitingOnSomeone2) {
11375
10736
  return { observe, allows };
11376
10737
  }
11377
10738
 
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))) {
10739
+ // src/runtime/goalScopes.ts
10740
+ var isLinePaused = (project2, workLineId) => project2.linePauses?.[workLineId] === true;
10741
+ var isMemberPausedOnLine = (project2, workLineId, memberId) => (project2.pausedMembers?.[workLineId] ?? []).includes(memberId);
10742
+ var isLineStopped = (project2, workLineId) => workLineId !== void 0 && project2.lineStops?.[workLineId] !== void 0;
10743
+ var isLineGated = (project2, workLineId) => isLineStopped(project2, workLineId) || workLineId !== void 0 && isLinePaused(project2, workLineId);
10744
+ var scopesOf = (project2) => [
10745
+ project2,
10746
+ ...Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([, scope2]) => scope2)
10747
+ ];
10748
+ var scopeForLine = (project2, workLineId) => (workLineId === void 0 ? void 0 : project2.lineScopes?.[workLineId]) ?? project2;
10749
+ var scopeOfThread = (project2, threadId) => scopesOf(project2).find((scope2) => scope2.threads.includes(threadId));
10750
+ var teamSessions = (scope2) => [
10751
+ .../* @__PURE__ */ new Set([...scope2.threads, ...Object.keys(scope2.memberIds ?? {})])
10752
+ ];
10753
+ var lineScopeOfThread = (project2, threadId) => {
10754
+ for (const [workLineId, scope2] of Object.entries(project2.lineScopes ?? {}))
10755
+ if (teamSessions(scope2).includes(threadId)) return { workLineId, scope: scope2 };
10756
+ return void 0;
10757
+ };
10758
+ var scopeOfPrincipal = (project2, principalId) => scopesOf(project2).find((scope2) => scope2.remoteMembers.includes(principalId));
10759
+ var isGoalThread = (project2, threadId) => scopeOfThread(project2, threadId) !== void 0;
10760
+ var isLeadThread = (project2, threadId) => scopesOf(project2).some((scope2) => scope2.leadThreads?.includes(threadId) === true);
10761
+ var isRemoteMember2 = (project2, principalId) => scopeOfPrincipal(project2, principalId) !== void 0;
10762
+ var leadOfThread = (project2, threadId) => scopeOfThread(project2, threadId)?.leadThreads?.[0];
10763
+ var placeOfRecipient = (project2, who, workLineId) => {
10764
+ if ("threadId" in who) {
10765
+ const line = lineScopeOfThread(project2, who.threadId);
10766
+ if (line !== void 0) return line;
10767
+ return project2.threads.includes(who.threadId) ? { scope: project2 } : void 0;
10768
+ }
10769
+ const own = workLineId === void 0 ? void 0 : project2.lineScopes?.[workLineId];
10770
+ if (workLineId !== void 0 && own?.remoteMembers.includes(who.principalId) === true)
10771
+ return { workLineId, scope: own };
10772
+ if (project2.remoteMembers.includes(who.principalId)) return { scope: project2 };
10773
+ const found = Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).find(([, scope2]) => scope2.remoteMembers.includes(who.principalId));
10774
+ return found === void 0 ? void 0 : { workLineId: found[0], scope: found[1] };
10775
+ };
10776
+ var scopeOfRecipient = (project2, who, workLineId) => placeOfRecipient(project2, who, workLineId)?.scope;
10777
+ var memberOfRecipientIn = (scope2, who) => {
10778
+ if ("threadId" in who) return scope2.memberIds?.[who.threadId];
10779
+ for (const [memberId, recipient] of Object.entries(scope2.bindings ?? {}))
10780
+ if ("principalId" in recipient && recipient.principalId === who.principalId) return memberId;
10781
+ return void 0;
10782
+ };
10783
+ var isRecipientGated = (project2, who, workLineId) => {
10784
+ const place = placeOfRecipient(project2, who, workLineId);
10785
+ if (place === void 0 || place.workLineId === void 0) return false;
10786
+ if (isLineGated(project2, place.workLineId)) return true;
10787
+ const member = memberOfRecipientIn(place.scope, who);
10788
+ return member !== void 0 && isMemberPausedOnLine(project2, place.workLineId, member);
10789
+ };
10790
+ var isThreadGated = (project2, threadId) => isRecipientGated(project2, { threadId });
10791
+ var teamLeadCandidates = (project2, memberId) => Object.entries(project2.lineScopes ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).flatMap(([workLineId, scope2]) => {
10792
+ const leadThreadId = scope2.leadThreads?.[0];
10793
+ return scope2.bindings?.[memberId] !== void 0 && leadThreadId !== void 0 && !isLineStopped(project2, workLineId) ? [{ workLineId, leadThreadId }] : [];
10794
+ });
10795
+
10796
+ // src/runtime/memberWaiting.ts
10797
+ var readMemberExecution = async (ctx, threadId, known) => {
10798
+ const shell = await ctx.threads.get({ threadId });
10799
+ const summary = known ?? await ctx.threads.execution.summary({ threadId, limit: 1 });
10800
+ return { ...shell, queuePaused: summary.queuePaused, summary };
10801
+ };
10802
+ var waitingOnSomeone = (execution) => execution.summary.queuePaused || execution.summary.pendingApprovalCount > 0 || execution.summary.pendingUserInputCount > 0 || (execution.pendingAsyncQuestionCount ?? 0) > 0 || (execution.awaitingPersonReplyCount ?? 0) > 0;
10803
+
10804
+ // src/runtime/wakeQueries.ts
10805
+ function memberWorktreeFor(goals, project2, projectId, recipient) {
10806
+ if (!("threadId" in recipient) || project2 === void 0) return;
10807
+ const { threadId } = recipient;
10808
+ const scope2 = scopeOfThread(project2, threadId);
10809
+ if (scope2 === void 0 || scope2.leadThreads?.includes(threadId)) return;
10810
+ const leadThreadId = scope2.leadThreads?.[0];
10811
+ if (leadThreadId === void 0) return;
10812
+ return {
10813
+ leadThreadId,
10814
+ failure: project2.worktreeFailures?.[threadId],
10815
+ // 只在要建树或报失败时读目标取名字,叫醒的热路径不多读。
10816
+ name: async () => (await goals.read(projectId, { forThread: threadId }))?.members.find(
10817
+ (member) => member.threadId === threadId
10818
+ )?.name ?? scope2.memberIds?.[threadId] ?? threadId
10819
+ };
10820
+ }
10821
+ var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
10822
+ var compareWakeItems = (a, b) => a.sequenceId - b.sequenceId || compare(a.workItemId, b.workItemId);
10823
+ var OPEN_ITEM_GROUPS = ["backlog", "unstarted", "started"];
10824
+ var isClosed = (item) => item?.stateGroup === "completed" || item?.stateGroup === "cancelled";
10825
+ var hasOpenBlocker = (project2, workItemId) => (readFact(project2.blockers, workItemId) ?? []).some(
10826
+ (blocker) => !isClosed(readFact(project2.items, blocker))
10827
+ );
10828
+ var isAssignedOpen = (project2, workItemId, threadId) => {
10829
+ const item = readFact(project2.items, workItemId);
10830
+ return item !== null && item !== void 0 && !isClosed(item) && item.assigneeThreads.includes(threadId);
10831
+ };
10832
+ var isWorkable = (project2, workItemId) => !isClosed(readFact(project2.items, workItemId)) && !hasOpenBlocker(project2, workItemId);
10833
+ var isOpenWorkFor = (project2, workItemId, threadId) => isAssignedOpen(project2, workItemId, threadId) && !hasOpenBlocker(project2, workItemId);
10834
+ var isHandedOver = (item, threadId) => {
10835
+ if (item.stateGroup === "started") return true;
10836
+ const status = threadId === void 0 ? void 0 : item.wakeStatus?.[threadId];
10837
+ return status === "active" || status === "released";
10838
+ };
10839
+ var memberOfThread = (item, threadId) => {
10840
+ for (const [memberId, thread] of Object.entries(item.memberThreads ?? {}))
10841
+ if (thread === threadId) return memberId;
10842
+ return void 0;
10843
+ };
10844
+ var isPinnedTo = (item, threadId) => !isClosed(item) && memberOfThread(item, threadId) !== void 0 && isHandedOver(item, threadId);
10845
+ var hasPinnedOpenItem = (project2, threadId) => Object.values(project2.items).some((item) => isPinnedTo(item, threadId));
10846
+ var pinnedThreadsOf = (item) => Object.values(item.memberThreads ?? {}).filter((threadId) => isPinnedTo(item, threadId));
10847
+ var pinnedOpenThreads = (project2) => {
10848
+ const threads = /* @__PURE__ */ new Set();
10849
+ for (const item of Object.values(project2.items))
10850
+ for (const threadId of pinnedThreadsOf(item)) threads.add(threadId);
10851
+ return threads;
10852
+ };
10853
+ var memberLookup = (project2) => {
10854
+ let pinned;
10855
+ return (threadId) => isGoalThread(project2, threadId) || (pinned ??= pinnedOpenThreads(project2)).has(threadId);
10856
+ };
10857
+ var isMember = (project2, recipient) => "threadId" in recipient ? isGoalThread(project2, recipient.threadId) || hasPinnedOpenItem(project2, recipient.threadId) : isRemoteMember2(project2, recipient.principalId);
10858
+ var isQueuedUnclaimed = (item, threadId) => item.wakeStatus?.[threadId] === "pending" && !item.claimedBy.includes(threadId);
10859
+ var isDeferredWake = (project2, item, threadId) => isGoalThread(project2, threadId) && !isLeadThread(project2, threadId) && item.assigneeThreads.includes(threadId) && isQueuedUnclaimed(item, threadId) && !isClosed(item);
10860
+ function countDeferredWakes(project2, threadId) {
10861
+ let count2 = 0;
10862
+ for (const workItemId in project2.items) {
10863
+ const item = readFact(project2.items, workItemId);
10864
+ if (item !== null && isDeferredWake(project2, item, threadId)) count2++;
10865
+ }
10866
+ return count2;
10867
+ }
10868
+ var exemptFromTrial = (project2, recipient, workItemId) => {
10869
+ if (project2 === void 0 || !("threadId" in recipient)) return false;
10870
+ if (isLeadThread(project2, recipient.threadId)) return true;
10871
+ if (workItemId === void 0) return false;
10872
+ const item = readFact(project2.items, workItemId);
10873
+ return item !== null && item !== void 0 && pinnedThreadsOf(item).includes(recipient.threadId);
10874
+ };
10875
+ function memberOfRecipient(project2, recipient) {
10876
+ if (project2 === void 0) return void 0;
10877
+ const key = recipientKey(recipient);
10878
+ for (const scope2 of scopesOf(project2))
10879
+ for (const [memberId, bound] of Object.entries(scope2.bindings ?? {}))
10880
+ if (recipientKey(bound) === key) return memberId;
10881
+ return void 0;
10882
+ }
10883
+ function executorOf(project2, workItemId, memberId) {
10884
+ if (project2 === void 0) return void 0;
10885
+ const item = readFact(project2.items, workItemId);
10886
+ const target = targetOf(
10887
+ scopeForLine(project2, item?.workLineId).bindings,
10888
+ { kind: "member", memberId },
10889
+ item?.memberThreads
10890
+ );
10891
+ if (target === void 0) return void 0;
10892
+ if ("threadId" in target && isLeadThread(project2, target.threadId)) return void 0;
10893
+ return isMember(project2, target) ? target : void 0;
10894
+ }
10895
+
10896
+ // src/runtime/reviewWakes.ts
10897
+ var decodeCompletion = decodeUnknownOption2(
10898
+ Struct({
10899
+ projectId: String4,
10900
+ workItemId: String4,
10901
+ state: Struct({ group: String4 }),
10902
+ actor: Union2([
10903
+ Struct({ kind: Literal2("agent"), threadId: String4 }),
10904
+ Struct({ kind: Literal2("remote-agent"), principalId: String4 })
10905
+ ])
10906
+ })
10907
+ );
10908
+ var openLeadItemsBlockedBy = (project2, workItemId, lead) => lead === void 0 ? [] : Object.entries(project2.items).filter(
10909
+ ([id]) => isAssignedOpen(project2, id, lead) && readFact(project2.blockers, id)?.includes(workItemId) === true
10910
+ );
10911
+ function reviewOnUpdate(previous, next, event) {
10912
+ const decoded = decodeCompletion(event.payload);
10913
+ if (isNone2(decoded) || decoded.value.state.group !== "completed") return null;
10914
+ const { projectId, workItemId, actor } = decoded.value;
10915
+ if (readFact(previous.items, workItemId)?.stateGroup === "completed") return null;
10916
+ const by = actor.kind === "agent" ? { threadId: actor.threadId } : { principalId: actor.principalId };
10917
+ const lead = scopeOfRecipient(next, by, readFact(next.items, workItemId)?.workLineId)?.leadThreads?.[0] ?? next.leadThreads?.[0];
10918
+ if (lead === void 0) return null;
10919
+ if (!isMember(next, by) || "threadId" in by && isLeadThread(next, by.threadId)) return null;
10920
+ const goals = openLeadItemsBlockedBy(next, workItemId, lead);
10921
+ if (goals.length === 0 || goals.some(([id]) => isWorkable(next, id))) return null;
10922
+ return {
10923
+ key: `review:${workItemId}:${lead}#${event.sequence}`,
10924
+ projectId,
10925
+ workItemId,
10926
+ threadId: lead,
10927
+ by
10928
+ };
10929
+ }
10930
+ var isLiveReview = (project2, review) => isLeadThread(project2, review.threadId) && readFact(project2.items, review.workItemId)?.stateGroup === "completed" && openLeadItemsBlockedBy(project2, review.workItemId, review.threadId).length > 0;
10931
+ var isReviewDue = (project2, key) => project2.pendingReviews?.some((review) => review.key === key && isLiveReview(project2, review)) === true;
10932
+ var dueReviews = (project2) => (project2.pendingReviews ?? []).filter((review) => isLiveReview(project2, review)).map((review) => {
10933
+ const item = readFact(project2.items, review.workItemId);
10934
+ return {
10935
+ ...review,
10936
+ kind: "review",
10937
+ lead: true,
10938
+ ...item === null ? {} : { sequenceId: item.sequenceId }
10939
+ };
10940
+ });
10941
+ function pruneReviews(previous, state) {
10942
+ let next = state;
10943
+ for (const [projectId, project2] of Object.entries(state)) {
10944
+ const pending = project2.pendingReviews;
10945
+ if (pending === void 0 || pending.length === 0) continue;
10946
+ const before = previous[projectId];
10947
+ if (before !== void 0 && before.leadThreads === project2.leadThreads && before.lineScopes === project2.lineScopes && before.items === project2.items && before.blockers === project2.blockers)
10948
+ continue;
10949
+ const live = pending.filter((review) => isLiveReview(project2, review));
10950
+ if (live.length !== pending.length)
10951
+ next = { ...next, [projectId]: { ...project2, pendingReviews: live } };
10952
+ }
10953
+ return next;
10954
+ }
10955
+ function reviewFacts(project2, queues, lead) {
10956
+ const { busy, waiting } = queues;
10957
+ const team = scopeOfThread(project2, lead)?.threads ?? project2.threads;
10958
+ return {
10959
+ idle: team.filter(
10960
+ (threadId) => !isLeadThread(project2, threadId) && !busy.has(threadId) && (waiting.get(threadId) ?? []).length === 0
10961
+ ),
10962
+ queued: [...busy].filter((threadId) => team.includes(threadId)).flatMap(
10963
+ (threadId) => [...waiting.get(threadId) ?? []].sort(compareWakeItems).map(({ workItemId, sequenceId }) => ({ workItemId, sequenceId, threadId }))
10964
+ )
10965
+ };
10966
+ }
10967
+
10968
+ // src/runtime/wakeDue.ts
10969
+ var itemWakeKey = (workItemId, threadId, generation) => generation === 0 ? `item:${workItemId}:${threadId}` : `item:${workItemId}:${threadId}#${generation}`;
10970
+ var unblockedWakeKey = (workItemId, threadId, generation) => `unblocked:${workItemId}:${threadId}#${generation}`;
10971
+ var compare2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
10972
+ var itemWakeCandidate = (projectId, project2, workItemId, item, threadId, woken, isMemberThread) => {
10973
+ if (!isWorkable(project2, workItemId) || !isMemberThread(threadId)) return null;
10974
+ if (isThreadGated(project2, threadId)) return null;
10975
+ const unblocked = readFact(project2.blockedAfterClaim, workItemId) ?? 0;
10976
+ const finishing = unblocked > 0 && item.claimedBy.includes(threadId);
10977
+ if (!finishing && !item.assigneeThreads.includes(threadId)) return null;
10978
+ const lead = isLeadThread(project2, threadId);
10979
+ const leadThreadId = leadOfThread(project2, threadId);
10980
+ const key = finishing ? unblockedWakeKey(workItemId, threadId, unblocked) : itemWakeKey(workItemId, threadId, readFact(project2.rearms, `${workItemId}:${threadId}`) ?? 0);
10981
+ if (woken.has(key) || !finishing && !lead && item.wakeStatus?.[threadId] === "released")
10982
+ return null;
10983
+ const memberId = finishing ? void 0 : memberOfThread(item, threadId);
10984
+ return {
10985
+ key,
10986
+ projectId,
10987
+ threadId,
10988
+ kind: finishing ? "unblocked" : "item",
10989
+ workItemId,
10990
+ sequenceId: item.sequenceId,
10991
+ ...memberId === void 0 ? {} : { memberId },
10992
+ // 成员的叫醒话里点名负责人(卡住时找谁);带队自己和解挡续做不带。
10993
+ ...lead ? { lead: true } : finishing || leadThreadId === void 0 ? {} : { leadThreadId }
10994
+ };
10995
+ };
10996
+ 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;
10997
+ var wakeGated = (project2, wake) => isRecipientGated(project2, wake, readFact(project2.items, wake.workItemId)?.workLineId);
10998
+ var needsMemberSlot = (wake) => wake.kind === "item" && wake.lead !== true;
10999
+ function memberQueues(projectId, project2, woken = new Set(project2.woken), onlyThreadId) {
11000
+ const isMemberThread = memberLookup(project2);
11001
+ const busy = /* @__PURE__ */ new Set();
11002
+ const waiting = /* @__PURE__ */ new Map();
11003
+ for (const [workItemId, item] of Object.entries(project2.items)) {
11004
+ if (onlyThreadId !== void 0 && !item.assigneeThreads.includes(onlyThreadId)) continue;
11005
+ if (!isWorkable(project2, workItemId)) continue;
11006
+ for (const threadId of item.assigneeThreads) {
11007
+ if (onlyThreadId !== void 0 && threadId !== onlyThreadId || isLeadThread(project2, threadId) || !isMemberThread(threadId))
11008
+ continue;
11009
+ if (item.wakeStatus?.[threadId] === "active") busy.add(threadId);
11010
+ const candidate = itemWakeCandidate(
11011
+ projectId,
11012
+ project2,
11013
+ workItemId,
11014
+ item,
11015
+ threadId,
11016
+ woken,
11017
+ isMemberThread
11018
+ );
11019
+ if (candidate === null || !needsMemberSlot(candidate)) continue;
11020
+ const queue = waiting.get(threadId);
11021
+ if (queue === void 0) waiting.set(threadId, [candidate]);
11022
+ else queue.push(candidate);
11023
+ }
11024
+ }
11025
+ return { busy, waiting };
11026
+ }
11027
+ function memberItemSlots(projectId, project2, woken, onlyThreadId) {
11028
+ const { busy, waiting } = memberQueues(projectId, project2, woken, onlyThreadId);
11029
+ const slots = /* @__PURE__ */ new Map();
11030
+ for (const [threadId, candidates] of waiting)
11031
+ if (!busy.has(threadId))
11032
+ slots.set(
11033
+ threadId,
11034
+ candidates.reduce(
11035
+ (first, candidate) => compareWakeItems(candidate, first) < 0 ? candidate : first
11036
+ )
11037
+ );
11038
+ return slots;
11039
+ }
11040
+ function isWakeDue(state, wake) {
11041
+ const project2 = readFact(state, wake.projectId);
11042
+ if (project2 === null || engineDispatches(project2, wake)) return false;
11043
+ if (wake.kind === "review")
11044
+ return isReviewDue(project2, wake.key) && !wakeGated(project2, wake);
11045
+ if (!("threadId" in wake)) return false;
11046
+ const item = readFact(project2.items, wake.workItemId);
11047
+ if (item === null) return false;
11048
+ const woken = new Set(project2.woken);
11049
+ const candidate = itemWakeCandidate(
11050
+ wake.projectId,
11051
+ project2,
11052
+ wake.workItemId,
11053
+ item,
11054
+ wake.threadId,
11055
+ woken,
11056
+ memberLookup(project2)
11057
+ );
11058
+ if (candidate?.key !== wake.key) return false;
11059
+ return !needsMemberSlot(candidate) || memberItemSlots(wake.projectId, project2, woken, wake.threadId).get(wake.threadId)?.key === wake.key;
11060
+ }
11061
+ function dueWakes(state) {
11062
+ const due = [];
11063
+ for (const [projectId, project2] of Object.entries(state)) {
11064
+ const woken = new Set(project2.woken);
11065
+ const isMemberThread = memberLookup(project2);
11066
+ const slots = memberItemSlots(projectId, project2, woken);
11067
+ for (const [workItemId, item] of Object.entries(project2.items)) {
11068
+ const unblocked = readFact(project2.blockedAfterClaim, workItemId) ?? 0;
11069
+ const recipients = /* @__PURE__ */ new Set([
11070
+ ...item.assigneeThreads,
11071
+ ...unblocked > 0 ? item.claimedBy : []
11072
+ ]);
11073
+ for (const threadId of recipients) {
11074
+ const candidate = itemWakeCandidate(
11075
+ projectId,
11076
+ project2,
11077
+ workItemId,
11078
+ item,
11079
+ threadId,
11080
+ woken,
11081
+ isMemberThread
11082
+ );
11083
+ if (candidate === null || engineDispatches(project2, candidate)) continue;
11084
+ if (needsMemberSlot(candidate) && slots.get(threadId)?.key !== candidate.key) continue;
11085
+ due.push(candidate);
11086
+ }
11087
+ }
11088
+ due.push(...dueReviews(project2).filter((review) => !wakeGated(project2, review)));
11089
+ }
11090
+ return due.sort((a, b) => compare2(a.projectId, b.projectId) || compare2(a.key, b.key));
11091
+ }
11092
+ function teamItemDecision(project2, projectId, workItemId, threadId) {
11093
+ const item = readFact(project2.items, workItemId);
11094
+ if (item === null) return { kind: "held", reason: "the team has not seen this work item yet" };
11095
+ if (isClosed(item)) return { kind: "held", reason: "the work item is closed" };
11096
+ if (hasOpenBlocker(project2, workItemId))
11097
+ return { kind: "held", reason: "the work item is blocked by unfinished work items" };
11098
+ if (isThreadGated(project2, threadId))
11099
+ return { kind: "held", reason: "the team is stopped or paused" };
11100
+ const woken = new Set(project2.woken);
11101
+ const candidate = itemWakeCandidate(
11102
+ projectId,
11103
+ project2,
11104
+ workItemId,
11105
+ item,
11106
+ threadId,
11107
+ woken,
11108
+ memberLookup(project2)
11109
+ );
11110
+ if (candidate === null)
11111
+ return {
11112
+ kind: "held",
11113
+ reason: item.assigneeThreads.includes(threadId) ? "the team already handed this work item over" : "the team runs this member in another session"
11114
+ };
11115
+ if (needsMemberSlot(candidate)) {
11116
+ const slot = memberItemSlots(projectId, project2, woken, threadId).get(threadId);
11117
+ if (slot?.key !== candidate.key)
11118
+ return {
11119
+ kind: "held",
11120
+ reason: slot === void 0 ? "the member is still working on another work item" : `the member gets W${slot.sequenceId} first`
11121
+ };
11122
+ }
11123
+ return { kind: "due", wake: candidate };
11124
+ }
11125
+
11126
+ // src/runtime/dispatchGate.ts
11127
+ var allow = { allowed: true };
11128
+ var hold = (detail) => ({ allowed: false, detail });
11129
+ var allowIn = (threadId, picked) => threadId === picked ? allow : { allowed: true, threadId };
11130
+ var JOINING = "the member is joining this team; its team session opens within a couple of minutes";
11131
+ function registerDispatchGate(ctx, deps) {
11393
11132
  const pacing = createDeliveryPacing(
11394
11133
  ctx.derivedState(deliveryPacingStateDefinition),
11395
11134
  async (threadId) => waitingOnSomeone(await readMemberExecution(ctx, threadId))
11396
11135
  );
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;
11136
+ ctx.reactor({
11137
+ name: "team-delivery-pacing",
11138
+ events: ["thread.session-set"],
11139
+ handle: (event) => pacing.observe(event)
11140
+ });
11141
+ const legacyProjectGoal = async (project2, input) => {
11142
+ if (project2.goalId === void 0) return allow;
11143
+ if (readFact(project2.bindings ?? {}, input.memberId) !== null)
11144
+ return hold("the project's older team goal dispatches this member");
11145
+ if (isClosed(readFact(project2.items, teamGoalItemId(input.projectId, project2.goalId))))
11146
+ return allow;
11147
+ const lines = await ctx.work.lines.list({
11148
+ projectId: input.projectId,
11149
+ includeArchived: true,
11150
+ limit: 1
11151
+ });
11152
+ return lines.items.length === 0 ? hold("the project's older team goal takes this member in and dispatches it") : allow;
11444
11153
  };
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
- });
11154
+ const memberThreadsOf = (scope2, memberId) => teamSessions(scope2).filter((threadId) => scope2.memberIds?.[threadId] === memberId);
11155
+ const itemGate = async (project2, scope2, input) => {
11156
+ const workItemId = input.workItemId;
11157
+ const binding = readFact(scope2.bindings ?? {}, input.memberId);
11158
+ if (binding === null) return hold(JOINING);
11159
+ if (!("threadId" in binding)) return hold("the team runs this member on another machine");
11160
+ const lead = scope2.leadThreads?.includes(binding.threadId) === true;
11161
+ const executor = lead ? { threadId: binding.threadId } : executorOf(project2, workItemId, input.memberId);
11162
+ if (executor === void 0 || !("threadId" in executor)) return hold(JOINING);
11163
+ const threadId = executor.threadId;
11164
+ const decision = teamItemDecision(project2, input.projectId, workItemId, threadId);
11165
+ if (decision.kind === "held") return hold(decision.reason);
11166
+ const isReady2 = await deps.readiness();
11167
+ if (!exemptFromTrial(project2, { threadId }, workItemId) && !isReady2(input.projectId, input.memberId, scope2.goalId))
11168
+ return hold("the member has not passed its trial run yet");
11169
+ const worktree = lead ? void 0 : memberWorktreeFor(deps.goals, project2, input.projectId, { threadId });
11170
+ if (worktree !== void 0 && worktree.failure === void 0) {
11171
+ const execution = await ctx.projections.getThreadExecution(threadId);
11172
+ if (execution !== null && !execution.worktreePath) {
11173
+ deps.requestWorktree(input.projectId, threadId);
11174
+ return hold("the member's own worktree is being prepared");
11474
11175
  }
11475
11176
  }
11177
+ return allowIn(threadId, input.threadId);
11476
11178
  };
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
- }
11179
+ const messageGate = async (project2, scope2, input) => {
11180
+ const binding = readFact(scope2.bindings ?? {}, input.memberId);
11181
+ if (binding === null) return hold(JOINING);
11182
+ if (!("threadId" in binding)) return allow;
11183
+ const threadId = input.threadId !== null && memberThreadsOf(scope2, input.memberId).includes(input.threadId) ? input.threadId : binding.threadId;
11184
+ const isReady2 = await deps.readiness();
11185
+ if (!exemptFromTrial(project2, { threadId }, input.workItemId ?? void 0) && !isReady2(input.projectId, input.memberId, scope2.goalId))
11186
+ return hold("the member has not passed its trial run yet");
11187
+ if (input.reason === "group" && input.recordId !== void 0) {
11188
+ const [record2] = await ctx.communication.records.get({ recordIds: [input.recordId] });
11189
+ const fromAgent = record2 !== void 0 && record2.from.kind !== "user" && record2.from.kind !== "local-owner";
11190
+ if (fromAgent && !await pacing.allows(threadId, await deps.now()))
11191
+ return hold("slowed down: the member made no progress in its last turns");
11192
+ }
11193
+ return allowIn(threadId, input.threadId);
11484
11194
  };
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);
11195
+ const check = async (input) => {
11196
+ const project2 = readFact(
11197
+ await deps.wakes.get({ throughSequence: input.throughSequence }),
11198
+ input.projectId
11199
+ );
11200
+ if (project2 === null) return allow;
11201
+ const scope2 = input.workLineId === null ? void 0 : project2.lineScopes?.[input.workLineId];
11202
+ if (scope2 === void 0) {
11203
+ if (input.reason === "item") return legacyProjectGoal(project2, input);
11204
+ return project2.goalId !== void 0 && readFact(project2.bindings ?? {}, input.memberId) !== null ? messageGate(project2, project2, input) : allow;
11513
11205
  }
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 };
11206
+ if (project2.lineStops?.[input.workLineId] !== void 0)
11207
+ return hold("the team on this work line is stopped");
11208
+ return input.reason === "item" ? itemGate(project2, scope2, input) : messageGate(project2, scope2, input);
11209
+ };
11210
+ return ctx.operations.guard({ operations: ["work.dispatch"], check });
11520
11211
  }
11521
11212
 
11522
11213
  // src/runtime/memberBinding.ts
@@ -12189,261 +11880,85 @@ function foldRecovery(state, event, previous) {
12189
11880
  remindedItemIds,
12190
11881
  escalatedItemIds,
12191
11882
  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);
11883
+ pending,
11884
+ handledTurnIds: pending === null && member.pending !== null ? [.../* @__PURE__ */ new Set([...member.handledTurnIds, member.pending.turnId])] : member.handledTurnIds
11885
+ };
12364
11886
  }
11887
+ return changed ? { ...state, [work.projectId]: { ...project2, recovery } } : state;
12365
11888
  }
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;
11889
+ return state;
12405
11890
  }
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(
11891
+ function dueRecoveries(state, changedThreadId) {
11892
+ return Object.entries(state).flatMap(
11893
+ ([projectId, project2]) => Object.entries(project2.recovery ?? {}).flatMap(([threadId, member]) => {
11894
+ const scope2 = scopeOfThread(project2, threadId);
11895
+ const memberId = scope2?.memberIds?.[threadId];
11896
+ const leadThreadId = scope2?.leadThreads?.[0];
11897
+ 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 }))
11898
+ return [];
11899
+ const stalled = member.pending.kind === "stall";
11900
+ if (member.handledTurnIds.includes(member.pending.turnId)) return [];
11901
+ const items = Object.entries(project2.items).filter(
11902
+ ([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))
11903
+ ).map(([workItemId, item]) => ({
11904
+ workItemId,
11905
+ sequenceId: item.sequenceId,
11906
+ lastCommentId: item.lastCommentId,
11907
+ waitingForReply: item.lastActionCommentThreadId === threadId
11908
+ })).sort((a, b) => a.sequenceId - b.sequenceId);
11909
+ const deferredWakeCount = countDeferredWakes(project2, threadId);
11910
+ return items.length === 0 ? [] : [
11911
+ {
11912
+ ...member.pending,
12420
11913
  projectId,
12421
- project2,
12422
- workItemId,
12423
- item,
12424
11914
  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));
11915
+ memberId,
11916
+ leadThreadId,
11917
+ key: JSON.stringify([threadId, member.pending.turnId]),
11918
+ alreadyRetried: items.some(
11919
+ (item) => member.retriedItemIds.includes(item.workItemId) || (member.remindedItemIds ?? []).includes(item.workItemId) || (member.escalatedItemIds ?? []).includes(item.workItemId) || (member.pending?.continuedItemIds ?? []).includes(item.workItemId)
11920
+ ),
11921
+ items,
11922
+ ...deferredWakeCount > 0 ? { deferredWakeCount } : {}
11923
+ }
11924
+ ];
11925
+ })
11926
+ );
12445
11927
  }
12446
11928
 
11929
+ // src/runtime/wakeTexts.ts
11930
+ var workLabel = (workItemId, sequenceId) => sequenceId === void 0 ? `the work item with workItemId ${workItemId}` : `work item W${sequenceId} (workItemId ${workItemId})`;
11931
+ var titled = (workItemId, sequenceId, title) => `${workLabel(workItemId, sequenceId)}${title ? `: ${title}` : ""}`;
11932
+ var ITEM_SUMMARY_MAX_CHARS = 300;
11933
+ var descriptionStart = (description) => {
11934
+ const first = description?.trim().split(/\n\s*\n/u)[0]?.trim() ?? "";
11935
+ if (first === "") return "";
11936
+ const points = Array.from(first);
11937
+ const summary = points.length > ITEM_SUMMARY_MAX_CHARS ? `${points.slice(0, ITEM_SUMMARY_MAX_CHARS).join("")}\u2026` : first;
11938
+ return `Its description starts: ${summary}`;
11939
+ };
11940
+ var itemHeadline = (sequenceId, item) => {
11941
+ const title = item?.title ? `${sequenceId === void 0 ? "Work item" : `W${sequenceId}`}: ${item.title}.` : "";
11942
+ const parts = [title, descriptionStart(item?.description)].filter((part2) => part2 !== "");
11943
+ return parts.length === 0 ? "" : `${parts.join(" ")} `;
11944
+ };
11945
+ var MEMBER_INSTRUCTIONS_LIMIT = 2e3;
11946
+ 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.";
11947
+ 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}.)`}`;
11948
+ 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}`;
11949
+ 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}`;
11950
+ 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}`;
11951
+ 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.`;
11952
+ var leadReviewText = (workItemId, sequenceId, title, by, facts) => [
11953
+ `${by} marked ${titled(workItemId, sequenceId, title)} done. Read it with work_read_items, passing workItemIds: ["${workItemId}"], and check it now.`,
11954
+ LEAD_ACCEPT_RULE,
11955
+ LEAD_SEND_BACK_RULE,
11956
+ ...facts.idle.length === 0 ? [] : [`Nothing in progress now: ${facts.idle.join("; ")}.`],
11957
+ ...facts.queued.length === 0 ? [] : [`Waiting in line, not blocked: ${facts.queued.join("; ")}.`],
11958
+ ...facts.idle.length > 0 && facts.queued.length > 0 ? [LEAD_REBALANCE_RULE] : [],
11959
+ LEAD_NO_MEMBER_TASKS
11960
+ ].join(" ");
11961
+
12447
11962
  // src/runtime/memberAssignments.ts
12448
11963
  var threadOf = (bindings, memberId) => {
12449
11964
  const binding = bindings === void 0 ? null : readFact(bindings, memberId);
@@ -12583,6 +12098,8 @@ var WAKE_EVENTS = [
12583
12098
  "work.relation-added",
12584
12099
  "work.relation-removed",
12585
12100
  "work.comment-posted",
12101
+ // 看板把件交到了成员会话上(引擎叫醒了它):当成这一次「你有活」已经叫过,占名额、固定执行者都照旧。
12102
+ "work.item-dispatched",
12586
12103
  "plugin.event-appended",
12587
12104
  "thread.session-set",
12588
12105
  // 线与线上成员的暂停:引擎的看板事实,派发闸读它(见 `linePauses` / `pausedMembers`)。
@@ -12599,7 +12116,6 @@ var emptyProject = () => ({
12599
12116
  remoteMembers: [],
12600
12117
  items: {},
12601
12118
  blockers: {},
12602
- pendingComments: [],
12603
12119
  woken: [],
12604
12120
  rearms: {},
12605
12121
  blockedAfterClaim: {}
@@ -12665,6 +12181,24 @@ var withoutInitialLeadWake = (project2, projectId, workItemId) => {
12665
12181
  woken: [...project2.woken, ...leads.map((threadId) => itemWakeKey(workItemId, threadId, 0))]
12666
12182
  };
12667
12183
  };
12184
+ var decodeDispatched = decodeUnknownOption2(
12185
+ Struct({ projectId: String4, workItemId: String4, threadId: String4 })
12186
+ );
12187
+ var markWoken = (project2, key) => {
12188
+ if (project2.woken.includes(key)) return project2;
12189
+ const items = { ...project2.items };
12190
+ for (const [workItemId, item] of Object.entries(items)) {
12191
+ for (const threadId of item.assigneeThreads) {
12192
+ if (item.wakeStatus?.[threadId] !== "released" && isWorkable(project2, workItemId) && key === itemWakeKey(
12193
+ workItemId,
12194
+ threadId,
12195
+ readFact(project2.rearms, `${workItemId}:${threadId}`) ?? 0
12196
+ ))
12197
+ items[workItemId] = { ...item, wakeStatus: { ...item.wakeStatus, [threadId]: "active" } };
12198
+ }
12199
+ }
12200
+ return { ...project2, items, woken: [...project2.woken, key] };
12201
+ };
12668
12202
  var markBlockedAfterClaim = (project2, workItemId) => ({
12669
12203
  ...project2,
12670
12204
  blockedAfterClaim: {
@@ -12692,14 +12226,7 @@ function foldWorkWakes(state, event) {
12692
12226
  )
12693
12227
  };
12694
12228
  }
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
- };
12229
+ return { ...state, [goal.projectId]: rebindItems({ ...project4, ...scope2 }) };
12703
12230
  }
12704
12231
  if (fact.name === "team/member-bound") {
12705
12232
  const { projectId: projectId3, memberId } = fact.data;
@@ -12714,17 +12241,17 @@ function foldWorkWakes(state, event) {
12714
12241
  lineId === void 0 ? project4 : project4.lineScopes[lineId],
12715
12242
  fact.data
12716
12243
  );
12717
- const next2 = rebindItems(
12244
+ const next3 = rebindItems(
12718
12245
  lineId === void 0 ? { ...project4, ...bound } : { ...project4, lineScopes: { ...project4.lineScopes, [lineId]: bound } },
12719
12246
  memberId,
12720
12247
  lineId
12721
12248
  );
12722
- return { ...state, [projectId3]: next2 };
12249
+ return { ...state, [projectId3]: next3 };
12723
12250
  }
12724
12251
  if (fact.name === "team/line-paused" || fact.name === "team/line-member-paused") {
12725
12252
  const { projectId: projectId3, workLineId, status } = fact.data;
12726
12253
  const project4 = readFact(state, projectId3) ?? emptyProject();
12727
- const next2 = withPause(
12254
+ const next3 = withPause(
12728
12255
  project4,
12729
12256
  {
12730
12257
  projectId: projectId3,
@@ -12734,22 +12261,7 @@ function foldWorkWakes(state, event) {
12734
12261
  },
12735
12262
  event.eventId
12736
12263
  );
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
- };
12264
+ return next3 === project4 ? state : { ...state, [projectId3]: next3 };
12753
12265
  }
12754
12266
  if (fact.name === "team/member-worktree-failed" || fact.name === "team/member-worktree-reported") {
12755
12267
  const { projectId: projectId3, threadId } = fact.data;
@@ -12767,15 +12279,6 @@ function foldWorkWakes(state, event) {
12767
12279
  }
12768
12280
  const { projectId: projectId2, key } = fact.data;
12769
12281
  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
12282
  if (project3.pendingReviews?.some((review) => review.key === key)) {
12780
12283
  return {
12781
12284
  ...state,
@@ -12785,19 +12288,22 @@ function foldWorkWakes(state, event) {
12785
12288
  }
12786
12289
  };
12787
12290
  }
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] } };
12291
+ const next2 = markWoken(project3, key);
12292
+ return next2 === project3 ? state : { ...state, [projectId2]: next2 };
12293
+ }
12294
+ if (event.type === "work.item-dispatched") {
12295
+ const decoded2 = decodeDispatched(event.payload);
12296
+ if (isNone2(decoded2)) return state;
12297
+ const { projectId: projectId2, workItemId: workItemId2, threadId } = decoded2.value;
12298
+ const project3 = readFact(state, projectId2);
12299
+ if (project3 === null) return state;
12300
+ const key = itemWakeKey(
12301
+ workItemId2,
12302
+ threadId,
12303
+ readFact(project3.rearms, `${workItemId2}:${threadId}`) ?? 0
12304
+ );
12305
+ const next2 = markWoken(project3, key);
12306
+ return next2 === project3 ? state : { ...state, [projectId2]: next2 };
12801
12307
  }
12802
12308
  const decoded = decodeWorkEvent(event);
12803
12309
  if (isNone2(decoded)) return state;
@@ -12928,20 +12434,11 @@ function foldWorkWakes(state, event) {
12928
12434
  }
12929
12435
  break;
12930
12436
  }
12437
+ // 评论里 @ 谁、回复谁由引擎投递(提案 0085 切片 3);这里只记最近一条评论,恢复与问进度要用。
12931
12438
  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
- );
12439
+ const { actor, commentId } = work.payload;
12938
12440
  next = {
12939
12441
  ...project2,
12940
- pendingComments: queueCommentWakes(
12941
- project2,
12942
- { projectId, workItemId, commentId },
12943
- recipients
12944
- ),
12945
12442
  items: currentItem === null ? project2.items : {
12946
12443
  ...project2.items,
12947
12444
  [workItemId]: {
@@ -13020,38 +12517,6 @@ function foldWakes(state, event) {
13020
12517
  }
13021
12518
 
13022
12519
  // 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
12520
  function startWakeRuntime(ctx, goals, options = {}) {
13056
12521
  const wakes = ctx.derivedState({
13057
12522
  key: WAKE_STATE_KEY,
@@ -13078,8 +12543,6 @@ function startWakeRuntime(ctx, goals, options = {}) {
13078
12543
  };
13079
12544
  };
13080
12545
  const baseText = async (wake, state) => {
13081
- if ("principalId" in wake || wake.kind === "comment")
13082
- return commentText(wake.workItemId, wake.sequenceId);
13083
12546
  const item = await readItem(wake.projectId, wake.workItemId);
13084
12547
  const title = item?.title;
13085
12548
  if (wake.kind === "review") {
@@ -13090,7 +12553,7 @@ function startWakeRuntime(ctx, goals, options = {}) {
13090
12553
  const key = "threadId" in id ? id.threadId : id.principalId;
13091
12554
  return `${member?.name ?? key} (${"threadId" in id ? "threadId" : "principalId"} ${key})`;
13092
12555
  };
13093
- const facts = project2 === void 0 ? { idle: [], queued: [] } : reviewFacts(project2, memberQueues(wake.projectId, project2), wake.threadId);
12556
+ const facts = project2 === void 0 || !("threadId" in wake) ? { idle: [], queued: [] } : reviewFacts(project2, memberQueues(wake.projectId, project2), wake.threadId);
13094
12557
  return leadReviewText(
13095
12558
  wake.workItemId,
13096
12559
  wake.sequenceId,
@@ -13118,6 +12581,42 @@ function startWakeRuntime(ctx, goals, options = {}) {
13118
12581
  title
13119
12582
  );
13120
12583
  };
12584
+ const compose = async (wake, state) => {
12585
+ const team = "threadId" in wake ? lineScopeOfThread(state[wake.projectId], wake.threadId) : void 0;
12586
+ const memberId = team === void 0 || !("threadId" in wake) ? void 0 : team.scope.memberIds?.[wake.threadId];
12587
+ 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({
12588
+ projectId: wake.projectId,
12589
+ workLineId: team.workLineId,
12590
+ memberId,
12591
+ threadId: wake.threadId,
12592
+ leadThreadId: team.scope.leadThreads?.[0] ?? "",
12593
+ wakeKey: wake.key,
12594
+ // 群记录的发件人取成员名(会话号 → 成员号 → 名单),读不到就退回 Lead / Member。
12595
+ // 名单在这一次摘要里只读一次:惰性缓存同一个 Promise,不随每条记录重读。
12596
+ nameOfThread: /* @__PURE__ */ (() => {
12597
+ let roster;
12598
+ return async (threadId) => {
12599
+ if (team.scope.memberIds?.[threadId] === void 0) return null;
12600
+ roster ??= goals.read(wake.projectId, { forLine: team.workLineId });
12601
+ return findGoalMember((await roster)?.members ?? [], { threadId })?.name ?? null;
12602
+ };
12603
+ })()
12604
+ });
12605
+ const waiting = team === void 0 || !("threadId" in wake) || team.scope.leadThreads?.includes(wake.threadId) !== true || options.teamWaiting === void 0 ? null : await (async () => {
12606
+ const members = (await goals.read(wake.projectId, { forThread: wake.threadId }))?.members ?? [];
12607
+ return options.teamWaiting(
12608
+ team.scope.threads.filter((id) => id !== wake.threadId),
12609
+ (id) => findGoalMember(members, { threadId: id })?.name ?? id
12610
+ );
12611
+ })().catch(() => null);
12612
+ return {
12613
+ digest,
12614
+ text: async () => {
12615
+ const body = await baseText(wake, state);
12616
+ return [digest?.body, body, waiting].filter((part2) => part2 != null).join("\n\n");
12617
+ }
12618
+ };
12619
+ };
13121
12620
  const execute = async () => {
13122
12621
  let state = await wakes.get();
13123
12622
  const isReady2 = options.readiness === void 0 ? () => true : await options.readiness();
@@ -13137,33 +12636,7 @@ function startWakeRuntime(ctx, goals, options = {}) {
13137
12636
  try {
13138
12637
  const id = hash2(wake.key);
13139
12638
  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);
12639
+ const { digest, text } = await compose(wake, state);
13167
12640
  const delivered = await deliverWake(ctx, {
13168
12641
  projectId: wake.projectId,
13169
12642
  recipient: wake,
@@ -13171,12 +12644,7 @@ function startWakeRuntime(ctx, goals, options = {}) {
13171
12644
  messageId: `team-wake-msg:${id}`,
13172
12645
  wake: { reason: "work", sequenceId: wake.sequenceId },
13173
12646
  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
- }
12647
+ text
13180
12648
  });
13181
12649
  if (!delivered) continue;
13182
12650
  if (digest !== null) await options.groupDigest.confirm(wake.projectId, digest);
@@ -13212,7 +12680,73 @@ function startWakeRuntime(ctx, goals, options = {}) {
13212
12680
  });
13213
12681
  const ready = flush();
13214
12682
  void ready.catch((error) => ctx.log?.warn("team wake catch-up failed", { error }));
13215
- return { flush, ready, state: wakes, worktrees };
12683
+ return { flush, ready, state: wakes, worktrees, compose };
12684
+ }
12685
+
12686
+ // src/runtime/wakeSupplement.ts
12687
+ var decodeDispatched2 = decodeUnknownOption2(
12688
+ Struct({ projectId: String4, threadId: String4 })
12689
+ );
12690
+ function registerWakeSupplement(ctx, wakes, groupDigest) {
12691
+ const add3 = async (input) => {
12692
+ const state = await wakes.state.get({ throughSequence: input.throughSequence });
12693
+ const project2 = readFact(state, input.projectId);
12694
+ if (project2 === null || lineScopeOfThread(project2, input.threadId) === void 0) return null;
12695
+ const decision = teamItemDecision(project2, input.projectId, input.workItemId, input.threadId);
12696
+ if (decision.kind === "held")
12697
+ throw new Error(`the team cannot hand this work item over now: ${decision.reason}`);
12698
+ const { text } = await wakes.compose(decision.wake, state);
12699
+ return text();
12700
+ };
12701
+ ctx.reactor({
12702
+ name: "team-board-handover",
12703
+ events: ["work.item-dispatched"],
12704
+ handle: async (event) => {
12705
+ const decoded = decodeDispatched2(event.payload);
12706
+ if (isNone2(decoded) || groupDigest === void 0) return;
12707
+ await groupDigest.confirmPrepared(decoded.value.projectId, decoded.value.threadId);
12708
+ }
12709
+ });
12710
+ return ctx.operations.supplement({ operations: ["work.dispatch"], add: add3 });
12711
+ }
12712
+
12713
+ // src/runtime/worktreeSetup.ts
12714
+ 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.";
12715
+ function startWorktreeSetupRuntime(ctx, deps) {
12716
+ const requested = /* @__PURE__ */ new Map();
12717
+ let running = Promise.resolve();
12718
+ const setUp = async (projectId, threadId) => {
12719
+ const project2 = (await deps.wakes.get())[projectId];
12720
+ const worktree = memberWorktreeFor(deps.goals, project2, projectId, { threadId });
12721
+ if (worktree === void 0 || worktree.failure !== void 0) return;
12722
+ const execution = await ctx.projections.getThreadExecution(threadId);
12723
+ if (execution === null || execution.archivedAt !== null || execution.worktreePath) return;
12724
+ const id = stableHash(threadId);
12725
+ await deliverWake(ctx, {
12726
+ projectId,
12727
+ recipient: { threadId },
12728
+ requestId: `team-worktree-setup:${id}`,
12729
+ messageId: `team-worktree-setup-msg:${id}`,
12730
+ wake: { reason: "notice" },
12731
+ memberWorktree: { ...worktree, lane: deps.lane },
12732
+ text: WORKTREE_SETUP_TEXT
12733
+ });
12734
+ };
12735
+ const drain = async () => {
12736
+ for (const [key, entry] of requested) {
12737
+ requested.delete(key);
12738
+ try {
12739
+ await setUp(entry.projectId, entry.threadId);
12740
+ } catch (error) {
12741
+ ctx.log.warn("team member worktree setup failed", { ...entry, error });
12742
+ }
12743
+ }
12744
+ };
12745
+ const request = (projectId, threadId) => {
12746
+ requested.set(`${projectId}|${threadId}`, { projectId, threadId });
12747
+ running = running.then(drain, drain);
12748
+ };
12749
+ return { request, idle: () => running };
13216
12750
  }
13217
12751
 
13218
12752
  // src/runtime/recovery.ts
@@ -13863,7 +13397,7 @@ var emptyHandbookProject = () => ({
13863
13397
  acknowledgements: {},
13864
13398
  reminded: {}
13865
13399
  });
13866
- var decodeFact6 = decodeUnknownOption2(
13400
+ var decodeFact5 = decodeUnknownOption2(
13867
13401
  Union2([
13868
13402
  factEnvelope("goal-started", GoalStartedFact),
13869
13403
  factEnvelope("member-bound", MemberBoundFact),
@@ -13947,7 +13481,7 @@ function foldHandbook(state, event) {
13947
13481
  };
13948
13482
  }
13949
13483
  if (event.type !== "plugin.event-appended") return state;
13950
- const decoded = decodeFact6(event.payload);
13484
+ const decoded = decodeFact5(event.payload);
13951
13485
  if (isNone2(decoded)) return state;
13952
13486
  const fact = decoded.value;
13953
13487
  const projectId = fact.name === "team/goal-started" ? fact.data.goal.projectId : fact.name === "team/roster-set" ? fact.data.roster.projectId : fact.data.projectId;
@@ -14831,12 +14365,6 @@ var LineChangeCompleted = Struct({
14831
14365
  ])
14832
14366
  });
14833
14367
 
14834
- // src/messages.ts
14835
- var interpolateMessage = (template, values) => template.replace(
14836
- /{{(\w+)}}/g,
14837
- (whole, name) => Object.hasOwn(values, name) ? String(values[name]) : whole
14838
- );
14839
-
14840
14368
  // src/team/messages.ts
14841
14369
  function teamMessage(key, values = {}) {
14842
14370
  return interpolateMessage(en_default.team[key], values);
@@ -14991,7 +14519,7 @@ function teamLeadBrief(team) {
14991
14519
  'Then use work_add_relation with type "blocked_by" to record dependencies: the dependent workItemId is blocked by the prerequisite relatedWorkItemId.',
14992
14520
  LEAD_OWN_ITEM_RULE,
14993
14521
  memberAssignRule(true),
14994
- "Add or remove members of the team with team_set_members. To add someone who is not in the project yet, create them first with team_create_member (it starts their trial run), then add them. Use team_roster_overview to see everyone in the project and which teams they are in; work_list_machines lists the other machines that joined this project and the providers and models each one offers, for creating a member on one of them.",
14522
+ "Add or remove members of the team with team_set_members. To add someone who is not in the project yet, create them first with team_create_member (it starts their trial run), then add them. Use team_roster_overview to see everyone in the project and which teams they are in; work_list_machines lists the other machines that joined this project and the providers and models each one offers; to place a member on one of them, pass its environmentId to team_create_member or team_update_member.",
14995
14523
  LEAD_SEND_BACK_RULE,
14996
14524
  LEAD_ACCEPT_RULE,
14997
14525
  LEAD_STUCK_RULE,
@@ -15977,6 +15505,14 @@ function createGroupDigest(ctx, state) {
15977
15505
  await state.get({ throughSequence: receipt.sequence });
15978
15506
  return { digestId, threadId: input.threadId, body };
15979
15507
  },
15508
+ /**
15509
+ * 引擎叫醒了这条会话(看板交件):随那次叫醒补充出去的那一份算已投递,水位前进。没有装配好的就什么也不做。
15510
+ */
15511
+ async confirmPrepared(projectId, threadId) {
15512
+ const prepared = readFact(await state.get(), threadId)?.prepared;
15513
+ if (prepared == null) return;
15514
+ await this.confirm(projectId, { digestId: prepared.digestId, threadId, body: prepared.body });
15515
+ },
15980
15516
  /** 叫醒已被宿主受理:水位前进。同一份只落一次。 */
15981
15517
  async confirm(projectId, digest) {
15982
15518
  const receipt = await ctx.events.append({
@@ -15998,6 +15534,10 @@ function createGroupDigest(ctx, state) {
15998
15534
 
15999
15535
  // locales/zh-CN.json
16000
15536
  var zh_CN_default = {
15537
+ presentation: {
15538
+ name: "\u56E2\u961F",
15539
+ description: "\u7ED9\u9879\u76EE\u56E2\u961F\u4E00\u4E2A\u76EE\u6807\uFF1A\u8D1F\u8D23\u4EBA\u5728\u770B\u677F\u4E0A\u62C6\u6D3B\uFF0C\u6D3E\u7ED9\u6210\u5458\u53BB\u505A\u3002"
15540
+ },
16001
15541
  handbook: {
16002
15542
  requestConflict: "\u8FD9\u4E2A requestId \u5DF2\u7528\u4E8E\u53E6\u4E00\u4EFD\u624B\u518C\u8BF7\u6C42\uFF0C\u8BF7\u6362\u4E00\u4E2A\u65B0\u7684 requestId\u3002",
16003
15543
  requestVoided: "\u8FD9\u4E2A requestId \u5728\u624B\u518C\u6536\u5230\u4E4B\u524D\u5C31\u4F5C\u5E9F\u4E86\uFF0C\u8BF7\u6362\u4E00\u4E2A\u65B0\u7684 requestId\u3002",
@@ -16042,8 +15582,8 @@ var zh_CN_default = {
16042
15582
  versionConflict: "\u56E2\u961F\u521A\u597D\u540C\u65F6\u88AB\u6539\u4E86\u3002\u91CD\u65B0\u8BFB\u4E00\u904D\uFF0C\u7528\u65B0\u7684 requestId \u518D\u8BD5\u3002"
16043
15583
  },
16044
15584
  coordinator: {
16045
- createTool: '\u4EE5\u5F53\u524D\u76EE\u6807\u8D1F\u8D23\u4EBA\u7684\u8EAB\u4EFD\uFF0C\u7ED9\u8FD9\u4E2A\u9879\u76EE\u7684\u56E2\u961F\u6DFB\u52A0\u4E00\u540D AI \u6210\u5458\u3002requestId \u8981\u7528\u65B0\u7684\uFF0C\u53EA\u6709\u91CD\u8BD5\u540C\u4E00\u4E2A\u8BF7\u6C42\u65F6\u624D\u6CBF\u7528\u3002\u6210\u5458\u5728\u8FD9\u53F0\u673A\u5668\u4E0A\u8FD0\u884C\uFF0C\u7528\u4F60\u7ED9\u7684 provider \u5B9E\u4F8B instanceId \u548C\u6A21\u578B model\uFF08\u8981\u4E48\u90FD\u7ED9\uFF0C\u8981\u4E48\u90FD\u4E0D\u7ED9\uFF09\uFF1B\u90FD\u4E0D\u7ED9\u5C31\u7528\u9879\u76EE\u7684\u9ED8\u8BA4\u6A21\u578B\u3002\u65B0\u6210\u5458\u4F1A\u52A0\u5165\u5F53\u524D\u76EE\u6807\uFF0C\u5728\u4F60\u7684\u4F1A\u8BDD\u540D\u4E0B\u5F97\u5230\u81EA\u5DF1\u7684\u4F1A\u8BDD\uFF0C\u901A\u5E38\u5F88\u5FEB\u5C31\u7EEA\uFF08\u6CA1\u6210\u529F\u4F1A\u544A\u8BC9\u4F60\u539F\u56E0\uFF0C\u5E76\u6BCF\u9694\u51E0\u5206\u949F\u91CD\u8BD5\u4E00\u6B21\uFF09\uFF1B\u7528 team_open \u5F00\u7684\u56E2\u961F\u91CC\uFF0C\u8981\u7B49\u4F60\u7528 team_set_members \u628A\u8FD9\u540D\u6210\u5458\u62C9\u8FDB\u56E2\u961F\u624D\u5F00\u59CB\uFF1B\u4F1A\u8BDD\u5C31\u7EEA\u540E\uFF0C\u53EF\u4EE5\u7528 work_update_item \u628A\u6D3B\u6D3E\u7ED9\u5B83\uFF0Cassignees \u5199 [{ "kind": "member", "memberId": <\u672C\u5DE5\u5177\u8FD4\u56DE\u7684 memberId> }]\u3002\u5148\u7528 work_list_members \u770B\u56E2\u961F\u91CC\u73B0\u5728\u6709\u8C01\u3002',
16046
- updateTool: "\u4EE5\u5F53\u524D\u76EE\u6807\u8D1F\u8D23\u4EBA\u7684\u8EAB\u4EFD\u4FEE\u6539\u4E00\u540D\u6210\u5458\uFF1A\u540D\u5B57\u3001\u5934\u8854\u3001\u804C\u8D23\u3001\u5DE5\u4F5C\u8BF4\u660E\uFF08\u7A7A\u5B57\u7B26\u4E32\u8868\u793A\u6E05\u7A7A\uFF09\uFF0C\u6216 provider \u5B9E\u4F8B instanceId \u548C\u6A21\u578B model\uFF08\u8981\u4E48\u90FD\u7ED9\uFF0C\u8981\u4E48\u90FD\u4E0D\u7ED9\uFF09\uFF1B\u65B0\u6A21\u578B\u53EA\u5BF9\u4E4B\u540E\u65B0\u5F00\u7684\u4F1A\u8BDD\u751F\u6548\uFF0C\u5DF2\u5728\u8FD0\u884C\u7684\u4F1A\u8BDD\u4FDD\u6301\u539F\u6765\u7684\u6A21\u578B\u3002expectedVersion \u586B work_read_member \u8BFB\u5230\u7684\u6210\u5458\u7248\u672C\uFF1B\u7248\u672C\u53D8\u4E86\u4F1A\u8FD4\u56DE stale-version\uFF0C\u91CD\u65B0\u8BFB\u4E00\u904D\u518D\u6539\u3002\u4E0D\u80FD\u6539\u81EA\u5DF1\u7684 provider \u548C\u6A21\u578B\u3002requestId \u53EA\u6709\u91CD\u8BD5\u540C\u4E00\u4E2A\u8BF7\u6C42\u65F6\u624D\u6CBF\u7528\u3002",
15585
+ createTool: '\u4EE5\u5F53\u524D\u76EE\u6807\u8D1F\u8D23\u4EBA\u7684\u8EAB\u4EFD\uFF0C\u7ED9\u8FD9\u4E2A\u9879\u76EE\u7684\u56E2\u961F\u52A0\u4E00\u4E2A AI \u961F\u53CB\u3002requestId \u8981\u7528\u65B0\u7684\uFF0C\u53EA\u6709\u91CD\u8BD5\u540C\u4E00\u6B21\u8BF7\u6C42\u65F6\u624D\u6CBF\u7528\u3002\u6210\u5458\u8DD1\u5728\u8FD9\u53F0\u673A\u5668\u4E0A\uFF0C\u9664\u975E\u4F60\u7ED9 environmentId\uFF1A\u52A0\u8FDB\u8FD9\u4E2A\u9879\u76EE\u7684\u53E6\u4E00\u53F0\u673A\u5668\uFF08work_list_machines \u5217\u51FA\u8FD9\u4E9B\u673A\u5668\u548C\u5404\u81EA\u63D0\u4F9B\u7684 provider\uFF09\u3002provider \u7684 instanceId \u548C model \u8981\u4E48\u90FD\u7ED9\uFF0C\u8981\u4E48\u90FD\u4E0D\u7ED9\uFF1B\u5728\u8FD9\u53F0\u673A\u5668\u4E0A\u4E0D\u7ED9\u5C31\u7528\u9879\u76EE\u7684\u9ED8\u8BA4\u6A21\u578B\uFF0C\u5728\u522B\u7684\u673A\u5668\u4E0A\u5FC5\u987B\u7ED9\uFF0C\u800C\u4E14\u5FC5\u987B\u662F\u90A3\u53F0\u673A\u5668\u62A5\u6765\u7684\u3001\u5DF2\u5C31\u7EEA\u7684 provider \u5B9E\u4F8B\u3002\u65B0\u6210\u5458\u52A0\u5165\u5F53\u524D\u76EE\u6807\uFF0C\u5728\u4F60\u7684\u4F1A\u8BDD\u540D\u4E0B\u5F97\u5230\u81EA\u5DF1\u7684\u4F1A\u8BDD\uFF0C\u901A\u5E38\u7247\u523B\u5C31\u597D\uFF08\u5931\u8D25\u4F1A\u544A\u8BC9\u4F60\u539F\u56E0\uFF0C\u5E76\u6BCF\u9694\u51E0\u5206\u949F\u91CD\u8BD5\uFF09\uFF1B\u5728 team_open \u5F00\u7684\u56E2\u961F\u91CC\uFF0C\u8981\u7B49\u4F60\u7528 team_set_members \u628A\u5B83\u62C9\u8FDB\u6765\u624D\u5F00\u59CB\uFF1B\u5C31\u7EEA\u540E\u53EF\u4EE5\u7528 work_update_item \u5E76\u5E26 assignees [{ "kind": "member", "memberId": <\u8FD9\u4E2A\u5DE5\u5177\u8FD4\u56DE\u7684 memberId> }] \u7ED9\u5B83\u6D3E\u6D3B\u3002\u5148\u7528 work_list_members \u770B\u56E2\u961F\u91CC\u6709\u8C01\u3002',
15586
+ updateTool: "\u4EE5\u5F53\u524D\u76EE\u6807\u8D1F\u8D23\u4EBA\u7684\u8EAB\u4EFD\u6539\u4E00\u4E2A\u961F\u53CB\uFF1A\u540D\u5B57\u3001\u5934\u8854\u3001\u804C\u8D23\u3001\u5DE5\u4F5C\u8981\u6C42\uFF08\u7A7A\u5B57\u7B26\u4E32\u8868\u793A\u6E05\u7A7A\uFF09\u3001\u5B83\u8DD1\u5728\u54EA\u53F0\u673A\u5668\u4E0A\uFF08environmentId\uFF1A\u8FD9\u53F0\u673A\u5668\u7684\u53F7\uFF0C\u6216 work_list_machines \u91CC\u7684\u53E6\u4E00\u53F0\u673A\u5668\uFF09\uFF0C\u6216 provider \u7684 instanceId \u548C model\uFF08\u8981\u4E48\u90FD\u7ED9\uFF0C\u8981\u4E48\u90FD\u4E0D\u7ED9\uFF09\u3002\u4E0D\u7ED9 environmentId \u65F6\u6210\u5458\u7559\u5728\u539F\u6765\u7684\u673A\u5668\u4E0A\u3002\u632A\u5230\u53E6\u4E00\u53F0\u673A\u5668\u65F6\uFF0C\u53EA\u6709\u90A3\u53F0\u673A\u5668\u62A5\u4E86\u540C\u4E00\u4E2A\u5DF2\u5C31\u7EEA\u7684\u5B9E\u4F8B\u624D\u6CBF\u7528\u6210\u5458\u539F\u6765\u7684 provider \u5B9E\u4F8B\uFF1B\u5426\u5219\u540C\u65F6\u7ED9\u51FA\u90A3\u53F0\u673A\u5668\u4E0A\u7684 instanceId \u548C model\u3002\u65B0\u673A\u5668\u6216\u65B0\u6A21\u578B\u53EA\u5BF9\u4E4B\u540E\u5F00\u7684\u4F1A\u8BDD\u751F\u6548\uFF0C\u5DF2\u7ECF\u5728\u8DD1\u7684\u4F1A\u8BDD\u4FDD\u6301\u539F\u6765\u7684\u8BBE\u7F6E\u3002expectedVersion \u586B work_read_member \u8BFB\u5230\u7684\u6210\u5458\u7248\u672C\uFF1B\u7248\u672C\u53D8\u4E86\u4F1A\u5F97\u5230 stale-version\uFF0C\u91CD\u65B0\u8BFB\u4E00\u6B21\u6210\u5458\u3002\u4E0D\u80FD\u6539\u81EA\u5DF1\u7684\u673A\u5668\u3001provider \u548C\u6A21\u578B\u3002\u53EA\u6709\u91CD\u8BD5\u540C\u4E00\u6B21\u8BF7\u6C42\u65F6\u624D\u6CBF\u7528 requestId\u3002",
16047
15587
  removeTool: "\u4EE5\u5F53\u524D\u76EE\u6807\u8D1F\u8D23\u4EBA\u7684\u8EAB\u4EFD\u628A\u4E00\u540D\u6210\u5458\u79FB\u51FA\u56E2\u961F\u3002\u8BB0\u5F55\u4F1A\u4FDD\u7559\uFF0C\u4EE5\u540E\u53EF\u4EE5\u6062\u590D\u3002\u6D3E\u7ED9\u5B83\u3001\u4F46\u8FD8\u6CA1\u5230\u5B83\u624B\u4E0A\u7684\u6D3B\u4F1A\u88AB\u6536\u56DE\u5E76\u901A\u77E5\u4F60\uFF1B\u5DF2\u7ECF\u5728\u5B83\u624B\u4E0A\u7684\u6D3B\u7559\u5728\u5B83\u90A3\u91CC\uFF0C\u7531\u4F60\u51B3\u5B9A\u3002\u4E0D\u80FD\u79FB\u9664\u81EA\u5DF1\u3002requestId \u53EA\u6709\u91CD\u8BD5\u540C\u4E00\u4E2A\u8BF7\u6C42\u65F6\u624D\u6CBF\u7528\u3002",
16048
15588
  restoreTool: "\u4EE5\u5F53\u524D\u76EE\u6807\u8D1F\u8D23\u4EBA\u7684\u8EAB\u4EFD\u628A\u5DF2\u79FB\u9664\u7684\u6210\u5458\u8BF7\u56DE\u56E2\u961F\u3002requestId \u53EA\u6709\u91CD\u8BD5\u540C\u4E00\u4E2A\u8BF7\u6C42\u65F6\u624D\u6CBF\u7528\u3002",
16049
15589
  noProject: "\u8FD9\u6B21\u8C03\u7528\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF0C\u6CA1\u6709\u53EF\u6539\u7684\u56E2\u961F\u3002",
@@ -16061,7 +15601,12 @@ var zh_CN_default = {
16061
15601
  notAgentMember: "\u8FD9\u4E2A\u5DE5\u5177\u53EA\u80FD\u6539 agent \u6210\u5458\u3002",
16062
15602
  memberNotFound: "\u8FD9\u4E2A\u9879\u76EE\u91CC\u6CA1\u6709\u8FD9\u540D\u6210\u5458\u3002\u5148\u7528 work_list_members \u5217\u51FA\u6210\u5458\u3002",
16063
15603
  cannotRemoveLead: "\u4E0D\u80FD\u79FB\u9664\u81EA\u5DF1\uFF08\u5F53\u524D\u76EE\u6807\u7684\u8D1F\u8D23\u4EBA\uFF09\u3002",
16064
- cannotReplaceLead: "\u4E0D\u80FD\u6539\u81EA\u5DF1\u7684 provider \u548C\u6A21\u578B\u3002"
15604
+ cannotReplaceLead: "\u4E0D\u80FD\u6539\u81EA\u5DF1\u7684\u673A\u5668\u3001provider \u548C\u6A21\u578B\u3002",
15605
+ machineNotInProject: "\u673A\u5668 {{environmentId}} \u4E0D\u5728\u8FD9\u4E2A\u9879\u76EE\u91CC\u3002\u8C03 work_list_machines \u770B\u52A0\u8FDB\u8FD9\u4E2A\u9879\u76EE\u7684\u673A\u5668\uFF0C\u6216\u8005\u4E0D\u7ED9 environmentId\uFF0C\u7528\u8FD9\u53F0\u673A\u5668\u3002",
15606
+ machineNeedsModel: "\u653E\u5728\u673A\u5668 {{environmentId}} \u4E0A\u7684\u6210\u5458\u8981\u7528\u90A3\u53F0\u673A\u5668\u4E0A\u7684 provider\u3002\u6309 work_list_machines \u5BF9\u90A3\u53F0\u673A\u5668\u62A5\u7684\u5185\u5BB9\u7ED9\u51FA instanceId \u548C model\u3002",
15607
+ machineInstanceNotFound: "\u673A\u5668 {{environmentId}} \u6CA1\u6709\u62A5 provider \u5B9E\u4F8B {{instanceId}}\u3002\u8C03 work_list_machines\uFF08\u5E26 includeModels\uFF09\uFF0C\u7ED9\u51FA\u90A3\u53F0\u673A\u5668\u62A5\u8FC7\u7684 instanceId \u548C model\u3002\u8FD9\u4EFD\u6E05\u5355\u662F\u90A3\u53F0\u673A\u5668\u4E0A\u6B21\u8FDE\u4E0A\u65F6\u62A5\u7684\uFF1A\u5982\u679C\u4E4B\u540E\u624D\u5728\u90A3\u53F0\u673A\u5668\u4E0A\u88C5\u597D\u8FD9\u4E2A provider\uFF0C\u8BF7\u4EBA\u91CD\u65B0\u8FDE\u63A5\u90A3\u53F0\u673A\u5668\uFF08\u6216\u91CD\u542F\u5B83\u4E0A\u9762\u7684 Ycodium\uFF09\u5237\u65B0\u6E05\u5355\uFF0C\u518D\u8C03 work_list_machines\u3002",
15608
+ machineInstanceNotReady: "\u673A\u5668 {{environmentId}} \u4E0A\u7684 provider \u5B9E\u4F8B {{instanceId}} \u8FD8\u4E0D\u80FD\u63A5\u6D3B\u3002\u8C03 work_list_machines \u6311\u4E00\u4E2A\u5DF2\u5C31\u7EEA\u7684\u5B9E\u4F8B\uFF0C\u6216\u8005\u8BF7\u4EBA\u5230\u90A3\u53F0\u673A\u5668\u4E0A\u628A\u8FD9\u4E2A provider \u4FEE\u597D\u3002\u5C31\u7EEA\u72B6\u6001\u662F\u90A3\u53F0\u673A\u5668\u4E0A\u6B21\u8FDE\u4E0A\u65F6\u62A5\u7684\uFF1A\u5982\u679C\u4E4B\u540E\u5DF2\u7ECF\u4FEE\u597D\uFF0C\u8BF7\u4EBA\u91CD\u65B0\u8FDE\u63A5\u90A3\u53F0\u673A\u5668\uFF08\u6216\u91CD\u542F\u5B83\u4E0A\u9762\u7684 Ycodium\uFF09\u5237\u65B0\u6E05\u5355\uFF0C\u518D\u8C03 work_list_machines\u3002",
15609
+ machinesUnavailable: "\u73B0\u5728\u8BFB\u4E0D\u51FA\u8FD9\u4E2A\u9879\u76EE\u7684\u673A\u5668\uFF0C\u7A0D\u540E\u518D\u8BD5\u3002"
16065
15610
  },
16066
15611
  board: {
16067
15612
  openTeam: "\u5728\u770B\u677F\u4E0A\u6253\u5F00\u8FD9\u4E2A\u56E2\u961F",
@@ -16247,7 +15792,8 @@ var zh_CN_default = {
16247
15792
  thisMachine: "\u8FD9\u53F0\u673A\u5668",
16248
15793
  machineOffline: "{{name}}\uFF08\u79BB\u7EBF\uFF09",
16249
15794
  machineRemoved: "\u5DF2\u79FB\u51FA\u9879\u76EE\u7684\u673A\u5668",
16250
- noMachine: "\u8FD8\u6CA1\u9009\u673A\u5668"
15795
+ noMachine: "\u8FD8\u6CA1\u9009\u673A\u5668",
15796
+ machineJoining: "\u6B63\u5728\u628A {{name}} \u63A5\u5165\u8FD9\u4E2A\u9879\u76EE\u2026"
16251
15797
  },
16252
15798
  handbookPanel: {
16253
15799
  title: "\u624B\u518C",
@@ -17224,7 +16770,7 @@ function startTrialRuntime(ctx, deps, options = {}) {
17224
16770
  // src/index.ts
17225
16771
  var index_default = definePlugin({
17226
16772
  id: "team",
17227
- apiVersion: 52,
16773
+ apiVersion: 56,
17228
16774
  activate(ctx) {
17229
16775
  const trialState = ctx.derivedState(trialStateDefinition);
17230
16776
  const rosters = createRosterService(ctx);
@@ -17256,7 +16802,19 @@ var index_default = definePlugin({
17256
16802
  },
17257
16803
  memberQueue
17258
16804
  );
17259
- registerDispatchClaim(ctx, wakes.state);
16805
+ const worktreeSetup = startWorktreeSetupRuntime(ctx, {
16806
+ wakes: wakes.state,
16807
+ goals,
16808
+ lane: wakes.worktrees
16809
+ });
16810
+ registerDispatchGate(ctx, {
16811
+ wakes: wakes.state,
16812
+ goals,
16813
+ readiness,
16814
+ requestWorktree: worktreeSetup.request,
16815
+ now: () => runPromise2(map5(now2, toEpochMillis2))
16816
+ });
16817
+ registerWakeSupplement(ctx, wakes, groupDigest);
17260
16818
  registerPauseCommands(ctx, { wakes: wakes.state, queue: memberQueue });
17261
16819
  startArchiveMirror(
17262
16820
  ctx,
@@ -17268,9 +16826,7 @@ var index_default = definePlugin({
17268
16826
  rebindTargets: rebindReader(trialState)
17269
16827
  });
17270
16828
  startMemberRemovalRuntime(ctx, goals, wakes.state);
17271
- startCommentMentionRuntime(ctx, goals);
17272
- startGroupDeliveryRuntime(ctx, wakes.state);
17273
- registerBoardCommands(ctx, goals, rosters, wakes.state);
16829
+ registerBoardCommands(ctx, goals, rosters);
17274
16830
  startRecoveryRuntime(ctx, goals, wakes.state, wakes.worktrees);
17275
16831
  startLeadCheckinRuntime(ctx, wakes.state, goals);
17276
16832
  startProgressCheckinRuntime(ctx, goals, wakes.state);