@lmzhen/dsh-evolution-merge-ui 0.28.0 → 0.30.0

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/README.md CHANGED
@@ -1,12 +1,11 @@
1
1
  # @lmzhen/dsh-evolution-merge-ui
2
2
 
3
- 技能合并的只读浏览器面板:三队列计数、行列表与刷新;动作只展示为命令文本,不在浏览器写技能。
4
-
5
- ## 数据面
6
-
7
- 读取 `/api/dsh-evolution/merge/view` 与 `/api/dsh-evolution/merge/rows`,走生成的路由表和 route-client。失败按四态显示,不伪造空成功。
8
-
9
- ## 已知限制
10
-
11
- - 只读;approve/reject 等动作需复制命令执行。
12
- - 面板仅在 `evolution-merge` host 行与其 bundle 同时挂载时可用。
3
+ Browser-only sidebar panel for the merge queues. It reads the four host routes
4
+ and never writes skills: row actions are copyable `/evolution merge ...`
5
+ commands.
6
+
7
+ - Queues: pending / retry / skipped.
8
+ - Row facts: target, confidence, failure, attempts, origin, staging expiry.
9
+ - Run strip: latest run id/state/stage/progress.
10
+ - Four states: loading / ready / empty / failed; view schemaVersion guard.
11
+ - Mounted by the `all` bundle only.
package/lib/client.js CHANGED
@@ -31,8 +31,8 @@ window.__ModuleLoader__.load({
31
31
  merge: {
32
32
  view: "/api/dsh-evolution/merge/view",
33
33
  rows: "/api/dsh-evolution/merge/rows",
34
- run: "/api/dsh-evolution/merge/run",
35
- runs: "/api/dsh-evolution/merge/runs"
34
+ body: "/api/dsh-evolution/merge/body",
35
+ history: "/api/dsh-evolution/merge/history"
36
36
  },
37
37
  diagnostics: { view: "/api/dsh-evolution/diagnostics" }
38
38
  };
@@ -100,12 +100,16 @@ window.__ModuleLoader__.load({
100
100
  ok: true,
101
101
  data: record.data
102
102
  };
103
- if (record.ok === false && typeof record.message === "string" && record.message !== "") return {
103
+ const nested = record.error;
104
+ const errorRecord = typeof nested === "object" && nested !== null ? nested : void 0;
105
+ const code = typeof record.code === "string" && record.code !== "" ? record.code : typeof errorRecord?.code === "string" ? errorRecord.code : void 0;
106
+ const message = typeof record.message === "string" && record.message !== "" ? record.message : typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
107
+ if (record.ok === false && code !== void 0 && message !== void 0 && message !== "") return {
104
108
  ok: false,
105
109
  failure: {
106
110
  kind: "refused",
107
- code: typeof record.code === "string" && record.code !== "" ? record.code : "refused",
108
- message: record.message
111
+ code,
112
+ message
109
113
  }
110
114
  };
111
115
  }
@@ -154,42 +158,91 @@ window.__ModuleLoader__.load({
154
158
  const zh = {
155
159
  "entry.label": "技能合并",
156
160
  "hint": "手动触发;后台逐对判定。这里只读,不写技能。",
161
+ "history": "历史",
162
+ "close": "关闭",
163
+ "tab.verify": "待判断",
157
164
  "tab.pending": "待批准",
158
- "tab.retry": "待重跑",
165
+ "tab.fuse": "待合并",
159
166
  "tab.skipped": "无需合并",
167
+ "empty.verify": "没有待判断的对。",
160
168
  "empty.pending": "没有待批准的合并。",
161
- "empty.retry": "没有待重跑的对。",
169
+ "empty.fuse": "没有待合并的对。",
162
170
  "empty.skipped": "没有标记为无需合并的对。",
163
171
  "loading": "正在读取…",
164
172
  "refresh": "刷新",
173
+ "loadMore": "加载更多",
174
+ "approval.on": "审批开:合并前需人工批准。",
175
+ "approval.off": "审批关:合并自动执行。",
165
176
  "failed": "读取失败,保留上次结果。",
166
177
  "stage.decide": "判定",
167
178
  "stage.author": "合并",
168
179
  "stage.validate": "检查",
169
- "stage.commit": "提交"
180
+ "stage.commit": "提交",
181
+ "row.target": "目标",
182
+ "row.confidence": "置信度",
183
+ "row.attempts": "尝试",
184
+ "row.origin": "来源",
185
+ "row.expires": "暂存到期",
186
+ "row.expired": "已过期",
187
+ "row.reason": "原因",
188
+ "row.failure": "失败",
189
+ "row.noRetry": "不可重试",
190
+ "row.copy": "复制命令",
191
+ "origin.agent": "自动",
192
+ "origin.human": "人工",
193
+ "state.verify": "待判定",
194
+ "state.pending": "待批准",
195
+ "state.approved": "已批准",
196
+ "state.fuse": "待合并",
197
+ "state.skipped": "已跳过"
170
198
  };
171
199
  const en = {
172
200
  "entry.label": "Skill merge",
173
201
  "hint": "Manual only; pairs are judged in the background. Read-only here.",
174
- "tab.pending": "Pending",
175
- "tab.retry": "Retry",
202
+ "history": "History",
203
+ "close": "Close",
204
+ "tab.verify": "To judge",
205
+ "tab.pending": "Pending approval",
206
+ "tab.fuse": "Ready to merge",
176
207
  "tab.skipped": "Skipped",
208
+ "empty.verify": "No pairs to judge.",
177
209
  "empty.pending": "No pending merges.",
178
- "empty.retry": "No pairs waiting to retry.",
210
+ "empty.fuse": "No pairs ready to merge.",
179
211
  "empty.skipped": "No pairs marked as not worth merging.",
180
212
  "loading": "Loading…",
181
213
  "refresh": "Refresh",
214
+ "loadMore": "Load more",
215
+ "approval.on": "Approval on: merges wait for a human.",
216
+ "approval.off": "Approval off: merges run automatically.",
182
217
  "failed": "Read failed; keeping the last result.",
183
218
  "stage.decide": "Decide",
184
219
  "stage.author": "Author",
185
220
  "stage.validate": "Validate",
186
- "stage.commit": "Commit"
221
+ "stage.commit": "Commit",
222
+ "row.target": "Target",
223
+ "row.confidence": "Confidence",
224
+ "row.attempts": "Attempts",
225
+ "row.origin": "Origin",
226
+ "row.expires": "Staging expires",
227
+ "row.expired": "Expired",
228
+ "row.reason": "Reason",
229
+ "row.failure": "Failure",
230
+ "row.noRetry": "not retryable",
231
+ "row.copy": "Copy command",
232
+ "origin.agent": "auto",
233
+ "origin.human": "human",
234
+ "state.verify": "Unverified",
235
+ "state.pending": "Pending approval",
236
+ "state.approved": "Approved",
237
+ "state.fuse": "Ready to merge",
238
+ "state.skipped": "Skipped"
187
239
  };
188
240
  //#endregion
189
241
  //#region src/client/styles.ts
190
242
  const CSS_TAG_ID = "evolution-merge-panel";
191
243
  const CSS = [
192
- ".evolution-merge-panel { display:flex; flex-direction:column; gap:var(--evo-space-12); padding:var(--evo-space-16); }",
244
+ ":where(.evolution-merge-panel) { --evo-type-lg:calc(1em + 2px);--evo-type-body:1em;--evo-type-sm:calc(1em - 1px);--evo-type-xs:calc(1em - 2px);--evo-space-2:2px;--evo-space-4:4px;--evo-space-6:6px;--evo-space-8:8px;--evo-space-10:10px;--evo-space-12:12px;--evo-space-14:14px;--evo-space-16:16px;--evo-space-20:20px;--evo-radius-chip:4px;--evo-radius-control:8px;--evo-radius-surface:12px;--evo-radius-pill:999px;--evo-hairline-width:.5px;--evo-hairline-marker:2px;--evo-measure-read:80ch;--evo-leading-base:1.5;--evo-tone-quiet:var(--dsw-alias-label-secondary);--evo-tone-primary:var(--dsw-alias-brand-primary);--evo-tone-danger:var(--dsw-alias-state-error-primary);--evo-tone-success:var(--dsw-alias-state-success-primary);--evo-focus-ring:2px solid var(--dsw-alias-label-primary);--evo-focus-offset:-2px;--evo-cap-aside:280px;--evo-cap-block:260px }",
245
+ ".evolution-merge-panel { display:flex; flex-direction:column; gap:var(--evo-space-12); padding:var(--evo-space-16); color:var(--dsw-alias-label-primary); font-size:var(--evo-type-sm); line-height:var(--evo-leading-base); }",
193
246
  ".evolution-merge-panel [data-role=\"hint\"] { margin:0; color:var(--dsw-alias-label-tertiary); font-size:var(--evo-type-xs); }",
194
247
  ".evolution-merge-panel [data-role=\"stats\"] { display:flex; gap:var(--evo-space-8); flex-wrap:wrap; }",
195
248
  ".evolution-merge-panel [data-role=\"stat\"] { border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2); border-radius:var(--evo-radius-control); padding:var(--evo-space-6) var(--evo-space-10); min-width:calc(var(--evo-space-16) * 4); }",
@@ -258,8 +311,9 @@ window.__ModuleLoader__.load({
258
311
  * intentionally copied commands, not buttons that write from the browser.
259
312
  */
260
313
  const QUEUES = [
314
+ "verify",
261
315
  "pending",
262
- "retry",
316
+ "fuse",
263
317
  "skipped"
264
318
  ];
265
319
  function failure(t, failure) {
@@ -274,15 +328,22 @@ window.__ModuleLoader__.load({
274
328
  const [view, setView] = (0, react.useState)(face.view);
275
329
  const [tab, setTab] = (0, react.useState)("pending");
276
330
  const [rows, setRows] = (0, react.useState)(face.rows);
277
- const [loading, setLoading] = (0, react.useState)(false);
331
+ const [loading, setLoading] = (0, react.useState)(face.loadRows !== void 0 || face.loadView !== void 0);
278
332
  const [failed, setFailed] = (0, react.useState)(void 0);
279
- const loadRows = (queue) => {
333
+ const [nextCursor, setNextCursor] = (0, react.useState)(void 0);
334
+ const [overlay, setOverlay] = (0, react.useState)(void 0);
335
+ const request = (0, react.useRef)(0);
336
+ const loadRows = (queue, cursor = 0) => {
280
337
  const load = face.loadRows;
281
338
  if (load === void 0) return;
339
+ const token = request.current + 1;
340
+ request.current = token;
282
341
  setLoading(true);
283
- load(queue).then((outcome) => {
342
+ load(queue, cursor).then((outcome) => {
343
+ if (token !== request.current) return;
284
344
  if (outcome.ok) {
285
- setRows(outcome.payload.items);
345
+ setRows((current) => [...cursor === 0 ? [] : current, ...outcome.payload.items].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)));
346
+ setNextCursor(outcome.payload.nextCursor);
286
347
  setFailed(void 0);
287
348
  } else setFailed(outcome.failure);
288
349
  }).catch(() => {
@@ -298,15 +359,65 @@ window.__ModuleLoader__.load({
298
359
  const load = face.loadView;
299
360
  if (load === void 0) return;
300
361
  load().then((outcome) => {
301
- if (outcome.ok) setView(outcome.payload);
302
- }).catch(() => {});
362
+ if (!outcome.ok) {
363
+ setFailed(outcome.failure);
364
+ return;
365
+ }
366
+ if (outcome.payload.schemaVersion !== void 0 && outcome.payload.schemaVersion !== 2) {
367
+ setFailed({
368
+ kind: "server",
369
+ status: 0
370
+ });
371
+ return;
372
+ }
373
+ setView(outcome.payload);
374
+ }).catch(() => {
375
+ setFailed({
376
+ kind: "server",
377
+ status: 0
378
+ });
379
+ });
380
+ };
381
+ const openBody = (id, side) => {
382
+ const load = face.loadBody;
383
+ if (load === void 0) return;
384
+ load(id, side).then((outcome) => {
385
+ if (outcome.ok) setOverlay({
386
+ kind: "body",
387
+ html: outcome.payload.html,
388
+ label: id + ":" + side
389
+ });
390
+ else setFailed(outcome.failure);
391
+ }).catch(() => {
392
+ setFailed({
393
+ kind: "server",
394
+ status: 0
395
+ });
396
+ });
397
+ };
398
+ const openHistory = () => {
399
+ const load = face.loadHistory;
400
+ if (load === void 0) return;
401
+ load().then((outcome) => {
402
+ if (outcome.ok) setOverlay({
403
+ kind: "history",
404
+ items: outcome.payload.items
405
+ });
406
+ else setFailed(outcome.failure);
407
+ }).catch(() => {
408
+ setFailed({
409
+ kind: "server",
410
+ status: 0
411
+ });
412
+ });
303
413
  };
304
414
  (0, react.useEffect)(() => {
305
415
  loadView();
306
416
  loadRows("pending");
307
417
  }, [face]);
308
418
  (0, react.useEffect)(() => {
309
- loadRows(tab);
419
+ setNextCursor(void 0);
420
+ loadRows(tab, 0);
310
421
  }, [tab]);
311
422
  const counts = view.counts;
312
423
  const stats = (0, react.createElement)("div", { "data-role": "stats" }, QUEUES.map((queue) => (0, react.createElement)("div", {
@@ -314,25 +425,88 @@ window.__ModuleLoader__.load({
314
425
  "data-role": "stat",
315
426
  "data-queue": queue
316
427
  }, (0, react.createElement)("div", { "data-role": "stat-value" }, String(counts[queue])), (0, react.createElement)("div", { "data-role": "stat-label" }, t("tab." + queue)))));
317
- const tabs = (0, react.createElement)("div", { "data-role": "tabs" }, QUEUES.map((queue) => (0, react.createElement)("button", {
428
+ const latest = view.latestRun;
429
+ const runStrip = latest === void 0 ? null : (0, react.createElement)("div", { "data-role": "run-strip" }, (0, react.createElement)("span", { "data-role": "run-id" }, latest.id), (0, react.createElement)("span", { "data-role": "run-state" }, latest.state), latest.stage === void 0 ? null : (0, react.createElement)("span", { "data-role": "run-stage" }, t("stage." + latest.stage)), latest.progress === void 0 ? null : (0, react.createElement)("span", { "data-role": "run-progress" }, String(latest.progress.done) + "/" + String(latest.progress.total)));
430
+ const tabs = (0, react.createElement)("div", {
431
+ "data-role": "tabs",
432
+ role: "tablist"
433
+ }, QUEUES.map((queue) => (0, react.createElement)("button", {
318
434
  key: queue,
319
435
  type: "button",
436
+ role: "tab",
437
+ "aria-selected": tab === queue ? "true" : "false",
320
438
  "data-role": "tab",
321
439
  "data-active": tab === queue ? "true" : "false",
322
440
  onClick: () => {
323
441
  setTab(queue);
324
442
  }
325
443
  }, t("tab." + queue) + " " + String(counts[queue]))));
326
- const list = rows.length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("empty." + tab)) : (0, react.createElement)("div", { "data-role": "rows" }, rows.map((row) => (0, react.createElement)("div", {
327
- key: row.id,
328
- "data-role": "row"
329
- }, (0, react.createElement)("div", null, (0, react.createElement)("div", { "data-role": "row-label" }, row.label), (0, react.createElement)("div", { "data-role": "row-meta" }, row.state + (row.stage === null ? "" : " · " + t("stage." + row.stage)))), (0, react.createElement)("div", { "data-role": "row-meta" }, row.updatedAt))));
330
- return (0, react.createElement)("section", { className: CSS_TAG_ID }, (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), stats, tabs, loading ? (0, react.createElement)("p", { "data-role": "note" }, t("loading")) : null, failed === void 0 ? null : (0, react.createElement)("div", { "data-role": "note" }, failure(t, failed)), list, (0, react.createElement)("button", {
444
+ const list = rows.length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("empty." + tab)) : (0, react.createElement)("div", { "data-role": "rows" }, rows.map((row) => {
445
+ const facts = [
446
+ row.target === void 0 ? void 0 : t("row.target") + " " + row.target,
447
+ row.confidence === void 0 ? void 0 : t("row.confidence") + " " + row.confidence.toFixed(2),
448
+ row.attempts === void 0 ? void 0 : t("row.attempts") + " " + String(row.attempts),
449
+ row.origin === void 0 ? void 0 : t("row.origin") + " " + t("origin." + row.origin),
450
+ row.stagingExpiresAt === void 0 ? void 0 : t("row.expires") + " " + new Date(row.stagingExpiresAt).toLocaleString(),
451
+ row.expired === void 0 ? void 0 : t("row.expired") + " " + row.expired,
452
+ row.reason === void 0 ? void 0 : t("row.reason") + " " + row.reason,
453
+ row.summary === void 0 ? void 0 : row.summary.filter(Boolean).join(" · "),
454
+ row.failure === void 0 ? void 0 : t("row.failure") + " " + row.failure.message + (row.failure.retryable ? "" : " · " + t("row.noRetry"))
455
+ ].filter((value) => value !== void 0).join(" · ");
456
+ return (0, react.createElement)("div", {
457
+ key: row.id,
458
+ "data-role": "row"
459
+ }, (0, react.createElement)("div", null, (0, react.createElement)("div", { "data-role": "row-label" }, row.label), (0, react.createElement)("div", { "data-role": "row-meta" }, t("state." + row.state) + (row.stage === null ? "" : " · " + t("stage." + row.stage))), facts === "" ? null : (0, react.createElement)("div", { "data-role": "row-facts" }, facts)), (0, react.createElement)("div", null, (0, react.createElement)("div", { "data-role": "row-meta" }, row.updatedAt), (0, react.createElement)("div", { "data-role": "row-actions" }, face.loadBody === void 0 ? null : (0, react.createElement)("button", {
460
+ type: "button",
461
+ "data-role": "body-a",
462
+ onClick: () => {
463
+ openBody(row.id, "a");
464
+ }
465
+ }, "A"), face.loadBody === void 0 ? null : (0, react.createElement)("button", {
466
+ type: "button",
467
+ "data-role": "body-b",
468
+ onClick: () => {
469
+ openBody(row.id, "b");
470
+ }
471
+ }, "B"), (row.actions ?? []).filter((action) => view.approval !== false || !action.includes("/evolution merge approve")).map((action) => (0, react.createElement)("button", {
472
+ key: action,
473
+ type: "button",
474
+ "data-role": "row-action",
475
+ title: action,
476
+ onClick: () => {
477
+ navigator.clipboard.writeText(action);
478
+ }
479
+ }, t("row.copy"))))));
480
+ }));
481
+ return (0, react.createElement)("section", {
482
+ className: CSS_TAG_ID,
483
+ "data-state": loading ? "loading" : failed !== void 0 ? "failed" : rows.length === 0 ? "empty" : "ready"
484
+ }, (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), view.approval === void 0 ? null : (0, react.createElement)("p", { "data-role": "approval" }, t(view.approval ? "approval.on" : "approval.off")), runStrip, stats, tabs, loading ? (0, react.createElement)("p", { "data-role": "note" }, t("loading")) : null, failed === void 0 ? null : (0, react.createElement)("div", { "data-role": "note" }, failure(t, failed)), list, nextCursor === void 0 ? null : (0, react.createElement)("button", {
485
+ type: "button",
486
+ "data-role": "load-more",
487
+ onClick: () => {
488
+ loadRows(tab, Number(nextCursor));
489
+ }
490
+ }, t("loadMore")), face.loadHistory === void 0 ? null : (0, react.createElement)("button", {
491
+ type: "button",
492
+ "data-role": "history",
493
+ onClick: openHistory
494
+ }, t("history")), overlay === void 0 ? null : (0, react.createElement)("section", { "data-role": "overlay" }, (0, react.createElement)("button", {
495
+ type: "button",
496
+ "data-role": "overlay-close",
497
+ onClick: () => {
498
+ setOverlay(void 0);
499
+ }
500
+ }, t("close")), overlay.kind === "body" ? (0, react.createElement)("div", {
501
+ "data-role": "body",
502
+ "data-label": overlay.label,
503
+ dangerouslySetInnerHTML: { __html: overlay.html }
504
+ }) : (0, react.createElement)("ul", { "data-role": "history-list" }, overlay.items.map((item) => (0, react.createElement)("li", { key: item.name }, item.name + " · " + item.kind + " · " + item.reason + " · v" + String(item.lastVersion))))), (0, react.createElement)("button", {
331
505
  type: "button",
332
506
  "data-role": "refresh",
333
507
  onClick: () => {
334
508
  loadView();
335
- loadRows(tab);
509
+ loadRows(tab, 0);
336
510
  }
337
511
  }, t("refresh")));
338
512
  }
@@ -356,17 +530,22 @@ window.__ModuleLoader__.load({
356
530
  function seedView() {
357
531
  return {
358
532
  counts: {
533
+ verify: 0,
359
534
  pending: 0,
360
- retry: 0,
535
+ fuse: 0,
361
536
  skipped: 0
362
537
  },
363
538
  tabs: [
539
+ {
540
+ queue: "verify",
541
+ count: 0
542
+ },
364
543
  {
365
544
  queue: "pending",
366
545
  count: 0
367
546
  },
368
547
  {
369
- queue: "retry",
548
+ queue: "fuse",
370
549
  count: 0
371
550
  },
372
551
  {
@@ -3,12 +3,16 @@
3
3
  * intentionally copied commands, not buttons that write from the browser.
4
4
  */
5
5
  import { type ReactNode } from 'react';
6
- import type { MergeOutcome, MergeQueue, MergeRowView, MergeViewPayload, QueuePage } from './api.ts';
6
+ import type { MergeBody, MergeHistoryItem, MergeOutcome, MergeQueue, MergeRowView, MergeViewPayload, QueuePage } from './api.ts';
7
7
  export interface MergeFace {
8
8
  view: MergeViewPayload;
9
9
  rows: MergeRowView[];
10
10
  loadView?: () => Promise<MergeOutcome<MergeViewPayload>>;
11
11
  loadRows?: (queue: MergeQueue, cursor?: number) => Promise<MergeOutcome<QueuePage>>;
12
+ loadBody?: (id: string, side: 'a' | 'b') => Promise<MergeOutcome<MergeBody>>;
13
+ loadHistory?: () => Promise<MergeOutcome<{
14
+ items: MergeHistoryItem[];
15
+ }>>;
12
16
  t: (key: string) => string;
13
17
  }
14
18
  export declare function MergePanel(face: MergeFace): ReactNode;
@@ -2,22 +2,37 @@ import { type RouteFailure } from './route-client.generated.ts';
2
2
  export declare const HOST_ROUTES: {
3
3
  readonly view: "/api/dsh-evolution/merge/view";
4
4
  readonly rows: "/api/dsh-evolution/merge/rows";
5
- readonly run: "/api/dsh-evolution/merge/run";
6
- readonly runs: "/api/dsh-evolution/merge/runs";
5
+ readonly body: "/api/dsh-evolution/merge/body";
6
+ readonly history: "/api/dsh-evolution/merge/history";
7
7
  };
8
- export type MergeQueue = 'pending' | 'retry' | 'skipped';
8
+ export type MergeQueue = 'verify' | 'pending' | 'fuse' | 'skipped';
9
9
  export interface MergeCounts {
10
+ verify: number;
10
11
  pending: number;
11
- retry: number;
12
+ fuse: number;
12
13
  skipped: number;
13
14
  }
15
+ export interface MergeRunView {
16
+ id: string;
17
+ state: string;
18
+ command?: string;
19
+ stage?: string;
20
+ progress?: {
21
+ done: number;
22
+ total: number;
23
+ };
24
+ startedAt: number;
25
+ }
14
26
  export interface MergeViewPayload {
27
+ schemaVersion?: number;
15
28
  counts: MergeCounts;
16
29
  tabs: {
17
30
  queue: MergeQueue;
18
31
  count: number;
19
32
  }[];
20
33
  commands: string[];
34
+ latestRun?: MergeRunView;
35
+ approval?: boolean;
21
36
  }
22
37
  export interface MergeRowView {
23
38
  id: string;
@@ -29,12 +44,41 @@ export interface MergeRowView {
29
44
  stage: string | null;
30
45
  label: string;
31
46
  updatedAt: string;
47
+ createdAt?: string;
48
+ target?: string;
49
+ confidence?: number;
50
+ failure?: {
51
+ stage: string;
52
+ code: string;
53
+ message: string;
54
+ retryable: boolean;
55
+ };
56
+ attempts?: number;
57
+ origin?: 'agent' | 'human';
58
+ stagingExpiresAt?: number;
59
+ expired?: 'target-taken' | 'source-changed';
60
+ reason?: string;
61
+ summary?: string[];
62
+ actions?: string[];
32
63
  }
33
64
  export interface QueuePage {
34
65
  queue: MergeQueue;
35
66
  items: MergeRowView[];
36
67
  nextCursor?: string;
37
68
  }
69
+ export interface MergeBody {
70
+ content: string;
71
+ markdown: boolean;
72
+ html: string;
73
+ }
74
+ export interface MergeHistoryItem {
75
+ name: string;
76
+ kind: 'merged' | 'deleted' | 'split' | 'other';
77
+ reason: string;
78
+ lastVersion: number;
79
+ archivedAt: string;
80
+ sourceRunId?: string;
81
+ }
38
82
  export type MergeOutcome<T> = {
39
83
  ok: true;
40
84
  payload: T;
@@ -44,4 +88,8 @@ export type MergeOutcome<T> = {
44
88
  };
45
89
  export declare function view(): Promise<MergeOutcome<MergeViewPayload>>;
46
90
  export declare function rows(queue: MergeQueue, cursor?: number): Promise<MergeOutcome<QueuePage>>;
91
+ export declare function body(id: string, side: 'a' | 'b'): Promise<MergeOutcome<MergeBody>>;
92
+ export declare function history(): Promise<MergeOutcome<{
93
+ items: MergeHistoryItem[];
94
+ }>>;
47
95
  //# sourceMappingURL=api.d.ts.map
@@ -27,8 +27,8 @@ export declare const ROUTES: {
27
27
  readonly merge: {
28
28
  readonly view: "/api/dsh-evolution/merge/view";
29
29
  readonly rows: "/api/dsh-evolution/merge/rows";
30
- readonly run: "/api/dsh-evolution/merge/run";
31
- readonly runs: "/api/dsh-evolution/merge/runs";
30
+ readonly body: "/api/dsh-evolution/merge/body";
31
+ readonly history: "/api/dsh-evolution/merge/history";
32
32
  };
33
33
  readonly diagnostics: {
34
34
  readonly view: "/api/dsh-evolution/diagnostics";
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  ]
12
12
  }
13
13
  },
14
- "version": "0.28.0",
14
+ "version": "0.30.0",
15
15
  "publishConfig": {
16
16
  "access": "public"
17
17
  },