@staix/agent-hub 0.29.0 → 0.31.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/CHANGELOG.md +51 -0
- package/README.md +15 -7
- package/docs/agent-notes/README.md +13 -0
- package/docs/agent-notes/adapters.md +7 -3
- package/docs/agent-notes/budget.md +1 -0
- package/docs/agent-notes/bus.md +10 -4
- package/docs/agent-notes/daemon.md +16 -8
- package/docs/agent-notes/local-worker.md +1 -0
- package/docs/agent-notes/tasks.md +10 -4
- package/docs/agent-notes/tests.md +8 -4
- package/docs/design/adapters.md +108 -0
- package/docs/design/agent-hub-architecture.md +58 -0
- package/docs/design/bus.md +147 -1
- package/docs/design/task-board.md +100 -0
- package/docs/dogfood.md +29 -8
- package/docs/events.md +3 -0
- package/docs/memory.md +17 -1
- package/docs/operations.md +410 -86
- package/docs/profiles.md +35 -0
- package/docs/quickstart.md +25 -5
- package/docs/sdlc.md +1 -1
- package/docs/security.md +182 -62
- package/docs/smoke.md +13 -2
- package/docs/specs/2026-09-19-agent-hub-design.md +84 -14
- package/docs/specs/2026-09-19-multi-project-design.md +7 -2
- package/docs/specs/2026-10-12-office-view-design.md +86 -3
- package/docs/verification/2026-11-03-0.30.0.md +54 -0
- package/docs/verification/2026-11-03-agy-1.4.12-launcher-nonce.md +42 -0
- package/docs/verification/2026-11-03-agy-1.4.12.md +90 -0
- package/docs/verification/2026-11-03-agy-tui-close.md +49 -0
- package/docs/verification/2026-11-03-grok-1.0.62-t0.md +150 -0
- package/docs/verification/2026-11-03-pi-150-compat.md +40 -0
- package/docs/verification/2026-11-03-pi-title-api.md +26 -0
- package/docs/verification/2026-11-03-qwen-0265-acp.md +55 -0
- package/docs/verification/2026-11-03-qwen-0265-dual-output.md +40 -0
- package/docs/verification/2026-11-04-1171-channel-client-evidence.md +31 -0
- package/docs/verification/2026-11-05-0.31.0.md +182 -0
- package/docs/verification/2026-11-05-claude-implementer-2.1.318.md +70 -0
- package/docs/verification/2026-11-05-hardcoded-values-classification-rules.md +257 -0
- package/docs/verification/2026-11-05-hardcoded-values-ratchet.md +55 -0
- package/docs/verification/2026-11-06-rooms-presence-cost.md +152 -0
- package/docs/verification/claude-shell-wrapper-native.py +1 -1
- package/docs/verification/dogfood/2026-11-03.md +3095 -0
- package/docs/verification/pi-150-model-free.ts +57 -0
- package/docs/verification/pi-150-native.mjs +21 -0
- package/docs/verification/pi-150-title-api.mjs +31 -0
- package/docs/verified.json +14 -14
- package/package.json +3 -4
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +4565 -3617
- package/src/adapters/acp.ts +5 -4
- package/src/adapters/channel-peer.ts +72 -3
- package/src/adapters/claude-channel-evidence.ts +52 -0
- package/src/adapters/claude-channel.ts +61 -18
- package/src/adapters/claude-implementer.ts +29 -7
- package/src/adapters/claude-print-child.ts +12 -2
- package/src/adapters/codex-appserver.ts +13 -12
- package/src/adapters/compat.ts +13 -13
- package/src/adapters/definitions/builtin-metadata.ts +13 -13
- package/src/adapters/definitions/qwen.ts +1 -0
- package/src/adapters/local-worker.ts +82 -23
- package/src/adapters/pi.ts +140 -16
- package/src/adapters/registry-types.ts +16 -0
- package/src/adapters/registry.ts +4 -0
- package/src/adapters/reviewer-print.ts +50 -11
- package/src/broker/server.ts +44 -13
- package/src/broker/store.ts +38 -8
- package/src/cli/answer-args.ts +50 -0
- package/src/cli/attention.ts +17 -2
- package/src/cli/autonomy.ts +66 -1
- package/src/cli/backlog-return-args.ts +37 -0
- package/src/cli/console-question.ts +110 -0
- package/src/cli/console-state.ts +82 -17
- package/src/cli/console.ts +54 -1
- package/src/cli/handoff-args.ts +15 -5
- package/src/cli/handoff-output.ts +15 -2
- package/src/cli/help.ts +10 -5
- package/src/cli/identity.ts +21 -4
- package/src/cli/main.ts +167 -16
- package/src/cli/memory.ts +107 -3
- package/src/cli/output.ts +29 -6
- package/src/cli/pet.ts +4 -4
- package/src/cli/results-args.ts +30 -0
- package/src/cli/runtime-version-warning.ts +50 -0
- package/src/cli/status-lines.ts +41 -1
- package/src/cli/tail-render.ts +1 -0
- package/src/cli/upgrade-interactive.ts +14 -2
- package/src/cli/worktree-tidy.ts +158 -0
- package/src/cli/worktrees-output.ts +43 -0
- package/src/hub/action-class.ts +9 -1
- package/src/hub/board.ts +396 -20
- package/src/hub/bus.ts +497 -57
- package/src/hub/completion-detail.ts +50 -0
- package/src/hub/completions-store.ts +168 -0
- package/src/hub/completions.ts +120 -0
- package/src/hub/conductor.ts +4 -3
- package/src/hub/config-apply.ts +8 -5
- package/src/hub/config-reload.ts +10 -1
- package/src/hub/control-client.ts +40 -4
- package/src/hub/daemon.ts +1795 -142
- package/src/hub/delivery-failure.ts +14 -0
- package/src/hub/delivery-hints.ts +14 -4
- package/src/hub/delivery-journal.ts +751 -42
- package/src/hub/delivery-superseded.ts +4 -1
- package/src/hub/dogfood-read.ts +62 -9
- package/src/hub/envelope.ts +3 -3
- package/src/hub/events.ts +6 -0
- package/src/hub/handoff.ts +145 -6
- package/src/hub/hub-tools.ts +66 -11
- package/src/hub/manager.ts +67 -3
- package/src/hub/memory-policy.ts +44 -1
- package/src/hub/memory-sync-policy.ts +109 -0
- package/src/hub/memory-sync-state.ts +145 -0
- package/src/hub/no-writes.ts +1 -1
- package/src/hub/operational-settings.ts +11 -2
- package/src/hub/operational-view.ts +12 -1
- package/src/hub/peer-doing.ts +33 -3
- package/src/hub/peers.ts +32 -2
- package/src/hub/planning.ts +6 -1
- package/src/hub/question-desk.ts +201 -0
- package/src/hub/question-detach.ts +6 -0
- package/src/hub/questions.ts +260 -0
- package/src/hub/research-evidence.ts +106 -0
- package/src/hub/restart.ts +14 -0
- package/src/hub/review-anchor.ts +181 -0
- package/src/hub/review-brief.ts +13 -0
- package/src/hub/review-comments.ts +115 -0
- package/src/hub/review-findings.ts +7 -2
- package/src/hub/review-material.ts +76 -0
- package/src/hub/routing.ts +3 -1
- package/src/hub/settings.ts +3 -0
- package/src/hub/task-sweep.ts +2 -0
- package/src/hub/tasks.ts +607 -71
- package/src/hub/ui.ts +2 -0
- package/src/hub/worktree-prs.ts +100 -0
- package/src/hub/worktree-shelf.ts +246 -0
- package/src/memory/capture.ts +5 -2
- package/src/pet/config.ts +25 -13
- package/src/pi/extension.ts +43 -3
- package/src/rooms/link.ts +209 -21
- package/src/rooms/remote-peer.ts +186 -19
- package/src/rooms/service.ts +338 -22
- package/src/rooms/store.ts +437 -65
- package/src/rooms/wire.ts +24 -6
- package/src/ui/index.html +271 -17
- package/src/ui/needs.ts +10 -4
- package/src/ui/office-model.ts +64 -8
- package/src/ui/office-space.ts +1 -0
- package/src/ui/plugin-questions.ts +56 -0
- package/src/ui/plugin-results.ts +81 -0
- package/src/ui/plugin.ts +219 -28
- package/src/pet/window.html +0 -114
- package/src/pet/window.jxa.js +0 -122
- package/src/pet/window.ts +0 -154
package/src/rooms/wire.ts
CHANGED
|
@@ -23,7 +23,7 @@ const FP = /^[0-9a-f]{64}$/;
|
|
|
23
23
|
export interface RoomMember extends PublicHubIdentity { label: string; exposedPeers: string[] }
|
|
24
24
|
export interface FrameWire {
|
|
25
25
|
v: 1; type: "frame"; room: string; epoch: string; sender: string;
|
|
26
|
-
recipients: string[]; seq: string; nonce: string; ciphertext: string; signature: string;
|
|
26
|
+
recipients: string[]; seq: string; ephemeral?: true; nonce: string; ciphertext: string; signature: string;
|
|
27
27
|
}
|
|
28
28
|
export interface RosterWire {
|
|
29
29
|
v: 1; type: "roster"; room: string; epoch: string; owner: string;
|
|
@@ -107,8 +107,10 @@ function wrap(x: unknown): WrappedEpochKey {
|
|
|
107
107
|
return { v: 1, owner: fingerprint(o.owner), recipient: fingerprint(o.recipient), ephemeralKey: b64(o.ephemeralKey, 32), salt: b64(o.salt, 32), nonce: b64(o.nonce, 12), ciphertext: ciphertext(o.ciphertext, 48, 48), signature: b64(o.signature, 64) };
|
|
108
108
|
}
|
|
109
109
|
function parseFrame(x: unknown): FrameWire {
|
|
110
|
-
const
|
|
111
|
-
|
|
110
|
+
const ephemeral = typeof x === "object" && x !== null && Object.hasOwn(x, "ephemeral");
|
|
111
|
+
const o = object(x, ["v", "type", "room", "epoch", "sender", "recipients", "seq", "nonce", "ciphertext", "signature", ...(ephemeral ? ["ephemeral"] : [])]); base(o, "frame");
|
|
112
|
+
if (ephemeral && o.ephemeral !== true) fail();
|
|
113
|
+
return { v: 1, type: "frame", room: b64(o.room, 32), epoch: positiveU64(o.epoch), sender: fingerprint(o.sender), recipients: sorted(array(o.recipients, 64, fingerprint, true), x => x), seq: positiveU64(o.seq), ...(ephemeral ? { ephemeral: true as const } : {}), nonce: b64(o.nonce, 12), ciphertext: ciphertext(o.ciphertext, 65552), signature: b64(o.signature, 64) };
|
|
112
114
|
}
|
|
113
115
|
function parseRoster(x: unknown): RosterWire {
|
|
114
116
|
const o = object(x, ["v", "type", "room", "epoch", "owner", "members", "wraps", "privateNonce", "privateCiphertext", "signature"]); base(o, "roster");
|
|
@@ -228,6 +230,7 @@ export interface VerifiedPublicFrame {
|
|
|
228
230
|
readonly digest: string;
|
|
229
231
|
readonly rosterDigest: string;
|
|
230
232
|
readonly recipients: readonly string[];
|
|
233
|
+
readonly ephemeral?: true;
|
|
231
234
|
}
|
|
232
235
|
interface PublicFrameState { wire: Uint8Array }
|
|
233
236
|
const publicFrames = new WeakMap<VerifiedPublicFrame, PublicFrameState>();
|
|
@@ -252,7 +255,7 @@ export async function verifyPublicFrame(input: { wire: Uint8Array; roster: Verif
|
|
|
252
255
|
await verifyFrameSignature(frame, sender);
|
|
253
256
|
const frameDigest = await digest(canonicalBytes(frame));
|
|
254
257
|
const proof = Object.freeze({ room: frame.room, epoch: frame.epoch, sender: frame.sender, seq: frame.seq,
|
|
255
|
-
digest: frameDigest, rosterDigest: roster.digest, recipients: Object.freeze([...frame.recipients]) });
|
|
258
|
+
digest: frameDigest, rosterDigest: roster.digest, ...(frame.ephemeral ? { ephemeral: true as const } : {}), recipients: Object.freeze([...frame.recipients]) });
|
|
256
259
|
publicFrames.set(proof, { wire });
|
|
257
260
|
return proof;
|
|
258
261
|
}
|
|
@@ -330,9 +333,10 @@ function frameAad(frame: Pick<FrameWire, "room" | "epoch" | "sender" | "seq">):
|
|
|
330
333
|
return enc.encode(`${frame.room}|${frame.epoch}|${frame.sender}|${frame.seq}`);
|
|
331
334
|
}
|
|
332
335
|
export async function sealFrame(input: {
|
|
333
|
-
epoch: VerifiedEpoch; identity: HubIdentity; recipients: string[]; plaintext: Uint8Array; sequence: SequencePort;
|
|
336
|
+
epoch: VerifiedEpoch; identity: HubIdentity; recipients: string[]; plaintext: Uint8Array; sequence: SequencePort; ephemeral?: true;
|
|
334
337
|
}): Promise<Uint8Array> {
|
|
335
338
|
await assertRoomCryptoReady();
|
|
339
|
+
if (input.ephemeral !== undefined && input.ephemeral !== true) fail();
|
|
336
340
|
const epoch = input.epoch, identity = input.identity, sequence = input.sequence;
|
|
337
341
|
const current = state(epoch), sender = identity.public.fingerprint;
|
|
338
342
|
const member = current.roster.members.find(m => m.fingerprint === sender);
|
|
@@ -344,7 +348,7 @@ export async function sealFrame(input: {
|
|
|
344
348
|
if (!sequence || typeof sequence.reserve !== "function" || typeof sequence.isCurrent !== "function") fail("state_unavailable");
|
|
345
349
|
const binding = { room: epoch.room, sender, epoch: epoch.epoch, rosterDigest: epoch.digest };
|
|
346
350
|
const seq = positiveU64(await portCall(() => sequence.reserve({ ...binding })));
|
|
347
|
-
const header = { v: 1 as const, type: "frame" as const, room: epoch.room, epoch: epoch.epoch, sender, recipients, seq };
|
|
351
|
+
const header = { v: 1 as const, type: "frame" as const, room: epoch.room, epoch: epoch.epoch, sender, recipients, seq, ...(input.ephemeral ? { ephemeral: true as const } : {}) };
|
|
348
352
|
const nonce = randomBytes(12);
|
|
349
353
|
const encrypted = await encryptEpoch({ key: current.key, purpose: "frame", context: frameContext(epoch, sender), aad: frameAad(header), nonce, plaintext });
|
|
350
354
|
const body = { ...header, nonce: encodeBase64url(nonce), ciphertext: encodeBase64url(encrypted) };
|
|
@@ -354,6 +358,20 @@ export async function sealFrame(input: {
|
|
|
354
358
|
if (fresh !== true) fail("state_changed");
|
|
355
359
|
return wire;
|
|
356
360
|
}
|
|
361
|
+
/** Read a retained local send for resealing, never receiver admission or replay evidence. */
|
|
362
|
+
export async function openOwnFrame(input: { wire: Uint8Array; identity: HubIdentity; epoch: VerifiedEpoch }): Promise<Uint8Array> {
|
|
363
|
+
await assertRoomCryptoReady();
|
|
364
|
+
const frame = decodeFrame(input.wire), current = state(input.epoch), sender = input.identity.public;
|
|
365
|
+
const member = current.roster.members.find(value => value.fingerprint === sender.fingerprint);
|
|
366
|
+
if (frame.ephemeral || frame.room !== input.epoch.room || frame.epoch !== input.epoch.epoch || frame.sender !== sender.fingerprint
|
|
367
|
+
|| !member || member.signingKey !== sender.signingKey || member.agreementKey !== sender.agreementKey
|
|
368
|
+
|| frame.recipients.some(recipient => !current.roster.members.some(value => value.fingerprint === recipient))) fail("untrusted");
|
|
369
|
+
await verifyFrameSignature(frame, member);
|
|
370
|
+
const plaintext = await decryptEpoch({ key: current.key, purpose: "frame", context: frameContext(input.epoch, frame.sender), aad: frameAad(frame),
|
|
371
|
+
nonce: decodeBase64url(frame.nonce, 12), ciphertext: decodeBase64url(frame.ciphertext) });
|
|
372
|
+
if (plaintext.length > ROOM_WIRE_LIMITS.plaintext) fail("limit");
|
|
373
|
+
return plaintext;
|
|
374
|
+
}
|
|
357
375
|
export type FrameOpenResult = { status: "accepted"; plaintext: Uint8Array; gap: string } | { status: "duplicate" | "stale" };
|
|
358
376
|
export async function openFrame(input: {
|
|
359
377
|
wire: Uint8Array; identity: HubIdentity; current: VerifiedEpoch;
|