weir-harness 1.2.0 → 1.2.1

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 (92) hide show
  1. package/lib/client.blackboard-model.js +172 -56
  2. package/lib/client.capability-manager.js +0 -1
  3. package/lib/client.capability-model.js +378 -31
  4. package/lib/client.chain-model.js +80 -0
  5. package/lib/client.disabled-categories-editor.js +22 -0
  6. package/lib/client.edit-lock-maintenance.js +239 -246
  7. package/lib/client.edit-lock-panel.js +110 -0
  8. package/lib/client.hash-edit-model.js +48 -0
  9. package/lib/client.js +267 -1549
  10. package/lib/client.lsp-model.js +92 -0
  11. package/lib/client.lsp-panel.js +11 -16
  12. package/lib/client.lsp-toggle.js +18 -0
  13. package/lib/client.notify-permissions.js +112 -20
  14. package/lib/client.notify-web.js +14 -11
  15. package/lib/client.robash-model.js +26 -0
  16. package/lib/client.settings-page.js +287 -17
  17. package/lib/client.worktree-model.js +225 -0
  18. package/package.json +1 -1
  19. package/src/blackboard/durable.js +12 -0
  20. package/src/blackboard/index.js +2 -2
  21. package/src/blackboard/kernel.js +23 -6
  22. package/src/blackboard/notify.js +10 -3
  23. package/src/blackboard/remote.js +31 -13
  24. package/src/blackboard/storage.js +13 -5
  25. package/src/capabilities/apply-engine.js +189 -101
  26. package/src/capabilities/defaults-transaction.js +136 -1
  27. package/src/capabilities/lifecycle-snapshot.js +83 -204
  28. package/src/capabilities/mcp-admission.js +1 -23
  29. package/src/capabilities/mcp-drain.js +30 -19
  30. package/src/capabilities/mcp-facade.js +23 -3
  31. package/src/capabilities/preset-library.js +247 -0
  32. package/src/capabilities/preset-package.js +212 -1
  33. package/src/capabilities/same-name-policy.js +3 -41
  34. package/src/capabilities/selection-notify.js +324 -21
  35. package/src/capabilities/skill-selection-plugin.js +315 -729
  36. package/src/capabilities/store/checked-contract.d.ts +5 -0
  37. package/src/capabilities/store/fs-adapter.js +52 -0
  38. package/src/capabilities/store/lock.js +86 -0
  39. package/src/capabilities/store/paths.js +5 -0
  40. package/src/capabilities/store/store.js +105 -5
  41. package/src/delegate/group-coordinator.js +1 -17
  42. package/src/delegate/index.js +4 -2
  43. package/src/delegate/rehydrate.js +2 -1
  44. package/src/delegate/robash-guard-core.js +2 -2
  45. package/src/delegate/supervision-mount.js +5 -4
  46. package/src/delegate/tool.js +26 -3
  47. package/src/{shared → delegate}/whitelist-defaults.js +2 -2
  48. package/src/edit-lock/admin-ledger.js +8 -11
  49. package/src/edit-lock/admin-recovery.js +2 -1
  50. package/src/edit-lock/admission.js +12 -2
  51. package/src/edit-lock/binding-guidance.js +28 -10
  52. package/src/edit-lock/binding-machine.js +312 -0
  53. package/src/edit-lock/derived-facts.js +67 -0
  54. package/src/edit-lock/index.js +44 -244
  55. package/src/edit-lock/inspect.js +113 -1
  56. package/src/edit-lock/lifecycle.js +8 -5
  57. package/src/edit-lock/limits.js +61 -0
  58. package/src/edit-lock/maintenance.js +5 -2
  59. package/src/edit-lock/manager.js +6 -2
  60. package/src/edit-lock/operation-history.js +1 -1
  61. package/src/edit-lock/peer-client.js +9 -2
  62. package/src/edit-lock/peer-transport.js +7 -1
  63. package/src/edit-lock/remote.js +11 -4
  64. package/src/edit-lock/state.js +2 -4
  65. package/src/edit-lock/view.js +10 -11
  66. package/src/lsp/admin.js +2 -24
  67. package/src/lsp/index.js +1 -1
  68. package/src/lsp/manager.js +1 -1
  69. package/src/notify/permissions-admin.js +1 -1
  70. package/src/{lsp/child-process.js → process/index.js} +12 -4
  71. package/src/settings/env-admin.js +1 -1
  72. package/src/settings/index.js +1 -1
  73. package/src/settings/sections.js +0 -54
  74. package/src/shared/audit.js +5 -0
  75. package/src/shared/http-json.js +28 -0
  76. package/src/{delegate/audit-readers.js → shared/terminal-evidence.js} +34 -4
  77. package/src/worktree/errors.js +3 -0
  78. package/src/worktree/index.js +1 -2
  79. package/src/worktree/lane-core.js +641 -0
  80. package/src/worktree/lane-views.js +147 -0
  81. package/src/worktree/lane-workflows.js +853 -0
  82. package/src/worktree/lanes.js +24 -1513
  83. package/src/worktree/runner.js +5 -4
  84. package/src/capabilities/content-pin.js +0 -88
  85. package/src/capabilities/content-refresh.js +0 -331
  86. package/src/capabilities/distribution-manifest.js +0 -78
  87. package/src/capabilities/isolated-exec.js +0 -106
  88. package/src/capabilities/publisher.js +0 -129
  89. package/src/capabilities/skill-admission.js +0 -181
  90. package/src/capabilities/update-lifecycle.js +0 -106
  91. package/src/edit-lock/remote-service.js +0 -28
  92. package/src/worktree/contract-guidance.test.js +0 -100
@@ -24,15 +24,23 @@ window.__ModuleLoader__.load({
24
24
  // hand-written src-json contribution cannot mount through
25
25
  // ctx.remote.$mount — its strict-codec gate rejects src-json codecs),
26
26
  // so read verbs produce zero session-log events and every mutation
27
- // shares the agent tools' arbitration kernel.
28
-
29
- /** The closed entryType enum, mirroring src/blackboard/kernel.js (same-package sync require is impossible in the ModuleLoader). */
27
+ // shares the agent tools' arbitration kernel. The carrier, the envelope
28
+ // folding and the warn-once degradation now live in the composition
29
+ // root's gateway channel adapter (lib/client.js); this chunk owns only
30
+ // the pinned wire shape and the verb mapping.
31
+
32
+ // DERIVED FROM src/blackboard/kernel.js (ENTRY_TYPES, KEY_PATTERN, SUMMARY_MAX_LENGTH
33
+ // and the write gate below) — the kernel is authoritative; this derived copy exists because
34
+ // same-package sync require is impossible in the ModuleLoader. Decision equivalence is
35
+ // pinned by test/client-blackboard-parity.test.js: a draft judged invalid here MUST throw
36
+ // kernel-side, one judged valid MUST be accepted. Change there = change here.
37
+ /** The closed entryType enum. */
30
38
  const ENTRY_TYPES = Object.freeze(["map", "contract", "deadend", "wiring", "recipe", "why"]);
31
39
 
32
- /** Verbatim copy of the kernel's key rule (src/blackboard/kernel.js KEY_PATTERN): a letter/digit first, then letters, digits and . _ - / */
40
+ /** Verbatim copy of the kernel's key rule (DERIVED FROM src/blackboard/kernel.js KEY_PATTERN, pinned by test/client-blackboard-parity.test.js): a letter/digit first, then letters, digits and . _ - / */
33
41
  const KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,159}$/;
34
42
 
35
- /** Free-text summary cap (mirrors the kernel's validateSummary). */
43
+ /** Free-text summary cap (DERIVED FROM src/blackboard/kernel.js SUMMARY_MAX_LENGTH, pinned by test/client-blackboard-parity.test.js). */
36
44
  const SUMMARY_MAX_LENGTH = 500;
37
45
 
38
46
  /** Watch polling gives up after this many consecutive carrier failures. */
@@ -40,47 +48,20 @@ window.__ModuleLoader__.load({
40
48
 
41
49
  // ---- wire channel ----
42
50
 
43
- /**
44
- * Fold one gateway envelope (or a carrier throw) into the panel's
45
- * uniform result shape: { ok: true, value } | { ok: false, error:
46
- * { code, message } }. The gateway already folds host-side failures
47
- * into { ok: false, error } — including the remote's typed
48
- * BlackboardRemoteError, which crosses as gateway/internal with its
49
- * message intact — so the panel never sees an exception from a call.
50
- */
51
- function foldRemoteResult(result) {
52
- if (result?.ok === true) return { ok: true, value: result.value };
53
- const failure = result?.error ?? result ?? null;
54
- return {
55
- ok: false,
56
- error: {
57
- code: typeof failure?.code === "string" ? failure.code : "call-failed",
58
- message: typeof failure?.message === "string" ? failure.message : String(failure ?? "unknown"),
59
- },
60
- };
61
- }
62
-
63
51
  /** Drop undefined fields so the wire payload carries exactly the arguments the caller meant (undefined is not JSON-safe). */
64
52
  const cleanArgs = (obj) => Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
65
53
  /**
66
- * The panel's board channel over the raw gateway carrier. rawCall is
67
- * (endpoint, payload) => Promise<RemoteResult> — the composition root
68
- * binds it to connection.rpc.call("/api", …) with its degradation
69
- * warnings; this factory owns the pinned wire shape:
54
+ * The panel's board channel. callBoard is the composition root's gateway
55
+ * channel adapter: (endpoint, payload) => Promise<{ ok: true, value } |
56
+ * { ok: false, error: { code, message } }>, NEVER rejecting (a carrier
57
+ * throw arrives pre-folded as call-threw, a missing connection as
58
+ * channel-unavailable). This factory owns the pinned wire shape:
70
59
  * POST /api/weirBlackboard/<method>
71
60
  * payload { args: { agentId: <session>, args: <one JSON object> } }
72
- * Every verb resolves the folded result, never rejects.
61
+ * Every verb resolves the adapter's folded result.
73
62
  */
74
- function createBlackboardChannel(rawCall, sessionId) {
75
- const call = (method, args) => {
76
- let pending;
77
- try {
78
- pending = Promise.resolve(rawCall(`weirBlackboard/${method}`, { args: { agentId: sessionId, args: args ?? {} } }));
79
- } catch (cause) {
80
- return Promise.resolve({ ok: false, error: { code: "call-threw", message: cause instanceof Error ? cause.message : String(cause) } });
81
- }
82
- return pending.then(foldRemoteResult, (cause) => ({ ok: false, error: { code: "call-threw", message: cause instanceof Error ? cause.message : String(cause) } }));
83
- };
63
+ function createBlackboardChannel(callBoard, sessionId) {
64
+ const call = (method, args) => callBoard(`weirBlackboard/${method}`, { args: { agentId: sessionId, args: args ?? {} } });
84
65
  return {
85
66
  /** list({ type?, query? }) — the panel polls the unfiltered board and shapes groups client-side. */
86
67
  list: (args) => call("list", cleanArgs({ type: args?.type, query: args?.query })),
@@ -110,25 +91,19 @@ window.__ModuleLoader__.load({
110
91
  * args object, NO agent lookup (the gateway's lookup would refuse a
111
92
  * subagent-owned session before the service runs). ok with a non-empty
112
93
  * value.sessionId folds to { ok: true, sessionId }; every other shape
113
- * (failure envelope, unusable payload, carrier throw) folds to
114
- * { ok: false, error } so the panel can fall back to the browsing id.
94
+ * (failure envelope, unusable payload; a carrier throw arrives
95
+ * pre-folded from the adapter) folds to { ok: false, error } so the
96
+ * panel can fall back to the browsing id.
115
97
  */
116
- function resolveBoardSession(rawCall, sessionId) {
117
- let pending;
118
- try {
119
- pending = Promise.resolve(rawCall("weirBlackboard/resolve", { args: { agentId: sessionId } }));
120
- } catch (cause) {
121
- return Promise.resolve({ ok: false, error: { code: "call-threw", message: cause instanceof Error ? cause.message : String(cause) } });
122
- }
123
- return pending.then((result) => {
124
- const folded = foldRemoteResult(result);
125
- if (folded.ok === true) {
126
- const root = folded.value?.sessionId;
98
+ function resolveBoardSession(callBoard, sessionId) {
99
+ return Promise.resolve(callBoard("weirBlackboard/resolve", { args: { agentId: sessionId } })).then((result) => {
100
+ if (result?.ok === true) {
101
+ const root = result.value?.sessionId;
127
102
  if (typeof root === "string" && root !== "") return { ok: true, sessionId: root };
128
103
  return { ok: false, error: { code: "bad-payload", message: "resolve returned no usable session id" } };
129
104
  }
130
- return { ok: false, error: folded.error };
131
- }, (cause) => ({ ok: false, error: { code: "call-threw", message: cause instanceof Error ? cause.message : String(cause) } }));
105
+ return { ok: false, error: result?.error ?? { code: "call-failed", message: "unknown" } };
106
+ });
132
107
  }
133
108
 
134
109
  // ---- entries ----
@@ -356,6 +331,9 @@ window.__ModuleLoader__.load({
356
331
  };
357
332
  }
358
333
 
334
+ // DERIVED FROM src/blackboard/kernel.js write gate (validateKey, validateEntryType,
335
+ // validateSummary, and write's content check) — decision equivalence with the kernel
336
+ // is pinned by test/client-blackboard-parity.test.js over one shared corpus.
359
337
  /**
360
338
  * Per-field draft errors mirroring the kernel's write gate (validateKey,
361
339
  * the closed entryType enum, non-empty free-text summary within the
@@ -385,10 +363,148 @@ window.__ModuleLoader__.load({
385
363
  };
386
364
  }
387
365
 
366
+ // Locale data face (pure data, NO ctx calls — a chunk factory re-executes
367
+ // under HMR, so a chunk-local register would throw on the duplicate
368
+ // (namespace, locale) pair): the entry registers these under "settings.weir.blackboard"
369
+ // inside a ctx.effect when this chunk arrives (lib/client.js
370
+ // registerChunkDictionaries). Pinned by test/client.test.js's dictionary
371
+ // union pin.
372
+ const dictionaries = {
373
+ en: {
374
+ "blackboard.title": "Blackboard",
375
+ "blackboard.entries": "{n} entries",
376
+ "blackboard.unavailable": "Blackboard unavailable for this session.",
377
+ "blackboard.parentBoard": "Showing the parent conversation's board.",
378
+ "blackboard.retry": "Retry",
379
+ "blackboard.empty": "The board is empty.",
380
+ "blackboard.emptyDesc": "Findings agents write to this session's board appear here.",
381
+ "blackboard.noMatch": "No entries match.",
382
+ "blackboard.search": "Search",
383
+ "blackboard.new": "New entry",
384
+ "blackboard.refresh": "Refresh",
385
+ "blackboard.back": "Back",
386
+ "blackboard.dismiss": "Dismiss",
387
+ "blackboard.filterAll": "All",
388
+ "blackboard.type.map": "map",
389
+ "blackboard.type.contract": "contract",
390
+ "blackboard.type.deadend": "dead end",
391
+ "blackboard.type.wiring": "wiring",
392
+ "blackboard.type.recipe": "recipe",
393
+ "blackboard.type.why": "why",
394
+ "blackboard.rowMeta": "{read} reads · {watching} watching",
395
+ "blackboard.updated": "updated {time}",
396
+ "blackboard.summary": "Summary",
397
+ "blackboard.content": "Content",
398
+ "blackboard.gone": "This entry is gone.",
399
+ "blackboard.goneDesc": "It was deleted from the board (possibly by another writer).",
400
+ "blackboard.error": "The entry could not be loaded.",
401
+ "blackboard.edit": "Edit",
402
+ "blackboard.delete": "Delete",
403
+ "blackboard.deleting": "Deleting…",
404
+ "blackboard.deleted": "Entry deleted.",
405
+ "blackboard.deleteConfirm": "Delete this entry? The write authority is consumed.",
406
+ "blackboard.contention": "Write authority is held by {holder}.",
407
+ "blackboard.watch": "Notify me when free",
408
+ "blackboard.watching": "watching {key}",
409
+ "blackboard.watchingState": "watching…",
410
+ "blackboard.released": "Key free — you hold the write authority.",
411
+ "blackboard.editorNew": "New entry",
412
+ "blackboard.formKey": "Key",
413
+ "blackboard.formType": "Type",
414
+ "blackboard.formRequired": "Required.",
415
+ "blackboard.formKeyInvalid": "Letters, digits and . _ - / only; start with a letter or digit.",
416
+ "blackboard.formSummaryPlaceholder": "One or two sentences: the finding, what it cost, how to verify it",
417
+ "blackboard.save": "Save",
418
+ "blackboard.saving": "Saving…",
419
+ "blackboard.cancel": "Cancel",
420
+ "blackboard.saved": "Saved (revision {n}).",
421
+ "blackboard.created": "Entry created.",
422
+ "blackboard.applying": "Acquiring write authority…",
423
+ "blackboard.reacquire": "Re-acquire",
424
+ "blackboard.tokenHeld": "write authority {time}",
425
+ "blackboard.tokenExpired": "Write authority expired — re-acquire before saving.",
426
+ "blackboard.tokenExpiredShort": "write authority expired",
427
+ // Session blackboard (slice 3): the promotion-evaluation button and
428
+ // the promoted read-only marker.
429
+ "blackboard.promote": "Evaluate for promotion",
430
+ "blackboard.promoteDisabled": "Every entry is already promoted — nothing to evaluate.",
431
+ "blackboard.promotionRequested": "The main agent received the promotion-evaluation request.",
432
+ "blackboard.promoted": "promoted",
433
+ "blackboard.promotedTo": "promoted → {destination}",
434
+ "blackboard.promotedReadonly": "This entry is promoted and read-only.",
435
+ "blackboard.promotedReadonlyDetail": "Promoted entries are read-only: the durable copy lives in the target document, this board copy preserves the session references.",
436
+ "blackboard.loading": "Loading the board…",
437
+ },
438
+ zh: {
439
+ "blackboard.title": "黑板",
440
+ "blackboard.entries": "{n} 条",
441
+ "blackboard.unavailable": "此会话的黑板不可用。",
442
+ "blackboard.parentBoard": "正在显示父会话的黑板。",
443
+ "blackboard.retry": "重试",
444
+ "blackboard.empty": "黑板为空。",
445
+ "blackboard.emptyDesc": "Agent 写入本会话黑板的发现会出现在这里。",
446
+ "blackboard.noMatch": "没有匹配的条目。",
447
+ "blackboard.search": "搜索",
448
+ "blackboard.new": "新建条目",
449
+ "blackboard.refresh": "刷新",
450
+ "blackboard.back": "返回",
451
+ "blackboard.dismiss": "忽略",
452
+ "blackboard.filterAll": "全部",
453
+ "blackboard.type.map": "索引",
454
+ "blackboard.type.contract": "硬约束",
455
+ "blackboard.type.deadend": "已否决方案",
456
+ "blackboard.type.wiring": "隐藏耦合",
457
+ "blackboard.type.recipe": "操作流程",
458
+ "blackboard.type.why": "设计缘由",
459
+ "blackboard.rowMeta": "{read} 次读取 · {watching} 个等待者",
460
+ "blackboard.updated": "更新于 {time}",
461
+ "blackboard.summary": "摘要",
462
+ "blackboard.content": "正文",
463
+ "blackboard.gone": "此条目已不存在。",
464
+ "blackboard.goneDesc": "它已从黑板删除(可能是其他写入者所为)。",
465
+ "blackboard.error": "条目加载失败。",
466
+ "blackboard.edit": "编辑",
467
+ "blackboard.delete": "删除",
468
+ "blackboard.deleting": "删除中…",
469
+ "blackboard.deleted": "条目已删除。",
470
+ "blackboard.deleteConfirm": "删除此条目?写入权将被消耗。",
471
+ "blackboard.contention": "写入权正被 {holder} 持有。",
472
+ "blackboard.watch": "释放时通知我",
473
+ "blackboard.watching": "正在监视 {key}",
474
+ "blackboard.watchingState": "监视中…",
475
+ "blackboard.released": "键已释放——写入权现由你持有。",
476
+ "blackboard.editorNew": "新建条目",
477
+ "blackboard.formKey": "键",
478
+ "blackboard.formType": "类型",
479
+ "blackboard.formRequired": "必填。",
480
+ "blackboard.formKeyInvalid": "仅限字母、数字与 . _ - /;须以字母或数字开头。",
481
+ "blackboard.formSummaryPlaceholder": "一两句话:发现是什么、代价几何、如何复核",
482
+ "blackboard.save": "保存",
483
+ "blackboard.saving": "保存中…",
484
+ "blackboard.cancel": "取消",
485
+ "blackboard.saved": "已保存(修订 {n})。",
486
+ "blackboard.created": "条目已创建。",
487
+ "blackboard.applying": "正在申请写入权…",
488
+ "blackboard.reacquire": "重新申请",
489
+ "blackboard.tokenHeld": "写入权 {time}",
490
+ "blackboard.tokenExpired": "写入权已过期——保存前请重新申请。",
491
+ "blackboard.tokenExpiredShort": "写入权已过期",
492
+ "blackboard.promote": "评估晋升",
493
+ "blackboard.promoteDisabled": "所有条目均已晋升——没有可评估的条目。",
494
+ "blackboard.promotionRequested": "主 Agent 已收到晋升评估请求。",
495
+ "blackboard.promoted": "已晋升",
496
+ "blackboard.promotedTo": "已晋升 → {destination}",
497
+ "blackboard.promotedReadonly": "此条目已晋升,只读。",
498
+ "blackboard.promotedReadonlyDetail": "已晋升条目只读:持久副本位于目标文档,黑板副本保留会话内引用。",
499
+ "blackboard.loading": "正在加载黑板…",
500
+ },
501
+ };
502
+
503
+ exports.dictionaries = dictionaries;
388
504
  exports.ENTRY_TYPES = ENTRY_TYPES;
505
+ exports.KEY_PATTERN = KEY_PATTERN;
389
506
  exports.SUMMARY_MAX_LENGTH = SUMMARY_MAX_LENGTH;
390
507
  exports.WATCH_GIVE_UP_AFTER = WATCH_GIVE_UP_AFTER;
391
- exports.foldRemoteResult = foldRemoteResult;
392
508
  exports.createBlackboardChannel = createBlackboardChannel;
393
509
  exports.resolveBoardSession = resolveBoardSession;
394
510
  exports.entryRowOf = entryRowOf;
@@ -1013,7 +1013,6 @@ window.__ModuleLoader__.load({
1013
1013
  commit?.phase === "same-name-conflict" ? react_jsx_runtime.jsx("span", { style: S.footer.chip(DANGER), role: "alert", children: t("capability.serverSameName", "Server rejected same-name Skill {names}. Uncheck one source, or refresh the listing; draft kept.").replace("{names}", commit.names.join(", ") || t("capability.unknownName", "unknown name")) }) : null,
1014
1014
  commit?.phase === "missing" ? react_jsx_runtime.jsx("span", { style: S.footer.chip(WARN), role: "alert", children: t("capability.serverMissing", "Server could not resolve {names}. Refresh the listing and choose an exact source or remove the name; draft kept.").replace("{names}", commit.names.join(", ") || t("capability.unknownName", "unknown name")) }) : null,
1015
1015
  commit?.phase === "revision-conflict" ? react_jsx_runtime.jsx("span", { style: S.footer.chip(WARN), children: t("capability.conflictState", "Changed elsewhere — review current state") }) : null,
1016
- commit?.phase === "install-or-configure" ? react_jsx_runtime.jsx("span", { style: S.footer.chip(ACCENT), title: (commit.missing ?? []).join(", "), children: t("capability.missingAction", "Missing items need install/configure") }) : null,
1017
1016
  commit?.phase === "indeterminate" ? react_jsx_runtime.jsx("span", { style: S.footer.chip(MUTED), children: t("capability.pending", "Result pending — query the receipt") }) : null
1018
1017
  ] }) : null
1019
1018
  ] });