@mstar-harness/dsh 3.8.1 → 3.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +16 -6
  3. package/README.zh.md +16 -6
  4. package/dist/client/panel/engine-status-client.d.ts +84 -6
  5. package/dist/client/panel/graph/project-graph.d.ts +26 -13
  6. package/dist/client/panel/guards.d.ts +41 -1
  7. package/dist/client/panel/locale.d.ts +1 -1
  8. package/dist/client/panel/pages/AgentListPage.d.ts +1 -1
  9. package/dist/client/panel/sidebar.d.ts +3 -2
  10. package/dist/client/panel/state-section.d.ts +25 -3
  11. package/dist/client/panel/use-mstar-engine-status.d.ts +28 -4
  12. package/dist/client.js +346 -48
  13. package/dist/engine-status-endpoint.d.ts +85 -8
  14. package/dist/engine-status-store.d.ts +91 -1
  15. package/dist/engine-status-wire.d.ts +9 -0
  16. package/dist/gates/_shared.d.ts +61 -9
  17. package/dist/gates/adapter.d.ts +32 -2
  18. package/dist/gates/agent-flow.d.ts +312 -60
  19. package/dist/gates/catalog.d.ts +58 -37
  20. package/dist/gates/dispatch.d.ts +11 -2
  21. package/dist/gates/goal-bridge.d.ts +10 -130
  22. package/dist/gates/plan-mode-bridge.d.ts +20 -11
  23. package/dist/gates/role-persona.d.ts +16 -0
  24. package/dist/gates/steering.d.ts +41 -0
  25. package/dist/gates/workflow-ledger.d.ts +31 -4
  26. package/dist/gates/workflow-selection.d.ts +41 -20
  27. package/dist/index.js +1206 -392
  28. package/dist/types.d.ts +36 -11
  29. package/harness-commands/amazing-e2e-check.md +10 -0
  30. package/harness-commands/amazing-pr-review.md +2 -0
  31. package/harness-commands/codebase-audit.md +2 -0
  32. package/harness-commands/iteration-drive.md +1 -1
  33. package/harness-skills/mstar-artifacts/references/plan-files-and-reports.md +2 -2
  34. package/harness-skills/mstar-artifacts/references/plan-quality-bar.md +14 -12
  35. package/harness-skills/mstar-artifacts/templates/plan.main.md +19 -6
  36. package/harness-skills/mstar-audit/SKILL.md +5 -5
  37. package/harness-skills/mstar-coding-behavior/SKILL.md +8 -8
  38. package/harness-skills/mstar-dispatch-gates/SKILL.md +9 -7
  39. package/harness-skills/mstar-e2e/SKILL.md +40 -0
  40. package/harness-skills/mstar-e2e/references/report-template.md +32 -0
  41. package/harness-skills/mstar-engine-legacy/references/qc-seat-n-restatements.md +3 -3
  42. package/harness-skills/mstar-harness-core/SKILL.md +14 -1
  43. package/harness-skills/mstar-host/SKILL.md +3 -1
  44. package/harness-skills/mstar-host/references/_shared/host-role-binding-core.md +1 -1
  45. package/harness-skills/mstar-host/references/cursor.md +1 -1
  46. package/harness-skills/mstar-host/references/dsh-workflow-scripts.md +424 -0
  47. package/harness-skills/mstar-host/references/dsh.md +180 -51
  48. package/harness-skills/mstar-host/references/kimi.md +3 -3
  49. package/harness-skills/mstar-host/references/omp.md +3 -3
  50. package/harness-skills/mstar-host/references/parallel-dispatch.md +6 -6
  51. package/harness-skills/mstar-host/references/zcode.md +4 -4
  52. package/harness-skills/mstar-iteration/SKILL.md +1 -1
  53. package/harness-skills/mstar-iteration/references/phase-1-prepare.md +2 -2
  54. package/harness-skills/mstar-iteration/references/phase-2-worktree-lease.md +3 -3
  55. package/harness-skills/mstar-review-qc/SKILL.md +4 -4
  56. package/harness-skills/mstar-review-qc/references/review-responsibility-boundaries.md +9 -7
  57. package/harness-skills/mstar-roles/SKILL.md +2 -0
  58. package/harness-skills/mstar-roles/references/_shared/leaf-executor-core.md +9 -0
  59. package/harness-skills/mstar-roles/references/ops-engineer.md +3 -0
  60. package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +12 -9
  61. package/harness-skills/mstar-roles/references/project-manager/qa-trigger-matrix.md +7 -5
  62. package/harness-skills/mstar-roles/references/project-manager/qc-and-residuals.md +2 -2
  63. package/harness-skills/mstar-roles/references/project-manager/routing-and-dev-allocation.md +2 -2
  64. package/harness-skills/mstar-roles/references/project-manager.md +4 -2
  65. package/harness-skills/mstar-roles/references/qa-engineer/acceptance-gate.md +12 -13
  66. package/harness-skills/mstar-roles/references/qa-engineer.md +5 -4
  67. package/harness-skills/mstar-roles/references/qc-specialist/deep-review-lenses.md +5 -5
  68. package/harness-skills/mstar-roles/references/qc-specialist/report-template.md +2 -0
  69. package/harness-skills/mstar-roles/references/qc-specialist/reviewer-checklist.md +1 -1
  70. package/harness-skills/mstar-roles/references/qc-specialist/reviewer-workflow.md +5 -4
  71. package/harness-skills/mstar-roles/references/qc-specialist-shared.md +3 -1
  72. package/harness-skills/mstar-sdd/SKILL.md +17 -9
  73. package/harness-skills/mstar-sdd/references/file-handoffs.md +40 -20
  74. package/harness-skills/mstar-sdd/references/implementer-continuation-prompt.md +9 -4
  75. package/harness-skills/mstar-sdd/references/implementer-prompt.md +11 -6
  76. package/harness-skills/mstar-sdd/references/sticky-implementer-session.md +4 -2
  77. package/harness-skills/mstar-sdd/references/task-reviewer-prompt.md +8 -4
  78. package/package.json +2 -2
package/dist/client.js CHANGED
@@ -110,6 +110,9 @@ function mstarPanelDefinition(t) {
110
110
  // src/engine-status-wire.ts
111
111
  var ENGINE_STATUS_CHANNEL = "/api";
112
112
  var ENGINE_STATUS_ENDPOINT = "mstar/engineStatus";
113
+ var MSTAR_ENGINE_STATUS_NAMESPACE = "mstar";
114
+ var MSTAR_SELECT_WORKFLOW_METHOD = "selectWorkflow";
115
+ var SELECT_WORKFLOW_ENDPOINT = `${MSTAR_ENGINE_STATUS_NAMESPACE}/${MSTAR_SELECT_WORKFLOW_METHOD}`;
113
116
 
114
117
  // src/client/panel/guards.ts
115
118
  function str(value) {
@@ -127,6 +130,36 @@ function record(value) {
127
130
  function unavailable(reason) {
128
131
  return { status: "unavailable", reason };
129
132
  }
133
+ function selectionView(value) {
134
+ const view = record(value);
135
+ if (view === null)
136
+ return null;
137
+ const workflowId = str(view.workflowId);
138
+ const dir = str(view.dir);
139
+ if (view.kind === "active" || view.kind === "terminal") {
140
+ if (workflowId === null || dir === null)
141
+ return null;
142
+ if (view.kind === "terminal")
143
+ return { kind: "terminal", workflowId, dir };
144
+ const warning = record(view.warning);
145
+ const code2 = warning === null ? null : str(warning.code);
146
+ const message2 = warning === null ? null : str(warning.message);
147
+ return {
148
+ kind: "active",
149
+ workflowId,
150
+ dir,
151
+ ...code2 === null || message2 === null ? {} : { warning: { code: code2, message: message2 } }
152
+ };
153
+ }
154
+ if (view.kind !== "error")
155
+ return null;
156
+ const code = str(view.code);
157
+ const message = str(view.message);
158
+ if (code === null || message === null)
159
+ return null;
160
+ const ids = Array.isArray(view.activeWorkflowIds) ? view.activeWorkflowIds.filter((id) => str(id) !== null) : undefined;
161
+ return { kind: "error", code, message, ...ids === undefined ? {} : { activeWorkflowIds: ids } };
162
+ }
130
163
  function parseEngineStatusResult(raw, sessionId, cwd) {
131
164
  const envelope = record(raw);
132
165
  if (envelope === null)
@@ -156,13 +189,45 @@ function parseEngineStatusResult(raw, sessionId, cwd) {
156
189
  const payload = record(value.payload);
157
190
  if (payload === null)
158
191
  return unavailable("malformed-payload");
159
- return { status: "ok", payload, at, turn };
192
+ const binding = record(value.binding);
193
+ return {
194
+ status: "ok",
195
+ payload,
196
+ at,
197
+ turn,
198
+ binding: binding === null ? null : selectionView(binding.selection)
199
+ };
200
+ }
201
+ function parseSelectWorkflowResult(raw, sessionId, workflowId) {
202
+ const envelope = record(raw);
203
+ if (envelope === null)
204
+ return { status: "unavailable", reason: "malformed-response" };
205
+ if (envelope.ok !== true) {
206
+ const error = record(envelope.error);
207
+ const code = str(error?.code);
208
+ return { status: "unavailable", reason: code === null ? "transport-error" : `transport-error:${code}` };
209
+ }
210
+ const value = record(envelope.value);
211
+ if (value === null)
212
+ return { status: "unavailable", reason: "malformed-response" };
213
+ if (value.status === "unavailable") {
214
+ return { status: "unavailable", reason: str(value.reason) ?? "unavailable" };
215
+ }
216
+ if (value.status !== "selected")
217
+ return { status: "unavailable", reason: "malformed-response" };
218
+ const acknowledgedSession = str(value.sessionId);
219
+ const acknowledgedWorkflow = str(value.workflowId);
220
+ if (acknowledgedSession !== sessionId)
221
+ return { status: "unavailable", reason: "session-mismatch" };
222
+ if (acknowledgedWorkflow !== workflowId)
223
+ return { status: "unavailable", reason: "workflow-mismatch" };
224
+ return { status: "selected", sessionId, workflowId };
160
225
  }
161
226
 
162
227
  // src/client/panel/engine-status-client.ts
163
228
  var ENGINE_STATUS_REQUEST_TIMEOUT_MS = 5000;
164
229
  var ENGINE_STATUS_REFRESH_INTERVAL_MS = 30000;
165
- var EMPTY = { entries: new Map };
230
+ var EMPTY = { entries: new Map, selections: new Map };
166
231
 
167
232
  class MstarEngineStatusClient {
168
233
  connection;
@@ -176,6 +241,8 @@ class MstarEngineStatusClient {
176
241
  refreshTimer = null;
177
242
  generationDisposer = null;
178
243
  generation = 0;
244
+ sessionGenerations = new Map;
245
+ stale = new Set;
179
246
  disposed = false;
180
247
  constructor(connection, options = {}) {
181
248
  this.connection = connection;
@@ -200,8 +267,10 @@ class MstarEngineStatusClient {
200
267
  ensure(sessionId, cwd, anchorTime) {
201
268
  if (this.disposed)
202
269
  return;
203
- if (this.snapshot.entries.get(sessionId)?.anchorTime === anchorTime)
204
- return;
270
+ if (this.snapshot.entries.get(sessionId)?.anchorTime === anchorTime) {
271
+ if (!this.stale.delete(sessionId))
272
+ return;
273
+ }
205
274
  const key = `${sessionId}\x00${anchorTime}`;
206
275
  if (this.inFlight.has(key))
207
276
  return;
@@ -217,9 +286,56 @@ class MstarEngineStatusClient {
217
286
  this.inFlight.clear();
218
287
  if (this.snapshot.entries.size === 0)
219
288
  return;
220
- this.snapshot = EMPTY;
289
+ this.snapshot = { ...this.snapshot, entries: new Map };
221
290
  this.notify();
222
291
  }
292
+ invalidateSession(sessionId) {
293
+ if (this.disposed)
294
+ return;
295
+ this.sessionGenerations.set(sessionId, this.sessionGen(sessionId) + 1);
296
+ const prefix = `${sessionId}\x00`;
297
+ for (const key of [...this.inFlight.keys()]) {
298
+ if (key.startsWith(prefix))
299
+ this.inFlight.delete(key);
300
+ }
301
+ this.stale.add(sessionId);
302
+ }
303
+ async selectWorkflow(sessionId, cwd, workflowId) {
304
+ if (this.disposed)
305
+ return { status: "unavailable", reason: "disposed" };
306
+ if (this.snapshot.selections.get(sessionId)?.state === "pending") {
307
+ return { status: "unavailable", reason: "selection-pending" };
308
+ }
309
+ this.setSelection(sessionId, { state: "pending" });
310
+ const answer = await this.callSelect(sessionId, cwd, workflowId);
311
+ if (answer.status === "selected") {
312
+ this.invalidateSession(sessionId);
313
+ this.setSelection(sessionId, { state: "selected", workflowId: answer.workflowId });
314
+ } else {
315
+ this.setSelection(sessionId, { state: "failed", reason: answer.reason });
316
+ }
317
+ return answer;
318
+ }
319
+ async callSelect(sessionId, cwd, workflowId) {
320
+ if (this.connection === null || this.connection === undefined || typeof this.connection.rpc?.call !== "function") {
321
+ return { status: "unavailable", reason: "no-connection" };
322
+ }
323
+ try {
324
+ const raw = await this.withDeadline((signal) => this.connection.rpc.call(ENGINE_STATUS_CHANNEL, SELECT_WORKFLOW_ENDPOINT, { args: { sessionId, cwd, workflowId } }, signal));
325
+ return parseSelectWorkflowResult(raw, sessionId, workflowId);
326
+ } catch (error) {
327
+ return { status: "unavailable", reason: `transport-error:${error?.message ?? "unknown"}` };
328
+ }
329
+ }
330
+ setSelection(sessionId, state) {
331
+ const selections = new Map(this.snapshot.selections);
332
+ selections.set(sessionId, state);
333
+ this.snapshot = { ...this.snapshot, selections };
334
+ this.notify();
335
+ }
336
+ sessionGen(sessionId) {
337
+ return this.sessionGenerations.get(sessionId) ?? 0;
338
+ }
223
339
  dispose() {
224
340
  this.disposed = true;
225
341
  this.generationDisposer?.();
@@ -263,6 +379,7 @@ class MstarEngineStatusClient {
263
379
  }
264
380
  async fetch(sessionId, cwd, anchorTime) {
265
381
  const generation = this.generation;
382
+ const sessionGeneration = this.sessionGen(sessionId);
266
383
  let answer;
267
384
  if (this.connection === null || this.connection === undefined || typeof this.connection.rpc?.call !== "function") {
268
385
  answer = { status: "unavailable", reason: "no-connection" };
@@ -274,7 +391,7 @@ class MstarEngineStatusClient {
274
391
  answer = { status: "unavailable", reason: `transport-error:${error?.message ?? "unknown"}` };
275
392
  }
276
393
  }
277
- this.write(generation, sessionId, cwd, anchorTime, answer);
394
+ this.write(generation, sessionGeneration, sessionId, cwd, anchorTime, answer);
278
395
  }
279
396
  async withDeadline(call) {
280
397
  if (this.timeoutMs <= 0 || typeof setTimeout !== "function")
@@ -299,19 +416,22 @@ class MstarEngineStatusClient {
299
416
  this.abort.signal.removeEventListener("abort", relay);
300
417
  }
301
418
  }
302
- write(generation, sessionId, cwd, anchorTime, fetch) {
419
+ write(generation, sessionGeneration, sessionId, cwd, anchorTime, fetch) {
303
420
  if (this.disposed)
304
421
  return;
305
422
  if (generation !== this.generation)
306
423
  return;
424
+ if (sessionGeneration !== this.sessionGen(sessionId))
425
+ return;
307
426
  if ((this.snapshot.entries.get(sessionId)?.anchorTime ?? anchorTime) > anchorTime)
308
427
  return;
309
428
  this.writeEntry(sessionId, { anchorTime, cwd, fetchedAt: Date.now(), fetch });
310
429
  }
311
430
  writeEntry(sessionId, entry) {
431
+ this.stale.delete(sessionId);
312
432
  const entries = new Map(this.snapshot.entries);
313
433
  entries.set(sessionId, entry);
314
- this.snapshot = { entries };
434
+ this.snapshot = { ...this.snapshot, entries };
315
435
  this.notify();
316
436
  }
317
437
  notify() {
@@ -368,6 +488,7 @@ var zh = {
368
488
  "event-log.field.expected": "预期角色",
369
489
  "event-log.field.settled": "已结算",
370
490
  "event-log.field.duration": "耗时",
491
+ "event-log.field.child-id": "子会话 ID",
371
492
  "event-log.field.run-id": "运行 ID",
372
493
  "event-log.field.name": "运行名称",
373
494
  "event-log.field.members": "成员数",
@@ -437,6 +558,7 @@ var zh = {
437
558
  "zone.agents.group.plan-more": "+{n} 更多",
438
559
  "flow.empty": "暂无实际派发(记录自 agent-flow plan 合并起生效)",
439
560
  "flow.settle-only": "仅有结算记录(无派发证据)",
561
+ "flow.link-only": "仅有身份记录(无派发证据)",
440
562
  "flow.degraded": "agentFlow 证据缺失",
441
563
  "flow.unexpected": "未匹配角色",
442
564
  "flow.in-flight": "已派发",
@@ -447,6 +569,10 @@ var zh = {
447
569
  "state.title": "工作区状态",
448
570
  "state.selection": "所选工作流",
449
571
  "state.selection.history": "历史视图(终端快照)",
572
+ "state.selection.unbound": "存在多个活动工作流——请为本会话选择一个。本会话没有租约,也不在任何工作流工作区内。选择之前写入会暂停。其他会话保持各自的绑定。",
573
+ "state.selection.pick": "选择 {workflowId}",
574
+ "state.selection.pending": "正在提交选择…",
575
+ "state.selection.failed": "选择失败:{reason}",
450
576
  "state.plans": "计划",
451
577
  "state.residuals": "未决残留",
452
578
  "state.policy": "策略",
@@ -492,6 +618,7 @@ var en = {
492
618
  "event-log.field.expected": "Expected role",
493
619
  "event-log.field.settled": "Settled",
494
620
  "event-log.field.duration": "Duration",
621
+ "event-log.field.child-id": "Child session ID",
495
622
  "event-log.field.run-id": "Run ID",
496
623
  "event-log.field.name": "Run name",
497
624
  "event-log.field.members": "Members",
@@ -561,6 +688,7 @@ var en = {
561
688
  "zone.agents.group.plan-more": "+{n} more",
562
689
  "flow.empty": "No actual dispatches yet (recording starts at agent-flow plan merge)",
563
690
  "flow.settle-only": "Settle records only (no dispatch evidence)",
691
+ "flow.link-only": "Identity records only (no dispatch evidence)",
564
692
  "flow.degraded": "No agent-flow evidence (ledger missing)",
565
693
  "flow.unexpected": "Unexpected roles",
566
694
  "flow.in-flight": "dispatched",
@@ -571,6 +699,10 @@ var en = {
571
699
  "state.title": "Workspace state",
572
700
  "state.selection": "Selected workflow",
573
701
  "state.selection.history": "history view (terminal snapshot)",
702
+ "state.selection.unbound": "Multiple active workflows — pick one for this session. This session has no lease and is not inside a workflow worktree. Writes pause until you pick. Other sessions stay on their own binding.",
703
+ "state.selection.pick": "Select {workflowId}",
704
+ "state.selection.pending": "Committing your pick…",
705
+ "state.selection.failed": "Pick failed: {reason}",
574
706
  "state.plans": "Plans",
575
707
  "state.residuals": "Open residuals",
576
708
  "state.policy": "Policy",
@@ -1281,6 +1413,65 @@ var css = `
1281
1413
  font-weight: 600;
1282
1414
  }
1283
1415
 
1416
+
1417
+ .\\36 7818fcf_picker {
1418
+ display: flex;
1419
+ flex-direction: column;
1420
+ gap: var(--mstar-space-2);
1421
+ }
1422
+
1423
+ .c6520e3e_pickerCopy {
1424
+ margin: 0;
1425
+ color: var(--dsw-alias-label-caption);
1426
+ font: var(--dsw-font-xxxs-11);
1427
+ }
1428
+
1429
+ .e81be137_pickerList {
1430
+ display: flex;
1431
+ flex-direction: column;
1432
+ gap: var(--mstar-space-1);
1433
+ margin: 0;
1434
+ padding: 0;
1435
+ list-style: none;
1436
+ }
1437
+
1438
+ .\\36 3bf9208_pickerItem {
1439
+ display: flex;
1440
+ min-width: 0;
1441
+ }
1442
+
1443
+ .\\35 9b39fff_pickerButton {
1444
+ appearance: none;
1445
+ min-width: 0;
1446
+ max-width: 100%;
1447
+ padding: var(--mstar-space-1) var(--mstar-space-2);
1448
+ border: 1px solid var(--dsw-alias-border-l2);
1449
+ border-radius: 999px;
1450
+ background: none;
1451
+ color: var(--dsw-alias-label-secondary);
1452
+ font: var(--dsw-font-xxxs-11);
1453
+ text-align: left;
1454
+ overflow-wrap: anywhere;
1455
+ cursor: pointer;
1456
+ transition: color 150ms ease, border-color 150ms ease;
1457
+ }
1458
+
1459
+ .\\35 9b39fff_pickerButton:hover:not(:disabled) {
1460
+ color: var(--dsw-alias-label-primary);
1461
+ border-color: var(--dsw-alias-state-business-primary);
1462
+ }
1463
+
1464
+ .\\35 9b39fff_pickerButton:disabled {
1465
+ color: var(--dsw-alias-label-caption);
1466
+ cursor: default;
1467
+ }
1468
+
1469
+ .e14449f9_pickerStatus {
1470
+ margin: 0;
1471
+ color: var(--dsw-alias-label-caption);
1472
+ font: var(--dsw-font-xxxs-11);
1473
+ }
1474
+
1284
1475
  .\\31 8a7beee_empty {
1285
1476
  margin: 0;
1286
1477
  color: var(--dsw-alias-label-caption);
@@ -1312,7 +1503,7 @@ if (typeof document !== "undefined" && document.querySelector("style[data-plugin
1312
1503
  tag.textContent = css;
1313
1504
  document.head.appendChild(tag);
1314
1505
  }
1315
- var panel_module_default = { root: "20fd0e45_root", emptyRoot: "f83e09fa_emptyRoot", groupGrid: "1843711c_groupGrid", scroll: "dd4ec22c_scroll", meta: "81fe4f58_meta", noHarnessCard: "58245a20_noHarnessCard", noHarnessIcon: "ffb65da7_noHarnessIcon", noHarnessTitle: "81e1817c_noHarnessTitle", noHarnessHint: "a2289da7_noHarnessHint", tabNav: "4bb38bc9_tabNav", tab: "98f72e4c_tab", tabActive: "59e199de_tabActive", iterationPage: "3b1c4147_iterationPage", iterationHead: "8a629a8e_iterationHead", iterationSummary: "2d1bee34_iterationSummary", iterationSummaryId: "17162a99_iterationSummaryId", iterationSummaryVerdict: "d3d74135_iterationSummaryVerdict", iterationSummaryStatus: "d9a886be_iterationSummaryStatus", iterationSummaryStatusMuted: "13bf2f41_iterationSummaryStatusMuted", iterationSummaryHint: "ede5708d_iterationSummaryHint", iterationHeadBody: "64d1da62_iterationHeadBody", iterationHeadSplit: "77aea5a6_iterationHeadSplit", iterationBranches: "c3b8dcb0_iterationBranches", iterationStepsRow: "c52f7b85_iterationStepsRow", iterationStepItem: "81013a9d_iterationStepItem", iterationStepBadge: "7befde45_iterationStepBadge", iterationStepPhase: "d6cd23a5_iterationStepPhase", iterationStepChip: "de293024_iterationStepChip", iterationVerdictSeat: "afea5850_iterationVerdictSeat", iterationVerdict: "ebe6bbcd_iterationVerdict", iterationBranchesTitle: "e6921da6_iterationBranchesTitle", iterationBranchList: "8a2cd65c_iterationBranchList", iterationBranchRow: "997b0230_iterationBranchRow", iterationBranchLabel: "6cafc032_iterationBranchLabel", iterationBranchValue: "cdcd06ed_iterationBranchValue", iterationTasks: "87f7efbe_iterationTasks", sidebar: "c87acfc7_sidebar", metaLine: "0fc275e2_metaLine", section: "fcdd0ccc_section", sectionTitle: "aded23ea_sectionTitle", subTitle: "b7190159_subTitle", defList: "26763614_defList", defTerm: "670df156_defTerm", defValue: "3f1b3a05_defValue", planList: "a129f16e_planList", residualList: "113e0aa2_residualList", leaseList: "33dfcc85_leaseList", violationList: "999efdc0_violationList", planId: "744bf63f_planId", leasePlan: "560663a6_leasePlan", leaseHolder: "a68a6d03_leaseHolder", leaseWorktree: "db027b44_leaseWorktree", findingSeverity: "1797fd51_findingSeverity", findingTitle: "c80b7bb0_findingTitle", findingPlan: "616667ad_findingPlan", violationCode: "e42c0bfd_violationCode", knowledgeCategories: "e2098247_knowledgeCategories", planStatus: "a3021d7c_planStatus", truncated: "1849d129_truncated", knowledge: "3eb5feeb_knowledge", direction: "df6dc76a_direction", selection: "4364e615_selection", selectionId: "0ae21b10_selectionId", selectionMeta: "5e43d3a8_selectionMeta", selectionWarning: "da64723f_selectionWarning", selectionCode: "ac988a64_selectionCode", empty: "18a7beee_empty", freshness: "c75c59b0_freshness" };
1506
+ var panel_module_default = { root: "20fd0e45_root", emptyRoot: "f83e09fa_emptyRoot", groupGrid: "1843711c_groupGrid", scroll: "dd4ec22c_scroll", meta: "81fe4f58_meta", noHarnessCard: "58245a20_noHarnessCard", noHarnessIcon: "ffb65da7_noHarnessIcon", noHarnessTitle: "81e1817c_noHarnessTitle", noHarnessHint: "a2289da7_noHarnessHint", tabNav: "4bb38bc9_tabNav", tab: "98f72e4c_tab", tabActive: "59e199de_tabActive", iterationPage: "3b1c4147_iterationPage", iterationHead: "8a629a8e_iterationHead", iterationSummary: "2d1bee34_iterationSummary", iterationSummaryId: "17162a99_iterationSummaryId", iterationSummaryVerdict: "d3d74135_iterationSummaryVerdict", iterationSummaryStatus: "d9a886be_iterationSummaryStatus", iterationSummaryStatusMuted: "13bf2f41_iterationSummaryStatusMuted", iterationSummaryHint: "ede5708d_iterationSummaryHint", iterationHeadBody: "64d1da62_iterationHeadBody", iterationHeadSplit: "77aea5a6_iterationHeadSplit", iterationBranches: "c3b8dcb0_iterationBranches", iterationStepsRow: "c52f7b85_iterationStepsRow", iterationStepItem: "81013a9d_iterationStepItem", iterationStepBadge: "7befde45_iterationStepBadge", iterationStepPhase: "d6cd23a5_iterationStepPhase", iterationStepChip: "de293024_iterationStepChip", iterationVerdictSeat: "afea5850_iterationVerdictSeat", iterationVerdict: "ebe6bbcd_iterationVerdict", iterationBranchesTitle: "e6921da6_iterationBranchesTitle", iterationBranchList: "8a2cd65c_iterationBranchList", iterationBranchRow: "997b0230_iterationBranchRow", iterationBranchLabel: "6cafc032_iterationBranchLabel", iterationBranchValue: "cdcd06ed_iterationBranchValue", iterationTasks: "87f7efbe_iterationTasks", sidebar: "c87acfc7_sidebar", metaLine: "0fc275e2_metaLine", section: "fcdd0ccc_section", sectionTitle: "aded23ea_sectionTitle", subTitle: "b7190159_subTitle", defList: "26763614_defList", defTerm: "670df156_defTerm", defValue: "3f1b3a05_defValue", planList: "a129f16e_planList", residualList: "113e0aa2_residualList", leaseList: "33dfcc85_leaseList", violationList: "999efdc0_violationList", planId: "744bf63f_planId", leasePlan: "560663a6_leasePlan", leaseHolder: "a68a6d03_leaseHolder", leaseWorktree: "db027b44_leaseWorktree", findingSeverity: "1797fd51_findingSeverity", findingTitle: "c80b7bb0_findingTitle", findingPlan: "616667ad_findingPlan", violationCode: "e42c0bfd_violationCode", knowledgeCategories: "e2098247_knowledgeCategories", planStatus: "a3021d7c_planStatus", truncated: "1849d129_truncated", knowledge: "3eb5feeb_knowledge", direction: "df6dc76a_direction", selection: "4364e615_selection", selectionId: "0ae21b10_selectionId", selectionMeta: "5e43d3a8_selectionMeta", selectionWarning: "da64723f_selectionWarning", selectionCode: "ac988a64_selectionCode", picker: "67818fcf_picker", pickerCopy: "c6520e3e_pickerCopy", pickerList: "e81be137_pickerList", pickerItem: "63bf9208_pickerItem", pickerButton: "59b39fff_pickerButton", pickerStatus: "e14449f9_pickerStatus", empty: "18a7beee_empty", freshness: "c75c59b0_freshness" };
1316
1507
 
1317
1508
  // src/client/panel/plan-sort.ts
1318
1509
  var PLAN_CAP = 5;
@@ -1743,7 +1934,7 @@ function projectAgents(source, currentStep) {
1743
1934
  }
1744
1935
  const entries = classifyFlowRows(rawEvents);
1745
1936
  const empty = rawEvents.length === 0;
1746
- const note = empty ? "empty" : entries.some((e) => e.view.kind === "dispatch") ? null : "settle-only";
1937
+ const note = empty ? "empty" : entries.some((e) => e.view.kind === "dispatch") ? null : entries.length > 0 && entries.every((e) => e.view.kind === "subagent-link") ? "link-only" : "settle-only";
1747
1938
  const iterationRow = source == null ? null : source.iteration;
1748
1939
  const iterationId = iterationRow === null || iterationRow === undefined ? null : str(iterationRow.iterationId);
1749
1940
  const planRows = Array.isArray(rawPlans) ? rawPlans.map(planRow) : [];
@@ -1823,6 +2014,8 @@ function flowEventOf(raw, index, stageIndex) {
1823
2014
  };
1824
2015
  }
1825
2016
  if (kind === "settle") {
2017
+ const childId2 = str(row?.childId);
2018
+ const taskRef = str(row?.taskRef);
1826
2019
  return {
1827
2020
  id: `${ts}-${kind}-${index}`,
1828
2021
  ts,
@@ -1837,7 +2030,30 @@ function flowEventOf(raw, index, stageIndex) {
1837
2030
  stage: matched ?? null,
1838
2031
  settled: false,
1839
2032
  ...row?.paired === true ? { paired: true } : {},
1840
- durationMs: count(row?.durationMs)
2033
+ durationMs: count(row?.durationMs),
2034
+ ...childId2 !== null ? { childId: childId2 } : {},
2035
+ ...taskRef !== null ? { taskRef } : {}
2036
+ };
2037
+ }
2038
+ if (kind === "subagent-link") {
2039
+ const childId2 = str(row?.childId);
2040
+ const taskRef = str(row?.taskRef);
2041
+ return {
2042
+ id: `${ts}-${kind}-${index}`,
2043
+ ts,
2044
+ kind,
2045
+ role,
2046
+ planId: str(row?.planId),
2047
+ taskId: str(row?.taskId),
2048
+ taskCategory: null,
2049
+ agent: str(row?.agent),
2050
+ status: "unknown",
2051
+ expected: matched !== undefined,
2052
+ stage: matched ?? null,
2053
+ settled: false,
2054
+ durationMs: null,
2055
+ ...childId2 !== null ? { childId: childId2 } : {},
2056
+ ...taskRef !== null ? { taskRef } : {}
1841
2057
  };
1842
2058
  }
1843
2059
  const runId = str(row?.runId);
@@ -1971,6 +2187,7 @@ function PanelMeta({ t, source }) {
1971
2187
 
1972
2188
  // src/client/panel/state-section.tsx
1973
2189
  var jsx_runtime4 = require("react/jsx-runtime");
2190
+ var NO_IDS = [];
1974
2191
  function enforcementLabel(t, enforcement) {
1975
2192
  if (enforcement === null || enforcement === undefined || typeof enforcement !== "object") {
1976
2193
  return t("panel.unknown");
@@ -1980,7 +2197,16 @@ function enforcementLabel(t, enforcement) {
1980
2197
  const flag = hard === null ? t("panel.unknown") : hard ? t("state.enforcement.hard") : t("state.enforcement.soft");
1981
2198
  return source === null ? flag : `${flag} (${source})`;
1982
2199
  }
1983
- function StateSection({ t, state, enforcement }) {
2200
+ function ackStandsFor(selection, workflowId) {
2201
+ if (selection === null)
2202
+ return true;
2203
+ if (selection.kind === "active")
2204
+ return selection.workflowId === workflowId;
2205
+ if (selection.kind === "error")
2206
+ return selection.activeWorkflowIds?.includes(workflowId) ?? false;
2207
+ return false;
2208
+ }
2209
+ function StateSection({ t, state, enforcement, selection, pick, select }) {
1984
2210
  const plans = Array.isArray(state?.plans) ? state.plans : [];
1985
2211
  const sortedPlans = sortPlans(plans);
1986
2212
  const visiblePlans = sortedPlans.slice(0, PLAN_CAP);
@@ -1990,7 +2216,13 @@ function StateSection({ t, state, enforcement }) {
1990
2216
  const hiddenFindings = findings === null ? 0 : Math.max(0, findings.length - FINDINGS_CAP);
1991
2217
  const leases = Array.isArray(state?.leases) ? state.leases : [];
1992
2218
  const knowledge = state?.knowledge ?? null;
1993
- const selection = state?.selection;
2219
+ const acked = pick.state === "selected" && ackStandsFor(selection, pick.workflowId) ? pick.workflowId : null;
2220
+ const activeId = selection?.kind === "active" ? selection.workflowId : acked;
2221
+ const activeWarning = selection?.kind === "active" ? selection.warning : undefined;
2222
+ const errorView = selection?.kind === "error" ? selection : null;
2223
+ const unbound = selection?.kind === "error" && (selection.activeWorkflowIds?.length ?? 0) > 0 ? selection : null;
2224
+ const candidates = unbound?.activeWorkflowIds ?? NO_IDS;
2225
+ const commit = select;
1994
2226
  return /* @__PURE__ */ jsx_runtime4.jsxs("section", {
1995
2227
  className: panel_module_default.section,
1996
2228
  "data-mstar-section": "state",
@@ -2003,19 +2235,19 @@ function StateSection({ t, state, enforcement }) {
2003
2235
  className: panel_module_default.subTitle,
2004
2236
  children: t("state.selection")
2005
2237
  }),
2006
- selection?.kind === "active" ? /* @__PURE__ */ jsx_runtime4.jsxs("p", {
2238
+ activeId !== null ? /* @__PURE__ */ jsx_runtime4.jsxs("p", {
2007
2239
  className: panel_module_default.selection,
2008
2240
  "data-selection-kind": "active",
2009
- "data-selection-workflow": selection.workflowId,
2241
+ "data-selection-workflow": activeId,
2010
2242
  children: [
2011
2243
  /* @__PURE__ */ jsx_runtime4.jsx("span", {
2012
2244
  className: panel_module_default.selectionId,
2013
- children: selection.workflowId
2245
+ children: activeId
2014
2246
  }),
2015
- selection.warning !== undefined ? /* @__PURE__ */ jsx_runtime4.jsx("span", {
2247
+ activeWarning !== undefined ? /* @__PURE__ */ jsx_runtime4.jsx("span", {
2016
2248
  className: panel_module_default.selectionWarning,
2017
2249
  "data-selection-warning": true,
2018
- children: selection.warning.message
2250
+ children: activeWarning.message
2019
2251
  }) : null
2020
2252
  ]
2021
2253
  }) : selection?.kind === "terminal" ? /* @__PURE__ */ jsx_runtime4.jsxs("p", {
@@ -2036,18 +2268,55 @@ function StateSection({ t, state, enforcement }) {
2036
2268
  }) : /* @__PURE__ */ jsx_runtime4.jsxs("p", {
2037
2269
  className: panel_module_default.selection,
2038
2270
  "data-selection-kind": "error",
2039
- "data-selection-code": selection?.code ?? t("panel.unknown"),
2271
+ "data-selection-code": errorView?.code ?? t("panel.unknown"),
2040
2272
  children: [
2041
2273
  /* @__PURE__ */ jsx_runtime4.jsx("span", {
2042
2274
  className: panel_module_default.selectionCode,
2043
- children: selection?.code ?? t("panel.unknown")
2275
+ children: errorView?.code ?? t("panel.unknown")
2044
2276
  }),
2045
2277
  /* @__PURE__ */ jsx_runtime4.jsx("span", {
2046
2278
  className: panel_module_default.selectionMeta,
2047
- children: selection?.message ?? t("state.none")
2279
+ children: errorView?.message ?? t("state.none")
2048
2280
  })
2049
2281
  ]
2050
2282
  }),
2283
+ unbound !== null && commit !== null ? /* @__PURE__ */ jsx_runtime4.jsxs("div", {
2284
+ className: panel_module_default.picker,
2285
+ "data-mstar-picker": true,
2286
+ "data-picker-code": unbound.code,
2287
+ children: [
2288
+ /* @__PURE__ */ jsx_runtime4.jsx("p", {
2289
+ className: panel_module_default.pickerCopy,
2290
+ "data-picker-copy": true,
2291
+ children: t("state.selection.unbound")
2292
+ }),
2293
+ /* @__PURE__ */ jsx_runtime4.jsx("ul", {
2294
+ className: panel_module_default.pickerList,
2295
+ children: candidates.map((id) => /* @__PURE__ */ jsx_runtime4.jsx("li", {
2296
+ className: panel_module_default.pickerItem,
2297
+ children: /* @__PURE__ */ jsx_runtime4.jsx("button", {
2298
+ type: "button",
2299
+ className: panel_module_default.pickerButton,
2300
+ "data-picker-option": id,
2301
+ disabled: pick.state === "pending",
2302
+ onClick: () => commit(id),
2303
+ children: t("state.selection.pick", { workflowId: id })
2304
+ })
2305
+ }, id))
2306
+ }),
2307
+ pick.state === "pending" ? /* @__PURE__ */ jsx_runtime4.jsx("p", {
2308
+ className: panel_module_default.pickerStatus,
2309
+ "data-picker-pending": true,
2310
+ children: t("state.selection.pending")
2311
+ }) : null,
2312
+ pick.state === "failed" ? /* @__PURE__ */ jsx_runtime4.jsx("p", {
2313
+ className: panel_module_default.pickerStatus,
2314
+ "data-picker-failed": true,
2315
+ "data-picker-reason": pick.reason,
2316
+ children: t("state.selection.failed", { reason: pick.reason })
2317
+ }) : null
2318
+ ]
2319
+ }) : null,
2051
2320
  /* @__PURE__ */ jsx_runtime4.jsx("h3", {
2052
2321
  className: panel_module_default.subTitle,
2053
2322
  children: t("state.plans")
@@ -2225,7 +2494,7 @@ function StateSection({ t, state, enforcement }) {
2225
2494
 
2226
2495
  // src/client/panel/sidebar.tsx
2227
2496
  var jsx_runtime5 = require("react/jsx-runtime");
2228
- function Sidebar({ t, state, source }) {
2497
+ function Sidebar({ t, state, source, selection, pick, select }) {
2229
2498
  return /* @__PURE__ */ jsx_runtime5.jsx("aside", {
2230
2499
  className: panel_module_default.sidebar,
2231
2500
  "data-mstar-sidebar": true,
@@ -2236,7 +2505,10 @@ function Sidebar({ t, state, source }) {
2236
2505
  }) : /* @__PURE__ */ jsx_runtime5.jsx(StateSection, {
2237
2506
  t,
2238
2507
  state,
2239
- enforcement: source.enforcement
2508
+ enforcement: source.enforcement,
2509
+ selection,
2510
+ pick,
2511
+ select
2240
2512
  })
2241
2513
  });
2242
2514
  }
@@ -3331,7 +3603,7 @@ function subBucketLabel(id, t) {
3331
3603
  function AgentListPage({ view, iteration, t }) {
3332
3604
  const { degraded, note, executing, pending } = view;
3333
3605
  const groups = import_react2.useMemo(() => buildGroups(view), [view]);
3334
- const noteInfo = degraded ? { anchor: "degraded", text: t("flow.degraded") } : note === "empty" ? { anchor: "empty", text: t("flow.empty") } : note === "settle-only" ? { anchor: "settle-only", text: t("flow.settle-only") } : null;
3606
+ const noteInfo = degraded ? { anchor: "degraded", text: t("flow.degraded") } : note === "empty" ? { anchor: "empty", text: t("flow.empty") } : note === "settle-only" ? { anchor: "settle-only", text: t("flow.settle-only") } : note === "link-only" ? { anchor: "link-only", text: t("flow.link-only") } : null;
3335
3607
  return /* @__PURE__ */ jsx_runtime9.jsxs("div", {
3336
3608
  className: agent_list_module_default.listPage,
3337
3609
  "data-mstar-page": "agents",
@@ -3869,18 +4141,23 @@ function EventDetailsBody({
3869
4141
  /* @__PURE__ */ jsx_runtime10.jsx(DetailField, {
3870
4142
  field: "expected",
3871
4143
  label: t("event-log.field.expected"),
3872
- value: entry.eventKind === "settle" || isWorkflowKind(entry.eventKind) ? "" : entry.expected ? t("event-log.yes") : t("event-log.no")
4144
+ value: entry.eventKind === "settle" || entry.eventKind === "subagent-link" || isWorkflowKind(entry.eventKind) ? "" : entry.expected ? t("event-log.yes") : t("event-log.no")
3873
4145
  }),
3874
4146
  /* @__PURE__ */ jsx_runtime10.jsx(DetailField, {
3875
4147
  field: "settled",
3876
4148
  label: t("event-log.field.settled"),
3877
- value: entry.eventKind === "settle" || isWorkflowKind(entry.eventKind) ? "" : entry.settled ? t("event-log.yes") : t("event-log.no")
4149
+ value: entry.eventKind === "settle" || entry.eventKind === "subagent-link" || isWorkflowKind(entry.eventKind) ? "" : entry.settled ? t("event-log.yes") : t("event-log.no")
3878
4150
  }),
3879
4151
  /* @__PURE__ */ jsx_runtime10.jsx(DetailField, {
3880
4152
  field: "duration",
3881
4153
  label: t("event-log.field.duration"),
3882
4154
  value: entry.durationMs === null ? "" : `${entry.durationMs}ms`
3883
4155
  }),
4156
+ /* @__PURE__ */ jsx_runtime10.jsx(DetailField, {
4157
+ field: "child-id",
4158
+ label: t("event-log.field.child-id"),
4159
+ value: source?.childId ?? ""
4160
+ }),
3884
4161
  isWorkflowKind(entry.eventKind) && /* @__PURE__ */ jsx_runtime10.jsxs(jsx_runtime10.Fragment, {
3885
4162
  children: [
3886
4163
  /* @__PURE__ */ jsx_runtime10.jsx(DetailField, {
@@ -4362,7 +4639,7 @@ function IterationTaskPage({ view, t }) {
4362
4639
 
4363
4640
  // src/client/panel/use-mstar-engine-status.ts
4364
4641
  var import_react3 = require("react");
4365
- var EMPTY_SNAPSHOT = { entries: new Map };
4642
+ var EMPTY_SNAPSHOT = { entries: new Map, selections: new Map };
4366
4643
  var NO_SUBSCRIBE = () => () => {};
4367
4644
  var readEmptySnapshot = () => EMPTY_SNAPSHOT;
4368
4645
  var ANCHOR_PLUGIN = "mstar-engine";
@@ -4400,11 +4677,13 @@ function selectSessionCwd(sessionId) {
4400
4677
  };
4401
4678
  }
4402
4679
  var WAITING = { state: "waiting", anchorTime: null, payload: null, at: null, turn: null, reason: null };
4680
+ var IDLE_PICK = { state: "idle" };
4403
4681
  function unavailable2(anchorTime, reason) {
4404
4682
  return { state: "unavailable", anchorTime, payload: null, at: null, turn: null, reason };
4405
4683
  }
4406
4684
  function useMstarEngineStatus(seats) {
4407
4685
  const { useChat, useSessions, sessionId, engineStatus } = seats;
4686
+ const session = sessionId === undefined ? null : String(sessionId);
4408
4687
  let anchorTime = null;
4409
4688
  let cwd = null;
4410
4689
  try {
@@ -4418,26 +4697,42 @@ function useMstarEngineStatus(seats) {
4418
4697
  cwd = null;
4419
4698
  }
4420
4699
  const snapshot = import_react3.useSyncExternalStore(engineStatus?.subscribe ?? NO_SUBSCRIBE, engineStatus?.getSnapshot ?? readEmptySnapshot, engineStatus?.getSnapshot ?? readEmptySnapshot);
4421
- if (anchorTime === null)
4422
- return WAITING;
4423
- if (engineStatus !== undefined && sessionId !== undefined && cwd !== null) {
4424
- try {
4425
- engineStatus.ensure(String(sessionId), cwd, anchorTime);
4426
- } catch {}
4427
- }
4428
- const entry = sessionId === undefined ? undefined : snapshot.entries.get(String(sessionId));
4429
- if (entry === undefined) {
4430
- if (engineStatus === undefined)
4431
- return unavailable2(anchorTime, "no-connection");
4432
- if (sessionId === undefined)
4433
- return unavailable2(anchorTime, "session-unknown");
4434
- if (cwd === null)
4435
- return unavailable2(anchorTime, "session-cwd-unknown");
4436
- return { state: "loading", anchorTime, payload: null, at: null, turn: null, reason: null };
4700
+ const target = engineStatus !== undefined && session !== null && cwd !== null ? { client: engineStatus, sessionId: session, cwd } : null;
4701
+ let view;
4702
+ if (anchorTime === null) {
4703
+ view = WAITING;
4704
+ } else {
4705
+ if (target !== null) {
4706
+ try {
4707
+ target.client.ensure(target.sessionId, target.cwd, anchorTime);
4708
+ } catch {}
4709
+ }
4710
+ const entry = session === null ? undefined : snapshot.entries.get(session);
4711
+ if (entry === undefined) {
4712
+ if (engineStatus === undefined)
4713
+ view = unavailable2(anchorTime, "no-connection");
4714
+ else if (session === null)
4715
+ view = unavailable2(anchorTime, "session-unknown");
4716
+ else if (cwd === null)
4717
+ view = unavailable2(anchorTime, "session-cwd-unknown");
4718
+ else
4719
+ view = { state: "loading", anchorTime, payload: null, at: null, turn: null, reason: null };
4720
+ } else if (entry.fetch.status === "unavailable") {
4721
+ view = unavailable2(anchorTime, entry.fetch.reason);
4722
+ } else {
4723
+ view = { state: "ok", anchorTime, payload: entry.fetch.payload, at: entry.fetch.at, turn: entry.fetch.turn, binding: entry.fetch.binding, reason: null };
4724
+ }
4437
4725
  }
4438
- if (entry.fetch.status === "unavailable")
4439
- return unavailable2(anchorTime, entry.fetch.reason);
4440
- return { state: "ok", anchorTime, payload: entry.fetch.payload, at: entry.fetch.at, turn: entry.fetch.turn, reason: null };
4726
+ return {
4727
+ view,
4728
+ selection: view.state === "ok" ? view.binding ?? view.payload.state?.selection ?? null : null,
4729
+ pick: session === null ? IDLE_PICK : snapshot.selections.get(session) ?? IDLE_PICK,
4730
+ select: target === null ? null : (workflowId) => {
4731
+ try {
4732
+ target.client.selectWorkflow(target.sessionId, target.cwd, workflowId);
4733
+ } catch {}
4734
+ }
4735
+ };
4441
4736
  }
4442
4737
 
4443
4738
  // src/client/panel/PanelView.tsx
@@ -4466,7 +4761,7 @@ function PanelContent({ section, source, t }) {
4466
4761
  });
4467
4762
  }
4468
4763
  function PanelView({ t, useChat, useSessions, sessionId, engineStatus, useTabInfo, useStore, actions }) {
4469
- const view = useMstarEngineStatus({ useChat, useSessions, sessionId, engineStatus });
4764
+ const { view, selection, pick, select } = useMstarEngineStatus({ useChat, useSessions, sessionId, engineStatus });
4470
4765
  const tabInfo = useTabInfo();
4471
4766
  const tabId = tabInfo.tab.id;
4472
4767
  const section = useStore((s) => s.byTab[tabId]) ?? "tasks";
@@ -4603,7 +4898,10 @@ function PanelView({ t, useChat, useSessions, sessionId, engineStatus, useTabInf
4603
4898
  /* @__PURE__ */ jsx_runtime14.jsx(Sidebar, {
4604
4899
  t,
4605
4900
  state: source.state,
4606
- source
4901
+ source,
4902
+ selection,
4903
+ pick,
4904
+ select
4607
4905
  }),
4608
4906
  freshness
4609
4907
  ]