@staix/agent-hub 0.16.0 → 0.18.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.
Files changed (96) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +2 -2
  3. package/docs/agent-notes/README.md +5 -0
  4. package/docs/agent-notes/adapters.md +20 -5
  5. package/docs/agent-notes/benchmarks.md +1 -0
  6. package/docs/agent-notes/daemon.md +5 -3
  7. package/docs/agent-notes/local-worker.md +1 -0
  8. package/docs/agent-notes/tasks.md +9 -3
  9. package/docs/agent-notes/tests.md +6 -3
  10. package/docs/dogfood.md +21 -4
  11. package/docs/memory.md +1 -1
  12. package/docs/operations.md +150 -34
  13. package/docs/profiles.md +36 -0
  14. package/docs/quickstart.md +14 -4
  15. package/docs/roles.md +1 -0
  16. package/docs/sdlc.md +39 -13
  17. package/docs/security.md +41 -9
  18. package/docs/smoke.md +51 -3
  19. package/docs/specs/2026-09-19-agent-hub-design.md +56 -10
  20. package/docs/specs/2026-09-20-upgrade-recovery-design.md +3 -1
  21. package/docs/verification/2026-10-25-0.17.0.md +74 -0
  22. package/docs/verification/2026-10-25-0.18.0.md +76 -0
  23. package/docs/verification/2026-10-25-codex-0164-reviewer-tool-call.md +52 -0
  24. package/docs/verification/2026-10-25-codex-review-live-verdict.md +35 -0
  25. package/docs/verification/2026-10-25-kimi-231-reviewer-compat.md +48 -0
  26. package/docs/verification/2026-10-25-kimi-usage-ac11.md +48 -0
  27. package/docs/verification/2026-10-25-qwen-dual-output-hub-e2e.md +23 -0
  28. package/docs/verification/2026-10-25-qwen-dual-output-t0.md +20 -0
  29. package/docs/verification/2026-10-25-remote-docker-transport.md +46 -0
  30. package/docs/verification/2026-10-25-reviewer-no-verdict-diagnostics.md +35 -0
  31. package/docs/verification/profiles/YYYY-MM-DD-general.md +46 -0
  32. package/docs/verification/profiles/YYYY-MM-DD-research.md +61 -0
  33. package/docs/verification/profiles/YYYY-MM-DD-writing.md +46 -0
  34. package/docs/verified.json +28 -19
  35. package/package.json +2 -2
  36. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  37. package/plugins/agent-hub/server.js +1115 -192
  38. package/src/adapters/codex-review-isolation.ts +10 -1
  39. package/src/adapters/compat.ts +7 -2
  40. package/src/adapters/conformance/enrollment.ts +18 -4
  41. package/src/adapters/conformance/factories.ts +4 -1
  42. package/src/adapters/conformance/kit.ts +11 -0
  43. package/src/adapters/definitions/builtin-metadata.ts +1 -1
  44. package/src/adapters/definitions/native-cli-entry-qwen.ts +32 -4
  45. package/src/adapters/definitions/native-cli-preview-qwen.ts +9 -1
  46. package/src/adapters/definitions/native-factories.ts +8 -0
  47. package/src/adapters/definitions/native-recovery.ts +8 -0
  48. package/src/adapters/definitions/qwen.ts +7 -4
  49. package/src/adapters/local-worker.ts +5 -3
  50. package/src/adapters/qwen-dual.ts +578 -0
  51. package/src/adapters/registry-types.ts +5 -1
  52. package/src/adapters/registry.ts +9 -1
  53. package/src/adapters/reviewer-mcp.ts +12 -6
  54. package/src/adapters/reviewer-print.ts +100 -17
  55. package/src/cli/console-state.ts +9 -2
  56. package/src/cli/help.ts +29 -28
  57. package/src/cli/identity.ts +87 -6
  58. package/src/cli/main.ts +190 -69
  59. package/src/cli/output.ts +7 -2
  60. package/src/cli/sdlc-output.ts +242 -0
  61. package/src/cli/task-output.ts +212 -0
  62. package/src/cli/upgrade-output.ts +57 -0
  63. package/src/cli/upgrade.ts +4 -3
  64. package/src/hub/board.ts +17 -0
  65. package/src/hub/check-runner.ts +252 -22
  66. package/src/hub/conductor.ts +5 -0
  67. package/src/hub/config-apply.ts +16 -7
  68. package/src/hub/config-edit-host.ts +1 -1
  69. package/src/hub/config-tier.ts +1 -1
  70. package/src/hub/daemon.ts +487 -94
  71. package/src/hub/dogfood-profile.ts +248 -0
  72. package/src/hub/dogfood-read.ts +77 -14
  73. package/src/hub/dogfood.ts +5 -1
  74. package/src/hub/events.ts +1 -1
  75. package/src/hub/host-executable.ts +2 -1
  76. package/src/hub/hub-tools.ts +2 -2
  77. package/src/hub/memory-plan-guard.ts +8 -33
  78. package/src/hub/operational-authority.ts +60 -0
  79. package/src/hub/operational-view.ts +141 -0
  80. package/src/hub/orphan-process.ts +69 -0
  81. package/src/hub/permission-mode.ts +1 -1
  82. package/src/hub/read-only-shell-proof.ts +80 -0
  83. package/src/hub/read-only-shell.ts +162 -0
  84. package/src/hub/reviewer-pool-config.ts +3 -2
  85. package/src/hub/reviewer-pool.ts +3 -0
  86. package/src/hub/routing.ts +23 -6
  87. package/src/hub/sdlc-merge.ts +299 -0
  88. package/src/hub/sdlc-mirror.ts +129 -0
  89. package/src/hub/sdlc.ts +58 -12
  90. package/src/hub/settings.ts +11 -5
  91. package/src/hub/snapshots.ts +60 -0
  92. package/src/hub/task-repository.ts +31 -5
  93. package/src/hub/tasks.ts +266 -28
  94. package/src/local/tools.ts +17 -4
  95. package/src/memory/brief.ts +45 -5
  96. package/src/ui/index.html +2 -1
@@ -1,3 +1,4 @@
1
+ import { buildIntentBrief, type IntentBriefOptions, type IntentBriefSource } from "@staix/agent-mem";
1
2
  import type { PeerId } from "../hub/envelope.ts";
2
3
  import type { MemoryClient } from "./client.ts";
3
4
 
@@ -30,17 +31,20 @@ export async function related(client: MemoryClient, project: string, query: stri
30
31
  /** A native memory line for a brief (#258): `mem:<id8> <kind> <tier>: <title or text>`, fails first. */
31
32
  export interface NativeItem { id: string; line: string; kind?: string; about?: readonly string[]; tier?: string; peer?: string; machine?: string; at?: string }
32
33
  export type NativeBrief = (task: BriefTask) => NativeItem[] | Promise<NativeItem[]>;
34
+ export interface IntentBriefContext { source: IntentBriefSource; options: IntentBriefOptions }
35
+ /** Host-owned policy and store binding; the library owns relevance, ordering and text. */
36
+ export type IntentBriefHost = (task: BriefTask) => IntentBriefContext | Promise<IntentBriefContext>;
33
37
  export interface BriefTask { id?: number; created?: number; title: string; refs: { paths?: string[] }; plan?: { paths?: string[]; symbols?: string[] } }
34
38
 
35
39
  /**
36
- * Task briefs: what memory already knows about a piece of work, handed over with it. The hub's own store first (#258:
37
- * entries one or two hops from the task's paths, symbols, issue and task, a linked fail first, superseded entries
38
- * dropped), then amem: index first (search), then the neighbourhood of the top hit (timeline); details stay in
39
- * the store for the receiver to fetch by id. `brief_items` caps both together. A peer is never shown the same entry
40
- * twice in one hub run, except a directly matching plan fail that must accompany a new task.
40
+ * Production task briefs use the library's intent retrieval and rendering with host policy and bindings.
41
+ * The hub applies its per-peer delivery dedupe and item cap to complete library rows, reserving the front
42
+ * for host-screened plan fails even when the library's byte cap omitted them. Injected clients without
43
+ * the library port retain the legacy index/timeline bridge; intent mode retires its related/readNotice path.
41
44
  */
42
45
  export class Briefs {
43
46
  private readonly seen = new Map<PeerId, Set<string>>();
47
+ private readonly libraryRuns = new Map<PeerId, Promise<unknown>>();
44
48
 
45
49
  constructor(
46
50
  private readonly client: MemoryClient | undefined,
@@ -50,11 +54,47 @@ export class Briefs {
50
54
  /** Counts of what a brief carried, for the research records (#258 AC11); never its text. */
51
55
  private readonly observe?: (peer: PeerId, task: number, counts: { native: number; fails: number; claudeMem: number }) => void,
52
56
  private readonly displayNative?: (items: readonly NativeItem[]) => string[],
57
+ private readonly intent?: IntentBriefHost,
53
58
  ) {}
54
59
 
55
60
  async forTask(peer: PeerId, task: BriefTask, priority: readonly NativeItem[] = []): Promise<string | undefined> {
56
61
  // A brief snapshots its configured cap once, before either optional source is selected.
57
62
  const maximum = typeof this.maxItems === "function" ? this.maxItems() : this.maxItems;
63
+ if (this.intent) {
64
+ // Serialize only this peer's library reads, so awaited policy cannot duplicate a delivered note.
65
+ const previous = this.libraryRuns.get(peer);
66
+ const run = (async () => {
67
+ await previous?.catch(() => undefined);
68
+ if (maximum < 1) return undefined;
69
+ try {
70
+ const context = await this.intent!(task);
71
+ const seen = this.seen.get(peer) ?? new Set<string>();
72
+ this.seen.set(peer, seen);
73
+ const revision = context.options.policyVersion();
74
+ const result = await buildIntentBrief(context.source, context.options);
75
+ if (result.state === "unknown" || context.options.allowed?.() === false || context.options.policyVersion() !== revision) return undefined;
76
+ context.options.assertCurrent?.();
77
+ const lines = result.text.split("\n");
78
+ const rendered = new Map(result.items.map((item, index) => [`mem:${item.id}`, lines[index + 1]!]));
79
+ const selected: NativeItem[] = [];
80
+ // Priority is already screened and sanitized by the host's plan guard. Its advice must survive
81
+ // the standalone library's byte cap or narrower retrieval policy, and recur with the next task.
82
+ for (const item of priority) if (selected.length < maximum && !selected.some(row => row.id === item.id)) selected.push({ ...item, line: rendered.get(item.id) ?? item.line });
83
+ for (const item of result.items) {
84
+ const id = `mem:${item.id}`;
85
+ if (selected.length < maximum && !seen.has(id) && !selected.some(row => row.id === id)) selected.push({ id, line: rendered.get(id)!, kind: item.kind });
86
+ }
87
+ if (!selected.length) return undefined;
88
+ // Preserve complete library rows (including graph neighbours after a seen direct bridge).
89
+ const text = [lines[0] || "Live fail notes for this plan (advice, not a block):", ...selected.map(item => item.line)].join("\n");
90
+ for (const item of selected) seen.add(item.id);
91
+ if (task.id !== undefined) try { this.observe?.(peer, task.id, { native: selected.length, fails: selected.filter(item => item.kind === "fail").length, claudeMem: 0 }); } catch { /* telemetry never fails a brief */ }
92
+ return text;
93
+ } catch { return undefined; }
94
+ })();
95
+ this.libraryRuns.set(peer, run);
96
+ try { return await run; } finally { if (this.libraryRuns.get(peer) === run) this.libraryRuns.delete(peer); }
97
+ }
58
98
  const seen = this.seen.get(peer) ?? new Set<string>();
59
99
  this.seen.set(peer, seen);
60
100
  let native: NativeItem[] = [];
package/src/ui/index.html CHANGED
@@ -1399,7 +1399,8 @@ function renderBoard(snapshot, peers, ids) {
1399
1399
  const data=(snapshot.boardLegend || []).find(item=>item.peer===id), row=el('div',undefined,'agent-slot');
1400
1400
  row.append(peerMark(id),el('p',String(peers[id].state || 'unknown'),'meta'));
1401
1401
  const wip=data && Number.isSafeInteger(data.wip) && data.wip>=0 ? data.wip : 'unknown';
1402
- row.append(el('p','WIP '+wip+(Number.isSafeInteger(data?.wipLimit) ? '/'+data.wipLimit : '')+' / permission '+(data?.permissionMode || 'unknown'),'meta'));
1402
+ const split=data?.wipBreakdown,splitText=split && [split.active,split.reviewWait,split.reserved].every(Number.isSafeInteger) ? ' (active '+split.active+', review-wait '+split.reviewWait+', reserved '+split.reserved+')' : '';
1403
+ row.append(el('p','WIP '+wip+(Number.isSafeInteger(data?.wipLimit) ? '/'+data.wipLimit : '')+splitText+' / permission '+(data?.permissionMode || 'unknown'),'meta'));
1403
1404
  if (Array.isArray(data?.activeTasks) && data.activeTasks.length) row.append(el('p','Active tasks: '+data.activeTasks.map(id=>'#'+id).join(', '),'meta'));
1404
1405
  const filter=el('button','View '+id);filter.type='button';filter.addEventListener('click',()=>{if(!boardViewState)return;peerFilter.value=id;renderBoard(boardViewState.snapshot,boardViewState.peers,boardViewState.ids)});row.append(filter);
1405
1406
  if (eligible.includes(id)) dropTarget(row,card=>card.assignable,{action:'assign',peer:id});